Kapitel 15 Komponenten, Container und Ereignisse
Wenn die Reklame keinen Erfolg hat,
muss man die Ware ändern.
– Faure
15.1 Es tut sich was – Ereignisse beim AWT
 
Beim Arbeiten mit grafischen Oberflächen interagiert der Benutzer mit Komponenten. Er bewegt die Maus im Fenster, drückt eine Schaltfläche oder verschiebt einen Rollbalken. Das grafische System beobachtet die Aktionen der Benutzer und informiert die Applikation über die anfallenden Ereignisse. Dann kann das laufende Programm entsprechend reagieren.
15.1.1 Die Klasse AWTEvent
 
Die ausgesandten Botschaften werden in Ereignis-Klassen kategorisiert. Da es unterschiedliche Ereignisse (engl. events) gibt, kann das System somit die Ereignisse unterteilen und eine Vorauswahl treffen.
Alle Ereignisse der grafischen Oberfläche sind Objekte, die aus einer Unterklasse von AWTEvent gebildet sind. Die Klasse AWTEvent ist abstrakt und selbst von EventObject aus dem util-Paket abgeleitet. Obwohl sich alle Oberflächen-Ereignis-Klassen in dem Unterpaket java.awt.event befinden, ist AWTEvent selbst direkt unter java.awt und damit nicht im Ereignis-Paket.
AWTEvent unter Unterklassen (wie WindowEvent, KeyEvent)
Eine wichtige Methode ist getID(). Jede Ereignis-Klasse definiert eine ID, durch die sich die Ereignisse neben ihrer Klassenzugehörigkeit unterscheiden. Für Ereignisse von gedrückten Schaltflächen ist die ID etwa ActionEvent.ACTION_PERFORMED.
Natürlich stellt sich die Frage, wieso eine ID für die Ereignisse notwendig sein soll, weil die Vererbungsbeziehung doch den Typ klärt. Das ist zwar korrekt, doch gäbe es für mehr als dreißig Events zu viele Klassen. Daher haben die Entwickler ähnliche Ereignisse zu Gruppen zusammengefasst. So etwa bei einem WindowEvent, welches dann versandt wird, wenn etwa das Fenster geschlossen oder verkleinert wird. In diesem Fall gibt es ein Ereignis vom Typ WindowEvent, aber zwei unterschiedliche IDs. So wird eine unübersehbare Anzahl von Event-Klassen vermieden. Einige Klassen verwalten weitere Konstanten, etwa für die gedrückten Tasten. Es wäre kaum sinnvoll, für jede Taste eine eigene Klasse zu schreiben. Diese wird als eigenes Attribut im KeyEvent gespeichert.
 Hier klicken, um das Bild zu Vergrößern
15.1.2 Events auf verschiedenen Ebenen
 
