10.14Kontrollfelder, Optionsfelder, Kontrollfeldgruppen
Ein Kontrollfeld ist eine Komponente mit einem Zustand: »ein« oder »aus«. Der Zustand wird meistens als Rechteck oder Kreis neben einer Zeichenkette dargestellt. Kontrollfelder dienen dem Benutzer meistens als Auswahl von Optionen. Bei einer Pizza-Bestellung kann etwa ein Optionsfeld die Beläge anbieten. Hier wähle ich dann immer Pilze, Paprika und Zwiebeln.
In Swing sind zwei Klassen als besondere Schaltflächen vorgesehen, sodass der Benutzer aus einer Reihe von Optionen wählen kann: JCheckBox und JRadioButton. Beide sind Unterklassen von AbstractButton und haben daher die schon erwähnten Möglichkeiten für HTML-Text, unterschiedliche Grafiken, Abstand usw.
Abbildung 10.45Vererbungsbeziehung der Schaltflächenklassen
10.14.1Kontrollfelder (JCheckBox)
Die JCheckBox ist als Schaltfläche so flexibel wie ein JButton und lässt sich verschiedene Grafiken für den eingeschalteten und ausgeschalteten Zustand zuweisen. Dazu dienen die Methoden setIcon(Icon) und setSelectedIcon(Icon). Diese Methoden kommen alle aus der Oberklasse AbstractButton. Im Konstruktor lässt sich als zweites Argument ein Wahrheitswert angeben, der bestimmt, ob das Feld am Anfang gesetzt ist oder nicht.
Ändert sich der Zustand eines Feldes (Selektion oder Deselektion), wird ein ItemEvent an alle registrierten ItemListener weitergeleitet. Nach dem Anlegen des Objekts kann die Methode setState(boolean) den Status verändern. Das Argument true markiert die Option des Kontrollfeldes. getState() liefert den aktuellen Status des Kontrollfeldes.
Abbildung 10.46Kontrollkästchen für unsere Helden
Listing 10.37com/tutego/insel/ui/swing/JCheckBoxDemo.java, main()
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
Icon unchecked = new ImageIcon(
JCheckBoxDemo.class.getResource( "/images/cancel.png" ) );
Icon checked = new ImageIcon(
JCheckBoxDemo.class.getResource( "/images/ok.png" ) );
JCheckBox cb1 = new JCheckBox( "Ein Colt für alle Fälle", true );
cb1.setIcon( unchecked );
cb1.setSelectedIcon( checked );
f.add( cb1, BorderLayout.PAGE_START );
JCheckBox cb2 = new JCheckBox( "MacGyver", false );
cb2.setIcon( unchecked );
cb2.setSelectedIcon( checked );
f.add( cb2, BorderLayout.PAGE_END );
ItemListener herosListener = new ItemListener() {
@Override public void itemStateChanged( ItemEvent e ) {
System.out.print( ((JCheckBox) e.getItem()).getText() );
System.out.println( e.getStateChange() == ItemEvent.SELECTED ?
" selected" : " unselected" );
}
};
cb1.addItemListener( herosListener );
cb2.addItemListener( herosListener );
f.pack();
f.setVisible( true );
10.14.2ItemSelectable, ItemListener und das ItemEvent
In Swing gibt es eine Schnittstelle ItemSelectable, die alle Swing-Klassen implementieren, bei denen Einträge selektiert werden können:
void addItemListener(ItemListener l)
void removeItemListener(ItemListener l)
Object[] getSelectedObjects()
Folgende GUI-Komponenten implementieren diese Schnittstelle:
alle von AbstractButton abgeleiteten Schaltflächen, insbesondere JCheckBox, JRadioButton, JCheckBoxMenuItem, JRadioButtonMenuItem
JComboBox
ItemListener
Die Schnittstelle ItemListener wird von allen Objekten implementiert, die an einem Auswahlereignis interessiert sind. Wird ein Element ausgewählt, wird die Methode itemStateChanged( ItemEvent) aufgerufen.
Abbildung 10.47Klassendiagramme von ItemListener und ItemEvent
extends EventListener
void itemStateChanged(ItemEvent e)
Wird aufgerufen, wenn ein Eintrag selektiert oder deselektiert wird.
ItemEvent an ItemListener
Dem Listener wird in itemStateChanged(ItemEvent) ein Objekt übergeben, das Zugriff auf die Komponente oder den Zustand liefert.
extends AWTEvent
ItemSelectable getItemSelectable()
Gibt ein ItemSelectable-Objekt von der Komponente zurück, die das Ereignis ausgelöst hat.Object getItem()
Gibt das vom Ereigniserzeuger initialisierte Item zurück. Der Typ kann je nach Komponente unterschiedlich sein. (Die API-Dokumentation ist mit der Erläuterung »The item whose selection state has changed« relativ unspezifisch. Bei einer JCheckBox ist es jedenfalls nicht der String.)int getStateChange()
Gibt den Status des Eintrags zurück. Die Klasse deklariert dazu die Konstanten ItemEvent.SELECTED und ItemEvent.DESELECTED.
Innerhalb der Methode itemStateChanged(ItemEvent) bezieht wie üblich getSource() den Auslöser des Ereignisses. Es gibt aber noch einen zweiten Weg über die Methode getItemSelectable(). Das hat den Vorteil, dass die Rückgabe ein ItemSelectable ist, wobei eine Typanpassung entfallen kann, wenn etwa über die ItemSelectable-Methode getSelectedObjects() die selektierten Objekte erfragt werden sollen.
Ob nun eine Selektion oder Deselektion stattfand bzw. wie der aktuelle Zustand ist, lässt sich wiederum über unterschiedliche Wege ermitteln. Die Objektmethoden getStateChange() von ItemEvent erfragen den Wechsel (Selektion oder Deselektion). Der Rückgabewert ist eine Ganzzahl, und wir sollten ihn mit den Konstanten ItemEvent.SELECTED und ItemEvent.DESELECTED vergleichen. Der andere Weg geht über die Komponente selbst: Im Fall einer JCheckBox – und jeder allgemeinen Schaltflächen-Art, die Unterklasse von AbstractButton ist – ermittelt isSelected() den aktuellen Zustand.
10.14.3Sich gegenseitig ausschließende Optionen (JRadioButton)
Wir müssen unterscheiden, ob sich Kontrollkästchen gegenseitig ausschließen oder nicht. Falls sie sich ausschließen, kann nur ein Kontrollfeld markiert sein. Bei sich nicht ausschließenden Feldern gibt es keine Beschränkung. Sind die Kontrollkästchen in einer Gruppe (Kontrollfeldgruppe) organisiert, werden sie auch Optionsfelder genannt. Der Name sagt es bereits: Eine Option kann gesetzt werden oder nicht – bei einem Druckdialog könnte etwa zwischen einem Ausdruck in Farbe oder in Schwarzweiß gewählt werden.
Sich gegenseitig ausschließende Eingaben können die Swing-Komponenten JRadioButton und JComboBox realisieren. In diesem Abschnitt wollen wir uns mit JRadioButton beschäftigen.
Die ButtonGroup
Um die sich gegenseitig ausschließenden Auswahlknöpfe von den standardmäßig rechteckig gezeichneten JCheckBox-Objekten unterscheiden zu können, werden diese üblicherweise rund gezeichnet. Ohne Vorbereitung schließt ein JRadioButton den anderen allerdings nicht aus; hierfür wird ein spezielles ButtonGroup-Objekt verwendet, das die Selektionen überwacht. Werden die JRadioButton-Objekte erzeugt, lassen sie sich später einer ButtonGroup hinzufügen, sodass nur jeweils ein Element ausgewählt sein kann. Wir selbst müssen nicht durch Listener oder Ähnliches diesen Vorgang verfolgen. Die ButtonGroup deselektiert also automatisch ein Feld, wenn ein anderes angewählt wird.
Erzeugen wir zwei JRadioButton-Objekte, und fügen wir sie in eine ButtonGroup ein. setSelected(boolean) setzt den Radioauswahlknopf dort, wo er ausgewählt sein soll.
Abbildung 10.48Beispiel für ein Optionsfeld
Listing 10.38com/tutego/insel/ui/swing/JRadioButtonDemo.java, main()
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JRadioButton rb1 = new JRadioButton( "schwarz " );
f.add( rb1, BorderLayout.PAGE_START );
JRadioButton rb2 = new JRadioButton( "weiß" );
f.add( rb2, BorderLayout.PAGE_END );
rb1.setSelected( true );
// Setze die Radio-Buttons auf die ButtonGroup
ButtonGroup g = new ButtonGroup();
g.add( rb1 );
g.add( rb2 );
f.pack();
f.setVisible( true );
Das Hinzufügen ist nicht vergleichbar mit Thread-Gruppen, wo ein Thread beim Erzeugen schon gleich in eine Thread-Gruppe hineinpositioniert werden muss und die Gruppe auch später nie mehr ändern kann.
Verschiedene Komponenten über einen ItemListener
Wird ein ItemListener auf unterschiedliche Komponenten gleichzeitig angewendet, so ist es klug, mittels getSource() den Typ der Komponente zu erfragen und dann mit dem instanceof-Operator die Programmlogik weiter zu untergliedern:
Object comp = e.getSource();
if ( comp instanceof JCheckBox )
…
else if ( comp instanceof JRadioButton )
…
else if ( comp instanceof JComboBox )
…
}
Besonders empfehlenswert ist diese Variante aber nicht, und sie löst auch nicht das Problem, wenn zum Beispiel mehrere Exemplare vom Typ einer JCheckBox den gleichen Event-Handler bekommen.