Eventos


 

Eventos

Nota: Esta es una de las unidades más importante de todo el curso.

Cuando el usuario de un programa o applet mueve el ratón, o hace un clic o usa el teclado, genera un Evento. En Java los eventos, como cualquier otra cosa, se representan como instancias u objetos de alguna clase. Para programar una interfaz gráfica es necesario aprender a utilizar los eventos.

Nota: El JDK 1.0 tenía un modelo de eventos que cambió radicalmente en el JDK 1.1. Aquel modelo está "deprecated", es decir se sugiere dejar de usarlo. Por lo tanto no lo presentaremos, sólo estudiaremos el modelo introducido en el JDK 1.1 que es muy superior al original y tiene trazas de ser definitivo.

Cuando el usuario interactúa sobre los diversos componentes del awt, éstos generan eventos. La siguiente tabla muestra los eventos que cada tipo de componente puede generar y cuándo los genera.

Tipo de Componente Eventos generados Hechos que los generan
Button ActionEvent El usuario hace un clic sobre el botón.
Checkbox ItemEvent El usuario selecciona o deselecciona el interruptor (Checkbox)
CheckboxMenuItem ItemEvent El usuario selecciona o deselecciona el interruptor (Checkbox)
Choice ItemEvent El usuario selecciona o deselecciona un elemento de la lista
Component






ComponentEvent
El componente se mueve, cambia de tamaño, se esconde o se exhibe
FocusEvent El componente gana o pierde el foco
KeyEvent El usuario pulsa o suelta una tecla
MouseEvent El usuario pulsa o suelta un botón del ratón, el cursor del ratón entra o sale o el usuario mueve o arrastra el ratón
Container ContainerEvent Se agrega o se quita un componente al contenedor
List

 

ActionEvent El usuario hace doble clic en un elemento de la lista
ItemEvent El usuario selecciona o deselecciona un elemento de la lista
MenuItem ActionEvent El usuario selecciona un elemento del menú
Scrollbar AdjustmentEvent El usuario mueve la barra de desplazamiento
TextComponent TextEvent El usuario hace un cambio en el texto
TextField ActionEvent El usuario termina de editar el texto (hace un intro)
Window
WindowEvent
La ventana se abre, se cierra, se minimiza, se reestablece o se cierra.

Todos los eventos mencionados en la tabla están en el paquete java.awt.event.

Nota: En Java hay otros eventos aparte de los mencionados en la tabla. Los eventos en general se representan como objetos de subclases de la clase java.util.EventObject. Hay muchas subclases de java.util.EventObject conocidas hoy en día que el alumno puede consultar en la documentación, pero las que usaremos en el curso son sólo las que aparecen en la tabla anterior y todas ellas son subclases (derivadas directa o indirectamente) de java.awt.AWTEvent y pertenecen al paquete java.awt.event.

Para facilitar la tarea del programador se han creado una serie de interfaces que deben implementarse cuando se quieren procesar algunos de estos eventos. La siguiente tabla presenta estas interfaces con sus métodos.

Interfaz Métodos
ActionListener actionPerformed(ActionEvent)
AdjustmentListener adjustmentValueChanged(AdjustementEvent)
ComponentListener

componentHidden(ComponentEvent)
componentMoved(ComponentEvent)
componentResized(ComponentEvent)
componentShown(ComponentEvent)
ContainerListener
componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)
FocusListener
focusGained(FocusEvent)
focusLost(FocusEvent)
ItemListener itemStateChanged(ItemEvent)
KeyListener

keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)
MouseListener


mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)
MouseMotionListener
mouseDragged(MouseEvent)
mouseMoved(MouseEvent)
TextListener textValueChanged(TextEvent)
WindowListener




windowActivated(WindowEvent)
windowClosed(WindowEvent)
windowClosing(WindowEvent)
windowDeactivated(WindowEvent)
windowDeiconified(WindowEvent)
windowIconified(WindowEvent)
windowOpened(WindowEvent)