Bei den Ereignissen werden zwei Typen unterschieden: die Ereignisse auf niedriger und die auf hoher Ebene.
|
Ereignisse auf niedriger Ebene (engl. low level events). Damit ist gemeint, dass es Ereignisse gibt, die mit einem speziellen GUI-Element verbunden sind. Das sind etwa Mausbewegung oder Fokus auf Komponenten, Tastaturklicks oder das Schließen oder Vergrößern eines Fensters. |
|
Ereignisse auf höherer Ebene, semantische Ereignisse. Auf der anderen Seite gibt es Ereignisse, die von GUI-Komponenten erzeugt werden, wenn etwa eine Schaltfläche gedrückt oder ein Rollbalken bewegt wird. Der Unterschied zu den Low-level-Events besteht darin, dass mit unterschiedlichen Komponenten durchaus die gleichen semantischen Ereignisse verbunden sein können. So werden etwa bei der Aktivierung einer Liste oder einer Auswahlbox die gleichen Ereignisse versandt. |
Die Trennung fällt aber nicht weiter auf, sodass wir im Folgenden darauf auch nicht eingehen werden.
Da alle grafischen Komponenten von der Klasse Component abgeleitet sind, liefern sie automatisch eine Reihe von nicht semantischen Ereignissen. Wir finden die Unterklassen und die Ereignistypen in der folgenden Tabelle.
Tabelle 15.1
Ereignisklassen und ihre IDs
Klasse
|
ID
|
ComponentEvent
|
COMPONENT_MOVED, COMPONENT_RESIZED, COMPONENT_SHOWN, COMPONENT_HIDDEN
|
FocusEvent
|
FOCUS_GAINED, FOCUS_LOST
|
KeyEvent
|
KEY_PRESSED, KEY_RELEASED, KEY_TYPED
|
MouseEvent
|
MOUSE_CLICKED, MOUSE_DRAGGED, MOUSE_ENTERED, MOUSE_EXITED, MOUSE_MOVED, MOUSE_PRESSED, MOUSE_RELEASED
|
HierarchyEvent
|
ANCESTOR_MOVED, ANCESTOR_RESIZED, DISPLAYABILITY_CHANGED, HIERARCHY_CHANGED, PARENT_CHANGED SHOWING_CHANGED
|
InputMethodEvent
|
CARET_POSITION_CHANGED, INPUT_METHOD_TEXT_CHANGED
|
Weitere niedrige Ereignisse lösen Fenster und Dialoge aus; sie senden Objekte vom Typ WindowEvent. Wir werden uns in diesem Kapitel ebenfalls mit den unterschiedlichen Komponenten beschäftigen und auch immer gleich die zugehörigen Ereignisse untersuchen. Die folgende Tabelle zeigt für einige grafische Komponenten die Ereignisse und gibt an, wann sie ausgelöst werden können.
Tabelle 15.2
Einige Ereignisauslöser
Ereignistyp
|
Auslöser z. B.
|
Wann das Event ausgelöst wird
|
Action
|
JButton
|
Aktivierung der Schaltfläche
|
Adjustment
|
JScrollBar
|
Wertänderung
|
CaretEvent
|
JTextComponent
|
Verschiebung des Cursors
|
Change
|
JSlider
|
Änderung der Werte
|
Component
|
Component
|
Änderung der Sichtbarkeit oder Größe
|
Container
|
Container
|
Änderung des Inhalts
|
Focus
|
JComponent
|
neuer Fokus (bei Tastatureingaben)
|
HyperlinkEvent
|
JEditorPane
|
Hyperlink-Auswahl
|
Item
|
JList
|
Auswahl
|
Key
|
JComponent
|
Tastatur
|
Menu
|
JMenu
|
Menüauswahl
|
Mouse
|
JComponent
|
Betreten oder Verlassen einer Komponente
|
MouseMotion
|
JComponent
|
Bewegung
|
Window
|
JWindow
|
Zustandsänderung
|
Eyelid
|
Eye
|
Augenzwinkern
|
15.1.3 Ereignisquellen und Horcher (Listener)
 
Im Ereignismodell 1.1 gibt es eine Reihe von Ereignisauslösern (Ereignisquellen, engl. event source), wie zum Beispiel Schaltflächen. Die Ereignisse können von der grafischen Oberfläche kommen, etwa wenn der Benutzer eine Schaltfläche drückt, aber auch auf eigene Auslöser zurückzuführen sein. Es gibt eine Reihe von Interessenten, die gerne informiert werden wollen, wenn ein Ereignis aufgetreten ist. Da der Interessent in der Regel nicht an allen ausgelösten Oberflächen-Ereignissen interessiert ist, sagt er einfach, welche Ereignisse er empfangen möchte. Dies funktioniert so, dass er sich bei einer Ereignisquelle anmeldet, und diese informiert ihn, wenn sie ein Ereignis aussendet.1
Auf diese Weise leidet die Systemeffizienz nicht, da nur diejenigen informiert werden, die auch Verwendung für das Ereignis haben.
Da der Interessent an der Quelle horcht, heißt er auch Listener oder Horcher. Für jedes Ereignis gibt es einen eigenen Listener, an den das Ereignis weitergeleitet wird. Daher stammt auch der Name für das Modell: Delegation Model. (Die Entwickler hatten vorher den Namen »Command Model« vergeben, doch dies drückte die Arbeitsweise nicht richtig aus.)
15.1.4 Listener implementieren
 
