Tutorial de Java

Modelo de Delegación de Eventos

Anterior | Siguiente
De acuerdo con Javasoft, las principales características de partida que han originado el nuevo modelo de manejo de eventos en el AWT, son:
  • Que sea simple y fácil de aprender
  • Que soporte una clara separación entre el código de la aplicación y el código del interfaz
  • Que facilite la creación de robustos controladores de eventos, con menos posibilidad de generación de errores (chequeo más potente en tiempo de compilación)
  • Suficientemente flexible para permitir el flujo y propagación de eventos
  • Para herramientas visuales, permitir en tiempo de ejecución ver cómo se generan estos eventos y quien lo hace
  • Que soporte compatibilidad binaria con el modelo anterior

A continuación se muestra una revisión por encima del nuevo modelo de eventos, antes de entrar en el estudio detallado, y también se expone un ejemplo sencillito para poder entender de forma más fácil ese estudio detallado del modelo de Delegación de Eventos.

Los eventos ahora están organizados en jerarquías de clases de eventos.

El nuevo modelo hace uso de fuentes de eventos (Source) y receptores de eventos (Listener). Una fuente de eventos es un objeto que tiene la capacidad de detectar eventos y notificar a los receptores de eventos que se han producido esos eventos. Aunque el programador puede establecer el entorno en que se producen esas notificaciones, siempre hay un escenario por defecto.

Un objeto receptor de eventos es una clase (o una subclase de una clase) que implementa un interfaz receptor específico. Hay definidos un determinado número de interfaces receptores, donde cada interfaz declara los métodos adecuados al tratamiento de los eventos de su clase. Luego, hay un emparejamiento natural entre clases de eventos y definiciones de interfaces. Por ejemplo, hay una clase de eventos de ratón que incluye muchos de los eventos asociados con las acciones del ratón, y hay un interfaz que se utiliza para definir los receptores de esos eventos.

Un objeto receptor puede estar registrado con un objeto fuente para ser notificado de la ocurrencia de todos los eventos de la clase para los que el objeto receptor está diseñado. Una vez que el objeto receptor está registrado para ser notificado de esos eventos, el suceso de un evento en esta clase automáticamente invocará al método sobreescrito del objeto receptor. El código en el método sobreescrito debe estar diseñado por el programador para realizar las acciones específicas que desee cuando suceda el evento.

Algunas clases de eventos, como los de ratón, involucran a un determinado conjunto de eventos diferentes. Una clase receptor que implemente el interfaz que recoja estos eventos debe sobreescribir todos los métodos declarados en el interfaz. Para prevenir esto, de forma que no sea tan tedioso y no haya que sobreescribir métodos que no se van a utilizar, se han definido un conjunto de clases intermedias, conocidas como Adaptadores (Adapter).

Estas clases Adaptadores implementan los interfaces receptor y sobreescriben todos los métodos del interfaz con métodos vacíos. Una clase receptor puede estar definida como clase que extiende una clase Adapter en lugar de una clase que implemente el interfaz. Cuando se hace esto, la clase receptor solamente necesita sobreescribir aquellos métodos que sean de interés para la aplicación, porque todos los otros métodos serán resueltos por la clase Adapter. Por ejemplo, en el programa java1101.java, los dos objetos receptor instanciados desde dos clases diferentes, están registrados para recibir todos los eventos involucrados en la manipulación de un objeto de tipo Frame (apertura, cierre, minimización, etc.).

Uno de los objetos receptor implementa el interfaz WindowListener, por lo que debe sobreescribir los seis métodos del interfaz. La otra clase receptor extiende la clase WindowAdapter en vez de implementar el interfaz WindowListener. La clase WindowAdapter sobrescribe los seis métodos del interfaz con métodos vacíos, por lo que la clase receptor no necesita sobreescribir esos seis métodos.

El ejemplo se ha hecho lo más simple posible, de forma que los métodos sobreescritos solamente presentan un mensaje en pantalla indicando cuando han sido invocados.

Una de las cosas en las que debe reparar el lector es en la forma en que los objetos receptores son registrados para la notificación de los eventos; lo cual se hace en las sentencias que se reproducen de nuevo a continuación:

    // Se registran los dos objetos receptores para que sean 
    // notificados de los eventos que genere la ventana, que es el
    // objeto origen de los eventos
    ventana.addWindowListener( ventanaProceso1 );
    ventana.addWindowListener( ventanaProceso2 );  

La interpretación de este fragmento de código es que dos objetos receptores llamados ventanaProceso1 y ventanaProceso2 se añaden a la lista de objetos receptores que serán automáticamente notificados cuando se produzca un evento de la clase Window con respecto al objeto Frame llamado ventana.

Estos objetos receptores son notificados invocando los métodos sobrescritos de los objetos que recogen el tipo específico de evento( apertura de la ventana, cierre de la ventana, minimización de la ventana, etc.).

Los párrafos que siguen introducirán al lector en una visión más detallada del modelos de Delegación de Eventos.