Todos los métodos de estas interfaces son de tipo public void.

¿Cómo se utilizan estas interfaces para programar una interfaz gráfica?

Supongamos que deseamos que en un Componente que estamos desarrollando (típicamante un Applet, Marco, Diálogo, Ventana o Panel) responda a los eventos generados por el usuario sobre el mismo componente o sobre algunos otros (típicamente contenidos en él). Para ello convertimos a este componente en "escucha" (Listener) de ciertos eventos generados por él o por los otros componentes. Convertir a un componente en escucha de un tipo de eventos consiste en:

  1. declarar que implementa la interfaz correspondiente,
  2. implementar los métodos de la interfaz y
  3. agregarlo a la lista de escuchas del o de los componentes que originan ese tipo de eventos. Esto se hace normalmente usando un método como addActionListener, addMouseListener, etc...

Por ejemplo, si queremos que un Applet responda a los movimientos del ratón sobre el applet y a los clics sobre un botón b colocado en el applet, será necesario declarar en su línea definitoria que implementa MouseMotionListener y ActionListener, luego habrá que implementar los métodos de ambas interfaces y también habrá que agregar el applet a la lista de escuchas de eventos del ratón del propio applet y a la lista de escuchas de ActionEvent del botón. En otras palabras hay que escribir algo así:

public class miApplet extends Applet implements
        MouseMotionListener, ActionListener {

    public void init() {
        ...
        this.addMouseMotionListener(this);
        b.addActionListener(this);
        ...
    }

    /* -- MouseMotionListener methods --*/
    public void mouseDragged(MouseEvent e) {
        ...
    }

    public void mouseMoved(MouseEvent e) {
        ...
    }

    /* -- ActionListener method --*/
    public void actionPerformed(ActionEvent e) {
        ...
    }

}

Los ejemplos que se presentan a continuación ilustran este procedimiento y muestran cómo extraer información concreta de los eventos para responder a ellos adecuadamente.

 

ActionEvent e ItemEvent

Nuestro primer ejemplo consta sólo de tres botones con títulos rojo, verde y azul y un campo de texto en el que se escribe el evento generado al hacer clic sobre cualquiera de los botones. Cuando se pulsa uno de los botones el color de fondo del applet cambia al que indica el botón. 

Este es el código del applet.

El alumno debe revisar este código y estudiar cómo se logra distinguir cual es el botón que se ha pulsado. Observe que esto se hace utilizando e.getSource() que devuelve precisamente el objeto que produjo el evento. De ahí que para reconocer el botón pulsado se hagan comparaciones de cada botón con e.getSource(). Otra manera de distinguir los botones es comparando e.getActionCommand() con las etiquetas de los botones (esto debe hacerse usando el método equals de String y no la simple igualdad  == entre objetos).

El siguiente applet ilustra cómo se usan los eventos ItemEvent y ActionEvent. Los dos campos de texto de la parte superior informan cuál es el origen (source) del evento y el tipo de evento de que se trata con su valor descriptivo, respectivamente.

Pruebe a realizar selecciones en todas las componentes, escribir en el campo de texto y dar un intro (o return). Observe que se produce un ActionEvent al hacer clic sobre el botón, al hacer un doble clic en uno de los elementos de la lista, al dar intro en el campo de texto. Observe que un clic sencillo en la lista, una selección en el selector y un clic en el interruptor produce un ItemEvent.

Estudie detenidamente el código de este sencillo applet. Observe que fue construido como una extensión de preActionApplet que había sido creado en la unidad 2 con todos sus componentes. Aquí sólo se le agregaron las implementaciones de las interfaces de ItemListener y ActionListener.

