Manipulación de eventos

Chano Vera

Desarrollador
28 septiembre, 2023

Todo programa necesita ejecutar cierta funcionalidad cuando el usuario realiza una acción, ya sea, cuando presiona algún botón, mueva el mouse, etc.

Necesitamos conocer la manipulación de eventos, la cual nos permite saber:

  • Que está haciendo el usuario en la aplicación.
  • Qué lógica de programación se debe ejecutar.
  • Mostrar con mensajes informativos que fue lo que hizo internamente la aplicación.

Entonces, la manipulación de eventos consiste en:

  • Obtener las aciones que un usuario realiza sobre la interfaz gráfica de una aplicación.
  • Ejecutar cierta lógica de programación para brindarle al usuario una respuesta de lo que hizo internamente el sistema.

Java nos provee de una serie de métodos con los cuales podemos detectar las acciones que un usuario realiza sobre la interfaz de la aplicación.

Veremos más a fondo algunos de ellos.

Propagación de eventos

Te preguntarás:

  • ¿Cómo hacer la conexión entre las clases que generan los eventos (objeto fuente) y las clases que se encargarán de gestionar los eventos (objeto oyente).
  • ¿Cómo funciona algo tan sencillo, como presionar un botón?

Lo aprenderemos aquí.

La gestión de eventos se encarga de:

  • Ejecutar métodos.
  • Otorgar una a un usuario, lo que brinda un comportamiento más dinámico al programa.

Esta forma de trabajo es conocida como Programación basada a eventos o Programación dirigida a eventos.

La forma de tratar eventos se realiza mediante el Modelo de delegación de eventos o Event Model Delegation.

Modelo de delegación de eventos

Se basa en el concepto que consiste en:

El evento ocurre en un objeto [objeto fuente] y delega la gestión de su tratamiento o otro objeto [objeto oyente].

¿Cómo funciona el modelo de delegación de eventos?

  • El objeto fuente registra los oyentes.
  • Cuando un evento ocurre, el objeto fuente invoca un método del oyente.
  • Para que esta comunicación sea posible, el objeto fuente debe enviar el evento generado al objeto oyente.

¿Qué ventajas tiene usar el modelo de delegación de eventos?

  • Tener los conceptos de objeto fuente y objeto oyente.
  • Nos facilita la creación de código más robusto para la gestión de eventos.
  • Permite marcar una separación más precisa entre la interfaz y la lógica de programación.

Este modelo es muy versátil y fácil de ampliar, brinda la posibilidad de registrar varios objetos oyentes, y a la vez permite que varios objetos oyentes sean registrados por más de un objeto fuente.

Control de eventos

Ya hemos hablado de como funcional los eventos y su propagación, pero aún no conocemos la forma de controlar o ejecutar la acción internamente el la aplicación.

Enseguida conoceremos cómo es este proceso.

  1. Sabemos que la raíz de todos los eventos es la clase java.util.EventoObject.
  2. Esta clase posee como único atributo un objeto, el cual a su vez contiene el método getSource(). Dicho método nos permite acceder al objeto para conocer que evento lo generó.
  3. El resto de las clases que le proceden empiezan a añadir sus propios métodos y atributos para responder a cada tipo de acción del usuario.

A continuación se explica un ejemplo completo del uso de un evento cuando se presiona un botón y se realiza una modificación al texto inicial en la ventana.

Nota

Solo explicaremos como funcionan los eventos.

Funcionamiento gráfico del ejemplo anterior

Objeto oyente

El objeto fuente es aquel donde se genera el evento.

Para que el manejo de eventos sea posible, se deben registrar los objetos oyentes.

Para ello, Java nos provee de algunos métodos para estar en escucha, en el ejemplo anterior se utilizó addActionListener:

buttonA.addActionListener(listenButton);

Aunque este método no es el único, existe algunos más, tales como:

  • addKeyListener
  • addComponentListener
  • addFocusListener
  • addInputMethodListener
  • addMouseListener
  • addMouseMotionListener
  • addPropertyChangeListener

Nos es posible dejar de escuchar el evento mediante algunos métodos que nos provee Java, para remover la escucha del evento en el ejemplo anterior, sería de la siguiente forma:

buttonA.removeActionListener(listenButton);

Aunque este método no es el único, existen algunos más, como:

  • removeKeyListener
  • removeComponentListener
  • removeFocusListener
  • removeImputMethodListener
  • removeKeyListener
  • removeMouseListener
  • removeMouseMotionListener
  • removePropertyChangeListener

Un objeto oyente es el que controla cuál bloque de código será ejecutado cuando se emita un evento.

Con este propósito, puede definir más de un método para gestionar la respuesta por cada tipo de evento.

