Java y manejo de eventos

La mayoría de los programas, para ser útiles, deben responder a los comandos del usuario. Para hacerlo, los programas Java se basan en eventos que describen las acciones del usuario.

El mes pasado demostré cómo ensamblar una interfaz gráfica de usuario a partir de componentes proporcionados por el kit de herramientas de ventanas abstractas de la biblioteca de clases de Java. Después de ensamblar algunas de estas interfaces, hablé brevemente sobre el tema del manejo de eventos, pero me detuve antes de una descripción completa del manejo de eventos implementado por AWT. Este mes, retomamos donde lo dejamos.

Ser impulsado por eventos

En el pasado lejano, un programa que quería saber qué estaba haciendo el usuario tenía que recopilar activamente esa información. En la práctica, esto significaba que después de que un programa se inicializaba, entraba en un gran bucle en el que miraba repetidamente para ver si el usuario estaba haciendo algo interesante (por ejemplo, presionar un botón, tocar una tecla, mover un control deslizante, mover el mouse) y luego tomó la acción apropiada. Esta técnica se conoce como sondeo .

El sondeo hace el trabajo pero tiende a ser difícil de manejar cuando se usa en aplicaciones modernas por dos razones relacionadas: Primero, el uso del sondeo tiende a empujar todo el código de manejo de eventos a una ubicación (dentro del gran bucle); en segundo lugar, las interacciones resultantes dentro del gran bucle tienden a ser complejas. Además, el sondeo requiere que un programa permanezca en un bucle, consumiendo ciclos de CPU, mientras espera que el usuario haga algo, un desperdicio de un recurso valioso.

El AWT resolvió estos problemas adoptando un paradigma diferente, uno que subyace a todos los sistemas de ventanas modernos: la programación dirigida por eventos. Dentro del AWT, todas las acciones del usuario pertenecen a un conjunto abstracto de cosas llamadas eventos . Un evento describe, con suficiente detalle, la acción de un usuario en particular. En lugar de que el programa recopile activamente eventos generados por el usuario, el tiempo de ejecución de Java notifica al programa cuando ocurre un evento interesante. Se dice que los programas que manejan la interacción del usuario de esta manera son impulsados ​​por eventos .

La clase Event

La clase Event es el jugador principal en el juego de eventos. Intenta capturar las características fundamentales de todos los eventos generados por el usuario. La Tabla 1 enumera los miembros de datos públicos proporcionados por la clase Event.

Tipo Nombre Descripción
Objeto objetivo Una referencia al componente que recibió inicialmente el evento.
largo cuando El momento en el que ocurrió el evento.
En t carné de identidad El tipo de evento (consulte la sección Tipos de eventos para obtener más información).
En t X La coordenada x en la que ocurrió la acción en relación con el componente que actualmente está procesando el evento. Para un evento dado, la coordenada x cambiará de valor a medida que el evento asciende en la jerarquía de componentes. El origen del plano de coordenadas está en la esquina superior izquierda del componente.
En t y La coordenada y en la que se produjo la acción en relación con el componente que actualmente está procesando el evento. Para un evento dado, la coordenada y cambiará de valor a medida que el evento asciende en la jerarquía de componentes. El origen del plano de coordenadas está en la esquina superior izquierda del componente.
En t llave Para eventos de teclado, el código de tecla de la tecla que acaba de presionar. Normalmente, su valor será el valor Unicode del carácter que representa la clave. Otras posibilidades incluyen valores para las teclas especiales INICIO, FIN, F1, F2, etc.
En t modificadores Una combinación aritmética o de los valores SHIFT_MASK, CTRL_MASK, META_MASK y ALT_MASK. Su valor representa el estado de las teclas shift, control, meta y alt, respectivamente.
En t clickCount El número de clics consecutivos del mouse. Este miembro de datos solo es significativo en eventos MOUSE_DOWN.
Objeto arg Un argumento dependiente del evento. Para los objetos Button, este objeto es un objeto String que contiene la etiqueta de textura del botón.
Tabla 1: Miembros de datos públicos proporcionados por evento de clase

