ListSelectionListener/ItemListener wird zweimal aufgerufen

Status
Nicht offen für weitere Antworten.
B

Beni

Gast
Das Problem des ListSelectionListeners

Fügt man einen ListSelectionListener einer JList hinzu, wird der Listener manchmal doppelt aufgerufen.

Das kleine Beispielprogramm zeigt das Problem: einfach ein paar mal etwas anderes auswählen, und auf die Ausgabe achten.

Code:
import java.text.DateFormat;
import java.util.Date;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class Selection implements ListSelectionListener{
    private JTextArea area;
    private int count = 0;
    
    public Selection( JTextArea area ){
        this.area = area;
    }
    
    public void valueChanged( ListSelectionEvent e ) {
        // bei jedem Aufruf die aktuelle Zeit und ein Zähler ausgeben.
        count++;
        Date date = new Date();
        DateFormat format = DateFormat.getTimeInstance();
        area.append( count + " at time " + format.format( date ) + "\n" );
    }
    
    public static void main( String[] args ) {
        // Eine Textarea, in die bei jedem Aufruf des Listeners etwas
        // geschrieben wird.
        JTextArea area = new JTextArea();
        
        // Die Liste erzeugen, und den Listener registrieren
        JList list = new JList( new Object[]{ "a", "b", "c", "d", "e" } );
        list.addListSelectionListener( new Selection( area ) );
        
        // Ein JFrame erstellen um die Liste anzuzeigen
        JFrame.setDefaultLookAndFeelDecorated( true );
        JFrame frame = new JFrame( "ListSelectionListener" );
        JSplitPane split = new JSplitPane();
        frame.getContentPane().add( split );
        split.setLeftComponent( new JScrollPane( list ));
        split.setRightComponent( new JScrollPane( area ));
        frame.setSize( 400, 300 );
        split.setResizeWeight( 0.25 );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
}

Die Lösung
Es ist kein Fehler der Liste, dass der Listener mehr als einmal aufgerufen wird, schliesslich wird zuerst etwas deselektiert, und dann etwas neues selektiert.
Was gerade geschieht, kann man über die Methode getValueIsAdjusting des ListSelectionEvents herausfinden.
Bei einer Folge von Events, ist der Wert nur beim Letzen false.

Dies ist derselbe Code wie oben, nur wurde eine zusätzliche Ausgabe eingefügt, ob adjusting true oder false ist:
Code:
import java.text.DateFormat;
import java.util.Date;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

public class Selection implements ListSelectionListener{
    private JTextArea area;
    private int count = 0;
    
    public Selection( JTextArea area ){
        this.area = area;
    }
    
    public void valueChanged( ListSelectionEvent e ) {
        // bei jedem Aufruf die aktuelle Zeit und ein Zähler ausgeben.
        count++;
        Date date = new Date();
        DateFormat format = DateFormat.getTimeInstance();
        area.append( count + 
                " at time " + format.format( date ) + 
                " adjustring=" + e.getValueIsAdjusting() + "\n" );
    }
    
    public static void main( String[] args ) {
        // Eine Textarea, in die bei jedem Aufruf des Listeners etwas
        // geschrieben wird.
        JTextArea area = new JTextArea();
        
        // Die Liste erzeugen, und den Listener registrieren
        JList list = new JList( new Object[]{ "a", "b", "c", "d", "e" } );
        list.addListSelectionListener( new Selection( area ) );
        
        // Ein JFrame erstellen um die Liste anzuzeigen
        JFrame.setDefaultLookAndFeelDecorated( true );
        JFrame frame = new JFrame( "ListSelectionListener" );
        JSplitPane split = new JSplitPane();
        frame.getContentPane().add( split );
        split.setLeftComponent( new JScrollPane( list ));
        split.setRightComponent( new JScrollPane( area ));
        frame.setSize( 400, 300 );
        split.setResizeWeight( 0.25 );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
}

Dementsprechend sollte man nur auf die Events reagieren, wenn der adjusting-Wert falsch ist.

Der ItemListener und die JComboBox
Ein ganz ähnliches Problem hat man, wenn man einer JComboBox einen ItemListener hinzufügt. Der ItemListener wird beim deselektieren des alten, und beim selektieren des neuen Wertes der JComboBox aufgerufen.
Was gerade passiert, kann man über die Methode getStateChange des ItemEvents erfahren. Der Rückgabewert ist entweder ItemEvent.SELECTED oder ItemEvent.DESELECTED. Nur wenn der Wert SELECTED ist, sollte man weiterarbeiten.
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Oben