Para reaccionar adecuadamente, la clase oyente recibe el evento que se genera, esto garantiza el uso de la interfaz adecuada para el procesamiento de dicho evento.

Depende de cada interfaz proporcionar los métodos correspondientes al tipo de evento.

Considera que anteriormente ya fue agregada la clase oyente al registro de oyentes:

buttonA.addActionListener(listenButton);

buttonB.addActionListener(listenButton);

Entonces, la lógica que ejecutará cada botón se encuentra en su respectiva condicional, como se muestra a continuación:

public class ListenButton implements ActionListener {
    @Override
    public void ActionPerformed(ActionEvent e) {
        if(e.getSource() == buttonA) {
            // lógica de cuando sea presionado el botón A
        } if(e.getSource() == buttonB) {
            // lógica de cuando sea presionado el botón B
        }
    }
}

Sincronización de eventos

Hasta el momento solo hemos visto los eventos de la interfaz ActionListener.

Pero también existen eventos de mouse el cual contiene más de un método y es necesario conocer el orden de ejecución de cada uno de esos métodos para realizar un ejemplo de sincronización de eventos.

En las siguiente clase se pueden ver los métodos de la interfaz MouseListener.

Nota:

Para los siguiente ejemplos, supongamos que previamente ya se agregó el objeto oyente a un botón en la interfaz gráfica.

public class EventMouse implements MouseListener {
    
    /*
    Ocurre cuando el botón termina de ser presionado
    después del método mouseReleased
    */
    @Override
    public void mouseClicked(MouseEvent e) {/*código*/}

    /*
    Ocurre cuando se está presionando el botón
    */
    @Override
    public void mousePressed(MouseEvent e) {/*código*/}

    /*
    Ocurre cuando se deja de presionar el botón
    */
    @Override
    public void mouseReleased(MouseEvent e) {/*código*/}

    /*
    Ocurre cuando el puntero entra en el botón
    */
    @Override
    public void mouseEntered(MouseEvent e) {/*código*/}

    /*
    Ocurre cuando el puntero abandona el área del botón
    */
    @Override
    public void mouseExited(MouseEvent e) {/*código*/}
}

Manipulación de eventos

Ahora veamos un ejemplo completo utilizando la interfaz MouseListener.

A continuación veremos cómo cada uno de los métodos de la interfaz MouseListener tiene un orden de ejecución.

Si ejecutamos el código anterior nos mostrará la siguiente ventana, en la cual podemos ver un botón sobre el cual fueron registrados los eventos del mouse.
Si entramos al botón con el mouse, presionamos el botón y después abandonamos el área del botón, la consola nos mostrará estos mensajes.

Antes de ver un ejemplo utilizando la sincronización de eventos vamos a ver hilos en Java, mejor conocidos como Thread.

Para ello, primero comprenderemos como es que Java ejecuta cada línea de código.

Tenemos un método en Java que manda llamar cuatro métodos, pero estos serán ejecutados uno por uno, es decir, de forma secuencial, ejecutará todas las sentencias del método add para después continuar con las sentencias del método subtract y así con el resto de métodos.

public void basicOperations() {
    this.add();
    this.subtract();
    this.multiply();
    this.divide();
}
public double add() {/* código */}

Ejecuta las sentencias del método

public double subtract() {/* código */}

Ejecuta las sentencias del método

public double multiply() {/* código */}

Ejecuta las sentencias del método

public double divide() {/* código */}

Ejecuta las sentencias del método

Entonces, cómo habíamos comentado, en Java podemos usar hilos, los cuales nos permiten:

  • Ejecutar más de un proceso.
  • Esto hace que el programa se ejecute en menor tiempo y sea más eficiente.

Nota:

Aunque el número de hilos está limitado al número de núcleos que tiene cada computadora, en la actualidad, la mayoría de las computadoras tienen más de un núcleo.

Por lo tanto:

Si tenemos un procesador con dos núcleos, el programa puede ser capaz de ejecutar dos procesos a la vez.

Si fuera el caso de que tuviera cuatro núcleos, el número de procesos que podríamos ejecutar concurrentemente sería de cuatro.

Para este ejemplo consideramos que tenemos un procesador con cuatro núcleos, entonces el programa sería capaz de ejecutar los cuatro métodos al mismo tiempo y así cada método no estaría a la espera de que el otro termine para ser procesado.

Si durante el desarrollo de la aplicación no hacemos uso de hilos, el programa solo será capaz de ejecutar un solo proceso a la vez.

En cambio, si hacemos uso de hilos, el programa será capaz de ejecutar n cantidad de procesos. La única limitación es el número de núcleos que tiene el procesador de cada ordenador.

Nota:

En algunos sistemas operativos viene integrado un software, el cual permite virtualidad más núcleos y por ende ejecutar más procesos a la vez.

