Nach dem Verständnis der statischen Komponenten einer Benutzeroberfläche sollte der Leser beliebig komplexe GUIs erstellen können. Diese sind jedoch solange unvollständig, wie Interaktionsmöglichkeiten mit der Maus oder mit der Tastatur nicht integriert werden können. Hierzu zählen bspw. die Betätigung von Buttons, Texteingaben oder auch sonstige Verwendungen von Maus oder Tastatur. Aktionen, die ausgelöst werden, wenn statische Elemente eines GUIs manipuliert werden, werden in diesem Zusammenhang auch als Events bezeichnet. In Java werden grundsätzlich die folgenden Events verarbeitet: Betätigung der Maustasten (sog. Mausklicks), Mausbewegungen, Betätigungen der Tastatur und Betätigungen von GUI-Elementen wie Slider oder Pop-Up-Menüs. An dieser Stelle muss nachdrücklich darauf hingewiesen werden, dass sich das Event-Konzept von Java speziell zwischen der Version 1.02 und der Version 1.1 grundlegend verändert wird. Thema dieses Kapitels sind jedoch nur die neueren Verarbeitungsmöglichkeiten von Events. Event-Modell Grundsätzlich werden dynamische GUI-Elemente in Java durch Interfaces realisiert. Der Leser sollte den Grund hierfür bereits kennen: Soll bspw. eine Mausbewegung in einem GUI-Element erfasst werden, so muss dieselbe Funktionalität für derart unterschiedliche Komponenten wie Buttons, Slider, Textbereiche oder Canvas-Elemente angeboten werden. Die Definition einer abstrakten Funktionalität für verschiedene Anwendungsbereiche ist aber genau Aufgabe eines Interface. Im Event-Modell, das im AWT von Java verwendet wird, werden Events stets von sog. Event-Quellen generiert. Sogenannte Listener können sich dann für eine Quelle registrieren lassen und werden benachrichtigt, wenn ein Event eingetreten ist, für den sie registriert sind. Innerhalb dieses Event-Modells können Events einerseits abgearbeitet, andererseits aber auch erzeugt werden. Aufgrund der Realisierung in Form eines Interface können Event-Handler, also die Funktionskomponenten, die Events bearbeiten, Instanz jeglicher Klasse sein. Eine Voraussetzung hierfür ist allerdings wie bei jedem anderen Interface auch, dass eine Klasse das Interface eines Event-Listeners implementiert. Jedes Programm, das über einen Event-Handler verfügt, beinhaltet daher die folgenden drei Code-Bestandteile: In der Klassendefinition des Event-Handlers muss angegeben werden, dass der Code ein Interface eines Listeners implementiert, oder dass dieser eine Klasse erweitert, die ein Interface eines Listeners erweitert. Hierbei ist bspw. die folgende Syntax vorzufinden, wenn ein Listener für eine Aktion innerhalb einer Klasse beispielKlasse implementiert wird: public class beispielklasse implements ActionListener {....} Im zweiten Schritt muss Code implementiert werden, der eine Instanz einer Event-Handler-Klasse für eine oder mehrere Komponenten registriert. Registrierung bedeutet hierbei, dass der Event-Handler dann aktiv wird, wenn Aktionen dieser Komponenten eintreten. Zur Registrierung wird die Methode addActionListener(Klasse) verwendet, die Teil der Klasse Component ist. Das folgende Beispiel registriert einen Listener, der in der Instanz b der Klasse beispielKlasse implementiert ist, für die Komponente beispielKomponente: beispielKomponente.addActionListener(b); Im dritten und letzten Schritt müssen die Methoden des Listener-Interfaces implementiert werden. Ein Beispiel hierfür ist: public void actionPerformed(ActionEvent e) { // Code, der als Reaktion auf den Event ausgeführt wird } Das folgende Beispiel demonstriert die Verwendung des Event-Konzepts. Hierzu wird ein Applet verwendet, das einen Quit-Button enthält. Wird dieser betätigt, so wird die Ausgabe „Ende" erzeugt. Die Klasse QuitButton implementiert hierbei das Interface ActionListener, das die Methode actionPerformed enthält. Aufgrund dieser Implementierung kann ein QuitButton-Objekt als ein Listener-Objekt fungieren (Registrierung), das Events auffängt, die der Button erzeugt. Nachdem die Registrierung stattgefunden hat, wird die Methode actionPerformed jedes Mal aufgerufen, wenn der Button betätigt wird. import java.applet.*; import java.awt.*; import java.awt.event.*; public class Knoepfe extends Applet implements ActionListener { Button ende = new Button("Quit"); public void init () { add(ende); ende.addActionListener(this); } public void actionPerformed(ActionEvent e) { System.out.println("Ende"); } } Das im obigen Beispiel dargestellte Event-Modell ist umfassend und flexibel. Eine beliebige Anzahl von Event-Listener-Objekten kann auf beliebige Arten von Events warten, die von einer beliebigen Anzahl von Quellobjekten erzeugt werden. Ein Programm könnte bspw. einen Listener pro Event-Quelle generieren, aber auch lediglich einen Listener für alle Events aller Quellen verwenden. Darüber hinaus kann ein Programm aber auch mehr als einen Listener für einen einzigen Event, der von einer einzigen Quelle generiert wird, verwenden. Im Folgenden werden zunächst Adapter und anschließend die Verarbeitung der verschiedenen Event-Arten einzeln betrachtet. Adapter und innere Klassen Das oben dargestellte Beispiel verwendete mit ActionListener ein Interface, das nur eine einzige Methode enthält. Dies ist allerdings nicht der Regelfall. Das Interface MouseListener beinhaltet bspw. die folgenden fünf Methoden, die die mit der Maus erzeugbaren Events reflektieren: mousePressed (Maustaste gedrückt), mouseReleased (Maustaste losgelassen), mouseEntered (Maus über einer GUI-Komponente), mouseExited (Maus nicht mehr über einer GUI-Komponente) und mouseClicked (Maustaste betätigt). Diese Methoden müssen alle implementiert werden, wenn das Interface eingesetzt werden soll, selbst dann, wenn nur eine Teilmenge der Funktionalität erforderlich ist. Die dann implementierten Methoden haben meist einen leeren Rumpf, der bspw. wie folgt aussieht: public void mousePressed(MouseEvent e) {} Es ist offensichtlich, dass so große Mengen an Code generiert werden, die das Lesen des gesamten Programms und damit auch dessen Wartung erheblich erschweren. Um dieses Problem zu lösen, stehen im Java-AWT sog. Adapterklassen für jedes Listener-Interface, das mehr als eine Methode enthält, zur Verfügung. Eine Adapterklasse implementiert jeweils alle Versionen des dazugehörigen Interfaces mit einem leeren Methodenrumpf. Die Klasse MouseAdapter implementiert bspw. das Interface MouseListener. Um eine Adapterklasse verwenden zu können, muss eine Subklasse des Adapters erzeugt werden, anstatt ein Interface eines Listeners direkt zu implementieren. Indem bspw. eine Subklasse der Klasse MouseAdapter angelegt wird, erbt diese die leeren Definitionen aller Methoden, die das Interface MouseListener enthält. Das folgende Beispiel demonstriert die Verwendung von Adapterklassen: public class beispielKlasse extends MouseAdapter { //.... Button ende = new Button("Quit"); add(ende); ende.addMouseListener(this); //.... public void mouseClicked(MouseEvent e) { //Implementierung des Event-Handlers } } Problematisch ist allerdings, dass Java keine Mehrfachvererbung zulässt. Soll nun ein Applet geschrieben werden, das eine Subklasse enthält, die Maus-Events verarbeitet, so müsste das Applet theoretisch eine Subklasse der Klassen Applet und MouseAdapter sein, also mehrfach erben. Um dieses Problem zu lösen, müssen innere Klassen innerhalb der Applet-Subklasse definiert werden, die von der Klasse MouseAdapter erben. Ein Beispiel hierfür ist im Folgenden angegeben. public class beispielKlasse extends Applet { //.... Button ende = new Button("Quit"); add(ende); ende.addMouseListener(new BeispielAdapter); //.... class BeispielAdapter extends MouseAdapter { public void mouseClicked(MouseEvent e) { //Implementierung des Event-Handlers } } } Innere Klassen sollten immer dann verwendet werden, wenn ein Event-Handler als private gekennzeichnete Instanzvariablen von einer umgebenden Klasse aus benutzen soll. Solange eine innere Klasse nicht als static bezeichnet wird, kann sie Instanzvariablen und Methoden referenzieren, wie wenn der Code in der Klasse selbst wäre. Um innerhalb einer inneren Klasse eine lokale Variable verwenden zu können, sollte eine Kopie der Variablen als lokale Variable, die als final gekennzeichnet wird, erstellt werden. Event Listener Nachdem die Funktion der Event-Verarbeitung in Java allgemein erläutert wurde, werden die Event-Arten im Folgenden einzeln betrachtet. Eine Übersicht hierzu ist in Tab. 4-4 angegeben.
Listener-Interface | Funktion | Adapterklasse | Methoden
ActionListener | Erfassung allgemeiner Aktionen | keine | actionPerformed
AdjustmentLis- tener | Erfassung von Größenänderungen von Komponenten | keine | adjustmentValue -Changed
ComponentLis-t ener | Erfassung von Komponentenakti onen | ComponentAd apter | componentHidden componentMoved componentResize d componentShown
ContainerLis-t ener | Erfassung von Containeraktionen | ContainerAd apter | componentAdded componentRemove d
FocusListener | Erfassung von Fokusänderungen | FocusAdapte r | focusGained focusLost
ItemListener | Erfassung von Zustandsänderung en | keine | itemStateChange d
KeyListener | Erfassung von Tastaturaktionen | KeyAdapter | keyPressed keyReleased keyTyped
MouseListener | Erfassung von Mausaktionen | MouseAdapte r | mouseClicked mouseEntered mouseExited mousePressed mouseReleased
MouseMotionLis tener | Erfassung von Mausbewegungen | MouseMotion -Adapter | mouseDragged mouseMoved
TextListener | Erfassung von Textänderungen | keine | textValueChange d
WindowListener | Erfassung von Fensteraktionen | WindowAdapt er | windowActivated windowClosed windowClosing windowDeactivat ed windowDeiconifi ed windowIconified windowOpened
Tab. 4.4: Event-Verarbeitung AWT-Events können nach ihrer Komplexität in Events einer niedrigen Komplexität und in semantische Events unterschieden werden. Events einer niedrigen Komplexität sind bspw. Fensteroperationen oder Eingaben mit der Maus oder mit der Tastatur. Auch Komponenten-, Container-, Fokus- und Fenster-Events zählen zu dieser ersten Klasse von Events. Komponenten-Events ermöglichen es dem Anwender, Änderungen der Position, der Größe oder der Sichtbarkeit von GUI-Elementen zu überwachen. Container-Events werden dann verwendet, wenn eine Komponente einem Container hinzugefügt oder von diesem entfernt wird. Fokus-Events stellen fest, ob eine Komponente im Tastaturfokus ist, ob sie also Zeichen, die auf der Tastatur getippt werden, empfangen kann, oder ob die Komponente den Fokus verloren hat. Fenster-Events informieren den Benutzer über den Zustand von Fenstern, bspw. von Dialogfenstern. Maus-Events lassen sich ebenfalls in zwei Gruppen einteilen: Bewegungs-Events der Maus und alle weiteren Events, die eine Maus generieren kann. Semantische Events beinhalten Aktions-, Anpassungs- und Text-Events sowie solche Events, die von bestimmten Elementen, wie bspw. Einträgen eines Menüs, generiert werden. Diese Events haben die Eigenschaft gemeinsam, ein Resultat einer komponentenspezifischen Benutzerinteraktion zu sein. Ein Button erzeugt bspw. einen Action-Event, wenn er einmal betätigt wird, während dies bei einer Liste erst nach einem Doppelklick erfolgt. Text-Events treten immer dann auf, wenn sich der Text eines Textfeldes oder eines Textbereichs verändert.
AWT | action | adjust ment | compo nent | contai ner | focus | item | key | mouse | mouse motion | text | windo w
Button | x |  | x |  | x |  | x | x | x |  |
Canva s |  |  | x |  | x |  | x | x | x |  |
Check box |  |  | x |  | x | x | x | x | x |  |
Check box-M enuite m |  |  |  |  |  | x |  |  |  |  |
Choice |  |  | x |  | x |  | x | x | x |  |
Comp onent |  |  | x |  | x |  | x | x | x |  |
Contai ner |  |  | x |  | x | x | x | x | x |  |
Dialog |  |  | x | x | x |  | x | x | x |  | x
Frame |  |  | x | x | x |  | x | x | x |  | x
Label |  |  | x |  | x |  | x | x | x |  |
List | x |  | x |  | x | x | x | x | x |  |
MenuI tem | x |  |  |  |  |  |  |  |  |  |
Panel |  |  | x | x | x |  | x | x | x |  |
Scroll Bar |  | x | x |  | x |  | x | x | x |  |
ScrollP ane |  |  | x | x | x |  | x | x | x |  |
TextAr ea |  |  | x |  | x |  | x | x | x | x |
TextC ompon ent |  |  | x |  | x |  | x | x | x | x |
TextFi eld | x | x |  | x |  | x | x | x | x |  | x
Windo w |  |  | x | x | x |  | x | x | x |  | x

Tab. 4.5: Events und Komponenten Interface ActionListener Action-Listener werden häufig eingesetzt und sind eine einfache Form von Event-Handlern. Ein Action-Listener wird immer dann verwendet, wenn auf eine implementierungsabhängige Aktion eines Benutzers reagiert werden soll. Derartige Aktionen können bspw. das Betätigen eines Buttons, die Auswahl eines Listeneintrages oder auch Tastatureingaben sein. Tritt eine derartige Aktion ein, so wird eine actionPerformed-Nachricht generiert, die an alle Action-Listener gesendet wird, die sich für die Komponente registriert haben, die den Event auslöst. Das Interface ActionListener enthält lediglich eine einzige Methode, actionPerformed(ActionEvent), die als Parameter ein Objekt vom Typ ActionEvent erwartet. Die Klasse ActionEvent definiert die folgenden Methoden: String getActionCommand() liefert den String zurück, der mit der Aktion assoziiert ist. Die meisten Objekte, die Aktionen generieren können, erlauben die Verwendung einer Methode setActionCommand, in der dieser String gesetzt werden kann. Wird der String nicht explizit gesetzt, so beinhaltet er üblicherweise den Text, der in der Komponente dargestellt wird. Enthält ein Objekt mehrere Elemente, so wird im Regelfall der Name des ausgewählten Elements verwendet. int getModifiers() liefert eine Zahl zurück, die das Tastaturelement angibt, das der Benutzer gedrückt hat als der Action-Event ausgelöst wurde. Hierzu können die Konstanten SHIFT_MASK, CTRL_MASK, META_MASK oder ALT_MASK verwendet werden, um herauszufinden, welches Tastaturelement gedrückt wurde. Wenn bspw. die Control-Taste gedrückt wurde, um einen Menüeintrag auszuwählen, so ist der folgende Ausdruck ungleich null: actionEvent.getModifiers() & ActionEvent.CTRL_MASK die Methode getSource, die das Objekt (Komponente oder Menükomponente) zurückliefert, die einen Action-Event auslöste. getSource wird in einer Superklasse von ActionEvent definiert, in der Klasse EventObject. Eine Adapterklasse ist für Action-Listener folglich nicht erforderlich. Ein Beispiel für die Anwendung dieses Interfaces wurde bereits angeführt, indem auf die Betätigung eines Buttons reagiert wird. Der Vollständigkeit halber sei dieses Beispiel nochmals angeführt. import java.applet.*; import java.awt.*; import java.awt.event.*; public class Knoepfe extends Applet implements ActionListener { Button ende = new Button("Quit"); public void init () { add(ende); ende.addActionListener(this); } public void actionPerformed(ActionEvent e) { System.out.println("Ende"); } } Interface AdjustmentListener Adjustment-Events benachrichtigen den Anwender über Änderungen von Komponentenwerten, die das Interface Adjustable implementieren. Anpassungsfähige Objekte verwenden einen ganzzahligen Wert und generieren einen Adjustment-Event, wenn dieser Wert verändert wird. Als einzige AWT-Klasse wird dieses Interface von der Klasse Scrollbar implementiert. In Java werden die folgenden Adjustment-Events verwendet: block increment und block decrement Der Benutzer möchte den Wert einer Komponente um einen größeren Betrag ändern. Im Falle einer Scrollbar könnte dies das Klicken der Maus oberhalb des Abwärtspfeils oder unterhalb des Aufwärtspfeils bedeuten. track: Der Benutzer verändert den Wert einer Komponente explizit. Wird eine Scrollbar verwendet, so könnte dies das Ziehen des Scrollbar-Knopfs sein. unit increment und unit decrement: Der Benutzer möchte den Wert einer Komponente in geringem Maße verändern. Bei einer Scrollbar könnte dies das Betätigen der Aufwärts- oder Abwärtspfeile sein, um den Wert des Schiebebalkens um eine Einheit zu ändern. Das Interface AdjustmentListener enthält ebenfalls nur eine einzige Methode, adjustmentValueChanged(AdjustmentEvent), und verwendet daher keine Adapterklasse. Diese Methode wird dann aufgerufen, wenn ein Adjustment-Event eingetreten ist, wobei als Parameter ein Objekt vom Typ AdjustmentEvent übergeben wird. Die Klasse AdjustmentEvent definiert die folgenden Methoden: Adjustable getAdjustable() liefert die Komponente zurück, die den Event ausgelöst hat. getAdjustable kann hierbei anstelle der Methode getSource verwendet werden. int getAdjustmentType() liefert den Typ der Anpassung zurück, die aufgetreten ist. Hierzu werden in der Klasse AdjustmentEvent die folgenden Konstanten definiert: UNIT_INCREMENT, UNIT_DECREMENT, BLOCK_INCREMENT, BLOCK_DECREMENT und TRACK, die bereits erläutert wurden. int getValue() liefert den Wert der Komponente zurück, nachdem die Anpassung aufgetreten ist. Das folgende Beispiel demonstriert die Anwendung dieses Interfaces, indem ein Event-Handler für einen Adjustment-Listener implementiert wird. class Beispiel implements AdjustmentListener { //... Scrollbar slider; //... Beispiel (...) { //... slider.addAdjustmentListener(this); } //... // Antwort auf Aktion des Sliders public void adjustmentValueChanged(AdjustmentEvent e) { // Reaktion auf Ausloesen des Sliders } //... } Interface ComponentListener Komponenten-Events werden von einem Komponentenobjekt immer dann generiert, wenn eine Komponente sichtbar oder unsichtbar, verschoben oder verkleinert bzw. vergrößert wird. Ein Beispiel eines Komponenten-Listeners könnte ein GUI-Entwicklungswerkzeug sein, in dem Informationen über die Eigenschaften einer selektierten Komponente angezeigt werden. Im Regelfall wird aber ein derartiges Wissen nicht benötigt. Events der Art „Komponente sichtbar" treten nur dann auf, wenn die Komponentenmethode setVisible aufgerufen wurde. Wird bspw. ein Fenster ikonifiziert, so wird ein derartiger Event ausgelöst. Das Interface ComponentListener bzw. die hierzu gehörende Adapterklasse ComponentAdapter beinhaltet die folgenden vier Methoden: void componentHidden(ComponentEvent) wird vom AWT aufgerufen, nachdem die Komponente, für die ein Listener registriert ist, unsichtbar geworden ist. Dies impliziert einen vorangehenden Aufruf der Methode setVisible. void componentMoved(ComponentEvent) wird vom AWT entsprechend aufgerufen, wenn eine Komponente relativ zum Container, der sie enthält, verschoben wurde. Wird bspw. ein Fenster verschoben, so generiert dieses einen derartigen Event. Die Komponenten, die das Fenster enthält, generieren einen derartigen Event allerdings nicht, da sich deren relative Position nicht verändert hat. void componentResized(ComponentEvent) wird vom AWT aufgerufen, nachdem sich die Größe einer Komponente verändert hat. void componentShown(ComponentEvent) wird vom AWT aufgerufen, nachdem eine Komponente aufgrund eines Aufrufs der Methode setVisible sichtbar wird. Jede Methode der Klasse ComponentEvent verwendet einen einzigen Parameter: Ein Objekt vom Typ ComponentEvent. Hierzu definiert die Klasse ComponentEvent die Methode getComponent, die die Komponente feststellt, die den Event ausgelöst hat. Das folgende Applet demonstriert die Verwendung von Komponenten-Events. Das Applet enthält einen Knopf, dessen Betätigung ein neues Fenster (ein Frame-Objekt) öffnet. Verlässt man die Webseite, die das Applet enthält, so wird dieses unsichtbar. Analog wird es wieder angezeigt, wenn die Seite erneut geöffnet wird. Ein Textbereich stellt eine dementsprechende Nachricht jedes Mal dar, wenn ein derartiger Komponenten-Event ausgelöst wird. import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class KomponentenEvent extends Applet implements ComponentListener, ActionListener { Frame f; public boolean showIt = false; public void init() { Button b = new Button("Start"); b.setActionCommand("Zeige"); b.addActionListener(this); add(b); f = new Frame("Fenster"); f.addComponentListener(this); f.pack(); } public void actionPerformed(ActionEvent e) { if (e.getActionCommand() == "Zeige") { showIt = true; if.setVisible(true); } } public void stop() { f.setVisible(false); } public void start() { if (showIt) f.setVisible(true); } public void componentHidden(ComponentEvent e) { System.out.println("Komponente verborgen"); } public void componentMoved(ComponentEvent e) { System.out.println("Komponente verschoben"); } public void componentResized(ComponentEvent e) { System.out.println("Komponentengroesse veraendert"); } public void componentShown(ComponentEvent e) { System.out.println("Komponente sichtbar"); } } Interface ContainerListener Container-Events werden von einem Container immer dann ausgelöst, wenn eine Komponente einem Container hinzugefügt wurde, oder wenn eine Komponente entfernt wurde. Das Interface ContainerListener bzw. seine Adapterklasse, ContainerAdapter, enthalten die Methoden: void componentAdded(ContainerEvent) , die vom AWT aufgerufen wird, nachdem eine Komponente einem Container hinzugefügt wurde, der von einem Listener überwacht wird und void componentRemoved(ContainerEvent), die vom AWT analog aufgerufen wird, nachdem eine Komponente aus einem Container entfernt wurde. Jede Container-Event-Methode erwartet als Parameter ein Objekt vom Typ ContainerEvent. Die Klasse ContainerEvent wiederum definiert die folgenden beiden Methoden: Component getChild() liefert die Komponente zurück, deren Hinzufügen oder Entfernen einen Event ausgelöst hat und Container getContainer() liefert entsprechend den Container zurück, der den Event ausgelöst hat. Das folgende Beispiel demonstriert die Verwendung der Container-Events. Hierbei ist ausschnittsweise ein Programm angegeben, das Container-Events feststellt. Die Anwendung generiert diese Events immer dann, wenn eine Button-Komponente einem Panel hinzugefügt wurde oder entsprechend, wenn diese entfernt wurde. class BeispielContainer implements ContainerListener { //... BeispielContainer (...) { //... Panel p = new Panel();//... p.ContainerListener(this); } //... // Antwort auf Aktion des Sliders public void componentAdded(ContainerEvent e) { // Reaktion auf Hinzufuegen einer Komponente } public void componentRemoved(ContainerEvent e) { // Reaktion auf Entfernen einer Komponente } //... } Interface FocusListener Focus-Events werden dann generiert, wenn eine Komponente die Möglichkeit erhält, Tastatur-Events zu empfangen bzw., wenn sie diese wieder verliert. Aus Sicht des Anwenders ist die Verwendung einer Komponente, die momentan den Tastaturfokus hat, leicht erkennbar, da diese bspw. eine dickere Begrenzungslinie ausweist bzw. da das Fenster, das die Komponente enthält, besonders gekennzeichnet ist. In Windows 98 der Firma Microsoft ist bspw. der obere Rand eines Fensters, das den Tastaturfokus hat, blau gefärbt, während alle anderen Fenster grau gefärbt sind. Es ist dadurch auch verständlich, dass stets nur eine Komponente den Tastaturfokus haben kann, nie jedoch mehrere gleichzeitig. Die genaue Art und Weise, in der Komponenten den Tastaturfokus erhalten, hängt zu einem großen Teil vom verwendeten Fenstersystem ab. Typischerweise setzt ein Benutzer den Fokus, indem er auf ein Fenster oder auf eine Komponente klickt. Soll eine Komponente den Fokus erhalten, so kann aber auch die Methode requestFocus einer Komponente aufgerufen werden. Das Interface FocusListener bzw. seine Adapterklasse FocusAdapter enthalten die folgenden zwei Methoden: void focusGained(FocusEvent) wird vom AWT aufgerufen, nachdem eine Komponente, für die ein Listener registriert ist, den Fokus erhalten hat. void focusLost(FocusEvent) wird vom AWT aufgerufen, nachdem eine Komponente, für die ein Listener registriert ist, den Fokus verloren hat. Die Fokus-Event-Methoden erwarten jeweils einen Parameter: Ein Objekt vom Typ FocusEvent. Die Klasse FocusEvent definiert die Methode boolean isTemporary, die dann den Wert true zurückgibt, wenn ein FocusLost-Event nur temporär stattfindet. Hierdurch kann bspw. angegeben werden, dass eine bestimmte Komponente den Fokus erhalten wird, wenn das umgebende Fenster den Fokus zurückgewinnt. Oftmals wird die Methode getComponent, die in ComponentEvent definiert ist, dazu eingesetzt, einem FocusEvent-Objekt mitzuteilen, welche Komponente den Fokus gewonnen oder verloren und damit den Fokus-Event ausgelöst hat. Das folgende Beispiel demonstriert die Verwendung von Fokus-Events. Indem der obere der beiden Buttons gedrückt wird, wird ein Fenster geöffnet, das ein Textfeld und einen weiteren Button enthält. Ein Fokus-Listener überwacht das Auftreten von Fokus-Events, die die einzelnen Komponenten des Fensters auslösen können. Zur Realisierung des Fokus-Fensters wird eine neue Klasse FocusFenster mit Hilfe des Konstruktors der Superklasse Frame erzeugt. Die Ausgabe des Applets ist in Abb. 4-35 dargestellt. import java.awt.*; import java.awt.event.*; public class FokusBeispiel extends Applet implements FocusListener, ActionListener { TextArea anzeige; FocusFenster fenster; Button b1, b2; static final String ZEIGE= "zeige"; static final String LOESCHE = "loesche"; String neueZeile; public void init () { b1 = new Button("Öffnen des Fensters."); b1.setActionCommand(ZEIGE); b1.addActionListener(this); b2 = new Button("Loeschen der Anzeige."); b2.setActionCommand(LOESCHE); b2.addActionListener(this); anzeige = new TextArea(5, 20); setLayout(new BorderLayout()); add("North", anzeige); add("Center", b1); add("South", b2); //Neues Fenster erzeugen, aber nicht anzeigen. fenster = new FocusFenster(this); neueZeile = System.getProperty("line.separator"); } public void stop() { fenster.setVisible(false); } public void focusGained(FocusEvent e) { nachricht("Fokus erhalten", e); } public void focusLost(FocusEvent e) { nachricht("Fokus verloren", e); } void nachricht(String n, FocusEvent e) { anzeige.append(n+ ": "+ e.getComponent() + neueZeile); } public void actionPerformed(ActionEvent e) { if (e.getActionCommand() == ZEIGE) { fenster.pack(); fenster.setVisible(true); } else { //LOESCHE anzeige.setText(""); } } } class FocusFenster extends Frame { public FocusFenster(FocusListener l) { //Verwende Konstruktor der Superklasse Frame super("Fokus-Event-Fenster"); GridLayout g = new GridLayout(1,2); setLayout(g); this.addFocusListener(l); TextField tf = new TextField("TextField"); tf.addFocusListener(l); add(tf); Button b= new Button("Button"); b.addFocusListener(l); add(b); } } Abb. 4.35: Fokus-Applet Interface ItemListener Item-Events werden von Komponenten generiert, die das Interface ItemSelectable implementieren, und die den Status (bspw. „an" oder „aus") von Listen- oder Menüeinträgen überwachen sollen. In Java können Checkboxen, Menüeinträge in Form von Checkboxen, Auswahllisten und Listen derartige Events auslösen. Das Interface ItemListener besteht lediglich aus einer einzigen Methode, void itemStateChanged(ItemEvent), weshalb die Verwendung einer Adapterklasse überflüssig ist. Diese Methode wird vom AWT unmittelbar nach einer Zustandsänderung einer Komponente aufgerufen, für die ein Listener registriert ist, und erwartet als Parameter ein Objekt vom Typ ItemEvent. Die Klasse ItemEvent definiert die folgenden Methoden: Object getItem() liefert das komponentenspezifische Objekt zurück, das mit dem Eintrag assoziiert ist, dessen Zustand sich verändert hat. Verändert sich bspw. der Zustand eines Menüeintrags, so würde hier der Name des Menüeintrags zurückgegeben. Oftmals wird ein String verwendet, der einen Text bezüglich des ausgewählten Eintrags enthält. Wird ein derartiger Event durch eine Liste verursacht, so wird eine Ganzzahl zurückgegeben, die den Index des ausgewählten Eintrags wiedergibt. ItemSelectable getItemSelectable() liefert die Komponente zurück, die den Item-Event auslöste. int getStateChange() liefert den neuen Zustand des Eintrags zurück. Hierzu sind in der Klasse ItemEvent die Zustände SELECTED und DESELECTED definiert. Der folgende Code-Ausschnitt demonstriert die Funktionsweise von Item-Events. class BeispielItems implements ItemListener { //... BeispielItems (...) { //... Choice c = new Choice(); c.addItemListener(this); //... } //... public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { //Aktion dementsprechend ausfuehren } else { //Aktion dementsprechend ausfuehren } }//... } Interface KeyListener Key-Events werden dann ausgelöst, wenn ein Anwender die Tastatur verwendet, bzw. präziser formuliert, wenn der Anwender Tastaturkomponenten aus dem GUI-Element heraus betätigt, das den Fokus der Tastatur derzeitig besitzt (hierzu vergleiche die Funktion der Focus-Listener). Eine Benachrichtigung in Form eines Events kann weiterhin unterschieden werden in das Tippen eines Unicode-Zeichens (sog. Key-typed-Events) bzw. in das Drücken und Loslassen einer Taste der Tastatur (sog. Key-pressed-Events bzw. Key-released-Events). Java verwendet hierzu das Unicode-Zeichenset, in dem die Standardbuchstaben und viele weitere Zeichen internationaler Alphabete enthalten sind. Derartige Zeichen können in jeder Java-Implementierung verwendet werden, wenn sie eine Unicode-Nummer haben. Diese Nummern sind bspw. unter der URL http://www.unicode.org zu finden. Im Allgemeinen sollten stets Key-pressed-Events verwendet werden, es sei denn, es soll festgestellt werden, wann ein Anwender eine Taste drückt, die zu einem bestimmten Zeichen gehört. Soll bspw. überwacht werden, wann der Anwender ein Unicode-Zeichen tippt (bzw. eine Sequenz derartiger Zeichen), so sollten Key-typed-Events verwendet werden. Soll andererseits festgestellt werden, wann der Benutzer eine der Funktionstasten (bspw. F10) betätigt, so sollten Key-pressed- bzw. Key-released-Events eingesetzt werden. Beide Event-Arten setzen immer voraus, dass eine GUI-Komponente den Tastaturfokus besitzt. Der Tastaturfokus einer Komponente kann wie folgt gesetzt werden: Grundsätzlich muss eine GUI-Komponente überhaupt in der Lage sein, den Tastaturfokus erhalten zu können. Textmarken (Labels) können diesen bspw. nicht auf allen Systemen erhalten. Es muss sichergestellt sein, dass eine GUI-Komponente den Fokus anfordert, wenn dieser erforderlich ist. Komponenten, die selbst implementiert wurden, müssen diesen bspw. mit der requestFocus-Methode explizit anfordern, wenn mit Mausklicks gearbeitet wird. Wird eine selbst implementierte Komponente eingesetzt, so sollte die Komponentenmethode isFocusTraversable derart implementiert werden, dass sie den Wert true zurückliefert. Nur auf diese Art und Weise kann der Benutzer mittels der Tabulatortaste so zwischen Containern umschalten, dass der Fokus übergeben wird. Das Interface KeyListener bzw. die dazugehörige Adapterklasse KeyAdapter enthalten die folgenden drei Methoden: void keyTyped(KeyEvent) wird vom AWT dann aufgerufen, wenn der Benutzer ein Unicode-Zeichen in eine Komponente eingetippt hat, die von einem Listener überwacht wird. void keyPressed(KeyEvent) wird dann vom AWT aufgerufen, wenn der Benutzer eine Taste der Tastatur betätigt hat. void keyReleased(KeyEvent) wird vom AWT aufgerufen, nachdem der Benutzer eine Taste der Tastatur losgelassen hat. Jede der drei Methoden verwendet als Parameter ein Objekt vom Typ KeyEvent. Die Klasse KeyEvent definiert die folgenden Methoden: int getKeyChar() fragt ein Unicode-Tastaturzeichen ab, das einen Event ausgelöst hat. void setKeyChar(char) setzt ein Unicode-Zeichen, das mit einem Event assoziiert ist. int getKeyCode() fragt den Tasten-Code ab, der mit einem Event assoziiert ist. Der Tasten-Code identifiziert die Taste, die der Benutzer gedrückt oder losgelassen hat. Die Klasse KeyEvent definiert hierzu Tasten-Codes für viele der gebräuchlichen Tasten, bspw. VK_A für die Taste „A" oder VK_ESCAPE für die Escape-Taste. void setKeyCode(int) setzt entsprechend den Tasten-Code, der mit einem Event assoziiert ist. void setModifiers(int) setzt den Zustand der Modifikationstasten (Control-, Alt-, Shift- und Meta-Taste). In der Klasse KeyEvent stehen noch weitere Methoden zur Verfügung, mit denen bspw. Textbeschreibungen von Tasten-Codes und Modifikationstasten für bestimmte Länder erstellt werden können (hierzu siehe auch Kapitel 6 zum Thema Internationalisierung). Die Klasse KeyEvent erweitert die Klasse InputEvent, die ihrerseits wiederum die Klasse ComponentEvent erweitert. In der Klasse ComponentEvent steht die Methode getComponent zur Verfügung, mit deren Hilfe die Komponente festgestellt werden kann, die einen Event ausgelöst hat. Die Klasse InputEvent stellt die folgenden Methoden zur Verfügung: int getWhen() stellt den Zeitpunkt fest, zu dem ein bestimmter Event eingetreten ist. Je höher der Wert dieser Zeit, desto kürzer liegt das Auftreten des Events zurück. boolean isAltDown() stellt fest, ob die Alt-Taste gedrückt ist. boolean isControlDown() boolean isMetaDown() boolean isShiftDown() int getModifiers() liefert ein Flag zurück, das den Zustand aller Modifikationstasten zu dem Zeitpunkt wiedergibt, bei dem ein Event ausgelöst wurde. Das folgende Beispiel demonstriert die Verwendung von Key-Events. Hierbei wird ein Textfeld verwendet, in das Buchstaben eingetippt werden können. Die dann ausgelösten Events werden in der Kommandozeile angezeigt. Die Ausgabe dieses Applets ist in Abb. 4-36 dargestellt. import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class KeyBeispiel extends Applet implements KeyListener { TextField tippBereich; public void init() { tippBereich = new TextField(20); tippBereich.addKeyListener(this); add(tippBereich); requestFocus(); } public void keyTyped(KeyEvent e) { System.out.println("Taste getippt"); } public void keyPressed(KeyEvent e) { anzeige(e,"Taste gedrueckt: "); } public void keyReleased(KeyEvent e) { System.out.println("Taste losgelassen"); } protected void anzeige(KeyEvent e, String s){ String charS, keyS; char c = e.getKeyChar(); int keyCode = e.getKeyCode(); charS = "Buchstabe= '" + c + "'"; keyS = "Tastencode= " + keyCode+ " ("+ KeyEvent.getKeyText(keyCode) + ")"; System.out.println(s + "\n"+ " "+ charS +"\n"+" "+ keyS + "\n"); } } Abb. 4.36: Key-Events Interface MouseListener Maus-Listener gehören zu den am häufigsten verwendeten Event-Handlern. Maus-Events werden dann ausgelöst, wenn ein Anwender die Maus (oder ein ähnliches Eingabegeräte) verwendet und damit bspw. den Cursor in den Bildschirmbereich einer Komponente hinein- oder herausbewegt bzw. dann, wenn ein Mausknopf betätigt wird. Die Überwachung der Cursor-Aktivität verursacht dabei eine weit höhere Maschinenauslastung als das Feststellen sonstiger Mausaktivitäten. Events, die bei der Bewegung der Maus ausgelöst werden, werden daher von einem separaten Listener-Typ überwacht, der im nächsten Abschnitt beschrieben wird. Das Interface MouseListener bzw. die Adapterklasse MouseAdapter beinhalten die folgenden Methoden: void mouseClicked(MouseEvent) wird vom AWT aufgerufen, nachdem ein Anwender auf eine Komponente geklickt hat, die ein Listener überwacht. void mouseEntered(MouseEvent) wird vom AWT aufgerufen, wenn der Cursor in die Begrenzung einer Komponente, die von einem Listener überwacht wird, hineinbewegt wird. void mouseExited(MouseEvent) wird vom AWT aufgerufen, nachdem der Cursor die Begrenzung einer Komponente verlassen hat. void mousePressed(MouseEvent) wird vom AWT aufgerufen, nachdem der Anwender die Maus betätigt hat, während der Cursor über einer Komponente platziert ist. void mouseReleased(MouseEvent) wird vom AWT aufgerufen, nachdem der Mausknopf losgelassen wird, während sich der Cursor über einer Komponente befindet. Ein Problem ergibt sich hierbei bei Events, die das Bewegen der Maus in eine Komponente, aus dieser heraus bzw. das Loslassen des Mausknopfs betreffen. Wenn der Anwender die Maus derart verwendet, dass diese bei gedrücktem Knopf bewegt wird (sog. Dragging), so empfängt die Komponente, in der das Dragging begonnen wurde, alle folgenden Maus-Events, also auch jene, die das Loslassen des Mausknopfs repräsentieren. Dies impliziert aber, dass keine andere Komponente in dieser Zeit Maus-Events empfangen kann, auch solche nicht, die das Loslassen des Mausknopfs anzeigen. Jede der obigen Maus-Event-Methoden erwartet als Parameter ein Objekt vom Typ MouseEvent. Die Klasse MouseEvent definiert die folgenden Methoden: int getClickCount() zählt die Anzahl der aufeinander folgenden Mausklicks, die ein Benutzer auslöst. int getX() , int getY() und Point getPoint() liefert die (x,y)-Position zurück, an der ein Event ausgelöst wurde. Diese Position ist relativ zu der Komponente zu rechnen, über der der Event verursacht wurde. boolean isPopupTrigger() gibt dann den Wert true zurück, wenn ein Maus-Event ein Pop-Up-Menü erscheinen lassen soll. Da derartige Events aber plattformabhängig sind, sollte isPopupTrigger sowohl für MouseDown- als auch für MouseUp-Events aufgerufen werden, wenn Pop-Up-Menüs eingesetzt werden sollen. Die Klasse MouseEvent erweitert die Klasse InputEvent, die wiederum ihre Eigenschaften von der Klasse ComponentEvent erbt. Auch bei Maus-Events kann daher die Methode getComponent verwendet werden, die Teil der Klasse ComponentEvent ist. Weiterhin stehen in der Klasse InputEvent die folgenden Methoden zur Verfügung: int getWhen() gibt eine Zeitmarke zurück, die das Auftreten des Events repräsentiert. Je höher der Wert dieser Marke, desto kürzer liegt das Auftreten des Events zurück. boolean isAltDown(), boolean isControlDown(), boolean isMetaDown(), boolean isShiftDown() sind Methoden, die den Status der Modifikationstasten für den Zeitpunkt angeben, zu dem ein Event ausgelöst wurde. int getModifiers() gibt ein Flag zurück, das den Status aller Modifikationstasten zu dem Zeitpunkt wiedergibt, zu dem ein Event ausgelöst wurde. Neben den Modifikationstasten gibt dieses Flag aber auch an, welcher Mausknopf gedrückt wurde. Im folgenden Beispiel ist der Ausdruck dann wahr, wenn die linke Maustaste gedrückt wurde. (mouseEvent.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK Das folgende Beispiel demonstriert die Verwendung von Maus-Listenern. In einem Applet wird ein leerer Bereich von einem Maus-Listener überwacht. Findet dort ein Maus-Event statt, so wird ein Text in der Kommandozeile ausgegeben. Die Ausgabe dieses Beispiels findet sich in Abb. 4-37. import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class MausBeispiel extends Applet implements MouseListener { Bereich bereich; public void init() { bereich = new Bereich(); add(bereich); bereich.addMouseListener(this); addMouseListener(this); } public void mousePressed(MouseEvent e) { System.out.println("Maus gedrueckt: Anzahl Klicks: " + e.getClickCount()+"\n"); } public void mouseReleased(MouseEvent e) { System.out.println("Maus losgelassen: Anzahl Klicks: " + e.getClickCount()+ "\n"); } public void mouseEntered(MouseEvent e) { System.out.println("Maus im Bereich"); } public void mouseExited(MouseEvent e) { System.out.println("Maus ausserhalb des Bereichs"); } public void mouseClicked(MouseEvent e) { System.out.println("Maus geklickt: Anzahl Klicks: " + e.getClickCount() +"\n"); } } class Bereich extends Canvas { public void paint(Graphics g) { g.drawRect(0, 0, 90, 180); } } Abb. 4.37: Maus-Events Interface MouseMotionListener Mausbewegungs-Events treten dann auf, wenn der Benutzer den Bildschirm-Cursor mit der Maus oder mit einem ähnlichen Eingabegerät bewegt. Das Interface MouseMotionListener und die hierzu gehörende Adapterklasse MouseMotionAdapter enthalten die folgenden Methoden: void mouseDragged(MouseEvent) wird vom AWT aufgerufen, wenn der Anwender die Maus mit gedrücktem Knopf bewegt. Dieser Event-Typ wird von der Komponente ausgelöst, die den vorangehenden MousePressed-Event verursachte. Auch dann, wenn der Cursor sich nicht mehr über der Komponente befindet. void mouseMoved(MouseEvent) wird vom AWT aufgerufen, wenn der Anwender die Maus bewegt, ohne dass ein Mausknopf gedrückt wird. Dieser Event wird von der Komponente ausgelöst, die sich momentan unter dem Cursor befindet. Jede Methode zum Abfangen von Mausbewegungs-Events erwartet einen Parameter vom Typ MouseEvent. Dieser Typ wurde bereits im Zusammenhang von Maus-Events vorgestellt. Zum besseren Verständnis des MouseMotion-Listeners wird das Beispiel, das bereits die Funktion der Maus-Events erläuterte, erweitert, indem das Interface MouseMotionListener implementiert wird. public class MausBeispiel-2 extends MouseAdapter implements MouseMotionListener { // ..... Initialisierung vorab ... public void mouseDragged(MouseEvent e) { System.out.println("Maus gezogen nach: " + e.getX() +", " + e.getY() + ")\n"); } public void mouseMoved(MouseEvent e) { //kann aehnlich wie mouseDragged implementiert werden } public void mousePressed(MouseEvent e) { System.out.println("Maus gedrueckt: Anzahl Klicks: " + e.getClickCount()+"\n"); } public void mouseReleased(MouseEvent e) { System.out.println("Maus losgelassen: Anzahl Klicks: " + e.getClickCount()+ "\n"); } public void mouseEntered(MouseEvent e) { System.out.println("Maus im Bereich"); } public void mouseExited(MouseEvent e) { System.out.println("Maus ausserhalb des Bereichs"); } public void mouseClicked(MouseEvent e) { System.out.println("Maus geklickt: Anzahl Klicks: " + e.getClickCount() +"\n"); } } Interface TextListener Text-Events werden generiert, nachdem der Text einer Textkomponente verändert wurde. Hierzu stehen in Java Textfelder und Textbereiche zur Verfügung. Um bereits frühzeitig über Textänderungen benachrichtigt zu werden, muss ein Key-Listener verwendet werden. Key-Listener wurden in diesem Kapitel bereits erläutert. Das Interface TextListener beinhaltet eine Methode, void textValueChanged(TextEvent), die vom AWT aufgerufen wird, nachdem sich der Text einer Komponente ändert, die von einem Listener überwacht wird. Eine Adapterklasse ist deshalb nicht erforderlich. Die Text-Event-Methode verwendet als Parameter ein Objekt vom Typ TextEvent. Weiterhin kann die Methode getSource verwendet werden, die TextEvent von der Klasse EventObject erbt, um die Komponente abzufragen, die den Event ausgelöst hat. Window-Listener Fenster-Events werden dann generiert, wenn ein Fenster geöffnet, geschlossen, (de)ikonifiziert oder (de)aktiviert wird. Ein Fenster ist dann aktiviert, wenn es den Tastaturfokus hat, anderenfalls deaktiviert. Window-Listener werden hauptsächlich dazu eingesetzt, um das Schließen von Fenstern zu überwachen. Wird dieser Vorgang nicht überwacht, so geschieht dann nichts, wenn ein Benutzer ein Fenster schließen möchte. Erst das Erfassen des Events, gekoppelt mit der Anweisung, ein Fenster zu schließen, resultiert in der gewünschten Aktion. Dies impliziert, dass eine Anwendung, die lediglich aus einem Fenster besteht, beendet wird, wenn dieses Fenster geschlossen wird. Eine weitere wichtige Aufgabe der Window-Listener ist das Anhalten von Threads bzw. die Freigabe von Ressourcen, wenn ein Fenster ikonifiziert wird, bzw. das Neustarten eines Threads, wenn ein Fenster wieder geöffnet wird. Zur Benachrichtigung, dass ein Fenster erscheint oder verschwindet, muss ein Komponenten-Listener für das entsprechende Fenster registriert werden. Das Interface WindowListener bzw. die Adapterklasse WindowAdapter enthalten die folgenden Methoden: void windowOpened(WindowEvent) wird vom AWT aufgerufen, nachdem das von einem Listener überwachte Fenster das erste Mal sichtbar wurde. void windowClosing(WindowEvent) wird vom AWT als Antwort auf eine Benutzeranforderung aufgerufen, dass das Fenster geschlossen werden soll. Um das Fenster tatsächlich zu schließen, muss der Listener aber anschließend die Methoden dispose oder setVisible(false) aufrufen. void windowClosed(WindowEvent) wird vom AWT aufgerufen, nachdem das von einem Listener überwachte Fenster geschlossen wurde. void windowIconified(WindowEvent), void windowDeiconified(Window-Event) werden vom AWT aufgerufen, nachdem ein von einem Listener überwachtes Fenster ikonifiziert bzw. deikonifiziert wurde. void windowActivated(WindowEvent), void windowDeactivated(Window-Event) werden vom AWT aufgerufen, nachdem das von einem Listener überwachte Fenster aktiviert oder deaktiviert wurde. Jede dieser Methoden verwendet einen Parameter, ein Objekt vom Typ Window-Event. Die Klasse WindowEvent definiert die Methode getWindow, die das Fenster zurückliefert, die den Window-Event ausgelöst hat. Das folgende Beispiel demonstriert ausschnittsweise die Funktion der Window-Listener. public class FensterBeispiel implements WindowListener { // ..... fenster = new Frame("Window-Event-Fenster"); fenster.addWindowListener(this); fenster.add("Center",new Label("Window-Listener")); window.pack(); } public void windowClosing(WindowEvent e) { window.setVisible(false); System.out.println("Fenster wird geschlossen", e); } public void windowClosed(WindowEvent e) { System.out.println("Fenster geschlossen"); } public void windowOpened(WindowEvent e) { System.out.println("Fenster geoeffnet"); } public void windowIconified(WindowEvent e) { System.out.println("Fenster ikonifiziert"); } public void windowDeiconified(WindowEvent e) { System.out.println("Fenster deikonifiziert"); } public void windowActivated(WindowEvent e) { System.out.println("Fenster aktiviert"); } public void windowDeactivated(WindowEvent e) { System.out.println("Fenster deaktiviert"); } }