1. Introduzione molto breve a Swing e AWT
Se Java fosse un’automobile, Swing e AWT — sarebbero il suo «pannello strumenti» e il «volante». AWT (Abstract Window Toolkit) — la primissima libreria per creare applicazioni desktop in Java. Usa i controlli “nativi” del sistema operativo, quindi appare in modo diverso su Windows, Linux e Mac.
Swing — è un livello più moderno sopra AWT, scritto interamente in Java. Swing è più gradevole, flessibile, supporta molte funzionalità aggiuntive e ha lo stesso aspetto su tutte le piattaforme (beh, quasi). In entrambi i casi tutti i componenti dell’interfaccia (pulsanti, campi di testo, checkbox, ecc.) supportano il lavoro con gli eventi.
In questa lezione useremo Swing: è più semplice per i principianti e più comune negli esempi didattici. Vediamo come funziona la gestione degli eventi sull’esempio del compito più popolare — reagire alla pressione di un pulsante.
Creazione di un pulsante
In Swing un pulsante si crea così:
JButton button = new JButton("Cliccami!");
Qui «Cliccami!» è l’etichetta sul pulsante.
Aggiunta di un listener
Per reagire al clic, bisogna registrare un listener sull’evento:
button.addActionListener(new MyActionListener());
Ma chi è questo MyActionListener? È una classe che implementa l’interfaccia ActionListener:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Il pulsante è stato premuto!");
}
}
Quando l’utente preme il pulsante, Swing chiama il metodo actionPerformed di tutti i listener registrati tramite addActionListener.
Esempio completo: una finestra semplice con pulsante
Mettiamo tutto insieme e creiamo un’app funzionante:
import javax.swing.*;
import java.awt.event.*;
public class SimpleButtonApp {
public static void main(String[] args) {
JFrame frame = new JFrame("Esempio di evento");
JButton button = new JButton("Cliccami!");
// Aggiungiamo un listener al pulsante
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Il pulsante è stato premuto!");
JOptionPane.showMessageDialog(frame, "Evviva! Pulsante premuto!");
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300, 200);
frame.setVisible(true);
}
}
Cosa succede qui:
- Creiamo una finestra (JFrame).
- Creiamo un pulsante (JButton).
- Aggiungiamo un listener (classe anonima, per non creare file separati).
- Dentro actionPerformed stampiamo un messaggio in console e mostriamo una finestra di dialogo (JOptionPane).
- Aggiungiamo il pulsante alla finestra e mostriamo la finestra all’utente.
Provate voi stessi a copiare questo codice, eseguirlo — e premere il pulsante!
2. Classi anonime ed espressioni lambda
Nel codice Java moderno scrivere classi separate per un solo gestore — è come andare a comprare il pane con un carro armato. È molto più comodo usare classi anonime o espressioni lambda.
Classe anonima
Abbiamo già visto questo approccio sopra:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// codice di gestione
}
});
Qui la classe è dichiarata sul posto, senza nome.
Espressione lambda (Java 8+)
Se l’interfaccia del listener è funzionale (cioè contiene un solo metodo astratto), si può usare una lambda:
button.addActionListener(e -> {
System.out.println("Pulsante premuto tramite lambda!");
});
Oppure ancora più corto, se il corpo è monoriga:
button.addActionListener(e -> System.out.println("Lambda: pulsante premuto!"));
Questo non solo riduce il codice, ma lo rende anche più leggibile. Curiosità: l’interfaccia ActionListener è funzionale perché contiene un solo metodo, actionPerformed.
4. Altri tipi di eventi
Il mondo non si limita ai pulsanti! In Swing e AWT quasi ogni componente dell’interfaccia supporta i propri eventi. Ecco i più popolari:
Eventi del mouse: MouseListener e MouseAdapter
Se volete reagire ai clic del mouse, usate MouseListener:
button.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Clic del mouse sul pulsante!");
}
// Gli altri metodi possono restare vuoti
@Override public void mousePressed(MouseEvent e) {}
@Override public void mouseReleased(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
});
Scrivere cinque metodi vuoti per un solo gestore non è molto comodo. Per questo c’è MouseAdapter:
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Clic del mouse sul pulsante (tramite adapter)!");
}
});
MouseAdapter implementa tutti i metodi dell’interfaccia e voi sovrascrivete solo quelli necessari.
Eventi della tastiera: KeyListener/KeyAdapter
Se serve intercettare la pressione dei tasti:
button.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
System.out.println("Tasto premuto: " + e.getKeyChar());
}
});
Eventi di modifica del testo: DocumentListener
Per i campi di testo (JTextField, JTextArea) ci sono listener speciali:
JTextField textField = new JTextField();
textField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
System.out.println("Testo modificato: " + textField.getText());
}
@Override public void removeUpdate(DocumentEvent e) {}
@Override public void changedUpdate(DocumentEvent e) {}
});
A proposito, per la maggior parte degli eventi esistono adapter per non scrivere metodi vuoti.
5. Pratica: mini‑esempio con finestra e pulsante
Creiamo la GUI più semplice: una finestra con un pulsante che, quando premuto, incrementa un contatore e mostra un messaggio.
Esempio: contatore di clic
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ClickCounterApp {
public static void main(String[] args) {
JFrame frame = new JFrame("Contatore di clic");
JButton button = new JButton("Cliccami!");
JLabel label = new JLabel("Clic: 0");
// Contatore dei clic (deve essere final o effectively final)
final int[] count = {0};
button.addActionListener(e -> {
count[0]++;
label.setText("Clic: " + count[0]);
});
frame.setLayout(new FlowLayout());
frame.add(button);
frame.add(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250, 100);
frame.setVisible(true);
}
}
Spiegazione:
- Creiamo una finestra, un pulsante e una label per mostrare il contatore.
- Usiamo un array di un elemento (final int[] count = {0};) per aggirare il vincolo delle lambda sulle variabili final/effectively final.
- Ogni volta che si preme il pulsante, incrementiamo il contatore e aggiorniamo il testo della label.
Idea per esercitarsi: provate a cambiare il colore del pulsante a ogni quinto clic!
6. Come funziona il modello a eventi dietro le quinte
Diamo un’occhiata sotto il cofano. Quando chiamate:
button.addActionListener(listener);
il pulsante (oggetto JButton) aggiunge il vostro listener a un elenco interno. Quando l’utente preme il pulsante, internamente succede quanto segue:
- Il pulsante crea un oggetto evento (ActionEvent).
- Il pulsante itera la lista dei listener e invoca per ciascuno il metodo actionPerformed.
- Il vostro gestore esegue le azioni necessarie (ad esempio, aggiorna la label).
Importante da ricordare: ogni evento può avere un numero qualsiasi di listener — potete aggiungere più gestori per lo stesso pulsante; verranno chiamati in sequenza.
7. Schema visivo: gestione di un evento
graph TD
A[L’utente ha premuto il pulsante] --> B{JButton}
B --> C[Creare ActionEvent]
C --> D[Invocare actionPerformed su tutti i listener]
D --> E[L’handler esegue le azioni]
7. Errori tipici nel lavoro con gli eventi in Swing e AWT
Errore n. 1: vi siete dimenticati di registrare il listener. Il pulsante non reagisce ai clic perché avete dimenticato di chiamare addActionListener.
Errore n. 2: lavoro pesante nel gestore. Se nel gestore dell’evento avviate un ciclo lungo o scaricate qualcosa da internet, l’interfaccia si blocca. Per compiti lunghi usate thread separati o SwingWorker.
Errore n. 3: tentativo di modificare una variabile da una lambda senza final/effectively final. Dentro una lambda si possono usare solo variabili final o “effectively final”. Per i contatori usate array o classi speciali (AtomicInteger, se volete fare i seri).
Errore n. 4: vi siete dimenticati di rimuovere il listener. Se rimuovete un componente ma non rimuovete il listener, è possibile una perdita di memoria. Annullate le sottoscrizioni quando il componente non serve più.
Errore n. 5: non tutti i metodi dell’interfaccia sono implementati. Se implementate, ad esempio, direttamente MouseListener, non dimenticate tutti i metodi! Meglio usare gli adapter (MouseAdapter, KeyAdapter).
GO TO FULL VERSION