Classi interne e gestione degli eventi

In una precedente dispensa abbiamo visto come colorare un pannello con colori generati casualmente1. Di seguito è riportato il codice prodotto. InteractiveFrame.java
import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class InteractiveFrame { public static void main(String[] args) { final String TITOLO ="Colora il pannello"; JFrame frame = new JFrame(TITOLO); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel pane = new JPanel(); JButton button = new JButton("Colora"); //*****Registrazione del rilevatore di evento***** button.addActionListener(new ButtonListener(pane)); //************************************************ pane.add(button); frame.add(pane); frame.pack(); frame.setVisible(true);

} }

ButtonListener.java
import import import import java.awt.Color; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.util.Random;

import javax.swing.JPanel; public class ButtonListener implements ActionListener { public static final int MAX_VALUE = 256; private JPanel pane; public ButtonListener(JPanel pane){ this.pane = pane; } public void actionPerformed(ActionEvent e){ Random randomGenerator = new Random(); int red = randomGenerator.nextInt(MAX_VALUE); int green = randomGenerator.nextInt(MAX_VALUE); int blue = randomGenerator.nextInt(MAX_VALUE); pane.setBackground(new Color(red, green, blue)); } }
1

url alla dispensa http://www.scribd.com/doc/29330725/Gestione-di-eventi-azione-su-pulsanti

Bocchi Cinzia Ultimo aggiornamento: 05/11/2011

1

Una problematica rilevata è stata quella di dover “passare” al rilevatore dell’evento ButtonListener il riferimento al pannello, con la conseguente aggiunta di un costruttore e di una variaile di istanza nella classe rilevatore. Il problema può essere risolto più agevolmente facendo uso di classi interne. Una classe interna è una classe definita all'interno di un'altra, detta classe esterna. Nell'esempio, la classe interna è ButtonListener, mentre la classe esterna è InteractiveFrame. Utilizzando le classi interne bisogna adottare però alcuni accorgimenti: • la classe interna non deve avere lo specificatore di accesso public poiché solo una classe, tra quelle definite nello stesso file, può essere public; • le variabili della classe esterna, alle quali ci si vuole riferire nella classe interna, devono essere variabili di istanza e quindi vanno dichiarate all'esterno dei metodi della classe esterna. Se non si segue questo accorgimento, la gestione dell'evento con l'uso della classe interna non ha effetto. Di conseguenza, sarà necessario definire un costruttore per inizializzare tali variabili di istanza. Conviene, allora, modificare il codice della classe InteractiveFrame nel modo seguente:
public class InteractiveFrame extends JFrame { private JPanel pane; private JButton button; public InteractiveFrame(String title){ super(title); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pane = new JPanel(); button = new JButton("Colora"); //*****Registrazione del rilevatore di evento***** button.addActionListener(new ButtonListener()); //************************************************ pane.add(button); add(pane); pack(); setVisible(true); } public static void main(String[] args) { InteractiveFrame frame = new InteractiveFrame("Eventi di azione con classi interne"); } class ButtonListener implements ActionListener { public static final int MAX_VALUE = 256; public void actionPerformed(ActionEvent e){ Random randomGenerator = new Random(); int red = randomGenerator.nextInt(MAX_VALUE); int green = randomGenerator.nextInt(MAX_VALUE); int blue = randomGenerator.nextInt(MAX_VALUE); pane.setBackground(new Color(red, green, blue)); } } }

Osservate, nel riquadro, la classe interna.
Bocchi Cinzia Ultimo aggiornamento: 05/11/2011

2

I metodi di una classe interna possono accedere direttamente alle variabili di istanza della classe esterna. Osservando l'esempio precedente, vediamo che il metodo actionPerformed della classe interna, utilizza direttamente la variabile di istanza pane definita nella classe esterna, senza doverla ottenere come parametro passato al costruttore.

Gestione di eventi con uso di classi interne locali
Una classe interna locale è una classe definita all'interno di un metodo di un’altra classe. Se i metodi della classe interna locale utilizzano variabili locali del metodo all’interno del quale essa è definita, allora tali variabili devono essere dichiarate final. Possiamo inserire il gestore dell’evento all’interno del costruttore della classe InteractiveFrame.
import import import import import import import java.awt.Color; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.util.Random; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel;

public class InteractiveFrame extends JFrame { private JPanel pane; private JButton button; public InteractiveFrame(String title){ super(title); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pane = new JPanel(); button = new JButton("Colora"); //*****Classe interna locale********************* class ButtonListener implements ActionListener { public static final int MAX_VALUE = 256; public void actionPerformed(ActionEvent e){ Random randomGenerator = new Random(); int red = randomGenerator.nextInt(MAX_VALUE); int green = randomGenerator.nextInt(MAX_VALUE); int blue = randomGenerator.nextInt(MAX_VALUE); pane.setBackground(new Color(red, green, blue)); } } //************************************************ //*****Registrazione del rilevatore di evento***** button.addActionListener(new ButtonListener()); //************************************************ pane.add(button); add(pane); pack(); setVisible(true);

}

...................... Bocchi Cinzia Ultimo aggiornamento: 05/11/2011

3

Gestione di eventi con uso di classi interne locali anonime
Una classe interna locale anonima è una classe locale priva di nome. L’utilizzo di classi anonime semplifica notevolmente la gestione degli eventi. Invece di registrare il rilevatore dell'evento con l'istruzione b.addActionListener(new GestoreEvento()); e definire la classe rilevatore, si utilizza la sintassi seguente: b.addActionListener(new ActionListener() { //codice contenuto nella classe che gestisce l’evento });
import import import import import import import java.awt.Color; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.util.Random; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel;

@SuppressWarnings("serial") public class InteractiveFrame extends JFrame { private JPanel pane; private JButton button; public InteractiveFrame(String title){ super(title); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pane = new JPanel(); button = new JButton("Colora"); //*****Classe interna locale anonima********************* button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ final int MAX_VALUE = 256; Random randomGenerator = new Random(); int red = randomGenerator.nextInt(MAX_VALUE); int green = randomGenerator.nextInt(MAX_VALUE); int blue = randomGenerator.nextInt(MAX_VALUE); pane.setBackground(new Color(red, green, blue)); } }); //******************************************************** pane.add(button); add(pane); pack(); setVisible(true);

}

public static void main(String[] args) { InteractiveFrame frame = new InteractiveFrame("Eventi di azione con classi interne anonime"); }

Bocchi Cinzia Ultimo aggiornamento: 05/11/2011

4

}

Schema riassuntivo degli esempi
Interactiv eFrame Interactiv eFrame Interactiv eFrame public Interactiv eFrame Interactiv eFrame public Interactiv eFrame

ButtonListener ButtonListener ButtonListener listener anonimo

(A)

(B)

(C)

(D)

(A) Gestione con classi separate (B) Gestione con classi interne (C) Gestione con classi interne locali (D) Gestione con classi interne locali anonime

____________________________________________________________________
Quest'opera è stata rilasciata con licenza Creative Commons Attribution-ShareAlike 3.0 Unported. Per leggere una copia della licenza visita il sito web http://creativecommons.org/licenses/by-sa/3.0/ o spedisci una lettera a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.

Bocchi Cinzia Ultimo aggiornamento: 05/11/2011

5