Der Listener selbst ist eine Schnittstelle, die von den Interessenten implementiert wird. Da jede Schnittstelle Methoden vorschreibt, muss der Interessent diese Methoden implementieren. Wird im nächsten Schritt ein Horcher mit dem Ereignisauslöser verbunden, kann die Ereignisquelle davon ausgehen, dass der Horcher die entsprechende Methode besitzt. Diese ruft die Quelle auf.
Beispiel Definition der Schnittstelle ActionListener zum Beispiel für gedrückte Schaltflächen:
public interface ActionListener extends EventListener
{
void actionPerformed( ActionEvent e );
}
Alle Ereignisschnittstellen erweitern die Markierungsschnittstelle java.util.EventListener ab.
|
 Hier klicken, um das Bild zu Vergrößern
An diesem Beispiel ist abzulesen, dass jeder, der ein ActionListener sein möchte, die Methode actionPerformed() implementieren muss. Damit zeigt er Interesse an dem ActionEvent.
Beispiel Eine Klasse ActionListenerImpl implementiert für den ActionListener die Methode actionPerformed().
class ActionListenerImpl implements ActionListener
{
public void actionPerformed( ActionEvent e )
{
System.out.println( "Ich wurde berührt" );
}
}
|
Die implementierende Klasse wird anschließend der auslösenden Komponente, also zum Beispiel einer Schaltfläche, hinzugefügt. Tritt ein Ereignis auf, ruft die Ereignisquelle auf allen registrierten Listenern die actionPerformed()-Methode auf.
15.1.5 Listener bei Ereignisauslöser anmelden/abmelden
 
Hat der Listener die Schnittstelle implementiert, wird er mit dem Ereignisauslöser verbunden. Dafür gibt es eine Reihe von Zufüge- und Entfernen-Methoden, die einer Namenskonvention folgen.
addEreignisListener( EreignisListener )
removeEreignisListener( EreignisListener )
Dies bedeutet, dass etwa ein Listener für Schaltflächen, ein ActionListener, der ActionEvent-Ereignisse auslöst, mit der Methode addActionListener() an die Komponenten gebunden wird. Wenn die Klasse ActionListenerImpl die Schnittstelle ActionListener implementiert und die Komponente eine Schaltfläche mit der Referenz button ist, dann ergibt sich Folgendes für die Ereignisbehandlung:
ActionListener listener = new ActionListenerImpl();
button.addActionListener( listener );
Die letzte Zeile trägt den Listener in einer internen Liste beim Button, einer Schaltfläche, ein.
Natürlich kann nicht jede Komponente jedes Ereignis auslösen. Daher gibt es nur Zufügemethoden für Ereignisse, die die Komponenten tatsächlich auslösen. Ein Fenster wird zum Beispiel kein ActionEvent auslösen, daher fehlt ihm eine Methode addActionListener().
Dafür kann es Fenster-Ereignisse auslösen und besitzt daher eine Methode addWindowListener(). Eine Schaltfläche kann jedoch keine Fenster-Ereignisse auslösen, und daher gibt es die Methode addWindowListener() bei Schaltflächen nicht. Über diese addXXXListener() können wir auch gut die auslösenden Ereignisse ablesen, denn das XXX wird dann nach der Namenskonvention das Ereignis sein.
15.1.6 Aufrufen der Listener
 
Nachdem der Listener implementiert und angemeldet wurde, ist das System im Fall eines aufkommenden Ereignisses bereit, es zu verteilen. Aktiviert zum Beispiel der Benutzer eine Schaltfläche, so führt der AWT-Event-Thread den Programmcode im Listener selbstständig aus. Sehr wichtig ist Folgendes: Der Programmcode im Listener sollte nicht zu lange dauern, da sonst der AWT-Thread nicht mehr die anderen Ereignisse, die sich in einer Queue sammeln, verarbeiten kann. Dadurch entsteht leicht ein »stehendes System«.
Die Reihenfolge, in der die Listener abgearbeitet werden, ist im Prinzip undefiniert.
1 Das ist so, als ob ich einer Frau, die ich gerade kennen gelernt habe, meine Telefonnummer hinterlasse. Anstatt sie ewig anzurufen, warte ich. Wenn sie Interesse hat, wird sie sich melden.
|