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