Para crear un hilo en Java, hacemos uso de la clase Thread.

  1. Tenemos un método, el cual crea un hilo mediante la clase Thread.
  2. La clase Thread contiene el método aun, el cual contiene las sentencias que serán ejecutadas en un núcleo del ordenador.
  3. Para iniciar el hilo, debemos utilizar el método start de la clase Thread.
  4. Utilizamos el bloque try-catch para controlar los errores, en este caso el método sleep lo solicita obligatoriamente.
  5. Utilizamos el método estático sleep de la clase Thread para detener el flujo del programa por un periodo de tiempo, el parámetro que recibe corresponde al tiempo que el programa será detenido en milisegundos.
  6. Se imprime en consola los valores de los parámetros en el método.
public void createThread(String message, int time) {
    new Thread() {
        @Override
        public void run() { // 2
            try{ // 4
                Thread.sleep(time); // 5
                System.out.println(message + " - " + time + " ms"); // 6
            } catch (InterruptedException ex) {
                System.out.println(ex.getMessage());
            }
        }
    }.start(); // 3
}

Nota:

Un método estático es aquel que utiliza la palabra reservada static en su definición. No necesita de la instancia de una clase para poder ser utilizado.

La sincronización de eventos

Es la ejecución de un evento o más al mismo tiempo. Entonces, para hacer esto necesitamos implementar hilos en nuestro programa.

Anteriormente se explicaron los eventos de la interfaz MouseListener y un método para la creación de un hilo.

Ahora integraremos ambos ejemplos para hacer la sincronización de eventos.

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;

public class EventMouse extends Jframe implements MouseListener {

    // Constructor de la clase EventMouse, donde se crea
    // la ventana, se agrega un botón y se agrega el
    // listener al botón, además de algunas configuraciones
    // básicas para la creación de una ventana en Java
    public EventMouse() {
        setTitle("Evento del botón");
        setSize(500,500);
        setLayout(null);

        JButton button = new JButton("Presionar");
        button.setBounds(100, 100, 100, 50);

        button.addMouseListener(this);

        getContentPane().add(button);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

    // Método principal de la clase EventMouse
    public static void main(String[] args) {
        EventMouse e = new EventMouse();
    }

    // Métodos sobrescritos de la interfaz MouseListener
    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("MouseClicked");
    }

    @Override
    public void mousePressed(MouseEvent e) {
        System.out.println("MousePressed");
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        System.out.println("MouseReleased");
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        System.out.println("MouseEntered");
    }

    @Override
    public void mouseExited(MouseEvent e) {
        System.out.println("MouseExited");
    }
}
public void createThread(String message, int time) {
    new Thread() {
        @Override
        public void run() {
            try {
                Thread.sleep(time);
                System.out.println(message + " - " + time + " ms");
            } catch (InterruptedException ex) {
                System.out.println(ex.getMessage());
            }
        }
    }.start();
}

Se crea un hilo cada vez que es ejecutado un método de la interfaz MouseListener.

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;

public class EventMouse extends Jframe implements MouseListener {

    // Constructor de la clase EventMouse, donde se crea
    // la ventana, se agrega un botón y se agrega el
    // listener al botón, además de algunas configuraciones
    // básicas para la creación de una ventana en Java
    public EventMouse() {
        setTitle("Evento del botón");
        setSize(500,500);
        setLayout(null);

        JButton button = new JButton("Presionar");
        button.setBounds(100, 100, 100, 50);

        button.addMouseListener(this);

        getContentPane().add(button);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
    }

    // Método principal de la clase EventMouse
    public static void main(String[] args) {
        EventMouse e = new EventMouse();
    }

    // Observamos que se manda a llamar el método
    // createThread en los métodos de la
    // interfaz MouseListener
    @Override
    public void mouseClicked(MouseEvent e) {
        createThread("Hilo de mouseClicked", 500);
    }

    @Override
    public void mousePressed(MouseEvent e) {
        createThread("Hilo de mousePressed", 800);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        createThread("Hilo de mouseReleased", 300);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        createThread("Hilo de mouseEntered", 1200);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        createThread("Hilo de mouseExited", 800);
    }

    public void createThread(String message, int time) {
        // crea un hilo cada vez que el método es llamado
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(time);
                    System.out.println(message + " - " + time + " ms");
                } catch (InterruptedException ex) {
                    System.out.println(ex.getMessage());
                }
            }
        }.start();
    }
}

Nota:

De esta forma podemos obtener más de un evento ejecutándose al mismo tiempo.

Cuestionario

¿Qué es la manipulación de eventos?

