Si bien JDK 1.1 ciertamente ha simplificado el manejo de eventos con la introducción del modelo de eventos de delegación, no facilita a los desarrolladores la creación de sus propios tipos de eventos. El procedimiento básico que se describe aquí es bastante sencillo. En aras de la simplicidad, no discutiré conceptos de habilitación de eventos y máscaras de eventos. Además, debe saber que los eventos creados mediante este procedimiento no se publicarán en la cola de eventos y funcionarán solo con oyentes registrados.
Actualmente, el núcleo de Java consta de 12 tipos de eventos definidos en java.awt.events
:
- ActionEvent
- AjusteEvento
- ComponentEvent
- ContainerEvent
- FocusEvent
- InputEvent
- ItemEvent
- Evento clave
- MouseEvent
- PaintEvent
- TextEvent
- WindowEvent
Dado que la creación de nuevos tipos de eventos no es una tarea trivial, debe examinar los eventos que forman parte del núcleo de Java. Si es posible, intente utilizar esos tipos en lugar de crear otros nuevos.
Sin embargo, habrá ocasiones en las que será necesario desarrollar un nuevo tipo de evento para un nuevo componente. Para los propósitos de esta discusión, usaré el ejemplo de un componente simple, un panel de asistente, como un medio para demostrar cómo crear un nuevo tipo de evento.
Un panel de asistente implementa una interfaz de asistente simple . El componente consta de un panel de tarjetas que se puede avanzar con el botón SIGUIENTE. El botón ATRÁS le permite volver al panel anterior. También se proporcionan los botones FINALIZAR y CANCELAR.
Para hacer que el componente sea flexible, quería brindar un control total sobre las acciones realizadas por todos los botones al desarrollador que lo usa. Por ejemplo, cuando se presiona el botón SIGUIENTE, el desarrollador debería poder verificar primero si los datos requeridos se ingresaron en el componente actualmente visible antes de avanzar al siguiente componente.
Hay cinco tareas principales para crear su propio tipo de evento:
Crea un detector de eventos
Crea un adaptador de escucha
Crea una clase de evento
Modificar el componente
- Gestionar varios oyentes
Examinaremos cada una de estas tareas por turno y luego las juntaremos todas.
Crea un detector de eventos
Una forma (y hay muchas) de informar a los objetos que se ha producido una determinada acción es crear un nuevo tipo de evento que se pueda entregar a los oyentes registrados. En el caso del panel del asistente, un oyente debe admitir cuatro casos de eventos diferentes, uno para cada botón.
Empiezo por crear una interfaz de escucha. Para cada botón, defino un método de escucha de la siguiente manera:
import java.util.EventListener; La interfaz pública WizardListener extiende EventListener {vacío abstracto público nextSelected (WizardEvent e); público abstracto void backSelected (WizardEvent e); public abstract void cancelSelected (WizardEvent e); public abstract void finishSelected (WizardEvent e); }
Cada método toma un argumento:, WizardEvent
que se define a continuación. Tenga en cuenta que la interfaz se extiende EventListener
, que se utiliza para identificar esta interfaz como un oyente de AWT.
Crea un adaptador de escucha
La creación de un adaptador de escucha es un paso opcional. En AWT, un adaptador de escucha es una clase que proporciona una implementación predeterminada para todos los métodos de un determinado tipo de escucha. Todas las clases de adaptadores del java.awt.event
paquete proporcionan métodos vacíos que no hacen nada. Aquí hay una clase de adaptador para WizardListener
:
public class WizardAdapter implementa WizardListener {public void nextSelected (WizardEvent e) {} public void backSelected (WizardEvent e) {} public void cancelSelected (WizardEvent e) {} public void finishSelected (WizardEvent e) {}}
Al escribir una clase que va a ser un oyente asistente, es posible extender WizardAdapter
y proporcionar implementación para (o anular) solo los métodos de oyente que sean de interés. Esta es estrictamente una clase de conveniencia.
Crea una clase de evento
El siguiente paso es crear el actual Event
clase aquí: WizardEvent
.
import java.awt.AWTEvent; La clase pública WizardEvent extiende AWTEvent {public static final int WIZARD_FIRST = AWTEvent.RESERVED_ID_MAX + 1; public static final int NEXT_SELECTED = WIZARD_FIRST; public static final int BACK_SELECTED = WIZARD_FIRST + 1; public static final int CANCEL_SELECTED = WIZARD_FIRST + 2; public static final int FINISH_SELECTED = WIZARD_FIRST + 3; public static final int WIZARD_LAST = WIZARD_FIRST + 3; public WizardEvent (fuente del asistente, int id) {super (fuente, id); }}
Dos constantes, WIZARD_FIRST
y WIZARD_LAST
, marcan el rango inclusivo de máscaras que usa esta clase de evento. Tenga en cuenta que los ID de eventos utilizan la RESERVED_ID_MAX
constante de clase AWTEvent
para determinar el rango de ID que no entrarán en conflicto con los valores de ID de eventos definidos por el AWT. A medida que se agregan más componentes de AWT, es RESERVED_ID_MAX
posible que aumente en el futuro.
Las cuatro constantes restantes representan cuatro ID de eventos, cada uno correspondiente a un tipo de acción diferente, según lo definido por la funcionalidad del asistente.
El ID de evento y el origen del evento son dos argumentos para el constructor de eventos del asistente. El origen del evento debe ser de tipo Wizard
, es decir, el tipo de componente para el que está definido el evento. El razonamiento es que solo un panel de asistente puede ser una fuente de eventos de asistente. Tenga en cuenta que la WizardEvent
clase se extiende AWTEvent
.
Modificar el componente
El siguiente paso es equipar nuestro componente con métodos que le permitan registrar y eliminar oyentes para el nuevo evento.
Para entregar un evento a un oyente, normalmente se llamaría al método de oyente de eventos apropiado (dependiendo de la máscara del evento). Puedo registrar un oyente de acciones para recibir eventos de acción desde el botón SIGUIENTE y transmitirlos a los WizardListener
objetos registrados . El actionPerformed
método del oyente de acciones para el botón SIGUIENTE (u otras acciones) podría implementarse de la siguiente manera:
public void actionPerformed (ActionEvent e) {// no hacer nada si no hay oyentes registrados if (wizardListener == null) return; WizardEvent w; Fuente del asistente = esto; if (e.getSource () == nextButton) {w = new WizardEvent (fuente, WizardEvent.NEXT_SELECTED); wizardListener.nextSelected (w); } // maneja el resto de los botones del asistente de manera similar}
Nota: En el ejemplo anterior, el Wizard
panel en sí es el oyente del botón SIGUIENTE .
Cuando se presiona el botón SIGUIENTE, WizardEvent
se crea una nueva con la fuente y la máscara apropiadas que corresponde al botón SIGUIENTE que se está presionando.
En el ejemplo, la línea
wizardListener.nextSelected (w);
hace referencia al wizardListener
objeto que es una variable miembro privada Wizard
y es de tipo WizardListener
. Hemos definido este tipo como el primer paso para crear un nuevo evento componente.
A primera vista, el código anterior parece restringir el número de oyentes a uno. La variable privada wizardListener
no es una matriz y solo se realiza una nextSelected
llamada. Para explicar por qué el código anterior en realidad no plantea esa restricción, examinemos cómo se agregan los oyentes.
Cada componente nuevo que genera eventos (predefinidos o nuevos) debe proporcionar dos métodos: uno para admitir la adición de oyentes y otro para admitir la eliminación de oyentes. En el caso de la Wizard
clase, estos métodos son:
addWizardListener vacío sincronizado público (WizardListener l) {wizardListener = WizardEventMulticaster.add (wizardListener, l); } removeWizardListener vacío sincronizado público (WizardListener l) {wizardListener = WizardEventMulticaster.remove (wizardListener, l); }
Ambos métodos realizan una llamada a los miembros del método estático de la clase WizardEventMulticaster
.
Gestionar varios oyentes
Si bien es posible utilizar una Vector
de gestionar múltiples oyentes, JDK 1.1 define una clase especial para el mantenimiento de una lista de oyentes: AWTEventMulticaster
. Una única instancia de multidifusión mantiene referencias a dos objetos de escucha. Debido a que el multicaster también es un oyente en sí mismo (implementa todas las interfaces de oyentes), cada uno de los dos oyentes de los que realiza un seguimiento también puede ser multicaster, creando así una cadena de oyentes de eventos o multicasters:
Si un oyente también es un multicaster, entonces representa un eslabón en la cadena. De lo contrario, es simplemente un oyente y, por lo tanto, es el último elemento de la cadena.
Desafortunadamente, no es posible simplemente reutilizar AWTEventMulticaster
para manejar la multidifusión de eventos para nuevos tipos de eventos. Lo mejor que se puede hacer es ampliar el multicaster AWT, aunque esta operación es bastante cuestionable. AWTEventMulticaster
contiene 56 métodos. De estos, 51 métodos brindan soporte para los 12 tipos de eventos y sus correspondientes oyentes que forman parte de AWT. Si eres una subclase AWTEventMulticaster
, nunca los usarás de todos modos. De los cinco métodos restantes addInternal(EventListener, EventListener)
, y remove(EventListener)
deben recodificarse. (Digo recodificado porque in AWTEventMulticaster
, addInternal
es un método estático y, por lo tanto, no se puede sobrecargar. Por razones que desconozco en este momento, remove
hace una llamada a addInternal
y debe sobrecargarse).
Dos métodos, save
y saveInternal
, brindan soporte para la transmisión de objetos y se pueden reutilizar en la nueva clase multicaster. El último método que admite rutinas de eliminación de oyentes removeInternal
, también se puede reutilizar, siempre que se hayan implementado nuevas versiones de remove
y addInternal
.
En aras de la simplicidad, voy a subclase AWTEventMulticaster
, pero con muy poco esfuerzo, es posible codificar remove
, save
y saveInternal
y tienen una, multicaster completamente funcional evento independiente.
Aquí está el evento multicaster implementado para manejar WizardEvent
:
importar java.awt.AWTEventMulticaster; import java.util.EventListener; WizardEventMulticaster de clase pública extiende AWTEventMulticaster implementa WizardListener {protegido WizardEventMulticaster (EventListener a, EventListener b) {super (a, b); } public static WizardListener add (WizardListener a, WizardListener b) {return (WizardListener) addInternal (a, b); } público estático WizardListener remove (WizardListener l, WizardListener oldl) {return (WizardListener) removeInternal (l, oldl); } public void nextSelected (WizardEvent e) {// la excepción de conversión nunca ocurrirá en este caso // la conversión _ es_ necesaria porque este multicaster puede // manejar más de un oyente si (a! = null) ((WizardListener) a). nextSelected (e); if (b! = null) ((WizardListener) b) .nextSelected (e); } public void backSelected (WizardEvent e) {if (a! = null) ((WizardListener) a).backSelected (e); if (b! = null) ((WizardListener) b) .backSelected (e); } public void cancelSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .cancelSelected (e); if (b! = null) ((WizardListener) b) .cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } EventListener estático protegido addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}= nulo) ((WizardListener) b) .backSelected (e); } public void cancelSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .cancelSelected (e); if (b! = null) ((WizardListener) b) .cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } EventListener estático protegido addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}= nulo) ((WizardListener) b) .backSelected (e); } public void cancelSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .cancelSelected (e); if (b! = null) ((WizardListener) b) .cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } EventListener estático protegido addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}} public void cancelSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .cancelSelected (e); if (b! = null) ((WizardListener) b) .cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } EventListener estático protegido addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}} public void cancelSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .cancelSelected (e); if (b! = null) ((WizardListener) b) .cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } EventListener estático protegido addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } EventListener estático protegido addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}cancelSelected (e); } public void finishSelected (WizardEvent e) {if (a! = null) ((WizardListener) a) .finishSelected (e); if (b! = null) ((WizardListener) b) .finishSelected (e); } EventListener estático protegido addInternal (EventListener a, EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}EventListener b) {if (a == null) return b; if (b == null) return a; devolver nuevo WizardEventMulticaster (a, b); } protegido EventListener remove (EventListener oldl) {if (oldl == a) return b; if (oldl == b) return a; EventListener a2 = removeInternal (a, oldl); EventListener b2 = removeInternal (b, oldl); si (a2 == a && b2 == b) devuelve esto; return addInternal (a2, b2); }}
Métodos en la clase multicaster: una revisión
Repasemos los métodos que forman parte de la clase multicaster anterior. El constructor está protegido y para obtener uno nuevo WizardEventMulticaster
, se add(WizardListener, WizardListener)
debe llamar a un método estático . Se necesitan dos oyentes como argumentos que representan dos partes de una cadena de oyentes para vincular:
Para comenzar una nueva cadena, use null como primer argumento.
Para agregar un nuevo oyente, use el oyente existente como primer argumento y un nuevo oyente como segundo argumento.
Esto, de hecho, es lo que se ha hecho en el código de clase Wizard
que ya hemos examinado.
Otra rutina estática es remove(WizardListener, WizardListener)
. El primer argumento es un oyente (o un oyente multicaster) y el segundo es un oyente que debe eliminarse.
Four public, non-static methods were added to support event propagation through the event chain. For each WizardEvent
case (that is, next, back, cancel, and finish selected) there is one method. These methods must be implemented since the WizardEventMulticaster
implements WizardListener
, which in turn requires the four methods to be present.
How it all works together
Let's now examine how the multicaster actually is used by the Wizard
. Let's suppose a wizard object is constructed and three listeners are added, creating a listener chain.
Inicialmente, la variable privada wizardListener
de clase Wizard
es nula. Entonces, cuando se realiza una llamada a WizardEventMulticaster.add(WizardListener, WizardListener)
, el primer argumento wizardListener
,, es nulo y el segundo no (no tiene sentido agregar un oyente nulo). El add
método, a su vez, llama addInternal
. Dado que uno de los argumentos es nulo, el retorno de addInternal
es el oyente no nulo. El retorno se propaga al add
método que devuelve el oyente no nulo al addWizardListener
método. Allí, la wizardListener
variable se establece en el nuevo oyente que se agrega.