Introducción
Hasta ahora hemos aprendido a crear interfaces gráficas en Java con Swing, organizando los componentes con distintos Layouts. Sin embargo, nuestras aplicaciones aún no hacen nada cuando el usuario interactúa con ellas. En esta sesión aprenderemos sobre el manejo de eventos en Swing, que nos permitirá programar respuestas a acciones como clics en botones, movimientos del mouse o pulsaciones de teclas.
¿Qué es un evento en Java Swing?
Un evento es cualquier acción realizada por el usuario que genera una señal en la interfaz gráfica. Algunos ejemplos comunes son:
- Hacer clic en un botón.
- Escribir en un cuadro de texto.
- Mover el ratón sobre un componente.
- Cerrar una ventana.
Para que nuestra aplicación responda a estos eventos, debemos registrar y manejar eventos mediante Listeners (escuchadores de eventos).
Manejo de eventos con ActionListener
Uno de los eventos más comunes es hacer clic en un botón. Para capturar este evento en Swing, utilizamos la interfaz ActionListener
, que se encuentra en el paquete java.awt.event
.
Pasos para manejar eventos en un botón
- Crear el componente objetivo del evento en la interfaz.
- Registrar un escuchador de eventos (
ActionListener
). - Implementar el método
actionPerformed()
para definir la acción a ejecutar cuando se lance el evento.
Ejemplo básico: Capturar clic en un botón
Supongamos que tenemos una interfaz con un botón llamado btnSaludar
. Queremos que, al hacer clic en él, aparezca un mensaje en la consola.
import javax.swing.*;
import java.awt.event.*; // Importamos los eventos
public class EventoBoton {
public static void main(String[] args) {
JFrame ventana = new JFrame("Ejemplo de Evento");
ventana.setSize(300, 200);
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton btnSaludar = new JButton("Haz clic");
ventana.add(btnSaludar);
// Agregar el ActionListener al botón
btnSaludar.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("¡Hola! Has hecho clic en el botón.");
}
});
ventana.setVisible(true);
}
}
Explicación del código
addActionListener()
: Asigna un escuchador de eventos al botón.- Clase anónima
ActionListener
: Implementa el métodoactionPerformed()
, que ejecuta el código cuando se hace clic en el botón. System.out.println()
: Muestra un mensaje en la consola.
Manejo de eventos con clases internas
En el ejemplo anterior usamos una clase anónima, pero también podemos manejar eventos con clases internas para hacer el código más organizado y reutilizable.
import javax.swing.*;
import java.awt.event.*;
public class EventoConClaseInterna {
public static void main(String[] args) {
new MiVentana();
}
}
class MiVentana extends JFrame {
JButton btnSaludar;
public MiVentana() {
setTitle("Ejemplo de Evento con Clase Interna");
setSize(300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
btnSaludar = new JButton("Saludar");
add(btnSaludar);
// Agregamos un manejador de eventos de clase interna
btnSaludar.addActionListener(new ManejadorEventos());
setVisible(true);
}
static class ManejadorEventos implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("¡Has hecho clic en el botón!");
}
}
}
Ventajas del uso de clases internas
- ✅ Código más limpio y estructurado.
- ✅ Permite reutilizar la lógica de manejo de eventos.
- ✅ Mantiene la organización de la clase principal.
Manejo de eventos con expresiones lambda (Java 8+)
Si usamos Java 8 o superior, podemos simplificar el código con expresiones lambda, eliminando la necesidad de clases anónimas.
btnSaludar.addActionListener(e -> System.out.println("¡Has hecho clic!"));
Ventajas de las lambdas
- ✅ Código más compacto.
- ✅ Más legible y fácil de entender.
- ✅ Reduce la cantidad de líneas innecesarias.
Implementar ActionListener
en el propio Formulario
Una forma de manejar eventos en Swing es hacer que el formulario (JFrame
) implemente directamente la interfaz ActionListener
(también podría hacerse en un JPanel
de forma similar). Esto significa que la propia ventana actuará como su propio manejador de eventos.
🔹 Ejemplo: Implementación en el Formulario
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class VentanaFormulario extends JFrame implements ActionListener {
private JButton boton1;
private JButton boton2;
public VentanaFormulario() {
setTitle("Manejo de eventos");
setSize(300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new java.awt.FlowLayout());
boton1 = new JButton("Botón 1");
boton2 = new JButton("Botón 2");
boton1.addActionListener(this);
boton2.addActionListener(this);
add(boton1);
add(boton2);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == boton1) {
System.out.println("Se presionó el Botón 1");
} else if (e.getSource() == boton2) {
System.out.println("Se presionó el Botón 2");
}
}
public static void main(String[] args) {
new VentanaFormulario();
}
}
🔹 Explicación
- La clase
VentanaFormulario
extiendeJFrame
e implementaActionListener
. - Ambos botones (
boton1
yboton2
) registran la propia instancia de la ventana (this
) como suActionListener
. - El método
actionPerformed
usae.getSource()
para determinar qué botón fue presionado.
Manejo de Eventos con una Clase Externa
En esta técnica, creamos una clase separada que implemente ActionListener
y la usamos en la ventana principal. Esto mejora la separación entre la interfaz y la lógica de los eventos.
🔹 Ejemplo: Implementación con Clase Externa
Clase manejadora de eventos
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ManejadorBotones implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Se presionó: " + ((javax.swing.JButton) e.getSource()).getText());
}
}
Clase del formulario
import javax.swing.*;
public class VentanaClaseExterna extends JFrame {
private JButton boton1;
private JButton boton2;
public VentanaClaseExterna() {
setTitle("Manejo de eventos con clase externa");
setSize(300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new java.awt.FlowLayout());
boton1 = new JButton("Botón 1");
boton2 = new JButton("Botón 2");
ManejadorBotones manejador = new ManejadorBotones();
boton1.addActionListener(manejador);
boton2.addActionListener(manejador);
add(boton1);
add(boton2);
setVisible(true);
}
public static void main(String[] args) {
new VentanaClaseExterna();
}
}
🔹 Explicación
- Se crea una clase
ManejadorBotones
que implementaActionListener
y define la acción cuando se presiona un botón. - La clase
VentanaClaseExterna
crea una instancia delManejadorBotones
y la asocia a los botones. - Cuando se presiona un botón, el evento es manejado por
ManejadorBotones
, lo que mantiene limpio el código de la ventana.
Eventos en otros componentes de Swing
Además de los botones, podemos manejar eventos en otros componentes:
JTextField (eventos cuando el usuario presiona Enter)
JTextField campoTexto = new JTextField(20);
campoTexto.addActionListener(e -> System.out.println("Texto ingresado: " + campoTexto.getText()));
JCheckBox (eventos cuando el usuario marca/desmarca una casilla)
JCheckBox checkBox = new JCheckBox("Acepto los términos");
checkBox.addItemListener(e -> {
if (checkBox.isSelected()) {
System.out.println("Casilla marcada");
} else {
System.out.println("Casilla desmarcada");
}
});
JComboBox (eventos cuando el usuario selecciona un elemento de la lista)
JComboBox<String> combo = new JComboBox<>(new String[]{"Opción 1", "Opción 2", "Opción 3"});
combo.addActionListener(e -> System.out.println("Seleccionaste: " + combo.getSelectedItem()));
Buenas prácticas en el manejo de eventos en Swing
- ✅ Usar clases internas o lambdas para organizar mejor el código.
- ✅ Evitar escribir lógica compleja dentro del
actionPerformed()
. En su lugar, llamar a métodos auxiliares. - ✅ Cerrar correctamente los recursos usados en los eventos, si se usan archivos o conexiones a bases de datos.
- ✅ No bloquear el hilo principal de la interfaz (Event Dispatch Thread) con operaciones pesadas. Usar
SwingWorker
en estos casos.
📊 Tabla Comparativa de las Técnicas de Manejo de Eventos
Método | Ventajas | Inconvenientes |
---|---|---|
Clases anónimas | Código compacto, fácil para eventos simples. | Se vuelve confuso si hay muchos eventos. |
Expresiones Lambda | Código más limpio (a partir de Java 8). | Solo sirve para interfaces funcionales. |
Implementar ActionListener en el formulario | Código más directo, sin clases adicionales. | Difícil de mantener si hay muchos botones. |
Clase interna | Separa mejor la lógica del evento. | Sigue estando dentro de la misma clase. |
Clase externa | Máxima separación entre lógica e interfaz, reutilizable. | Puede ser innecesario para eventos pequeños. |
🔥 ¿Cuándo Usar Cada Método?
- ✅ Usar clases anónimas cuando el evento es simple y no afecta la legibilidad.
- ✅ Usar lambdas si solo necesitas una acción rápida y el código debe ser corto y claro.
- ✅ Implementar
ActionListener
en el formulario si hay pocos eventos y el código sigue siendo legible. - ✅ Usar una clase interna cuando hay múltiples eventos pero están relacionados con el mismo formulario.
- ✅ Usar una clase externa cuando se necesita reutilizar el manejador de eventos o separar completamente la lógica de la interfaz.
Cada método tiene su uso ideal dependiendo de la cantidad de eventos, la reutilización del código y la claridad que se quiera mantener en la aplicación.
Ejercicios propuestos
Ejercicio 1: Botón que cambia de color
Crea una ventana con un JButton
. Cada vez que el usuario haga clic en el botón, su color de fondo debe cambiar de forma aleatoria.
Pistas:
- Usa
Math.random()
para generar colores aleatorios. - Implementa el evento utilizando un
ActionListener
.
Ejercicio 2: Contador de clics
Crea una interfaz con un JButton
y un JLabel
. Cada vez que el usuario haga clic en el botón, el JLabel
debe actualizarse para mostrar el número total de clics realizados.
Pistas:
- Usa una variable de instancia para contar los clics.
- Implementa el
ActionListener
en una clase anónima o interna.
Ejercicio 3: Simulador de formulario
Diseña un formulario con un JTextField
y un JButton
. Cuando el usuario escriba su nombre en el campo de texto y pulse el botón, debe aparecer un mensaje de bienvenida en un JLabel
.
Pistas:
- Usa el método
getText()
delJTextField
. - Usa
JOptionPane.showMessageDialog()
para mostrar mensajes emergentes.
Ejercicio 4: Gestión de teclado
Crea una interfaz con un JTextField
y un JLabel
. Cada vez que el usuario escriba una letra en el campo de texto, el JLabel
debe actualizarse con el último carácter ingresado.
Pistas:
- Usa un
KeyListener
. - Implementa el evento de
keyTyped()
.
Ejercicio 5: Cambio de disposición con eventos
Crea una ventana con dos botones:
- Uno debe cambiar el
Layout
de la ventana aFlowLayout
. - El otro debe cambiarlo a
GridLayout
con 2 filas y 2 columnas.
Pistas:
- Usa
setLayout(new FlowLayout())
ysetLayout(new GridLayout(2,2))
. - Implementa los
ActionListener
en clases separadas.
Conclusión
El manejo de eventos es esencial para desarrollar aplicaciones interactivas en Java Swing. En esta sesión aprendimos:
- ✅ Qué es un evento y cómo funciona.
- ✅ Cómo manejar eventos con
ActionListener
. - ✅ Diferentes formas de manejar eventos: clases anónimas, internas y lambdas.
- ✅ Eventos en distintos componentes.
En la siguiente sesión aprenderemos sobre validación de datos en formularios y control de eventos avanzados. 🚀
Deja una respuesta