En el JDK 1.0.2 los eventos se encapsulan en una sola clase llamada Event, y desde el JDK 1.1 los eventos se encapsulan en una jerarquía de clases donde la clase raíz es java.util.EventObject; en el JDK 1.2 bajo esta misma clase se encapsulan las clases de los eventos de los APIs que se han incorporado como son BeanContextEvent, DragSourceEvent, DropTargetEvent y PropertyChangeEvent. La propagación de un evento desde un objeto Fuente hasta un objeto Receptor involucra la llamada a un método en el objeto receptor y el paso de una instancia de una subclase de eventos (un objeto) que define el tipo de evento generado. Cada subclase de eventos puede incluir más de un tipo de eventos.

Un objeto receptor es una instancia de una clase que implementa un interfaz específico EventListener extendido desde el receptor genérico java.util.EventListener. Un interfaz EventListener define uno o más métodos que deben ser invocados por la fuente de eventos en respuesta a cada tipo específico de evento controlado por el interfaz.

La invocación de estos métodos redefinidos es el mecanismo por el cual el objeto Fuente notifica al Receptor que uno o más eventos han sucedido. El objeto fuente mantiene una lista de objetos receptores y los tipos de eventos a los que están suscritos. El programador crea esa lista utilizando llamadas a los métodos add<TipoEvento>Listener().

Una vez que la lista de receptores está creada, el objeto fuente utiliza esta lista para notificar a cada receptor que ha sucedido un evento del tipo que controla, sin esfuerzo alguno por parte del programador. Esto es lo que se conoce como registrar Receptores específicos para recibir la notificación de eventos determinados.

La fuente de eventos es generalmente un componente del Interfaz Gráfico, por lo que se van a dejar de lado, por ahora, los eventos generados por los APIs incorporados al JDK 1.2 y centrar el estudio en los eventos que envían los componentes del AWT. Un receptor de eventos es normalmente un objeto de una clase que implementa el adecuado interfaz del receptor. El objeto receptor también puede ser otro componente del AWT que implementa uno o más interfaces receptor, con el propósito de comunicar unos objetos del interfaz gráfico con otros.

Como ya se ha mencionado, los eventos en el nuevo modelo de Delegación no están representados por una sola clase Event con identificaciones numéricas, como en el JDK 1.0.2; sino que cada tipo específico de evento es un miembro de la clase de tipos de eventos y estas clases constituyen una completa jerarquía de clases.

Como una sola clase de evento se puede utilizar para representar más de un tipo de evento, algunas clases pueden contar con un identificador (único para cada clase) que designa a cada uno de los eventos específicos; por ejemplo, MouseEvent representa el movimiento del cursor, la pulsación de un botón, el arrastre del ratón, etc.

No hay campos públicos en las nuevas clases. En su lugar, los datos del evento están encapsulados y solamente se puede acceder a ellos a través de los adecuados métodos set..() y get..(); los primeros sólo existen para modificar atributos de un evento y sólo pueden ser utilizados por un receptor.

El AWT define un conjunto determinado de eventos, aunque el programador también puede definir sus propios tipos de eventos, derivando de EventObject, o desde una de las clases de eventos del AWT.

El AWT proporciona dos tipos conceptuales de eventos: de bajo nivel y semánticos, que se introducen a continuación, aunque posteriormente se verán con detalle.

Un evento de bajo nivel es un evento que representa una entrada de bajo nivel o un suceso sobre un componente visual de un sistema de ventanas sobre la pantalla. Eventos de este tipo son:

java.util.EventObject
  java.awt.AWTEvent 
    java.awt.event.ComponentEvent     Componente redimensionado, desplazado
      java.awt.event.FocusEvent       Pérdida, ganancia del focus por un Componente 
      java.awt.event.InputEvent 
        java.awt.event.KeyEvent       El Componente recoge una pulsación de teclado
        java.awt.event.MouseEvent     El Componente recoge movimientos del ratón, 
                                      pulsación de botones
      java.awt.event.ContainerEvent 
      java.awt.event.WindowEvent 

Como ya se ha indicado, algunas clases de eventos engloban a varios tipos distintos de eventos. Normalmente, hay un interfaz correspondiente a cada clase de evento y hay métodos del interfaz para cada tipo distinto de evento en cada clase de evento.

Un evento semántico es un evento que se define a alto nivel y encapsula una acción de un componente del interfaz de usuario. Algunos eventos de este tipo son:

java.util.EventObject
  java.awt.AWTEvent
    java.awt.event.ActionEvent        Ejecución de un comando
    java.awt.event.AdjustmentEvent    Ajuste de un valor
    java.awt.event.ItemEvent          Cambio de estado de un item
    java.awt.event.TextEvent          Cambio de valor de un texto

Estos eventos no están pensados para atender a Componentes específicos de la pantalla, sino para aplicarlos a un conjunto de eventos que implementen un modelo semántico similar. Por ejemplo, un objeto Button generará un evento action cuando sea pulsado y un objeto List generará un evento action cuando se pulse dos veces con el ratón sobre uno de los items que componen la lista.

Navegador

Home | Anterior | Siguiente | Indice | Correo