Interfacce grafiche
Evoluzione dell’interazione programma/utente:
primi programmi
input da terminale (equivalente al moderno prompt dei comandi)
programmi recenti
input da finestra (es. BlueJ, notepad, ecc.)
Programmi visti finora
Output su terminale
Input da file o con JOptionPane
Ora: interazione evoluta con l’utente
Il flusso di controllo
Flusso di controllo lineare:
1. il programma inizia
2. si eseguono le sue istruzioni in sequenza
(eventualmente, invocando dei metodi)
3. il programma termina
Per programmi con interfaccia evoluta, non va bene
Esempi di programmi visti
javac
si lancia, il file sorgente viene compilato, termina
notepad
quando si lancia, viene aperta una finestra;
a questo punto, il programma aspetta che l’utente faccia qualcosa:
preme un tasto
aggiunge un carattere al file
va su File-Salva
salva il file
va su File-Salva come
apre una seconda finestra per far scegliere all’utente il nome del file
chiude
il programma termina
1
Differenza essenziale
Tipo di programma
Tipo di flusso
Programma senza
interfaccia
Flusso lineare, dall’inizio alla fine; ogni tanto si invoca un metodo oppure
si aspetta l’input dall’utente
Programma con
interfaccia
Aspetta che l’utente faccia qualcosa; soltanto in questo caso, si eseguono
delle istruzioni
I programmi visti finora
Se si esegue:
java Prova
Quello che succede è:
si cerca una classe Prova
se vede se ha un metodo main
si esegue il metodo main
Gli applet
L’applet è diverso:
1. si disegna la finestra
2. si specifica cosa deve fare il programma quando l’utente fa qualcosa
3. si aspetta che l’utente faccia qualcosa
Dato che la parte 3 c’è sempre, non è necessario scriverla!
(è implicita)
Un esempio di applet
Vediamo come si realizza l’applet qui sotto
Ogni volta che si preme il pulsante, il numero aumenta di uno
2
Cosa serve per realizzare questo applet?
1. devo dire che voglio disegnare un pulsante e una stringa
2. devo dire che, quando si preme il pulsante, il numero scritto nella stinga aumenta di uno
Il sorgente dell’applet
Questo è il sorgente completo
Sembra complicato, ma in effetti ci sono solo le due cose dette prima: il disegno della finestra e la
reazione a eventi
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Incr
extends Applet
implements ActionListener {
Button incr_button;
Label l;
int val;
public void init() {
this.val=0;
this.incr_button=new Button("Incrementa");
this.add(this.incr_button);
this.incr_button.addActionListener(this);
this.l=new Label("0");
this.add(this.l);
}
public void actionPerformed(ActionEvent e) {
this.val++;
this.l.setText(Integer.toString(val));
this.l.invalidate();
this.validate();
}
}
Vediamo le sue parti
La creazione di un applet
Quando si esegue un programma, oppure un applet, succedono due cose diverse:
programma:
si esegue il metodo statico main
applet:
si crea un oggetto della classe, e si invoca su di esso il metodo dinamico init
3
Notare la differenza: nel primo caso si esegue un metodo statico, nel secondo si crea un oggetto, e su
di esso si invoca un metodo
Il metodo init
In questo metodo, scrivo cosa ci va messo nella finestra
Questo applet disegna un pulsante e una etichetta:
import java.awt.*;
import java.applet.*;
public class Dis
extends Applet {
public void init() {
Button i=new Button("Incrementa");
this.add(i);
Label l=new Label("0");
this.add(l);
}
}
Se si preme il pulsante non succede niente!
Significato delle parti
Gli oggetti Button sono i pulsanti
Per aggiungere un pulsante a un applet:
Button i=new Button("Incrementa");
this.add(i);
1. creo il pulsante: il costruttore ha come argomento la stringa che ci deve essere scritta dentro
2. aggiungo il pulsante all’applet: questo si fa con this.add(...)
Se non faccio la seconda cosa, il pulsante non viene disegnato
Qui this chi è?
Il this dentro gli applet
Lanciare un applet=creare un oggetto della classe
Quindi, il this è l’oggetto della classe che è stato creato
init è un metodo dinamico: viene invocato su un oggetto, che è una finestra
Le finestre hanno anche il metodo dinamico add, per aggiungere componenti
4
Il metodo add, come altri metodi che servono, viene ereditato dalla classe Applet
È per questo che la classe deve estendere Applet (ossia, si deve fare extends Applet)
Le label
Sono stringhe scritte nella finestra
Label l=new Label("0");
this.add(l);
1. creo l’oggetto Label: la stringa è quello che viene stampato effettivamente
2. lo aggiungo all’applet
Esercizio
Creare un applet con due pulsanti Ok e Cancel e due etichette con le scritte "Questo essere" e
"applet sgrammaticato"
Soluzione
Faccio un applet
Dentro ci metto i due pulsanti e le due label
Implementazione
Nel metodo init devo mettere la creazione dei quattro oggetti
import java.awt.*;
import java.applet.*;
public class Sgramm
extends Applet {
public void init() {
Button i=new Button("OK");
this.add(i);
Button j=new Button("Cancel");
this.add(j);
Label l=new Label("Questo essere");
this.add(l);
Label t=new Label("applet sgrammaticato");
this.add(t);
}
}
5
Si poteva anche fare this.add(new Button("OK")) ecc.
Poi vediamo perchè di solito non si fa
Come si presenta
Se si fa click sui pulsanti, non succede niente!
Questo è normale: non gli ho ancora detto cosa deve fare quando l’utente preme un pulsante!
Interazioni
Ogni pulsante genera un evento
Per fare in modo che l’applet lo sappia, devo fare queste due cose:
1. nell’applet, devo creare un metodo che serve a ricevere l’evento
2. devo dire al pulsante che l’evento va inviato all’applet
Metafora della trasmissione
Quando il pulsante viene premuto, l’oggetto Button invia un messaggio per dire "sono stato
premuto!"
Però:
1. devo dire al pulsante che voglio anche io (l’applet) ricevere questi messaggi
2. devo scrivere l’applet in maniera che possa ricevere messaggi
(e faccia quello che deve fare quando li riceve)
6
La ricezione del messaggio
Per fare in modo che l’applet possa ricevere un messaggio, devo fare queste due cose:
public class Incr
extends Applet
implements ActionListener {
...
public void actionPerformed(ActionEvent e) {
}
}
Significato delle due cose:
implements ActionListener
dichiara che gli oggetti applet di questa classe sono in grado di ricevere messaggi
actionPerformed
è il metodo di "ricezione"
Quando qualcuno invia un messaggio all’applet, si attiva il metodo di ricezione actionPerformed
L’interfaccia ActionListener contiene il metodo actionPerformed. Quando si scrive
implements ActionListener dichiara che sugli oggetti della classe si può invocare il metodo
actionPerformed
L’invio del messaggio
Ora l’applet è in grado di ricevere messaggi
Devo dire al pulsante che deve informare l’applet ogni volta che viene premuto
Uso il metodo addActionListener
public void init() {
Button i=new Button("Incrementa");
this.add(i);
i.addActionListener(this);
Label l=new Label("0");
this.add(l);
}
Significato: sto dicendo al pulsante: quando vieni premuto, fammi sapere
Il metodo addActionListener ha come argomento un ActionListener
Si può quindi invocare solo su oggetti che hanno il metodo actionPerformed
7
L’appet completo
Finora, abbiamo questo codice
Serve una import in più
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Con
extends Applet
implements ActionListener {
public void init() {
Button i=new Button("Incrementa");
this.add(i);
i.addActionListener(this);
Label l=new Label("0");
this.add(l);
}
public void actionPerformed(ActionEvent e) {
}
}
Cosa succede...
Cosa succede se premo il pulsante?
1. il bottone invia un messaggio a tutti quelli che gli hanno detto di essere interessati a saperlo
2. fra questi, c’è l’applet: ha detto di essere interessato con i.addActionListener(this)
3. viene invocato il metodo actionPerformed (il metodo di ricezione)
Cosa succede in pratica?
Risposta
Quando si preme il pulsante, viene invocato il metodo actionPerformed
Dato che questo metodo non contiene istruzioni, quando viene invocato non succede niente
Quando si preme il pulsante, non succede niente di visibile (viene invocato un metodo senza
istruzioni)
Esempio di ricezione
Esempio facile: quando ricevo un messaggio, stampo una stringa sul prompt dei comandi:
8
public void actionPerformed(
ActionEvent e) {
System.out.println(
"Ho ricevuto un messaggio");
}
Cosa succede in questo caso?
Quando si preme il pulsante:
dato cho detto al pulsante che this vuole sapere quando viene premuto,
quando effettivamente viene premuto il pulsante, viene invocato this.actionPerformed
nel metodo, c’è scritto di stampare la stringa
Vedere la stampa da browser
Se si sta usando un browser (es. IE),
le stringhe stampate su terminale non si vedono
Per poterle vedere: attivare la console Java
Modifiche alla finestra
Tipicamente, gli applet non stampano niente sul terminale
Di solito, l’output di un applet viene scritto nella finestra
Esempio: ogni volta che si preme il pulsante, aggiungo una etichetta
public void actionPerformed(ActionEvent e) {
Label t=new Label("1");
this.add(t);
t.invalidate();
this.validate();
}
Le ultime due istruzioni servono per ridisegnare la finestra (prox pagina)
I metodi invalidate e validate
Quando uno degli oggetti che stanno dentro la finestra viene modificato, bisogna fare:
fare obj.invalidate() per tutti gli oggetti che sono stati modificati
fare this.validate()
Attenzione! le invocazioni di invalidate vanno fatte prima di this.validate()
Cosa significa:
9
invalidate()
serve a dichiarare che questa componente è cambiata, e quindi il modo in cui è attualmente
disegnata non è più valido
validate()
serve a chiedere al sistema di disegnare (rendere valide) tutte le componenti dell’applet
In pratica, basta sapere che si fa prima invalidate per tutti gli oggetti modificati, e poi
this.validate()
L’applet completo
Il codice complessivo contiene sia il metodo init (che crea il pulsante e la prima label) che il metodo
actionPerformed (che riceve i messaggio che provengono dal pulsante)
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Add
extends Applet
implements ActionListener {
public void init() {
Button i=new Button("Incrementa");
this.add(i);
i.addActionListener(this);
Label l=new Label("0");
this.add(l);
}
public void actionPerformed(ActionEvent e) {
Label t=new Label("1");
this.add(t);
t.invalidate();
this.validate();
}
}
invalidate e validate servono per ridisegnare l’applet
Come funziona
L’applet è riportato qui sotto:
Stato iniziale:
10
Quando premo il pulsante, viene creata una nuova etichetta:
Se premo il pulsante ancora, ne creo ancora un’altra, ecc.
Modificare una label già esistente
Modificare una label è il modo più semplice per fornire un output all’utente
Metodo setText della classe Label:
l.setText(string);
11
La stringa diventa il nuovo contenuto dell’etichetta
Visibilità della variabile
public class Con
extends Applet
implements ActionListener {
public void init() {
...
Label l=new Label("0");
this.add(l);
}
public void actionPerformed(ActionEvent e) {
// modifica l
}
}
La variabile l è locale al metodo init
La variabile l non si vede nel metodo actionPerfomed
Domanda
Quali variabili sono visibili in tutti i metodi dinamici dell’applet?
Suggerimento: notare "dinamici"
Risposta
Tutti i metodi dinamici vedono this, e quindi tutti vedono le componenti
Soluzione: invece di usare una variabile locale uso una componente l
Funziona perchè sia init che actionPerformed vengono invocati sullo stesso oggetto di
invocazione
// import
public class Mod
extends Applet
implements ActionListener {
Label l;
public void init() {
// creazione pulsante
this.l=new Label("0");
this.add(this.l);
}
public void actionPerformed(ActionEvent e) {
this.l.setText("1");
12
this.l.invalidate();
this.validate();
}
}
Funzionamento
Quando si lancia l’applet:
viene creato un oggetto della classe
su questo oggetto viene invocato init
questo crea il pulsante e la label
l’indirizzo della label viene scritto nella componente l dell’oggetto
quando il pulsante viene premuto, si invoca actionPerformed sull’oggetto
Cosa succede effettivamente
Quando si inizia:
Quando si preme il pulsante:
Se si preme di nuovo il pulsante, viene invocato di nuovo actionPerformed, che cambia il testo
della label in "1"
13
Quindi, non cambia niente
Esercizio
Modificare l’applet in modo che l’etichetta si incrementi ogni volta che si preme il pulsante
Nota+suggerimento: il metodo setText ha come argomento una stringa; per convertire un intero in
stringa, usare Integer.toString(int)
Questo metodo ha come argomento un intero e come valore di ritorno una stringa
Soluzione
Serve una variabile intera per contare quante volte il pulsante è stato premuto
Questa variabile non la posso mettere nel metodo actionPerformed, altrimenti verrebbe distrutta
ogni volta che il metodo termina
Uso una componente
Implementazione
Componente n
Quando inizializzo l’applet, metto a 0 questa componente
Ogni volta che si preme il pulsante, aumento di uno
// import
public class Inc
extends Applet
implements ActionListener {
Label l;
int n;
public void init() {
// crea pulsante
this.n=0;
this.l=new Label(Integer.toString(n));
this.add(this.l);
}
public void actionPerformed(ActionEvent e) {
this.n++;
this.l.setText(Integer.toString(n));
this.l.invalidate();
this.validate();
}
}
14
Dato che ho modificato la label l, devo fare l.invalidate()
Poi devo comunque fare this.validate()
Il programma completo
Nel programma di prima, sono state omesse le import e la creazione del pulsante
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Inc
extends Applet
implements ActionListener {
Label l;
int n;
public void init() {
Button i=new Button("Incrementa");
this.add(i);
i.addActionListener(this);
this.n=0;
this.l=new Label(Integer.toString(n));
this.add(this.l);
}
public void actionPerformed(ActionEvent e) {
this.n++;
this.l.setText(Integer.toString(n));
this.l.invalidate();
this.validate();
}
}
Esercizio
Modificare il programma di prima in modo tale che la label contenga si inizialmente, e poi si passi a
no e poi di nuovo a si ogni volta
Soluzione
Prima soluzione: uso sempre la variabile n e poi ogni volta vedo se è un numero pari oppure dispari:
// import
public class Mama
extends Applet
implements ActionListener {
Label l;
int n;
public void init() {
15
...
}
public void actionPerformed(ActionEvent e) {
this.n++;
if(n%2==0)
this.l.setText("si");
else
this.l.setText("no");
this.l.invalidate();
this.validate();
}
}
Altra soluzione
Memorizzo la stringa attualmente memorizzata
Ogni volta, cambio
// import
public class Cambio
extends Applet
implements ActionListener {
Label l;
String s;
public void init() {
// pulsante
this.s="si";
this.l=new Label(this.s);
this.add(this.l);
}
public void actionPerformed(ActionEvent e) {
if(this.s.equals("si"))
this.s="no";
else
this.s="si";
this.l.setText(this.s);
this.l.invalidate();
this.validate();
}
}
Variante
Uso una variabile booleana
16
// import
public class ConBool
extends Applet
implements ActionListener {
Label l;
boolean b;
public void init() {
// pulsante
this.b=true;
this.l=new Label("si");
this.add(this.l);
}
public void actionPerformed(ActionEvent e) {
this.b=!this.b;
if(this.b)
this.l.setText("si");
else
this.l.setText("no");
this.l.invalidate();
this.validate();
}
}
L’istruzione this.b=!this.b ha l’effetto di negare ogni volta il valore di this.b
Più pulsanti
Scrivere l’applet che ha due pulsanti: Incrementa e Azzera
Scrivere solo il metodo init
Disegno
Questo è facile: faccio due pulsanti invece di uno
public void init() {
Button i=new Button("Incrementa");
this.add(i);
i.addActionListener(this);
Button a=new Button("Azzera");
this.add(a);
a.addActionListener(this);
this.n=0;
this.l=new Label(Integer.toString(this.n));
this.add(this.l);
}
17
Azioni
Quando premo uno qualsiasi dei due pulsanti, viene comunque invocato il metodo
actionPerformed
Infatti, tutti e due i pulsanti sono stati istruiti a inviare messaggi a this
Il metodo di ricezione di this è sempre e comunque actionPerformed
Il parametro di actionPerformed
Il parametro di actionPerformed è un oggetto che permette di capire il mittente del messaggio
e.getSource();
Questo metodo ritorna un oggetto, che è il mittente del messaggio
Nel nostro caso: e.getSource() è l’oggetto Button che è stato premuto
Come fare cose diverse
Come faccio a fare cose diverse a seconda del pulsante premuto?
Confronto e.getSource() con gli oggetti pulsante
Ma il pulsante:
va creato in init
serve in actionPerformed per capire chi è il mittente
Al posto di variabili locali, uso due componenti
Implementazione di init
È come al solito, solo che le due variabili pulsante sono componenti e non più variabili locali
public class Azzera
extends Applet
implements ActionListener {
Label l;
int n;
Button i, a;
public void init() {
this.i=new Button("Incrementa");
this.add(this.i);
this.i.addActionListener(this);
this.a=new Button("Azzera");
this.add(this.a);
this.a.addActionListener(this);
18
this.n=0;
this.l=new Label(Integer.toString(this.n));
this.add(this.l);
}
...
}
Il metodo actionPerformed
A seconda di quale è l’oggetto che risulta da e.getSource(), faccio una cosa oppure l’altra
public void actionPerformed(ActionEvent e) {
if(e.getSource()==this.i)
this.n++;
if(e.getSource()==this.a)
this.n=0;
this.l.setText(Integer.toString(this.n));
this.l.invalidate();
this.validate();
}
L’applet completo
Il programma completo è quello che segue
Dato che i metodi init e actionPerformed vengono invocati sullo stesso oggetto, le componenti
this.a ecc. sono le stesse variabili
È come se fossero variabili che si possono usare in tutti i metodi della classe
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Azzera
extends Applet
implements ActionListener {
Label l;
int n;
Button i, a;
public void init() {
this.i=new Button("Incrementa");
this.add(this.i);
this.i.addActionListener(this);
this.a=new Button("Azzera");
this.add(this.a);
this.a.addActionListener(this);
this.n=0;
this.l=new Label(Integer.toString(this.n));
this.add(this.l);
}
19
public void actionPerformed(ActionEvent e) {
if(e.getSource()==this.i)
this.n++;
if(e.getSource()==this.a)
this.n=0;
this.l.setText(Integer.toString(this.n));
this.l.invalidate();
this.validate();
}
}
Osservazioni
L’applet sembra lungo, ma le cose che fa sono poche:
per gli oggetti pulsanti, etichette, ecc. uso delle componenti, cosí sono visibili in tutti i metodi
(tutti i metodi vengono invocati sullo stesso oggetto)
nel metodo init: faccio la creazione degli oggetti: per ogni oggetto, lo aggiungo all’applet, e poi
dico all’oggetto che voglio ricevere i suoi messaggi
il metodo actionPerformed è quello che viene invocato quando si ricevono messaggi; per
sapere il mittente uso e.getSource(), dove e è il parametro formale
Schema di applet
import ...
class NomeApplet extends Applet
implements ActionListener {
// oggetti pulsanti ecc.
// variabili che devono essere visibili
//
a tutti i metodi
public void init() {
// inizializzazioni
// creazione pulsanti, etichette, ecc.
}
public void actionPerformed(ActionEvent e) {
// cosa fare quando viene premuto un
// pulsante: il pulsante premuto e’ quello
// che risulta da e.getSource()
}
}
Esercizio
Realizzare un applet che contiene:
20
1. due pulsanti si e no
2. una etichetta che contiene si oppure no a seconda dell’ultimo pulsante che è stato premuto;
3. una etichetta che contiene il numero totale di volte che i pulsanti sono stati premuti
Progettazione di un applet
Il codice di un applet può essere lungo solo perchè ci sono molte cose da scrivere
Come per tutti i programmi lunghi:
pensare prima alla soluzione, poi implementare
Come realizzare l’applet
Serve: un intero per memorizzare il numero di volte che un pulsante è stato premuto
Cosa fa il metodo actionPerformed: cambia la prima etichetta in base al pulsante, aumenta il
numero, e cambia la seconda
Il numero va incrementato indipendentemente da quale sia pulsante premuto
Componenti
Sono le variabili che devono essere visibili a tutti i metodi
Servono: due pulsanti, due etichette, e un intero
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Indeciso
extends Applet
implements ActionListener {
Label quante, ultimo;
int num;
Button s, n;
public void init() {
...
}
public void actionPerformed(ActionEvent e) {
...
}
}
21
Il metodo init
Crea i quattro oggetti
Dato che non è stato ancora premuto nessun pulsante, nella prima etichetta ci metto la stringa vuota ""
Inizializza la componente num
Label quante, ultimo;
int num;
Button s, n;
public void init() {
this.s=new Button("Si");
this.add(this.s);
this.s.addActionListener(this);
this.n=new Button("No");
this.add(this.n);
this.n.addActionListener(this);
this.num=0;
this.quante=new Label(Integer.toString(this.num));
this.add(this.quante);
this.ultimo=new Label("");
this.add(this.ultimo);
}
Il metodo actionPerformed
Cambio il testo dell’etichetta a seconda del tasto premuto
Poi aumento il numero di volte che un pulsante è stato premuto
public class Indeciso
extends Applet
implements ActionListener {
Label quante, ultimo;
int num;
Button s, n;
public void init() {
...
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==this.s)
this.ultimo.setText("Si");
if(e.getSource()==this.n)
this.ultimo.setText("No");
this.num++;
this.quante.setText(Integer.toString(this.num));
this.quante.invalidate();
22
this.ultimo.invalidate();
this.validate();
}
}
Notare: dato che modifico sia quante che ultimo, devo fare invalidate su tutti e due
Gli oggetti TextField
Sono campi in cui l’utente può inserire dei dati
Esempio di campo:
L’utente può digitare una stringa
Come si usano
Creazione (in init):
this.t=new TextField(20);
this.add(this.t);
il numero dice quanto deve essere largo il campo
Vedere quale valore è stato inserito (in actionPerformed):
String s=this.t.getText();
Esercizio
Creare un applet con tre oggetti dentro:
un pulsante "Cambia"
un campo testo
une etichetta
Ogni volta che si preme il pulsante, nella etichetta viene messa la stringa che si trova attualmente nel
campo testo
Componenti
Servono: un pulsante, un textfield, e una label:
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Ultimo
extends Applet
23
implements ActionListener {
Label ultimo;
Button cambia;
TextField t;
public void init() {
...
}
public void actionPerformed(ActionEvent e) {
...
}
}
Il metodo init
Devo solo creare i tre oggetti
public class Ultimo
extends Applet
implements ActionListener {
Label ultimo;
Button cambia;
TextField t;
public void init() {
this.cambia=new Button("Cambia");
this.add(this.cambia);
this.cambia.addActionListener(this);
this.t=new TextField(20);
this.add(this.t);
this.ultimo=new Label("");
this.add(this.ultimo);
}
public void actionPerformed(ActionEvent e) {
...
}
}
Il metodo actionPerformed
Ogni volta che premo il pulsante:
vado a vedere cosa c’è scritto nel campo testo con il metodo getText
cambio il testo dell’etichetta
public class Ultimo
extends Applet
implements ActionListener {
Label ultimo;
Button cambia;
TextField t;
24
public void init() {
...
}
public void actionPerformed(ActionEvent e) {
this.ultimo.setText(t.getText());
this.ultimo.invalidate();
this.validate();
}
}
Gli eventi del TextField
Se si preme enter nel campo testo, non succede niente
Di solito, ci si aspetta che succeda qualcosa
I campi testi generano un messaggio quando si preme enter
Però, non ho detto a questo campo testo che volevo sapere i suoi messaggi
Soluzione: basta dirlo in init:
this.t.addActionListener(this);
Variante
Stesse componenti
Quando si preme enter nel campo testo, il contenuto viene messo nell’etichetta
Un solo pulsante "Clear", che rimette la stringa vuota nell’etichetta
Soluzione
Quasi tutto come prima
Però ora, se la sorgente dell’evento è il pulsante, si pulisce l’etichetta; se è il campo testo, si cambia
Componenti
Sono le stesse di prima
Ho solo cambiato i nomi (ma non era necessario)
25
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class Clear
extends Applet
implements ActionListener {
Label ultimo;
Button pulisci;
TextField t;
Metodo init
Crea i tre oggetti
Dato che voglio i messaggi del campo testo, devo invocare addActionListener su di esso
public class Clear extends Applet
implements ActionListener {
Label ultimo;
Button pulisci;
TextField t;
public void init() {
this.pulisci=new Button("Clear");
this.add(this.pulisci);
this.pulisci.addActionListener(this);
this.t=new TextField(20);
this.add(this.t);
this.t.addActionListener(this);
this.ultimo=new Label("");
this.add(this.ultimo);
}
public void actionPerformed(ActionEvent e) {
...
}
}
Metodo actionPerformed
Se il messaggio viene dal pulsante, metti "" nell’etichetta
Se viene dal campo testo, mettici il contenuto del campo testo
public class Clear
extends Applet
implements ActionListener {
Label ultimo;
Button pulisci;
TextField t;
public void init() {
26
...
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==this.pulisci)
this.ultimo.setText("");
if(e.getSource()==this.t)
this.ultimo.setText(t.getText());
this.ultimo.invalidate();
this.validate();
}
}
Cancellare il campo testo
Esiste un metodo setText anche per gli oggetti TextField
this.t.setText(stringa)
Al posto del contenuto attuale, ci mette la stringa
Esempio: se sto scrivendo:
E viene invocato setText("aaa"), il campo diventa:
Modifica applet di sopra
Nell’esempio di sopra, se il pulsante deve anche cancellare il campo testo, basta modificare
actionPerfomed
public void actionPerformed(ActionEvent e) {
if(e.getSource()==this.pulisci) {
this.ultimo.setText("");
this.t.setText("");
}
if(e.getSource()==this.t)
this.ultimo.setText(t.getText());
this.ultimo.invalidate();
this.t.invalidate();
this.validate();
}
Notare che invalidate va fatto per tutte le componenti che vanno ridisegnate
27
validate e invalidate
Se non si fa this.validate() le modifiche non si vedono
Se non si fa obj.invalidate(), spesso funziona lo stesso
Ci possono però essere problemi se la modifica comporta la dimensione dell’oggetto (es. una label
diventa più lunga)
Sempre meglio metterli tutti e due
Attenzione! Va fatto prima invalidate() e poi validate()
Il layout
Finora, i componenti li abbiamo aggiunti e basta
Vengono aggiunti in ordine, e si va ‘‘a capo’’ solo quando non c’è più spazio
Il modo in cui vengono disposti gli oggetti dipende dal layout manager
I layout manager
Esempi di layout manager:
FlowLayout
BorderLayout
GridLayout
CardLayout
Uso di un layout manager
Esempio di uso:
this.setLayout(new BorderLayout());
Button b=new Button("Ok");
this.add(b, BorderLayout.NORTH);
Cosa cambia?
1. si specifica quale layout manager va usato con setLayout
2. il metodo add si può invocare con argomenti addizionali
28
Come disporre gli oggetti
I layout manager di Java sono complicati da usare
Usiamo il RowLayout manager
Non fa parte delle classi standard di Java:
scaricare con il pulsante destro del mouse
e mettere il file nella directory dove stanno i programmi
Come si usa
Per prima cosa, si specifica che questo è il layout manager da usare
public void init() {
this.setLayout(new RowLayout());
Va fatto nel metodo init
Quando si aggiunge un componente che deve essere l’ultimo di una linea, si fa:
this.add(componente, RowLayout.NEWLINE);
Dopo questa invocazione, i prossimi oggetti verranno messi su una nuova linea
È una specie di println degli oggetti: dopo si va a capo
Esempio
L’applet di prima modificato:
si va a capo dopo il pulsante
public void init() {
this.setLayout(new RowLayout());
this.pulisci=new Button("Clear");
this.add(this.pulisci, RowLayout.NEWLINE);
this.pulisci.addActionListener(this);
this.t=new TextField(20);
this.add(this.t);
this.t.addActionListener(this);
this.ultimo=new Label("");
this.add(this.ultimo);
}
Esercizio
Realizzare un applet con questo layout
29
Voglio solo il metodo init (la disposizione degli oggetti)
Soluzione
Prima di tutto, specifico il layout a righe:
public void init() {
this.setLayout(new RowLayout());
La prima riga è fatta da una label e da un textfield
t1=new Label("Primo numero:");
this.add(t1);
f1=new TextField(20);
this.add(f1, RowLayout.NEWLINE);
f1.addActionListener(this);
La seconda riga è simile
Nella terza, ho i due pulsanti
sum=new Button("Somma");
this.add(sum);
sum.addActionListener(this);
clear=new Button("Clear");
this.add(clear);
clear.addActionListener(this);
Esercizio
Modificare l’applet in modo tale che, quando si preme il pulsante, sotto appaia la somma
30
Soluzione: il layout
Dopo il secondo pulsante, devo andare a capo e mettere la label con il risultato
public void init() {
this.setLayout(new RowLayout());
// prima riga: label+textfield
t1=new Label("Primo numero:");
this.add(t1);
f1=new TextField(20);
this.add(f1, RowLayout.NEWLINE);
f1.addActionListener(this);
// seconda riga: label+textfield
t2=new Label("Secondo numero:");
this.add(t2);
f2=new TextField(20);
this.add(f2, RowLayout.NEWLINE);
f2.addActionListener(this);
// terza riga: i due pulsanti
sum=new Button("Somma");
this.add(sum);
sum.addActionListener(this);
clear=new Button("Clear");
this.add(clear, RowLayout.NEWLINE);
clear.addActionListener(this);
// quarta riga: il risultato
res=new Label();
this.add(res);
}
Tutte le componenti vanno ovviamente definite prima
Soluzione: risposta a eventi
Risposta corretta ai pulsanti:
public void actionPerformed(ActionEvent e) {
// pulsante somma
if(e.getSource()==sum) {
int x=Integer.parseInt(f1.getText());
int y=Integer.parseInt(f2.getText());
res.setText(Integer.toString(x+y));
}
// pulsante clear
if(e.getSource()==clear) {
f1.setText("");
f2.setText("");
res.setText("");
}
31
// ridisegna
res.invalidate();
this.validate();
}
Il focus
Quando ci sono più campi testo nello stesso applet, il cursore si trova in uno dei due
Quello che l’utente digita va a finire nel campo dove si trova il cursore
Il campo che ha il cursore dentro si dice che ha il focus
Quando l’utente digita qualcosa, va a finire nel campo che ha il focus
Modificare il focus
Se f è un campo testo, allora f.requestFocus() dà a lui il focus
Dopo avere eseguito questa istruzione, il cursore viene messo in f
Quindi, tutto quello che l’utente ora digita va nel campo testo f
Esercizio
Modificare il programma:
quando si preme enter nel primo campo testo, il focus va al secondo
quando si preme enter nel secondo textfield, oppure nel pulsante somma, si fa la somma; il focus
torna al primo campo
quando si preme il tasto clear vengono ripuliti i campi testo e l’etichetta (ci va la stringa vuota), e
il fucus va al primo campo testo
Se f è un textfield, facendo f.requestFocus() dà il focus a lui (il cursore viene messo dentro f,
e i caratteri che l’utente inserisce vanno in f
Soluzione
Serve solo modificare il metodo actionPerformed
Basta controllare anche f1 ed f2 come possibili sorgenti dell’evento
public void actionPerformed(ActionEvent e) {
// pulsante somma oppure enter nel secondo
// textfield:
32
// si fa la somma
if((e.getSource()==sum) ||
(e.getSource()==f2)) {
int x=Integer.parseInt(f1.getText());
int y=Integer.parseInt(f2.getText());
res.setText(Integer.toString(x+y));
f1.requestFocus();
}
// pulsante clear
if(e.getSource()==clear) {
f1.setText("");
f2.setText("");
res.setText("");
f1.requestFocus();
}
// enter nel primo textfield:
// focus al secondo
if(e.getSource()==f1) {
f2.requestFocus();
}
// ridisegna
res.invalidate();
this.validate();
}
Osservazioni finali
Le GUI possono essere lunghe da scrivere, però di solito non ci sono algoritmi complessi
Sono fatte di due parti:
metodo init
qui si creano i pulsanti ecc. e si dice in che posizioni vanno disegnati
metodo actionPerformed
si specifica cosa deve succedere quando succede qualcosa
Il metodo init di solito è una sequenza creazione oggetto/aggiunta alla finestra
Nel metodo actionPerformed ci sono una serie di if per ognuno dei possibili oggetti che
possono inviare un messaggio
Ci sono dei programmi per disegnare il layout senza dover scrivere il metodo init
33