El siguiente applet presenta un conocido juego de bolsillo que en mi tierra y en mi época (México D.F., años 50) se le llamaba "imposible". Su objetivo es comenzar con un marco con los cuadritos en desorden y ordenarlos moviendo cada vez alguno de los cuadros adyacentes al espacio vacío. A veces contábamos el tiempo que tardábamos en ordenarlo y hacíamos competencias. El nombre "imposible" venía de que si uno malintencionadamente sacaba los cuadros de lugar y los ponía en orden pero haciendo una única permutación, por ejemplo del 1 con el 2, resultaba imposible ordenarlo sin volver a sacar los cuadros del marco. La forma aparentemente complicada en que se programó la acción de desordenar los cuadros es para simular un desordenamiento que deje los cuadros con una solución posible. Se hizo precisamente desordenando paso a paso como si se hiciera manualmente. También este applet se construyó como una extensión de preImposible, creado anteriormente en la unidad 2.

Juegue un poco con el applet. Para mover un cuadro basta hacer clic sobre él, se moverá al cuadro vacío si está adyacente a él. Convénzase de que la solución siempre es posible. Después estudie el código del applet y en especial cómo se utilizan los ActionEvents generados por los clics del ratón en los botones.

El alumno debe estudiar el código de este ejemplo con gran detenimiento. Debe observar sobre todo cómo se programó la respuesta a los clics sobre los botones. Observe que para saber sobre cual botón se hizo el clic el programa recorre todos los botones e investiga si la fuente (e.getSource()) del evento es ese botón. Por otro lado, e independientemente de la implementación de la interfaz ActionListener, el alumno puede obtener mucha experiencia de este código.

Es importante que el alumno observe la ventaja que ha representado en estos dos ejemplos separar el aspecto gráfico del aspecto funcional, lo cual se hizo programando primero un applet en el que sólo se crean y acomodan los componentes gráficos y luego creando una subclase que agrega la funcionalidad, es decir, el tratamiento de los eventos.

 

MouseEvent e MouseMotionEvent

Para comenzar presentamos un sencillo applet que muestra todos los MouseEvents que se producen sobre una pequeña pizarra roja. Pase el rarón por encima de la pizarra, haga click en ella, arrástrelo, etc... y vea el evento que se produce en cada caso escrito en el cuadro de texto si se trata de un MouseEvent y en el campo de texto si en un MouseMotionEvent.

Compruebe que cuando el ratón entra al cuadro rojo se produce un evento MOUSE_ENTERED y cuando sale se produce un MOUSE_EXITED. Compruebe que cuando se hace un clic se producen (en este orden) los eventos MOUSE_PRESSED, MOUSE_RELEASED y MOUSE_CLICKED.  Compruebe también que cuando se pulsa un botón, se hace un arrastre y luego se suelta el botón, hay un MOUSE_PRESSED y MOUSE_RELEASED pero no un MOUSE_CLICKED. ¿Qué diferencia hay si se pulsa el botón derecho en lugar del izquierdo? ¿Cómo se nota si un arrastre se está haciendo con el botón izquierdo o con el derecho? (Para responder a esta últimas preguntas observe el valor de mods, que son lo modificadores o "modifiers" del evento). ¿Qué pasa si se hace un doble clic?

Es importante saber que toda la información que aparece en el cuadro de texto o en el campo de texto puede extraerse del evento dentro del programa usando los métodos de la clase MouseEvent (o de alguna de sus superclases) como getModifiers(), getClickCount(), getX(), getY(), etc...

Este es el sencillo código de este applet:

Estúdielo y utilícelo como información y como fuente para otros programas que debar responder a las acciones del ratón. Observe que cada vez que se agrega un evento al cuadro de texto se pone el cursor (caret) al final para asegurar que lo último que se agrega es visible.

Ahora presentamos un applet que es ya un programa donde se utilizan los eventos del ratón y que sirve para hacer dibujos sencillos. Para ello implementa las interfaces MouseEvent y MouseMotionEvent, además de ActionEvent e ItemEvent. Está basado en el applet preDibuja creado en la unidad 2.