Como explicaré en la sección titulada Envío y propagación de eventos, el sistema de tiempo de ejecución de Java suele crear una instancia de la clase Evento. Sin embargo, es posible que un programa cree y envíe eventos a componentes a través de su postEvent()método.

Tipos de eventos

Como se mencionó anteriormente, la clase Event es un modelo de un evento de interfaz de usuario. Los eventos se clasifican naturalmente en categorías según el tipo de evento (el tipo de evento lo indica el idmiembro de datos). La Tabla 2 enumera todos los eventos definidos por el AWT, ordenados por categoría.

Tabla 2: Eventos definidos por el AWT, ordenados por categoría

Puede ser instructivo ver la generación de eventos en acción. El botón de la Figura 1, cuando se presiona, crea un navegador de eventos que muestra información sobre los eventos que recibe el navegador. El código fuente del navegador de eventos está disponible aquí.

Necesita un navegador compatible con Java para ver este subprograma

Figura 1: Generación de eventos en acción

Despacho y propagación de eventos

Considere el subprograma de la Figura 2. Consta de dos instancias de la clase Button, incrustadas dentro de una instancia de la clase Panel. Esta instancia de la clase Panel está incrustada en otra instancia de la clase Panel. La última instancia de la clase Panel se encuentra debajo de una instancia de la clase TextArea, y ambas instancias están incrustadas dentro de una instancia de la clase Applet. La Figura 3 presenta los elementos que componen este applet dispuestos como un árbol, con las instancias TextArea y Button como hojas y una instancia Applet como raíz. (Para obtener más información sobre el diseño jerárquico de los componentes en una interfaz de usuario, lea la introducción del mes pasado al AWT).

Necesita un navegador compatible con Java para ver este subprograma

Figura 2: Clases incrustadas dentro de clases

Figura 3: Árbol de elementos del applet (jerarquía)

Cuando un usuario interactúa con el subprograma de la Figura 2, el sistema de tiempo de ejecución de Java crea una instancia de la clase Evento y llena sus miembros de datos con información que describe la acción. El sistema de ejecución de Java permite que el subprograma maneje el evento. Comienza con el componente que recibió inicialmente el evento (por ejemplo, el botón en el que se hizo clic) y se mueve hacia arriba en el árbol de componentes, componente por componente, hasta que alcanza el contenedor en la parte superior del árbol. A lo largo del camino, cada componente tiene la oportunidad de ignorar el evento o reaccionar ante él de una (o más) de las siguientes formas:

  • Modificar los miembros de datos de la instancia de evento
  • Tomar medidas y realizar algunos cálculos basados ​​en la información contenida en el evento.
  • Indique al sistema de tiempo de ejecución de Java que el evento no debe propagarse más arriba en el árbol

El sistema de tiempo de ejecución de Java pasa información de eventos a un componente a través del handleEvent()método del componente . Todos los handleEvent()métodos válidos deben tener la forma

public boolean handleEvent (Evento e) 

Un controlador de eventos requiere una sola pieza de información: una referencia a la instancia de la clase Event que contiene información sobre el evento que acaba de ocurrir.

El valor devuelto por el handleEvent()método es importante. Indica al sistema de tiempo de ejecución de Java si el evento se ha manejado completamente dentro del controlador de eventos. Un valor verdadero indica que el evento se ha manejado y la propagación debe detenerse. Un valor falso indica que el evento se ha ignorado, no se pudo manejar o se ha manejado de forma incompleta y debería continuar en el árbol.

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 ha estado programando desde que las computadoras estuvieron disponibles en modelos de escritorio. Aunque originalmente estaba interesado en construir aplicaciones de objetos distribuidos en C ++, Todd se mudó al lenguaje de programación Java cuando Java se convirtió en una opción obvia para ese tipo de cosas. Además de escribir, Todd ofrece servicios de consultoría de aplicaciones web e Internet a empresas del sureste de los Estados Unidos.

Más información sobre este tema

  • El tutorial de Java de Mary Campione y Kathy Walrath. La versión preliminar en línea está disponible en //java.sun.com/tutorial/index.html.

Esta historia, "Java y manejo de eventos" fue publicada originalmente por JavaWorld.