Obtener las acciones que un usuario realiza sobre la interfaz gráfica de una aplicación y ejecutar cierta lógica de programación para brindarle al usuario una respuesta de lo que hizo internamente el sistema, para ello Java provee de una serie de métodos con los cuales puedes detectar las acciones que un usuario realiza sobre la interfaz de la aplicación.

¿Qué caracteriza la manipulación de eventos?

  1. Modelo de eventos: Java utiliza un modelo de eventos para gestionar la interacción del usuario con la aplicación. Este modelo se basa en la programación orientada a objetos y utiliza clases y objetos para representar eventos y manejadores de eventos.
  2. Clases de eventos: En Java, los eventos se representan como objetos de clases específicas. Por ejemplo, un evento de clic de ratón se representa mediante un objeto de la clase MouseEvent, mientras que un evento de teclado se representa mediante un objeto de la clase KeyEvent.
  3. Manejadores de eventos: Para responder a eventos, se utilizan manejadores de eventos, que son métodos o clases que se encargan de procesar los eventos específicos. En Java, se pueden implementar manejadores de eventos utilizando interfaces como ActionListener, MouseListener, KeyListener, etc.
  4. Registro de eventos: Para que un componente o un objeto pueda recibir eventos, debe registrarse como un oyente (listener) para el tipo de evento que desea manejar. Esto se hace mediante métodos como addMouseListener(), addActionListener(), addKeyListener(), entre otros.
  5. Métodos de callback: Los manejadores de eventos son métodos de callback, lo que significa que se ejecutan cuando ocurre un evento específico. Por ejemplo, cuando se hace clic en un botón, se llama al método actionPerformed() de un ActionListener registrado para ese botón.
  6. Evento de propagación: En Java, los eventos pueden propagarse a través de una jerarquía de componentes, desde el componente que generó el evento hasta sus contenedores. Esto permite que los eventos sean capturados y procesados en diferentes niveles de la jerarquía de componentes.
  7. Eventos personalizados: Además de los eventos estándar proporcionados por Java, es posible crear eventos personalizados para adaptarse a las necesidades específicas de una aplicación.

¿Cuáles son los tipo de manipulación de eventos?

  1. Eventos de interfaz de usuario (UI):
    • Eventos de acción (ActionEvents): Estos eventos ocurren cuando un componente de la interfaz de usuario, como un botón, menú o elemento de lista, realiza una acción que el usuario puede desencadenar, como hacer clic en un botón.
    • Eventos de ratón (MouseEvents): Estos eventos se generan cuando el usuario interactúa con el ratón, como hacer clic, mover o soltar el botón del ratón.
    • Eventos de teclado (KeyEvents): Estos eventos ocurren cuando se presionan o sueltan teclas en el teclado.
    • Eventos de ventana (WindowEvents): Estos eventos están relacionados con la interacción del usuario con ventanas, como abrir, cerrar, minimizar o restaurar una ventana.
  2. Eventos de componentes específicos:
    • Cada tipo de componente de interfaz de usuario en Java Swing (botones, cuadros de texto, listas, etc.) puede generar eventos específicos relacionados con su interacción. Por ejemplo, un botón puede generar eventos de acción, mientras que un cuadro de texto puede generar eventos de cambio de texto.
  3. Eventos personalizados:
    • Los desarrolladores tienen la flexibilidad de crear sus propios eventos personalizados en Java para manejar situaciones específicas en su aplicación. Esto implica definir una clase de evento personalizado y su correspondiente manejador de eventos.
  4. Eventos de tiempo (TimerEvents):
    • Estos eventos están relacionados con la programación de tareas para que ocurran en un momento específico o periódicamente. Los temporizadores (Timer) en Java pueden generar eventos de tiempo para manejar acciones programadas.
  5. Eventos de arrastrar y soltar (Drag-and-Drop Events):
    • Estos eventos ocurren cuando el usuario arrastra un objeto y lo suelta en una ubicación específica. Java proporciona clases y eventos relacionados con la funcionalidad de arrastrar y soltar.
  6. Eventos de cambio de estado (Change Events):
    • Estos eventos se utilizan para detectar cambios en el estado de un componente, como un cuadro de verificación (checkbox) o un botón de opción (radio button), cuando el usuario realiza una selección o deselección.

¿Cuál es una ventaja de utilizar la delegación de eventos?

Permite marcar una separación más precisa entre la interfaz y la lógica de programación.

¿Qué es el modelo de delegación de eventos?

Es un modelo que se basa en el concepto de que el evento ocurre en un objeto y se delega a otro.

¿Cuál es la interfaz que contiene los métodos para los eventos del mouse?

MouseListener

¿Cuál de los siguientes métodos pertenece a la interfaz KeyListener?

keyPressed

El modelo de delegación de eventos permite marcar una separación más precisa entre la interfaz y la lógica de programación. Verdadero o falso.

Verdadero.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *