CodeGym /Corsi /JAVA 25 SELF /Eventi in Swing e AWT: basi, esempi

Eventi in Swing e AWT: basi, esempi

JAVA 25 SELF
Livello 50 , Lezione 1
Disponibile

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:

  1. Il pulsante crea un oggetto evento (ActionEvent).
  2. Il pulsante itera la lista dei listener e invoca per ciascuno il metodo actionPerformed.
  3. 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).

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION