Java und Ereignisbehandlung
Die meisten Programme müssen auf Befehle des Benutzers reagieren, um nützlich zu sein. Zu diesem Zweck stützen sich Java-Programme auf Ereignisse, die Benutzeraktionen beschreiben.
Letzten Monat habe ich gezeigt, wie eine grafische Benutzeroberfläche aus Komponenten zusammengesetzt wird, die vom Toolkit für abstrakte Fenster der Java-Klassenbibliothek bereitgestellt werden. Nachdem ich einige solcher Schnittstellen zusammengestellt hatte, sprach ich kurz über das Thema Ereignisbehandlung, hielt jedoch an einer vollständigen Beschreibung der Ereignisbehandlung fest, wie sie von der AWT implementiert wurde. Diesen Monat machen wir dort weiter, wo wir aufgehört haben.
Ereignisorientiert sein
In der fernen Vergangenheit musste ein Programm, das wissen wollte, was der Benutzer tat, solche Informationen selbst aktiv sammeln. In der Praxis bedeutete dies, dass ein Programm, nachdem es sich selbst initialisiert hatte, in eine große Schleife eintrat, in der wiederholt geprüft wurde, ob der Benutzer etwas Interessantes tat (z. B. Drücken einer Taste, Berühren einer Taste, Bewegen eines Schiebereglers, Bewegen der Maus). und ergriff dann die entsprechenden Maßnahmen. Diese Technik wird als Polling bezeichnet .
Polling erledigt die Aufgabe, ist jedoch in modernen Anwendungen aus zwei Gründen unhandlich: Erstens wird durch die Verwendung von Polling der gesamte Code für die Ereignisbehandlung an einen Ort (innerhalb der großen Schleife) verschoben. Zweitens sind die resultierenden Wechselwirkungen innerhalb der großen Schleife tendenziell komplex. Darüber hinaus erfordert das Abrufen, dass ein Programm in einer Schleife sitzt, CPU-Zyklen verbraucht und darauf wartet, dass der Benutzer etwas unternimmt - eine ernsthafte Verschwendung einer wertvollen Ressource.
Das AWT löste diese Probleme, indem es ein anderes Paradigma übernahm, das allen modernen Fenstersystemen zugrunde liegt: ereignisgesteuerte Programmierung. Innerhalb der AWT gehören alle Benutzeraktionen zu einer abstrakten Gruppe von Dingen, die als Ereignisse bezeichnet werden . Ein Ereignis beschreibt eine bestimmte Benutzeraktion ausreichend detailliert. Anstatt dass das Programm vom Benutzer generierte Ereignisse aktiv sammelt, benachrichtigt die Java-Laufzeit das Programm, wenn ein interessantes Ereignis auftritt. Programme, die die Benutzerinteraktion auf diese Weise handhaben, werden als ereignisgesteuert bezeichnet .
Die Ereignisklasse
Die Event-Klasse ist der primäre Spieler im Event-Spiel. Es wird versucht, die grundlegenden Merkmale aller vom Benutzer generierten Ereignisse zu erfassen. In Tabelle 1 sind die öffentlichen Datenelemente aufgeführt, die von der Klasse Event bereitgestellt werden.
Art | Name | Beschreibung |
Objekt | Ziel | Ein Verweis auf die Komponente, die das Ereignis ursprünglich empfangen hat. |
lange | wann | Der Zeitpunkt, zu dem das Ereignis aufgetreten ist. |
int | Ich würde | Der Ereignistyp (weitere Informationen finden Sie im Abschnitt Ereignistypen). |
int | x | Die x-Koordinate, an der die Aktion relativ zu der Komponente ausgeführt wurde, die das Ereignis gerade verarbeitet. Für ein bestimmtes Ereignis ändert sich der Wert der x-Koordinate, wenn das Ereignis die Komponentenhierarchie nach oben bewegt. Der Ursprung der Koordinatenebene liegt in der oberen linken Ecke der Komponente. |
int | y | Die y-Koordinate, an der die Aktion relativ zu der Komponente ausgeführt wurde, die das Ereignis gerade verarbeitet. Für ein bestimmtes Ereignis ändert sich der Wert der y-Koordinate, wenn das Ereignis die Komponentenhierarchie nach oben bewegt. Der Ursprung der Koordinatenebene liegt in der oberen linken Ecke der Komponente. |
int | Schlüssel | Bei Tastaturereignissen der Tastencode der gerade gedrückten Taste. Sein Wert ist normalerweise der Unicode-Wert des Zeichens, das der Schlüssel darstellt. Weitere Möglichkeiten sind Werte für die Sondertasten HOME, END, F1, F2 usw. |
int | Modifikatoren | Eine arithmetische oder Kombination der Werte SHIFT_MASK, CTRL_MASK, META_MASK und ALT_MASK. Sein Wert repräsentiert den Status der Shift-, Control-, Meta- und Alt-Tasten. |
int | clickCount | Die Anzahl aufeinanderfolgender Mausklicks. Dieses Datenelement ist nur in MOUSE_DOWN-Ereignissen von Bedeutung. |
Objekt | arg | Ein ereignisabhängiges Argument. Bei Schaltflächenobjekten ist dieses Objekt ein Zeichenfolgenobjekt, das die Texturbezeichnung der Schaltfläche enthält. |
Wie ich im Abschnitt " Ereignisversand und -weitergabe" erläutern werde , wird eine Instanz der Klasse "Ereignis" normalerweise vom Java-Laufzeitsystem erstellt. Es ist jedoch möglich, dass ein Programm Ereignisse über ihre postEvent()
Methode erstellt und an Komponenten sendet .
Ereignistypen
Wie oben erwähnt, ist die Ereignisklasse ein Modell eines Benutzeroberflächenereignisses. Ereignisse fallen natürlich in Kategorien, die auf dem Typ des Ereignisses basieren (der Ereignistyp wird vom id
Datenelement angegeben). In Tabelle 2 sind alle von der AWT definierten Ereignisse nach Kategorien sortiert aufgeführt.
Es kann lehrreich sein, die Ereignisgenerierung in Aktion zu sehen. Wenn die Schaltfläche in Abbildung 1 gedrückt wird, wird ein Ereignisbrowser erstellt, der Ereignisinformationen zu den Ereignissen anzeigt, die der Browser empfängt. Den Quellcode für den Ereignisbrowser finden Sie hier.
Sie benötigen einen Java-fähigen Browser, um dieses Applet anzuzeigenAbbildung 1: Ereignisgenerierung in Aktion
Ereignisversand und -weitergabe
Betrachten Sie das Applet in Abbildung 2. Es besteht aus zwei Instanzen der Button-Klasse, die in eine Instanz der Panel-Klasse eingebettet sind. Diese Instanz der Panel-Klasse ist selbst in eine andere Instanz der Panel-Klasse eingebettet. Die letztere Instanz der Panel-Klasse befindet sich unter einer Instanz der Klasse TextArea, und beide Instanzen sind in eine Instanz der Applet-Klasse eingebettet. In Abbildung 3 sind die Elemente dargestellt, aus denen dieses Applet besteht, das als Baum angeordnet ist, wobei die Instanzen TextArea und Button als Blätter und eine Applet-Instanz als Stamm verwendet werden. (Weitere Informationen zum hierarchischen Layout von Komponenten in einer Benutzeroberfläche finden Sie in der Einführung zum AWT im letzten Monat.)
Sie benötigen einen Java-fähigen Browser, um dieses Applet anzuzeigenAbbildung 2: In Klassen eingebettete Klassen
Abbildung 3: Applet-Elementbaum (Hierarchie)
Wenn ein Benutzer mit dem Applet in Abbildung 2 interagiert, erstellt das Java-Laufzeitsystem eine Instanz der Klasse Event und füllt seine Datenelemente mit Informationen, die die Aktion beschreiben. Das Java-Laufzeitsystem ermöglicht es dem Applet dann, das Ereignis zu behandeln. Es beginnt mit der Komponente, die das Ereignis ursprünglich empfangen hat (z. B. die Schaltfläche, auf die geklickt wurde), und verschiebt den Komponentenbaum Komponente für Komponente nach oben, bis es den Container oben im Baum erreicht. Unterwegs hat jede Komponente die Möglichkeit, das Ereignis zu ignorieren oder auf eine (oder mehrere) der folgenden Arten darauf zu reagieren:
- Ändern Sie die Datenelemente der Ereignisinstanz
- Ergreifen Sie Maßnahmen und führen Sie einige Berechnungen basierend auf den im Ereignis enthaltenen Informationen durch
- Geben Sie dem Java-Laufzeitsystem an, dass sich das Ereignis nicht weiter im Baum ausbreiten soll
Das Java-Laufzeitsystem übergibt Ereignisinformationen über die handleEvent()
Methode der Komponente an eine Komponente . Alle gültigen handleEvent()
Methoden müssen die Form haben
public boolean handleEvent (Ereignis e)
Ein Ereignishandler benötigt eine einzelne Information: einen Verweis auf die Instanz der Ereignisklasse, die Informationen zu dem gerade aufgetretenen Ereignis enthält.
Der von der handleEvent()
Methode zurückgegebene Wert ist wichtig. Es zeigt dem Java-Laufzeitsystem an, ob das Ereignis vollständig im Ereignishandler behandelt wurde oder nicht. Ein wahrer Wert gibt an, dass das Ereignis behandelt wurde und die Weitergabe gestoppt werden sollte. Ein falscher Wert zeigt an, dass das Ereignis ignoriert wurde, nicht behandelt werden konnte oder unvollständig behandelt wurde und im Baum fortgesetzt werden sollte.
Consider the following description of an imaginary user's interaction with the applet in Figure 2. The user clicks on the button labeled "One." The Java language run-time system gathers information about the event (the number of clicks, the location of the click, the time the click occurred, and the component that received the click) and packages that information in an instance of the Event class. The Java run-time system then begins at the component that was clicked (in this case, the Button labeled "One") and, via a call to the component's handleEvent()
method, offers the component a chance to react to the event. If the component does not handle the event or handles the event incompletely (indicated by a return value of false), the Java run-time system offers the Event instance to the next higher component in the tree -- in this case an instance of the Panel class. The Java run-time system continues in this manner until the event is handled or the run-time system runs out of components to try. Figure 4 illustrates the path of this event as the applet attempts to handle it.
Figure 4: The path of an event
Each component making up the applet in Figure 2 adds a line to the TextArea object that indicates it received an event. It then allows the event to propagate to the next component in the tree. Listing 1 contains the code for a typical handleEvent()
method. The complete source code for this applet is available here.
public boolean handleEvent(Event evt) { if (evt.id == Event.ACTION_EVENT) { ta.appendText("Panel " + str + " saw action...\n"); } else if (evt.id == Event.MOUSE_DOWN) { ta.appendText("Panel " + str + " saw mouse down...\n"); }
return super.handleEvent(evt); }
Listing 1: A typical handleEvent()
method
Event helper methods
The handleEvent()
method is one place a programmer can put application code for handling events. Occasionally, however, a component will only be interested in events of a certain type (for example, mouse events). In these cases, the programmer can place the code in a helper method, rather than placing it in the handleEvent()
method.
Here is a list of the helper methods available to programmers. There are no helper methods for certain types of events.
action(Event evt, Object what)
gotFocus(Event evt, Object what)
lostFocus(Event evt, Object what)
mouseEnter(Event evt, int x, int y)
mouseExit(Event evt, int x, int y)
mouseMove(Event evt, int x, int y)
mouseUp(Event evt, int x, int y)
mouseDown(Event evt, int x, int y)
mouseDrag(Event evt, int x, int y)
keyDown(Event evt, int key)
keyUp(Event evt, int key)
false to indicate that the helper method did not handle the event.
The implementation of the handleEvent()
method provided by class Component invokes each helper method. For this reason, it is important that the redefined implementations of the handleEvent()
method in derived classes always end with the statement
return super.handleEvent(e);
The code in Listing 2 illustrates this rule.
public boolean handleEvent(Event e) { if (e.target instanceof MyButton) { // do something... return true; }
return super.handleEvent(e); }
Listing 2: Rule for ending statement in handleEvent()
method
Failure to follow this simple rule will prevent the proper invocation of helper methods.
Figure 5 contains an applet that handles mouse events solely through code placed in helper methods. The source code is available here.
Event | evt | The next event in a linked list of events. |
Window events | ||
Window events are generated in response to changes in the state of a window, frame, or dialog. | ||
Event | ID | |
WINDOW_DESTROY | 201 | |
WINDOW_EXPOSE | 202 | |
WINDOW_ICONIFY | 203 | |
WINDOW_DEICONIFY | 204 | |
WINDOW_MOVED | 205 | |
Keyboard events | ||
Keyboard events are generated in response to keys pressed and released while a component has input focus. | ||
Event | ID | |
KEY_PRESS | 401 | |
KEY_RELEASE | 402 | |
KEY_ACTION | 403 | |
KEY_ACTION_RELEASE | 404 | |
Mouse events | ||
Mouse events are generated in response to mouse actions occurring within the boundary of a component. | ||
Event | ID | |
MOUSE_DOWN | 501 | |
MOUSE_UP | 502 | |
MOUSE_MOVE | 503 | |
MOUSE_ENTER | 504 | |
MOUSE_EXIT | 505 | |
MOUSE_DRAG | 506 | |
Scroll events | ||
Scroll events are generated in response to manipulation of scrollbars. | ||
Event | ID | |
SCROLL_LINE_UP | 601 | |
SCROLL_LINE_DOWN | 602 | |
SCROLL_PAGE_UP | 603 | |
SCROLL_PAGE_DOWN | 604 | |
SCROLL_ABSOLUTE | 605 | |
List events | ||
List events are generated in response to selections made to a list. | ||
Event | ID | |
LIST_SELECT | 701 | |
LIST_DESELECT | 702 | |
Miscellaneous events | ||
Miscellaneous events are generated in response to a variety of actions. | ||
Event | ID | |
ACTION_EVENT | 1001 | |
LOAD_FILE | 1002 | |
SAVE_FILE | 1003 | |
GOT_FOCUS | 1004 | |
LOST_FOCUS | 1005 |
Erfahren Sie mehr über dieses Thema
- Das Java Tutorial von Mary Campione und Kathy Walrath. Die Online-Entwurfsversion ist unter //java.sun.com/tutorial/index.html verfügbar.
Diese Geschichte "Java und Ereignisbehandlung" wurde ursprünglich von JavaWorld veröffentlicht.