Saltar al contenido principal

Manejo de eventos

Swing usa el patrón Observer para gestionar eventos. Cuando el usuario interactúa con la interfaz (pulsa un botón, mueve el ratón, escribe…), se genera un evento. Los objetos que quieren reaccionar a ese evento deben registrarse como listeners.

El modelo de eventos

Cada tipo de evento tiene su propio par XxxEvent / XxxListener:

Interfaz listenerMétodo(s) que implementarCuándo se activa
ActionListeneractionPerformed(ActionEvent)Botón pulsado, Enter en TextField...
MouseListenermouseClicked/Pressed/Released/Entered/ExitedClics y movimiento sobre componente
MouseMotionListenermouseMoved(MouseEvent), mouseDraggedMovimiento del ratón
MouseWheelListenermouseWheelMoved(MouseWheelEvent)Rueda del ratón
KeyListenerkeyPressed/Released/Typed(KeyEvent)Pulsación de teclas
FocusListenerfocusGained/Lost(FocusEvent)Foco del teclado
ChangeListenerstateChanged(ChangeEvent)JSlider, JSpinner, JTabbedPane...
ItemListeneritemStateChanged(ItemEvent)JCheckBox, JRadioButton, JComboBox
DocumentListenerinsertUpdate/removeUpdate/changedUpdateCambios en el texto de un JTextComponent
WindowListenerwindowOpened/Closing/Closed/...Eventos de ventana
ComponentListenercomponentResized/Moved/Shown/HiddenRedimensión, movimiento...
ListSelectionListenervalueChanged(ListSelectionEvent)Selección en JList/JTable

ActionListener

El listener más común. Se añade con addActionListener.

JButton btn = new JButton("Enviar");

// Forma 1: clase anónima (Java 7 y anterior)
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Botón pulsado");
}
});

// Forma 2: expresión lambda (Java 8+) — la más usada hoy en día
btn.addActionListener(e -> System.out.println("Botón pulsado"));

// Forma 3: referencia a método
btn.addActionListener(this::manejarBoton);

private void manejarBoton(ActionEvent e) {
System.out.println("Acción: " + e.getActionCommand());
System.out.println("Tiempo: " + e.getWhen());
// e.getModifiers() devuelve qué teclas modificadoras estaban pulsadas
}

ActionEvent

btn.addActionListener(e -> {
String comando = e.getActionCommand(); // Texto del botón o actionCommand definido
int modificadores = e.getModifiers(); // Shift, Ctrl, Alt...
long tiempo = e.getWhen(); // Timestamp

// ¿Se pulsó con Ctrl?
boolean ctrl = (modificadores & ActionEvent.CTRL_MASK) != 0;
});

MouseListener y MouseMotionListener

JPanel panel = new JPanel();

panel.addMouseListener(new MouseAdapter() {
// MouseAdapter implementa todas las interfaces con métodos vacíos:
// puedes sobreescribir solo los que necesites

@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Clic en: " + e.getX() + ", " + e.getY());
if (e.getClickCount() == 2) {
System.out.println("Doble clic");
}
if (SwingUtilities.isRightMouseButton(e)) {
System.out.println("Clic derecho");
}
}

@Override
public void mousePressed(MouseEvent e) {
System.out.println("Botón presionado: " + e.getButton()); // 1=izq, 2=medio, 3=der
}

@Override
public void mouseEntered(MouseEvent e) {
panel.setBackground(Color.LIGHT_GRAY);
}

@Override
public void mouseExited(MouseEvent e) {
panel.setBackground(null); // Restaurar
}
});

panel.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
// Se llama cada vez que el ratón se mueve SIN pulsar
}

@Override
public void mouseDragged(MouseEvent e) {
// Se llama cuando se mueve CON un botón pulsado
System.out.println("Arrastrando a: " + e.getX() + ", " + e.getY());
}
});
Nota

MouseAdapter es una clase abstracta que implementa MouseListener, MouseMotionListener y MouseWheelListener con métodos vacíos. Así solo sobreescribes los que necesitas.

KeyListener

JTextField campo = new JTextField(20);

campo.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int codigo = e.getKeyCode(); // Código de tecla virtual (VK_ENTER, VK_ESCAPE...)
System.out.println("Tecla pulsada: " + KeyEvent.getKeyText(codigo));

if (codigo == KeyEvent.VK_ESCAPE) {
campo.setText("");
}
if (e.isControlDown() && codigo == KeyEvent.VK_S) {
guardar(); // Ctrl+S
}
}

@Override
public void keyTyped(KeyEvent e) {
// Se llama cuando se genera un carácter
char c = e.getKeyChar();
// Bloquear escritura si no es dígito:
if (!Character.isDigit(c)) {
e.consume(); // Cancela el evento → el carácter no aparece
}
}
});

Teclas especiales útiles (KeyEvent.VK_*)

