Swing usa el modelo de delegación de eventos: un componente (fuente) genera un evento, y uno o más objetos (listeners) lo reciben y procesan.
El flujo es: componente genera evento → Event Dispatch Thread (EDT) lo encola → Swing llama al método del listener correspondiente.
ActionListener: el evento más común
JButton boton = new JButton("Guardar");
// Lambda (Java 8+, recomendado)
boton.addActionListener(e -> {
System.out.println("Botón pulsado: " + e.getActionCommand());
guardarDatos();
});
// Clase anónima (estilo clásico)
boton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
guardarDatos();
}
});
MouseListener y MouseMotionListener
panel.addMouseListener(new MouseAdapter() { // MouseAdapter implementa todos los métodos vacíos
@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)) mostrarMenuContextual(e);
}
@Override
public void mouseEntered(MouseEvent e) { panel.setBackground(Color.LIGHT_GRAY); }
@Override
public void mouseExited(MouseEvent e) { panel.setBackground(Color.WHITE); }
});
KeyListener y KeyBinding
Para atajos de teclado, prefiere KeyBindings sobre KeyListener: funcionan aunque el componente no tenga el foco:
// KeyBinding (recomendado)
InputMap im = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = panel.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK), "guardar");
am.put("guardar", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) { guardarDatos(); }
});
Cambios en campos de texto
JTextField campo = new JTextField();
// DocumentListener: reacciona a cualquier cambio de texto
campo.getDocument().addDocumentListener(new DocumentListener() {
@Override public void insertUpdate(DocumentEvent e) { validar(); }
@Override public void removeUpdate(DocumentEvent e) { validar(); }
@Override public void changedUpdate(DocumentEvent e) { validar(); }
});
// FocusListener: detecta cuando el campo gana/pierde el foco
campo.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
if (campo.getText().isBlank()) campo.setBorder(BorderFactory.createLineBorder(Color.RED));
}
});
Regla fundamental: no bloquees el EDT
Las operaciones lentas (red, disco, BD) nunca deben ejecutarse directamente en los listeners, porque bloquean el EDT y la UI se congela. Usa SwingWorker para trabajo en segundo plano (se explica en el capítulo siguiente).
// MAL: bloquea la UI durante 5 segundos
boton.addActionListener(e -> {
Thread.sleep(5000); // esto bloquea el EDT
actualizar();
});
// BIEN: delegar a un hilo de fondo
boton.addActionListener(e -> {
new MiWorker().execute(); // SwingWorker
});
