Lezione 8 · Canale 1 · lunedì 12 ottobre 2026
Interfacce grafiche con Swing
Progettazione del Software
Finestre e componenti
Un'interfaccia grafica Swing è una finestra (JFrame) che contiene componenti: pulsanti, etichette, campi di testo. Il programma costruisce la finestra e poi attende le azioni dell'utente. Il comportamento in risposta a ciascuna azione sta negli ascoltatori.
import javax.swing.JButton;
import javax.swing.JFrame;
public class Principale {
public static void main(String[] args) {
JFrame finestra = new JFrame("Prova");
JButton pulsante = new JButton("OK");
finestra.add(pulsante);
finestra.pack();
finestra.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
finestra.setVisible(true);
System.out.println("main terminato");
}
}
new JFrame(titolo)crea la finestra, che nasce vuota e invisibile. Si rende visibile consetVisible(true): così si possono creare più finestre e mostrare subito solo la principale.- Creare un componente e inserirlo nella finestra sono due operazioni: il pulsante compare solo dopo
add. pack()dimensiona la finestra sulle dimensioni preferite dei componenti. Senza, la finestra è troppo piccola e il pulsante non si vede.- Per default la X nasconde la finestra e il programma continua a girare.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)lo fa terminare.
I componenti di uso più frequente:
| Componente | Uso |
|---|---|
JButton | pulsante che l'utente preme |
JLabel | etichetta che descrive altri elementi, non si preme |
JTextField | testo su una sola riga |
JTextArea | area rettangolare di testo su più righe |
Per un singolo valore si usa JTextField, la cui lunghezza è l'argomento del costruttore (new JTextField(20)). JTextArea vuole righe e colonne: new JTextArea(20, 20) ha 20 righe di 20 caratteri.
Programmazione guidata dagli eventi
Nei programmi sequenziali le istruzioni si eseguono una dopo l'altra. Qui main costruisce l'interfaccia e termina, ma il programma resta in esecuzione finché la finestra è aperta: main terminato compare con la finestra ancora aperta. Gli eventi non sono solo le pressioni dei pulsanti: anche il passaggio da un campo al successivo con Tab ne è uno. main resta semplice, perché la complessità sta negli ascoltatori.
Il pulsante deve ricevere il comportamento da eseguire. Un metodo riceve come argomenti valori (un intero, un booleano) oppure oggetti: il comportamento si passa quindi dentro un oggetto, il cui metodo contiene il codice da eseguire. La classe di questo ascoltatore implementa l'interfaccia ActionListener, che richiede un solo metodo, actionPerformed(ActionEvent e). L'evento, l'oggetto ActionEvent, descrive l'azione e arriva come argomento.
- Si definisce la classe che implementa
ActionListener, con il codice inactionPerformed. - Si registra un'istanza presso il pulsante:
pulsante.addActionListener(ascoltatore). Con un ascoltatore non registrato non succede niente.
Un ascoltatore che modifica la finestra
Esercizio: una finestra con due pulsanti e un'area di testo. Un pulsante aggiunge una riga all'area, l'altro ne cancella il contenuto.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextArea;
public class Ascoltatore implements ActionListener {
private JTextArea testo;
public Ascoltatore(JTextArea testo) {
this.testo = testo;
}
public void actionPerformed(ActionEvent e) {
String comando = e.getActionCommand();
if (comando.equals("Linea"))
testo.append("una linea\n");
else if (comando.equals("Cancella"))
testo.setText("");
}
}
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
public class Principale {
public static void main(String[] args) {
JFrame finestra = new JFrame("Ascoltatore");
finestra.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
JButton linea = new JButton("Linea");
JButton cancella = new JButton("Cancella");
JTextArea testo = new JTextArea(20, 20);
Ascoltatore ascoltatore =
new Ascoltatore(testo);
linea.addActionListener(ascoltatore);
cancella.addActionListener(ascoltatore);
finestra.add(linea, BorderLayout.NORTH);
finestra.add(cancella, BorderLayout.SOUTH);
finestra.add(testo, BorderLayout.CENTER);
finestra.pack();
finestra.setVisible(true);
}
}
- Un ascoltatore per più pulsanti.
getActionCommand()restituisce il comando dell'evento, che per un pulsante è il testo scritto sopra. Un solo ascoltatore, registrato su entrambi, li distingue così. Le stringhe si confrontano conequals, non con==. In alternativa si scrivono due ascoltatori. - Condividere l'area.
actionPerformedha l'unico argomento previsto dal sistema, e non se ne possono aggiungere. L'area di testo è però una variabile locale dimain, e l'ascoltatore non la vede. Si salva allora un riferimento in un campo dell'ascoltatore, passandolo al costruttore. La variabile dimaine il campotestoriferiscono la stessaJTextArea: quella mostrata nella finestra. - Modificare il testo.
appendaggiunge in fondo e non va a capo, comeprint: il ritorno a capo si scrive nel testo.setTextsostituisce tutto il contenuto, e con la stringa vuota lo cancella.
Gli oggetti dell'esercizio
Due errori tipici: dimenticare addActionListener, e lasciare che l'ascoltatore riferisca un'area diversa da quella mostrata, perché append scrive allora in un'area che nessuno vede. Per accorgersi del primo si comincia actionPerformed con una println: se non compare, il metodo non viene eseguito.
Layout manager
Il layout manager di un contenitore decide dove stanno i componenti e come cambiano le loro dimensioni quando la finestra si ridimensiona. La finestra ha di default un BorderLayout; con setLayout se ne sceglie un altro. I layout dispongono i componenti in posizione relativa, così lo spazio segue il loro ruolo: in un editor la barra del menu resta una striscia e l'area di testo si allarga.
Per provare un layout si usano pulsanti, che mostrano come si allarga ogni zona.
BorderLayout
Il contenitore ha cinque zone. add(componente, posizione) sceglie la zona. Senza posizione il componente va al centro: un secondo add finisce nella stessa zona e si vede solo l'ultimo.
| Zona | Posizione | Mantiene | Si estende |
|---|---|---|---|
| nord | BorderLayout.NORTH | altezza | in larghezza |
| sud | BorderLayout.SOUTH | altezza | in larghezza |
| ovest | BorderLayout.WEST | larghezza | in altezza |
| est | BorderLayout.EAST | larghezza | in altezza |
| centro | BorderLayout.CENTER | niente | nello spazio che resta |
Una disposizione tipica: in alto il menu, in basso la barra di stato, al centro il contenuto, ai lati i comandi.
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Posizioni {
public static void main(String[] args) {
JFrame finestra = new JFrame("BorderLayout");
finestra.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
finestra.add(new JButton("Nord"),
BorderLayout.NORTH);
finestra.add(new JButton("Sud"),
BorderLayout.SOUTH);
finestra.add(new JButton("Ovest"),
BorderLayout.WEST);
finestra.add(new JButton("Est"),
BorderLayout.EAST);
finestra.add(new JButton("Centro"),
BorderLayout.CENTER);
finestra.pack();
finestra.setVisible(true);
}
}
GridLayout
finestra.setLayout(new GridLayout(3, 2)) crea una griglia di 3 righe e 2 colonne. I componenti si aggiungono in sequenza, riga per riga, senza posizione. Le celle hanno tutte la stessa dimensione e riempiono lo spazio, quindi si allargano insieme.
Con righe e colonne entrambe diverse da zero uno dei due numeri è ignorato: conta quello delle righe. Conviene allora indicare solo uno dei due e mettere 0 all'altro. GridLayout(0, 1) è una colonna con tante righe quanti sono i componenti: aggiungerne uno non obbliga a cambiare il layout.
FlowLayout
new FlowLayout() dispone i componenti uno dopo l'altro, centrati, senza allargarli. Se la finestra si restringe, quelli che non entrano passano a una seconda riga, poi a una terza.
Pannelli annidati
Un JPanel è un contenitore con un proprio layout. Si aggiunge a una finestra o a un altro pannello, e può contenere componenti e altri pannelli. Così si combinano layout diversi: la finestra ha il BorderLayout, e in una sua zona sta un pannello con una griglia o con un flusso.
Due errori tipici: creare il pannello e non aggiungerlo alla finestra; aggiungere i componenti alla finestra invece che al pannello. I componenti che devono reagire vanno in variabili, per registrarvi gli ascoltatori: un pulsante creato dentro add basta per vedere la disposizione, ma se ne perde il riferimento.
Prima di scrivere il codice conviene disegnare su carta la gerarchia di contenimento. Per l'esercizio seguente:
- finestra, con
BorderLayout- al centro il pannello
centro, conGridLayout(0, 1)- tre
JTextField: primo addendo, secondo addendo, risultato
- tre
- a sud il pannello
sud, conFlowLayout- il pulsante «Somma»
- al centro il pannello
Esempio: somma di due numeri
Il pulsante somma due interi scritti nei primi due campi e mostra il risultato nel terzo. L'ascoltatore ha bisogno dei tre campi: li riceve dal costruttore, come prima l'area di testo.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Somma {
public static void main(String[] args) {
JFrame finestra = new JFrame("Somma");
finestra.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
JTextField primo = new JTextField(20);
JTextField secondo = new JTextField(20);
JTextField risultato = new JTextField(20);
JPanel centro =
new JPanel(new GridLayout(0, 1));
centro.add(primo);
centro.add(secondo);
centro.add(risultato);
JButton somma = new JButton("Somma");
JPanel sud = new JPanel(new FlowLayout());
sud.add(somma);
somma.addActionListener(new Addizione(
primo, secondo, risultato));
finestra.add(centro, BorderLayout.CENTER);
finestra.add(sud, BorderLayout.SOUTH);
finestra.pack();
finestra.setVisible(true);
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextField;
public class Addizione implements ActionListener {
private JTextField primo, secondo, risultato;
public Addizione(JTextField primo,
JTextField secondo,
JTextField risultato) {
this.primo = primo;
this.secondo = secondo;
this.risultato = risultato;
}
public void actionPerformed(ActionEvent e) {
int a = Integer.parseInt(primo.getText());
int b = Integer.parseInt(secondo.getText());
int r = a + b;
risultato.setText(Integer.toString(r));
}
}
getText() restituisce una stringa, e le stringhe non si sommano: darebbero la concatenazione. Per questo si convertono in interi, si sommano e si riconverte il risultato in stringa:
Con e il terzo campo mostra .
Se un campo contiene testo non numerico, per esempio 12A, Integer.parseInt lancia NumberFormatException: il metodo si interrompe e il risultato non cambia. L'errore compare solo nella console dell'ambiente di sviluppo, e fuori da esso l'utente non vede nulla. Va invece segnalato nell'interfaccia, con la gestione delle eccezioni.
Formulario
Finestra
new JFrame(titolo)- nasce invisibile
add(componente)pack(): dimensionasetVisible(true)- chiusura:
EXIT_ON_CLOSE
Layout
- finestra:
BorderLayout setLayout(layout)
BorderLayout
- nord, sud: altezza fissa
- ovest, est: larghezza fissa
- centro: lo spazio che resta
Griglia e flusso
GridLayout(righe, colonne)- uno dei due a
0: si adatta FlowLayout(): va a capo
Ascoltatore
implements ActionListeneractionPerformed(e)addActionListener(a)e.getActionCommand()- stringhe:
equals
Testo
append(s): in fondosetText(""): cancellagetText(): legge
Somma di due campi
, : interi dei campi.