Java et gestion des événements

La plupart des programmes, pour être utiles, doivent répondre aux commandes de l'utilisateur. Pour ce faire, les programmes Java s'appuient sur des événements qui décrivent les actions de l'utilisateur.

Le mois dernier, j'ai montré comment assembler une interface utilisateur graphique à partir de composants fournis par le toolkit de fenêtrage abstrait de la bibliothèque de classes Java. Après avoir assemblé quelques-unes de ces interfaces, j'ai brièvement parlé du sujet de la gestion des événements, mais je me suis arrêté avant une description complète de la gestion des événements telle qu'implémentée par l'AWT. Ce mois-ci, nous reprenons là où nous nous sommes arrêtés.

Être orienté événement

Dans un passé lointain, un programme qui voulait savoir ce que faisait l'utilisateur devait collecter activement ces informations lui-même. En pratique, cela signifiait qu'après l'initialisation d'un programme, il entrait dans une grande boucle dans laquelle il cherchait à plusieurs reprises si l'utilisateur faisait quelque chose d'intéressant (par exemple, appuyer sur un bouton, toucher une touche, déplacer un curseur, déplacer la souris) puis a pris les mesures appropriées. Cette technique est connue sous le nom d' interrogation .

L'interrogation fait le travail mais a tendance à être lourde lorsqu'elle est utilisée dans les applications modernes pour deux raisons liées: Premièrement, l'utilisation de l'interrogation a tendance à pousser tout le code de gestion des événements dans un seul endroit (à l'intérieur de la grande boucle); deuxièmement, les interactions qui en résultent dans la grande boucle ont tendance à être complexes. En outre, l'interrogation nécessite qu'un programme reste en boucle, consommant des cycles de processeur, en attendant que l'utilisateur fasse quelque chose - un gaspillage sérieux d'une ressource précieuse.

L'AWT a résolu ces problèmes en adoptant un paradigme différent, qui sous-tend tous les systèmes de fenêtres modernes: la programmation événementielle. Dans l'AWT, toutes les actions utilisateur appartiennent à un ensemble abstrait d'éléments appelés événements . Un événement décrit, de manière suffisamment détaillée, une action particulière de l'utilisateur. Plutôt que de collecter activement les événements générés par l'utilisateur, le programme d'exécution Java avertit le programme lorsqu'un événement intéressant se produit. On dit que les programmes qui gèrent l'interaction de l'utilisateur de cette manière sont pilotés par les événements .

La classe Event

La classe Événement est le joueur principal du jeu événementiel. Il tente de capturer les caractéristiques fondamentales de tous les événements générés par l'utilisateur. Le tableau 1 répertorie les membres de données publiques fournis par la classe Event.

Type Nom La description
Objet cible Une référence au composant qui a initialement reçu l'événement.
longue quand Le moment auquel l'événement s'est produit.
int id Le type d'événement (voir la section Types d'événement pour plus d'informations).
int X Coordonnée x à laquelle l'action s'est produite par rapport au composant qui traite actuellement l'événement. Pour un événement donné, la coordonnée x changera de valeur à mesure que l'événement montera dans la hiérarchie des composants. L'origine du plan de coordonnées se trouve dans le coin supérieur gauche du composant.
int y Coordonnée y à laquelle l'action s'est produite par rapport au composant qui traite actuellement l'événement. Pour un événement donné, la coordonnée y changera de valeur à mesure que l'événement montera dans la hiérarchie des composants. L'origine du plan de coordonnées se trouve dans le coin supérieur gauche du composant.
int clé Pour les événements de clavier, le code clé de la touche qui vient d'être pressée. Sa valeur sera généralement la valeur Unicode du caractère représenté par la clé. D'autres possibilités incluent des valeurs pour les touches spéciales HOME, END, F1, F2, etc.
int modificateurs Combinaison arithmétique des valeurs SHIFT_MASK, CTRL_MASK, META_MASK et ALT_MASK. Sa valeur représente respectivement l'état des touches shift, control, meta et alt.
int clickCount Le nombre de clics de souris consécutifs. Ce membre de données n'est significatif que dans les événements MOUSE_DOWN.
Objet arg Un argument dépendant de l'événement. Pour les objets Button, cet objet est un objet String qui contient l'étiquette texturale du bouton.
Tableau 1: membres de données publiques fournis par l'événement de classe

Comme je l'expliquerai dans la section intitulée Distribution et propagation d'événements , une instance de la classe Event est généralement créée par le système d'exécution Java. Il est cependant possible pour un programme de créer et d'envoyer des événements aux composants via leur postEvent()méthode.

Types d'événements

Comme mentionné ci-dessus, la classe Event est un modèle d'événement d'interface utilisateur. Les événements entrent naturellement dans des catégories basées sur le type de l'événement (le type d'événement est indiqué par le idmembre de données). Le tableau 2 répertorie tous les événements définis par l'AWT, triés par catégorie.

Tableau 2: Evénements définis par l'AWT, triés par catégorie

Il peut être instructif de voir la génération d'événements en action. Le bouton de la figure 1, lorsqu'il est enfoncé, crée un navigateur d'événements qui affiche des informations sur les événements que le navigateur reçoit. Le code source du navigateur d'événements est disponible ici.

Vous avez besoin d'un navigateur compatible Java pour afficher cette applet

Figure 1: Génération d'événements en action

Envoi et propagation d'événements

Considérez l'applet de la figure 2. Il se compose de deux instances de la classe Button, intégrées dans une instance de la classe Panel. Cette instance de la classe Panel est elle-même intégrée dans une autre instance de la classe Panel. La dernière instance de la classe Panel se trouve sous une instance de la classe TextArea, et les deux instances sont incorporées dans une instance de la classe Applet. La figure 3 présente les éléments qui composent cet applet disposé sous forme d'arborescence, avec les instances TextArea et Button comme feuilles et une instance Applet comme racine. (Pour plus d'informations sur la disposition hiérarchique des composants dans une interface utilisateur, lisez l'introduction du mois dernier à l'AWT.)

Vous avez besoin d'un navigateur compatible Java pour afficher cette applet

Figure 2: Classes intégrées dans les classes

Figure 3: arborescence des éléments de l'applet (hiérarchie)

Lorsqu'un utilisateur interagit avec l'applet de la figure 2, le système d'exécution Java crée une instance de la classe Event et remplit ses données membres avec des informations décrivant l'action. Le système d'exécution Java permet alors à l'applet de gérer l'événement. Il commence par le composant qui a initialement reçu l'événement (par exemple, le bouton sur lequel l'utilisateur a cliqué) et monte dans l'arborescence des composants, composant par composant, jusqu'à ce qu'il atteigne le conteneur en haut de l'arborescence. En cours de route, chaque composant a la possibilité d'ignorer l'événement ou d'y réagir de l'une (ou plusieurs) des manières suivantes:

  • Modifier les données membres de l'instance Event
  • Agissez et effectuez des calculs basés sur les informations contenues dans l'événement
  • Indiquez au système d'exécution Java que l'événement ne doit pas se propager plus haut dans l'arborescence

Le système d'exécution Java transmet les informations d'événement à un composant via la handleEvent()méthode du composant . Toutes les handleEvent()méthodes valides doivent être de la forme

public boolean handleEvent (événement e) 

Un gestionnaire d'événements nécessite une seule information: une référence à l'instance de la classe Event contenant des informations sur l'événement qui vient de se produire.

La valeur renvoyée par la handleEvent()méthode est importante. Il indique au système d'exécution Java si l'événement a été complètement géré ou non dans le gestionnaire d'événements. Une valeur vraie indique que l'événement a été géré et que la propagation doit s'arrêter. Une valeur fausse indique que l'événement a été ignoré, n'a pas pu être géré ou a été traité de manière incomplète et doit continuer dans l'arborescence.

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
Todd Sundsted a programmé depuis que les ordinateurs sont devenus disponibles dans les modèles de bureau. Bien qu'à l'origine intéressé par la création d'applications d'objets distribués en C ++, Todd est passé au langage de programmation Java lorsque Java est devenu un choix évident pour ce genre de chose. En plus de la rédaction, Todd fournit des services de consultation Internet et d'applications Web aux entreprises du sud-est des États-Unis.

En savoir plus sur ce sujet

  • Le tutoriel Java par Mary Campione et Kathy Walrath. La version préliminaire en ligne est disponible à l'adresse //java.sun.com/tutorial/index.html.

Cette histoire, "Java et gestion des événements" a été initialement publiée par JavaWorld.