VK_ENTER, VK_ESCAPE, VK_BACK_SPACE, VK_DELETE, VK_TAB, VK_F1VK_F12, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_HOME, VK_END, VK_PAGE_UP, VK_PAGE_DOWN

FocusListener

JTextField campo = new JTextField(20);

campo.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
campo.selectAll(); // Seleccionar todo al entrar
campo.setBackground(new Color(255, 255, 200)); // Amarillo suave
}

@Override
public void focusLost(FocusEvent e) {
campo.setBackground(Color.WHITE);
validar(campo.getText());
}
});

DocumentListener — cambios en texto en tiempo real

Para JTextField, JTextArea, etc. Es más preciso que KeyListener porque se activa también con copiar/pegar.

JTextField campo = new JTextField(20);

campo.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) { actualizar(); }

@Override
public void removeUpdate(DocumentEvent e) { actualizar(); }

@Override
public void changedUpdate(DocumentEvent e) { actualizar(); } // Cambios de atributos

private void actualizar() {
// CUIDADO: no modificar el documento aquí directamente
// (puede causar IllegalStateException). Usa SwingUtilities.invokeLater:
SwingUtilities.invokeLater(() -> {
lblContador.setText("Caracteres: " + campo.getText().length());
});
}
});

ItemListener vs ActionListener en CheckBox/RadioButton

JCheckBox chk = new JCheckBox("Activo");

// ActionListener: se dispara siempre que se pulsa (independientemente del estado)
chk.addActionListener(e -> System.out.println("Pulsado"));

// ItemListener: se dispara cuando el estado cambia (SELECTED / DESELECTED)
chk.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
System.out.println("Activado");
} else {
System.out.println("Desactivado");
}
});

WindowListener

JFrame ventana = new JFrame("Mi app");

// Para confirmar el cierre:
ventana.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
ventana.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int respuesta = JOptionPane.showConfirmDialog(
ventana,
"¿Seguro que quieres salir?",
"Confirmar",
JOptionPane.YES_NO_OPTION
);
if (respuesta == JOptionPane.YES_OPTION) {
System.exit(0);
}
}

@Override
public void windowIconified(WindowEvent e) {
System.out.println("Ventana minimizada");
}

@Override
public void windowDeiconified(WindowEvent e) {
System.out.println("Ventana restaurada");
}
});

Key Bindings (vinculación de teclas) — alternativa a KeyListener

KeyListener solo funciona si el componente tiene el foco. Los Key Bindings son más robustos: funcionan aunque el componente no tenga el foco.

JPanel panel = new JPanel();

// Obtener el mapa de acciones
InputMap inputMap = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = panel.getActionMap();

// Registrar tecla → nombre de acción
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK), "guardar");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), "actualizar");

// Registrar nombre de acción → código
actionMap.put("guardar", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
guardar();
}
});
actionMap.put("actualizar", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
actualizar();
}
});

Ámbitos de InputMap

ConstanteCuándo funciona
WHEN_FOCUSEDSolo cuando el componente tiene el foco
WHEN_ANCESTOR_OF_FOCUSED_COMPONENTCuando el componente o algún hijo tiene el foco
WHEN_IN_FOCUSED_WINDOWCuando la ventana que contiene el componente tiene el foco (global)

Timers — eventos periódicos

javax.swing.Timer ejecuta código repetidamente en el EDT.

// Crear un Timer que dispara cada 1000ms (1 segundo)
Timer timer = new Timer(1000, e -> {
lblTiempo.setText("Tick: " + System.currentTimeMillis());
});

timer.start(); // Iniciar
timer.stop(); // Parar
timer.restart(); // Reiniciar
timer.setDelay(500); // Cambiar intervalo

// Timer de un solo disparo
Timer timerUnico = new Timer(3000, e -> {
System.out.println("Han pasado 3 segundos");
});
timerUnico.setRepeats(false);
timerUnico.start();

Ejemplo completo: Dibujar con el ratón

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;

public class PizarraDigital extends JFrame {

private final java.util.List<Point> puntos = new ArrayList<>();
private Point puntoAnterior = null;

public PizarraDigital() {
super("Pizarra Digital");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(600, 400);

JPanel canvas = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (int i = 1; i < puntos.size(); i++) {
Point p1 = puntos.get(i - 1);
Point p2 = puntos.get(i);
if (p1 != null && p2 != null) {
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
}
}
};
canvas.setBackground(Color.WHITE);

canvas.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
puntos.add(null); // Separador de trazos
puntoAnterior = null;
}
});

canvas.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
puntos.add(e.getPoint());
canvas.repaint();
}
});

JButton limpiar = new JButton("Limpiar");
limpiar.addActionListener(e -> {
puntos.clear();
canvas.repaint();
});

add(canvas, BorderLayout.CENTER);
add(limpiar, BorderLayout.SOUTH);
setLocationRelativeTo(null);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new PizarraDigital().setVisible(true));
}
}