Estudie detenidamente el código de este applet, prestando particular atención a la implementación de los métodos que responden al ratón, es decir los métodos mousePressed, mouseDragged, etc... Con ello aprenderá la técnica que se suele emplear en los programas que responden a lo que el usuario hace con el ratón, es decir aprenderá a programar interfaces gráficas que usan el  ratón.


Las otras interfaces de eventos que no se han ilustrado funcionan igual que estas, aunque por supuesto cada una de ellas tiene sus peculiaridades y detalles que hay que conocer. Dos de las más importantes son WindowListener (que ya se usó en la unidad 3) y KeyListener. Los ejercicios 4.6 y 4.7 piden al alumno estudiar y aplicar estas interfaces.

Esta unidad tiene poco texto, pero el contenido de los ejemplos es muy denso e importante y conviene estudiarlos todos detenidamante. Como podrá comprobar el alumno, la unidad también tiene muchos ejercicios.

 

WindowEvent

Los WindowEvents los producen las ventanas (Window), los cuadros de diálogo (Dialog) y los marcos (Frame) cuando éstos se abren, se activan se desactivan, se minimizan o maximizan y cuando se cierran. El siguiente applet ayuda a entender cuándo se producen estos eventos durante la vida de un marco.

Al pulsar el botón del applet se crea un marco (rojo) que aparece en la pantalla. Al mismo tiempo se escriben en el cuadro de texto los eventos generados que normalmente son dos: windowOpened y windowActivated (por alguna razón extraña a veces windowActivated no se produce, así que no hay que contar con él). Si el alumno selecciona otra ventana, como por ejemplo la del navegador, verá que se produce el evento windowDeactivated. Si selecciona de nuevo el marco rojo verá que se produce el evento windowActivated. Si pulsa el botón de minimizar se produce el evento windowIconified (y también windowDeactivated) y si reactiva el marco seleccionándolo en la barra de tareas de windows verá que se produce el evento windowDeiconified (y también windowActivated). Finalmente si pulsa el botón de cerrar el marco verá que se produce el evento windowClosing y posteriormente windowClosed.

La pulsación del botón de cerrar sólo produce el evento windowClosing, es el hecho de que en el método windowClosing(WindowEvent e) del applet hemos puesto la instrucción frame.dispose() lo que realmente produce el windowClosed. Por esta razón, para realmente cerrar una ventana es necesario destruirla con el método dispose() para que realmente desaparezca. Si en lugar de dispose() se llama por ejemplo a setVisible(false) la ventana se hace invisible pero no deja de existir y el evento windowClosed no llega a producirse.

Abajo aparece el código de este applet. El alumno debe estudiarlo cuidadosamente para entender perfectamente su funcionamiento que ilustra de forma general el ciclo de vida de un marco (Frame).

En los ejercicios se pide al alumno crear dos applets semejantes a éste que lancen una ventana y un cuadro de diálogo respectivamente. Con ellos el alumno verá que las ventanas y los cuadros de diálogo también producen algunos de los WindowEvents. ¿Cuáles producen los cuadors de diálogo? ¿Cuáles producen las ventanas? ¿Son los mismos?

Aquí se presentas estos applets (sin código pues eso lo tendrán que hacer los alumnos) para que jugando con ellos el alumno pueda responder estas preguntas.

Finalmente repetimos abajo el applet de las ventanas que se introdujo en la unidad 3 como otra ilustración del uso de WindowEvent.

El código del marco de tipo ventanas que se abre al pulsar el botón del applet "ventanas" aparee abajo. El alumno podrá observar que este applet implementa WindowListener y por tal motivo tiene los métodos windowOpened, windowActivated, windowDeactivated, windowIconified, windowDeiconified, windowClosed, windowClosing. Casi todos ellos están vacíos pues para lo único que hemos usar esta interfaz es para responder al botón de cerrar que aparece siempre arriba a la derecha de los marcos, lo cual se logra con el código del método windowClosing. Casi siempre esta es la manera en que se utiliza WindowEvent y la interfaz WindowListener.


Ejercicios