INTERFACCE GRAFICHE IN JAVA CON SWING DISPENSE
La Gestione degli Eventi Ogni oggetto grafico è predisposto ad essere sollecitato in qualche modo dall utente (per esempio un pulsante può essere premuto). In particolare si dice che un oggetto grafico genera un evento. E' possibile associare a questi oggetti degli ascoltatori (listener); questi poi, quando l'oggetto associato genera un evento, indicano cosa bisogna fare. Quindi: ho un oggetto grafico e gli associo un listener; l'oggetto grafico genera un evento; il listener cattura l'evento; il listener esegue le azioni che gli erano state indicate. Ovvero un componente non sa (e non è interessato a sapere) cosa avverrà al momento della sua sollecitazione: esso si limita a notificare ai propri ascoltatori che l evento che essi attendevano è avvenuto, e questi provvederanno a produrre l effetto desiderato. Ogni componente può generare più eventi; può avere più ascoltatori per un determinato evento o per eventi differenti. È anche possibile utilizzare uno stesso ascoltatore su più componenti anche diversi a patto che entrambi possono essere sollecitati per generare l evento (per esempio quando ho più pulsanti che dovranno fare la stessa cosa ma posizionati in posti diversi). Ogni componente può generare una notevole quantità di eventi (si pensi che è un evento anche il semplice passaggio del mouse sopra di esso), il programmatore può però considerare solo gli eventi a cui è interessato e ignorare i restanti. Listener Abbiamo diverse categorie di eventi e, per ognuna di esse, una categoria di listener. In particolare Java ci fornisce solo le interfacce per quanto riguarda i listener; siamo quindi noi che dobbiamo creare una classe che implementa tale interfaccia e che quindi implementa i metodi elencati. Le varie interfacce dei listener che abbiamo a disposizione hanno i nomi del tipo Listener dove al posto di abbiamo la tipologia di listener (ESEMPI: ActionListener, MouseListener, etc). Ognuna di esse elencherà i metodi che bisogna implementare. Esempio: MouseListener L'interfaccia MouseListener è definita nel package java.awt.event ed è così definita: public interface MouseListener void mouseclicked(mouseevent e); void mouseentered(mouseevent e); void mouseexited (MouseEvent e); void mousepressed(mouseevent e); void mousereleased(mouseevent e); Vediamo che i metodi prendono come parametro un oggetto di tipo MouseEvent (per altri tipi di eventi ci saranno altri tipi di oggetti); lo scopo è dare informazioni aggiuntive sull evento del
mouse. Infatti, questa definisce due metodi che permettono di conoscere le coordinate del mouse allo scatenarsi dell evento (i primi due) e un terzo metodo che permette di determinare quale bottone del mouse è stato premuto: int getx( ); int gety( ); int getmodifiers( ) ; Consideriamo il caso in cui vogliamo gestire gli eventi click generici su una finestra (non su dei pulsanti). Le operazioni da fare sono le seguenti: Definiamo una classe che implementa l'interfaccia MouseListener; Implementiamo in essa tutti i metodi che sono stati elencati da tale interfaccia, indicando le istruzioni da seguire in tal caso (ese: mouseclicked, mousepressed, etc). Definiamo una classe che estende JFrame (la finestra della nostra applicazione); All'interno di questa classe creo un oggetto della suddetta classe listener e glie l'associo. Per aggiungere un listener di tipo MouseListener, una componente può utilizzare il metodo (che ogni componente ha) addmouselistener( MouseListener m). Esempio di utilizzo di MouseListener: import java.awt.event.*; public class MyFrame extends JFrame public MyFrame() super("mousetest"); this.addmouselistener(new MouseSpy()); setsize(200,200); setvisible(true); public static void main(string [] args) MyFrame f = new MyFrame(); f.setdefaultcloseoperation(jframe.exit_on_close); f.setvisible(true); class MouseSpy implements MouseListener public void mouseclicked(mouseevent e) System.out.println ("Click su ("+e.getx()+","+e.gety()+")"); public void mousepressed(mouseevent e) System.out.println ("Premuto su ("+e.getx()+","+e.gety()+")"); public void mousereleased(mouseevent e) System.out.println("Rilasciato su ("+e.getx()+","+e.gety()+")"); public void mouseentered(mouseevent e) public void mouseexited(mouseevent e)
Gli Ascoltatori più Utilizzati ActionListener : Definisce 1 metodo per ricevere eventi-azione. ComponentListener : Definisce quattro metodi per riconoscere quando un componente viene nascosto, spostato, mostrato o ridimensionato. FocusListener : Definisce due metodi per riconoscere quando un componente ottiene o perde il focus. KeyListener : Definisce tre metodi per riconoscere quando viene premuto, rilasciato o battuto un tasto. MouseMotionListener : Definisce due metodi per riconoscere quando il mouse e trascinato o spostato. MouseListener : Definisce due metodi che permettono di conoscere le coordinate del mouse allo scatenarsi dell evento. TextListener : Definisce 1 metodo per riconoscere quando cambia il valore di un campo testo. WindowListener : Definisce 7 metodi per riconoscere quando un finestra viene attivata, chiusa, disattivata, ripristinata, ridotta a icona, ecc.
La gestione degli eventi Azione La maggior parte dei componenti Swing generano eventi, noti come eventi azione, che possono essere catturati da un ActionListener. Essa definisce un unico metodo: public interface ActionListener public void actionperformed(actionevent ae); La maggior parte dei widget java sono predisposti per generare eventi azione. Ad esempio quando si clicca su un bottone, si preme INVIO in un campo di testo, si seleziona una voce di un menu oppure si seleziona/deseleziona una voce di una checkbox o un radiobutton, viene generato un evento azione. Esempio: CLASSE FINESTRA3.JAVA import java.awt.*; public class Finestra3 extends JFrame public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; //creo dei bottoni private JButton b1 = new JButton("uno"); private JButton b2 = new JButton("due"); private JButton b3 = new JButton("tre"); private JButton b4 = new JButton("quattro"); private JButton b5 = new JButton("cinque"); //creo un pannello JPanel p1 = new JPanel();
//listener Ascoltatore listener = new Ascoltatore(); public Finestra3( ) //chiamo il costruttore della superclasse JFrame super("la Mia Finestra 3"); setsize(default_width, DEFAULT_HEIGHT); //ottengo il content pane Container c = this.getcontentpane(); //setto il layout del pannello p1.setlayout(new FlowLayout()); //aggiungo i bottoni al pannello e gli aggiungo un listener p1.add(b1); b1.addactionlistener(listener); p1.add(b2); b2.addactionlistener(listener); p1.add(b3); b3.addactionlistener(listener); p1.add(b4); b4.addactionlistener(listener); p1.add(b5); b5.addactionlistener(listener); //aggiungo il pannello al content pane c.add(p1); public static void main(string [] args) Finestra3 f = new Finestra3(); f.setdefaultcloseoperation(jframe.exit_on_close); f.setvisible(true); CLASSE ASCOLTATORE.JAVA import java.awt.event.*; public class Ascoltatore implements ActionListener public void actionperformed(actionevent event) JButton b = (JButton)event.getSource(); JOptionPane.showMessageDialog(null,"e' stato premuto "+b.gettext()); Quando l utente seleziona uno dei bottoni della finestra Finestra3 viene aperta una finestra di dialogo con il testo del bottone premuto. Su ogni bottone è installato lo stesso ascoltatore per gli eventi azione. Alla pressione di un bottone viene eseguito il metodo actionperformed, il quale si fa restituire, con il metodo getsource(), il bottone premuto (si noti il cast giacchè il metodo lo restituisce come riferimento ad Object). A partire dal bottone premuto, con il metodo gettext() viene restituito il testo visualizzato nel bottone.
Accedere dall ascoltatore agli oggetti di una finestra Problema: la finestra e i suoi ascoltatori sono divisi in classi separate. Supponiamo di avere ascoltatori per gestire gli eventi scatenati da alcuni componenti installati in una data finestra. Essendo le due classi formalmente indipendenti tra loro, per quanto detto, non è possibile accedere dagli ascoltatori a tutti i componenti della finestra (tranne il componente che ha generato l evento). Esempio: Ho una finestra con un JTextField j1 ed un JButton b1. Voglio che premento il bottone b1, si cambi il testo. Quindi il bottone mi genera un evento; tale evento viene catturato da un listener; con in listener dovrei accedere al JTextField j1 e cambiargli il testo. Il problema è che il listener, essendo in una classe esterna, non vede il JTextField jl1. E' necessario che il costruttore del listener che definiamo prenda come parametro l'oggetto che deve modificare (in questo caso il JTextField). Esempio: CLASSE ASCOLTATORE2.JAVA import java.awt.event.*; public class Ascoltatore2 implements ActionListener JTextField txt; public Ascoltatore2(JTextField t) super(); txt=t; public void actionperformed(actionevent event) txt.settext("bottone Premuto"); CLASSE FINESTRA4.JAVA import java.awt.*; public class Finestra4 extends JFrame public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; //creo un bottone e un JTextArea private JButton b1 = new JButton("Cambia"); private JTextField txt = new JTextField(20); //creo un pannello JPanel p1 = new JPanel(); public Finestra4( ) //chiamo il costruttore della superclasse JFrame super("la Mia Finestra 3"); setsize(default_width, DEFAULT_HEIGHT);
//ottengo il content pane Container c = this.getcontentpane(); //setto il layout del pannello p1.setlayout(new FlowLayout()); //aggiungo il text field al pannello p1.add(txt); //aggiungo il bottone al pannello e gli aggiungo un listener p1.add(b1); Ascoltatore2 listener = new Ascoltatore2(txt); b1.addactionlistener(listener); //aggiungo il pannello al content pane c.add(p1); public static void main(string [] args) Finestra4 f = new Finestra4(); f.setdefaultcloseoperation(jframe.exit_on_close); f.setvisible(true);
Condividere gli ascoltatori per più oggetti Per semplicità cosideriamo da questo momento solo gli eventi actionlistener, considerando che comunque per le altre tipologie di eventi il ragionamento è simile. Con quanto visto finora, dovremmo avere un Listener per ogni elemento di cui voglio monitorare gli eventi. Se immaginiamo una finestra complessa con menù e pulsanti possiamo subito dedurre che avremmo una notevole quantità di listener da gestire. E' prassi comune allora suddividere gli elementi di una finestra in gruppi e creare un unico listener per ogni gruppo anziché per ogni elemento. Se però associo lo stesso listener a tutti gli elementi di un gruppo, ho che ogni elemento genera la stessa azione, e non è quello che voglio. È necessario, all'interno del listener, poter capire chi ha generato l'evento e differenziare le azioni da compiere. Ci viene in aiuto per questo la proprietà actioncommand implementata in ogni componente. Questa proprietà permette di associare una stringa identificativa univoca ad ogni componente che scatena un evento-azione. A questo punto è possibile definire all interno del metodo actionperformed un approccio di tipo switch/case sugli actioncommand. La procedura quindi è: Nella Finestra: creo un listener l creo la componente x (per esempio un Jbutton); creo la componente y (per esempio un Jbutton); associo il listener con x. addactionlistener(l) associo un etichetta alla componente con x.setactioncommand(... ) associo il listener con y. addactionlistener(l) associo un etichetta alla componente con y.setactioncommand(... ) Nel listener: all'interno del metodo actionperformed(actionevent e): ottengo l'etichetta della componente che ha generato l'evento con e.getactioncommand() faccio uno switch sul valore ottenuto e mi comporto di coseguenza. CLASSE ASCOLTATORE3.JAVA import java.awt.event.*; public class Ascoltatore3 implements ActionListener public static final String BOTTONE1 = "1"; public static final String BOTTONE2 = "2"; JTextField txt; public Ascoltatore3(JTextField t) super(); txt=t; public void actionperformed(actionevent e) String com = e.getactioncommand(); switch(com)
case BOTTONE1: txt.settext("bottone 1 Premuto"); break; case BOTTONE2: txt.settext("bottone 2 Premuto"); break; CLASSE FINESTRA5.JAVA import java.awt.*; public class Finestra5 extends JFrame public static final int DEFAULT_WIDTH = 300; public static final int DEFAULT_HEIGHT = 200; //creo 2 bottoni e una JTextArea private JButton b1 = new JButton("Cambia1"); private JButton b2 = new JButton("Cambia2"); private JTextField txt = new JTextField(20); //creo un pannello JPanel p1 = new JPanel(); public Finestra5( ) //chiamo il costruttore della superclasse JFrame super("la Mia Finestra 5"); setsize(default_width, DEFAULT_HEIGHT); //ottengo il content pane Container c = this.getcontentpane(); //setto il layout del pannello p1.setlayout(new FlowLayout()); //aggiungo il text field al pannello p1.add(txt); //aggiungo i bottoni al pannello e gli aggiungo un listener p1.add(b1); p1.add(b2); Ascoltatore3 listener = new Ascoltatore3(txt); b1.addactionlistener(listener); b1.setactioncommand(ascoltatore3.bottone1); b2.addactionlistener(listener); b2.setactioncommand(ascoltatore3.bottone2); //aggiungo il pannello al content pane c.add(p1); public static void main(string [] args) Finestra5 f = new Finestra5(); f.setdefaultcloseoperation(jframe.exit_on_close); f.setvisible(true);