Vous êtes sur la page 1sur 46

` di Roma

SAPIENZA Universita
` di Ingegneria dellInformazione,
Facolta
Informatica e Statistica
Corso di Laurea in Ingegneria Informatica ed
Automatica
Corso di Laurea in Ingegneria dei Sistemi
Informatici

Dispensa didattica

Sviluppo di Interfacce Grafiche in


Java. Concetti di Base ed Esempi.
Versione di Marzo 2011.

M. de Leoni, M. Mecella, S. Saltarelli

Creative Commons License Deed


Attribuzione - Non commerciale - Non opere derivate
2.5 Italia
Tu sei libero:
di riprodurre, distribuire, comunicare al pubblico, esporre in pubblico,
rappresentare, eseguire e recitare questa opera
Alle seguenti condizioni:
Attribuzione. Devi attribuire la paternit`a dellopera nei modi indicati dallautore o da chi ti ha dato lopera in licenza.
Non commerciale. Non puoi usare questa opera per fini commerciali.
Non opere derivate. Non puoi alterare o trasformare questopera, n usarla per
crearne unaltra.
Ogni volta che usi o distribuisci questa opera, devi farlo secondo i termini di
questa licenza, che va comunicata con chiarezza.
In ogni caso, puoi concordare col titolare dei diritti dautore utilizzi di questopera non consentiti da questa licenza.
Niente in questa licenza indebolisce o restringe i diritti degli autori.
Le utilizzazioni consentite dalla legge sul diritto dautore e gli
altri diritti non sono in alcun modo limitati da quanto sopra.
Questo `e un riassunto in linguaggio accessibile a tutti del Codice Legale
(la licenza integrale) disponibile allindirizzo:
http://creativecommons.org/licenses/by-nc-nd/2.5/it/legalcode.

Indice
1
2
3
4
5
6
7
7.1
8
9
9.1
9.2
9.3
10
11
12
13

Introduzione . . . . . . . . . . . . . . . . . . . . . . . . .
Il package Swing . . . . . . . . . . . . . . . . . . . . . .
Top Level Container . . . . . . . . . . . . . . . . . . . .
Paranoramica di alcuni widget . . . . . . . . . . . . . . .
Lereditariet`a per personalizzare i frame . . . . . . . . .
Layout Management . . . . . . . . . . . . . . . . . . . .
Progettazione della GUI con le gerarchie di contenimento
Progettazione top down di interfacce grache . . . . . . .
7.1.1 Esempio di Progettazione Top-Down . . . . . . .
Realizzare nestre di dialogo con JOptionPane . . . . . .
La Gestione degli Eventi . . . . . . . . . . . . . . . . . .
Implementazione dellevent delegation . . . . . . . . . . .
Un esempio: elaborare gli eventi del mouse . . . . . . . .
Uso di adapter nella denizione degli ascoltatori . . . . .
La gestione degli eventi Azione . . . . . . . . . . . . . .
Accedere dallascoltatore agli oggetti di una nestra . . .
Condividere gli ascoltatori per pi`
u oggetti . . . . . . . .
Conclusioni e Commenti Finali . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

3
3
5
8
11
13
21
22
23
25
27
27
29
31
32
33
37
43

1 Introduzione

Introduzione

Uno dei problemi pi`


u grossi emersi durante la progettazione di Java fu senza dubbio la realizzazione di un toolkit graco capace di funzionare con
prestazioni di buon livello su piattaforme molto dierenti tra loro. La soluzione
adottata nel 1996 fu AWT(Abstract Window Toolkit), un package graco
che mappa i componenti del sistema ospite con apposite classi dette peer,
scritte in gran parte in codice nativo. In pratica, ogni volta che il programmatore crea un componente AWT e lo inserisce in uninterfaccia graca, il
sistema AWT posiziona sullo schermo un oggetto graco della piattaforma ospite, e si occupa di inoltrare ad esso tutte le chiamate a metodo eettuate sulloggetto Java corrispondente, ricorrendo a procedure scritte in buona parte
in codice nativo; nel contempo, ogni volta che lutente manipola un elemento
dellinterfaccia graca, unapposita routine (scritta sempre in codice nativo)
crea un apposito oggetto Event e lo inoltra al corrispondente oggetto Java,
in modo da permettere al programmatore di gestire il dialogo con il componente e le azioni dellutente con una sintassi completamente Object Oriented
e indipendente dal sistema sottostante.
A causa di questa scelta progettuale, il set di componenti graci AWT
comprende solamente quel limitato insieme di controlli graci che costituiscono il minimo comune denominatore tra tutti i sistemi a nestre esistenti:
un grosso limite rispetto alle reali esigenze dei programmatori. In secondo luogo, questa architettura presenta un grave inconveniente: i programmi graci
AWT assumono un aspetto ed un comportamento dierente a seconda della
JVM su cui vengono eseguite, a causa delle macroscopiche dierenze implementative esistenti tra le versioni di uno stesso componente presenti nelle
diverse piattaforme. Spesso le interfacce grache realizzate su una particolare piattaforma mostrano grossi difetti se eseguite su un sistema dierente,
arrivando in casi estremi a risultare inutilizzabili.
Il motto della Sun per Java era scrivi (il codice) una volta sola ed eseguilo
ovunque; nel caso di AWT questo si era trasformato in scrivi una volta sola
e correggilo ovunque.

Il package Swing

Nel 1998, con luscita del JDK 1.2, venne introdotto il package Swing, i
cui componenti erano stati realizzati completamente in Java, ricorrendo unicamente alle primitive di disegno pi`
u semplici, tipo traccia una linea o
disegna un cerchio, accessibili attraverso i metodi delloggetto Graphics,
un oggetto AWT utilizzato dai componenti Swing per interfacciarsi con la

2 Il package Swing

Component
Container
JComponent
Panel

Window
JFrame
JDialog

JPanel

JApplet
java.awt
javax.swing

Fig. 1: Diagramma UML di base del package Swing.


piattaforma ospite. Le primitive di disegno sono le stesse su tutti i sistemi
graci, e il loro utilizzo non presenta sorprese: il codice java che disegna un
pulsante Swing sullo schermo di un PC produrr`a lo stesso identico risultato
su un Mac o su un sistema Linux. Questa architettura risolve alla radice i
problemi di uniformit`a visuale, visto che la stessa identica libreria viene ora
utilizzata, senza alcuna modifca, su qualunque JVM. Liberi dal vincolo del
minimo comune denominatore, i progettisti di Swing hanno scelto di percorrere la via opposta, creando un package ricco di componenti e funzionalit`a
spesso non presenti nella piattaforma ospite. Il procedimento di disegno `e
ovviamente pi`
u lento perche la JVM deve disegnare per proprio conto tutte
le linee degli oggetti graci e gestirne direttamente il comportamento, per`o
`e pi`
u coerente.
Le classi Swing sono denite nel pacchetto javax.swing, il cui nome javax
indica estensione standard a Java. Inizialmente Swing venne rilasciato, infatti, come estensione per poi divenire un omponente standad di Java 2. Per
motivi di compatibilita il nome del pacchetto javax non venne corretto in
java. Gli oggetti graci Swing derivano dai corrispettivi AWT; quindi `e possibile utilizzare oggetti Swing, ove erano previsti i oggetti antenati. Swing
usa ancora alcuni elementi AWT per disegnare; anche la gestione degli eventi
`e fatta per la maggior parte da classi AWT.
La Figura 1 riassume molto sinteticamente le classi base del package
Swing e come queste derivino da classi AWT. Ogni oggetto graco (una
nestra, un bottone, un campo di testo, . . . ) `e implementato come classe
del package javax.swing. Ogni classe che identica un oggetto Swing deriva

3 Top Level Container

per lo pi`
u dalla classe javax.swing.JComponent; si stima che esistono circa 70
o pi`
u oggetti diversi. Gli oggetti graci utilizzati per disegnare le interfacce
vengono chiamati anche controlli oppure tecnicamente widget. JComponent
eredita da java.awt.Container, una sorta di controllo che di default `e vuoto
e il cui scopo `e orire la possibilit`a di disporre altri componenti allinterno.
Non a caso la classe AWT Window e le sottoclassi Swing JFrame e JDialog, le
cui istanze rappresentano nestre, sono sottoclasse di Container. La cosa pi`
u
sorprendente `e che, siccome JComponent deriva da Container, `e possibile inserire allinterno di un qualsiasi widget qualsiasi altro. Ad esempio - sebbene
poco utile - `e possibile aggiungere un campo di testo allinterno di un bottone
oppure - molto usato - un Container allinterno di un altro Container. La
classe Container (e ogni sottoclasse) denisce un metodo per aggiungere un
controllo ad un Container:
void add ( Component ) ;

Il metodo prende come parametro un oggetto Component che `e la superclasse


di qualsiasi oggetto o container Swing o AWT.

Top Level Container

I top level container sono i componenti allinterno dei quali si creano le


interfacce grache: ogni programma graco ne possiede almeno uno, di solito
un JFrame, che rappresenta la nestra principale. Ogni top level container
possiede un pannello (accessibile tramite il metodo getContentPane()) allinterno del quale vanno disposti i controlli dellinterfaccia grafca. Esistono
tre tipi principali di top level Container: JFrame, JApplet e JDialog. Il
primo viene solitamente usato come nestra principale per il programma, il
secondo `e utilizzato per costruire Applet da visualizzare nella nestra di un
web browser mentre il terzo serve a creare le nestre di dialogo con lutente.
In questa dispensa ci si focalizzer`a solamente sui JFrame. Un oggetto
della classe JFrame pu`o essere creato usando i costruttori:
JFrame ( ) ;
JFrame ( S t r i n g t i t o l o F i n e s t r a ) ;

Il primo costruisce un JFrame senza titolo; il secondo permette di specicarlo.


` sempre possibile impostare il titolo ricorrendo al metodo
E
setTitle(String s). Due importanti propriet`a delloggetto sono la dimensione e la posizione, che possono essere impostate sia specicando le singole
componenti sia mediante oggetti Dimension e Point del package AWT:
public void s e t S i z e ( Dimension d ) ;
public void s e t S i z e ( int width , int h e i g h t ) ;

3 Top Level Container

Fig. 2: Anatomia di un Frame Swing.


public void s e t L o c a t i o n ( P oi nt p ) ;
public void s e t L o c a t i o n ( int x , int y ) ;

Ricorrendo al metodo setResizable(boolean b) `e possibile stabilire se


si vuole permettere allutente di ridimensionare la nestra manualmente.
Inne, vi sono tre metodi piuttosto importanti:
public void pack ( ) ;
public void s e t V i s i b l e ( boolean b ) ;
public void s e t D e f a u l t C l o s e O p e r a t i o n ( int o p e r a t i o n ) ;

Il primo ridimensiona la nestra tenendo conto delle dimensioni ottimali di


ciascuno dei componenti presenti allinterno. Il secondo permette di visualizzare o di nascondere la nestra. Il terzo imposta lazione da eseguire alla pressione del bottone close, con quattro impostazioni disponibili:
JFrame.DO NOTHING ON CLOSE (nessun eetto), JFrame.HIDE ON CLOSE
(nasconde la nestra), JFrame.DISPOSE ON CLOSE (chiude la nestra e libera
le risorse di sistema) e JFrame.EXIT ON CLOSE (chiude la nestra e conclude
lesecuzione del programma).
Per impostazione di default, un JFrame viene costruito non visibile e di
dimensione 0 x 0. Per questa ragione, anch`e la nestra sia visibile, `e necessario chiamare i metodi setSize() o pack() per specicare la dimensione e
mettere a true la propriet`a Visible chiamando il metodo: setVisible(true).
Per poter lavorare con i Frame Swing, `e opportuno conoscere il linea
generale la struttura della supercie. La supercie di un frame Swing `e
coperta da quattro lastre:
Glass Pane La lastra di vetro `e nascosta di default ed ha il compito di catturare gli eventi di input sulla nestra. Normalmente `e completamente
trasparente a meno che venga implementato il metodo paintComponent
del GlassPane. Poich`e `e davanti a tutte le altre, qualsiasi oggetto
disegnato su questa lastra nasconde qualsiasi altro disegnato sulle altre

3 Top Level Container

import j a v a x . swing . ;
import j a v a . awt . ;
public c l a s s A p p l i c a t i o n {
public s t a t i c void main ( S t r i n g a r g s [ ] )
{
JFrame win ;
win = new JFrame ( Prima f i n e s t r a ) ;
C o n t a i n e r c = win . getContentPane ( ) ;
c . add (new JLabel ( Buona L e z i o n e ) ) ;
win . s e t S i z e ( 2 0 0 , 2 0 0 ) ;
win . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ;
win . s e t V i s i b l e ( true ) ;
}
}

Listato 1: La prima finestra

u importante perch`e `e quella che


Content Pane La lastra dei contenuti `e la pi`
ospita i componenti che volete visualizzare nella nestra e la maggior
parte dei programmatori Java lavora solo su questa
Layered Pane Contiene la lastra dei contenuti ed eventualmente i menu. I
menu, infatti, non vengono mai aggiunti al Content Pane ma a questa
lastra.
Root Pane La lastra radice ospita la lastra di vetro insieme con la lastra dei
contenuti e i bordi della nestra.
Per maggiori dettagli si faccia riferimento a http://java.sun.com/docs/
books/tutorial/uiswing/components/rootpane.html.
Quindi, un componente, quale un pulsante, unimmagine o altro, non
viene aggiunto direttamente alla nestra ma alla lastra dei contenuti. Di
conseguenza, occorre per prima cosa procurarsi un riferimento alloggetto
ContentPane, mediante la chiamata al metodo:
public C o n t a i n e r getContentPane ( ) ;

Come era da aspettarsi, il ContentPane `e un Container perch`e predisposto


a contenere altri componenti. A questo punto, come per ogni altro Container,
`e possibile aggiungere ad esso un componente con il metodo add gi`a descritto.
A questo punto `e possibile disegnare la prima nestra. Il codice descritto
nel Listato 1 mostra la nestra in Figura 3.

4 Paranoramica di alcuni widget

Fig. 3: La prima finestra.

Paranoramica di alcuni widget

I nomi delle classi per la maggior parte dei componenti dellinterfaccia utente
Swing iniziano con la J.
JTextField `e un campo di testo Swing. Tale classe eredita da TextField,
lobsoleto analogo del package AWT. Per costruire un campo di testo occorre
fornirne lampiezza, cio`e il numero approssimato di caratteri che vi aspettate
verranno inseriti dallutente.
J T e x t F i e l d x F i e l d=new J T e x t F i e l d ( 5 ) ;

Gli utenti possono digitare anche un numero maggiore di caratteri ma sempre


5 contemporaneamente saranno vicini: i 5 attorno alla posizione del cursore
nel campo.
Sarebbe opportuno etichettare ciascun campo di testo in modo che lutente sappia cosa scriverci. Ogni etichetta `e un oggetto di tipo JLabel
che viene costruito, sfruttando il costruttore con un parametro stringa; tale
parametro rappresenta il testo delletichetta:
JLabel x F i e l d=new JLabel ( x = ) ;

Inoltre vorrete dare allutente la possibilit`a di inserire informazione in tutti


i campi di testo prima di elaborarli per cui avete bisogno di un pulsante che
lutente possa premere per segnalare che i dati sono pronti per essere elaborati. Un pulsante `e un oggetto JButton che pu`o essere costruito fornendo
una stringa che funger`o da etichetta, unimmagine come icona o entrambe
JButton moveButton=new JButton ( Move ) ;
JButton moveButton=
new JButton (new ImageIcon ( hand . g i f ) ) ;

4 Paranoramica di alcuni widget

Fig. 4: Alcuni Bottoni delle Swing.


JButton moveButton=
new JButton ( Move ,new ImageIcon ( hand . g i f ) ) ;

JCheckBox `e una sottoclasse di JButton che crea caselle di controllo,


con un aspetto simile a quello delle caselle di spunta dei questionari. Il
suo funzionamento `e analogo a quello della superclasse, ma di fatto tende a
essere utilizzato in contesti in cui si ore allutente la possibilit`a di scegliere
una o pi`
u opzioni tra un insieme, come avviene per esempio nei pannelli di
controllo. I costruttori disponibili sono gli stessi della superclasse, quindi non
sar`a necessario ripetere quanto `e stato gi`a detto.
JCheckBox check1=new JCheckBox ( JCheck ) ;

JRadioButton `e una sottoclasse di JButton, dotata dei medesimi costruttori. Questo tipo di controllo, chiamato pulsante di opzione, viene usato
tipicamente per fornire allutente la possibilit`a di operare una scelta tra
un insieme di possibilit`a, in contesti nei quali unopzione esclude laltra. I
costruttori disponibili sono gli stessi della superclasse. Per implementare il
comportamento di mutua esclusione, `e necessario registrare i JRadioButton
che costituiscono linsieme presso unistanza della classe ButtonGroup, come
viene mostrato nelle righe seguenti:
JRadioButton r a d i o B u t t o n 1=new JRadioButton ( R1 ) ;
JRadioButton r a d i o B u t t o n 2=new JRadioButton ( R2 ) ;
JRadioButton r a d i o B u t t o n 2=new JRadioButton ( R3 ) ;
ButtonGroup group = new ButtonGroup ( ) ;
group . add ( r a d i o B u t t o n 1 ) ;
group . add ( r a d i o B u t t o n 2 ) ;
group . add ( r a d i o B u t t o n 3 ) ;

4 Paranoramica di alcuni widget

10

import j a v a x . swing . ;
import j a v a . awt . ;
public c l a s s A p p l i c a t i o n {
public s t a t i c void main ( S t r i n g a r g s [ ] )
{
JFrame win ;
win = new JFrame ( Esempio d i JComboBox ) ;
S t r i n g l i s t a [ ] =new S t r i n g [ 1 0 ] ;
fo r ( int i =0; i < l i s t a . l e n g t h ; i ++)
l i s t a [ i ]= Elemento numero +i ;
JComboBox cBox=new JComboBox ( l i s t a ) ;
C o n t a i n e r c = win . getContentPane ( ) ;
c . add ( cBox ) ;
win . s e t S i z e ( 2 0 0 , 2 0 0 ) ;
win . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ;
win . s e t V i s i b l e ( true ) ;
}
}

Listato 2: Esempio di uso dei ComboBox

Ogni volta che lutente attiva uno dei pulsanti registrati presso il ButtonGroup,
gli altri vengono automaticamente messi a riposo.
I JComboBox orono allutente la possibilit`a di eettuare una scelta a
partire da un elenco elementi, anche molto lungo. A riposo il componente
si presenta come un pulsante, con letichetta corrispondente al valore attualmente selezionato. Un clic del mouse provoca la comparsa di un menu
provvisto di barra laterale di scorrimento, che mostra le opzioni disponibili.
Se si imposta la propriet`a editable di un JComboBox a true esso si comporter`a a riposo come un JTextField, permettendo allutente di inserire valori
` possibile creare un JComboBox usando i seguenti
non presenti nella lista. E
costruttori:
JComboBox ( ) ;
JComboBox ( Object [ ] i t e m s ) ;

Il secondo costruttore permette di inizializzare il componente con una


lista di elementi di qualsiasi tipo (ad esempio String). Se viene aggiunto
al ComboBox un oggetto generico (ad esempio un oggetto Libro), allora il
valore corrispondente visualizzato nella lista delle opzioni `e quello ottenuto
chiamando sulloggetto il metodo toString(). Quindi se si desidera aggiungere oggetti generici (magari deniti allinterno del programma), bisogner`a

5 Lereditariet`a per personalizzare i frame

11

Fig. 5: Uso di ComboBox.


avere la cura di ridenire tale metodo. Un gruppo di metodi permette di
aggiungere, togliere o manipolare gli elementi dellelenco, cos` come si fa con
un Vector:
public
public
public
public
public
public
public

void addItem ( Object anObject ) ;


void removeItem ( Object anObject )
void removeItemAt ( int anIndex ) ;
void r e m o v e A l l I t e m s ( ) ;
Object getItemAt ( int i n d e x ) ;
int getItemCount ( ) ;
void i n s e r t I t e m A t ( Object anObject , int i n d e x )

Per ottenere lelemento correntemente selezionato, `e disponibile il metodo:


public Object g e t S e l e c t e d I t e m ( ) ;

La Figura 5 mostra un esempio di suo uso. Il Listato 2 rappresenta il codice


corrispondente.

Lereditariet`
a per personalizzare i frame

Aggiungendo ad un frame molti componenti dellinterfaccia utente, il frame


stesso pu`o diventare abbastanza complesso: per frame che contengono molti
componenti `e opportuno utilizzare lereditariet`a. Infatti, se il software che si
sta sviluppando contiene molte nestre ricche di componenti ci si troverebbe
a dover fronteggiare una soluzione il cui caso limite `e di un metodo main che
contiene la denizione di tutte le nestre del programma. Senza arrivare a

5 Lereditariet`a per personalizzare i frame

12

import j a v a x . swing . ;
import j a v a . awt . ;
c l a s s MyFrame extends JFrame
{
JLabel j l = new JLabel ( Buona L e z i o n e ) ;
public MyFrame ( )
{
super ( Prima f i n e s t r a ) ;
C o n t a i n e r c = t h i s . getContentPane ( ) ;
c . add ( j l ) ;
this . s e t S i z e (200 ,200);
t h i s . s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ;
t h i s . s e t V i s i b l e ( true ) ;
}
}
public c l a s s A p p l i c a t i o n
{
public s t a t i c void main ( S t r i n g a r g s [ ] )
{
MyFrame = new MyFrame ( ) ;
}
}

Listato 3: Esempio con lereditariet`


a

questa situazione, si otterrebbero moduli sono molto accoppiati e poco coesi; inoltre, gli stessi moduli sarebbero poco leggibili e non sarebbe facile
manutenerli. In parole povere, non si sfrutterebbe le potenzialit`a dellObject Orientation: non sarebbe possibile permette linformation hiding dei
contenuti dei frame e dei controlli in essi contenuti; non sarebbe nemmeno
sfruttato lincapsulamento delle implementazione, mettendo insieme concetti
eterogenei e nestre diverse tra loro.
Il Listato 3 produce la stessa nestra in Figura 3 sfruttando lereditariet`a.
La dierenza rispetto agli esempi gi`a visti non `e da poco: a questo punto ogni
nestra diventa una nuova classe che, ereditando da JFrame, `e un modello
opportuno denire gli oggetti che mappano i widgets come
per le nestre. E
variabili di instanza (nel lesempio lunico widget `e una etichetta (JLabel). La
denizione della nestra e dei componenti contenuti viene fatta direttamente
nel costruttore (o in metodo chiamati da questo). La prima istruzione chiama il costruttore della superclasse che prende come parametro una stringa;

6 Layout Management

13

in questo modo `e possibile impostare il titolo della nestra. Infatti, se non


fosse stato esplicitamente chiamato il costruttore ad un parametro String,
sarebbe stato chiamato implicitamente il costruttore della superclasse di default (quello senza parametri) prima di continuare lesecuzione di quello della
classe derivata. Il costruttore senza parametri avrebbe impostato a vuoto il
titolo della nestra.1 Il resto del costruttore `e simile alla tecnica di denizione
delle nestre degli esempi precedenti con lunica dierenza che i metodi sono
chiamati su this (cio`e se stesso) perch`e a questo punto i metodi da chiamare
sono quelli ereditati dalla superclasse JFrame.

Layout Management

Quando si dispongono i componenti allinterno di un Container sorge il problema di come gestire il posizionamento: infatti, sebbene sia possibile specicare le coordinate assolute di ogni elemento dellinterfaccia, queste possono
cambiare nel corso della vita del programma allorquando la nestra principale
venga ridimensionata. In molti Container i controlli sono inseriti da sinistra
verso destra con su una ipotetica riga: pu`o non essere sempre la politica per
la GUI (Graphic User Interface) desiderata. Per semplicare il lavoro di impaginazione e risolvere questo tipo di problemi possibile ricorrere ai layout
manager, oggetti che si occupano di gestire la strategia di posizionamento
dei componenti allinterno di un contenitore.
Un gestore di layout `e una qualsiasi classe che implementa linterfaccia
LayoutManager; ogni container nasce con un certo Layout Manager ma `e
possibile assegnare il pi`
u opportuno per quel Container con il metodo:
public void s e t L a y o u t ( LayoutManager m) ;

Il primo layout da essere citato `e il gestore a scorrimento (Flow Layout)


che sono inseriti da sinistra verso destra con la loro Preferred Size, cio`e la
dimensione minima necessaria a disegnarlo interamente. Usando il paragone
con un editor di testi, ogni controllo rappresenta una parola che ha una sua
propria dimensione. Come le parole in un editor vengono inseriti da sinistra
verso destra nch`e entrano in una riga, cos` viene fatto per i controlli inseriti
da sinistra verso destra. Quando un componente non entra in una riga
viene posizionato in quella successiva. I costruttori pi`
u importanti di oggetti
FlowLayout sono i seguenti:
public FlowLayout ( ) ;
public FlowLayout ( int a l l i n ) ;
1
A dire il vero, sarebbe stato possibile impostare il titolo della nestra in un secondo
momento modicando la propriet`a Title: setTitle(Prima Finestra);

6 Layout Management

14

(a)

(b)

Fig. 6: Aspetto grafico del frame definito nel Listato 4 e comportamento del
Flow Layout rispetto al ridimensionamento
Il secondo costruttore specica lallineamento dei controlli su una riga; il
parametro pu`o essere una delle seguenti costanti che rispettivamente allineano
a sinistra, centro o destra:
FlowLayout . LEFT
FlowLayout .CENTER
FlowLayout . RIGHT

Il costruttore di default imposta lallineamento centrale. Il Listato 4 mostra


un esempio di uso del Flow Layout. Laspetto del frame risultato `e mostrato
opportuno osservare come lallineamento dei componenti del
in Figura 6. E
frame si adatti durante lesecuzione quando la stessa nestra viene ridimensionata (vedi situazione 6(b)). Ovviamente se non esiste nessun allineamento
per i componenti del frame tale che tutti siano contemporaneamente visibile, allora alcuni di questi risulteranno in parte o del tutto non visibili; per
esempio, la nestra `e troppo piccola.
Il gestore a griglia (GridLayout) suddivide il contenitore in una griglia
di celle di uguali dimensioni. Le dimensioni della griglia vengono denite
mediante il costruttore:
public GridLayout ( int rows , int columns )

in cui i parametri rows e columns specicano rispettivamente le righe e le


colonne della griglia. A dierenza di quanto avviene con FlowLayout, i componenti allinterno della griglia assumono automaticamente la stessa dimensione, dividendo equamente lo spazio disponibile. Lesempio descritto nel
Listato 5 permette di illustrare il funzionamento di questo pratico layout
manager; il risultato `e in Figura 7.
Il gestore a bordi (BorderLayout) suddivide il contenitore esattamente
in cinque aree, disposte a croce, come nella Figura 8. Ogni zona pu`o contenere
uno ed un solo widget (o Container): un secondo widget inserito in una
zona sostituisce il precedente. Se una o pi`
u zone non vengono riempite,
allora i componenti nelle altre zone sono estesi a riempire le zone vuote. Il

6 Layout Management

15

import j a v a x . swing . ;
import j a v a . awt . ;
public c l a s s MyFrame extends JFrame
{
JButton uno=new JButton ( Uno ) ;
JButton due=new JButton ( Due ) ;
JButton t r e=new JButton ( Tre ) ;
JButton q u a t t r o=new JButton ( Quattro ) ;
JButton c i n q u e = new JButton ( Cinque ) ;
public MyFrame ( )
{
super ( Flow Layout ) ;
C o n t a i n e r c = t h i s . getContentPane ( ) ;
c . s e t L a y o u t (new FlowLayout ( ) ) ;
c . add ( uno ) ;
c . add ( due ) ;
c . add ( t r e ) ;
c . add ( q u a t t r o ) ;
c . add ( c i n q u e ) ;
setSize (300 ,100);
s e t V i s i b l e ( true ) ;
}
}
public c l a s s A p p l i c a t i o n
{
public s t a t i c void main ( S t r i n g a r g s [ ] )
{
MyFrame = new MyFrame ( ) ;
}
}

Listato 4: Uso del Flow Layout

6 Layout Management

16

public c l a s s MyFrame extends JFrame


{
public MyFrame ( )
{
super ( Grid Layout ) ;
C o n t a i n e r c = t h i s . getContentPane ( ) ;
c . s e t L a y o u t (new GridLayout ( 4 , 4 ) ) ;
f o r ( int i = 0 ; i <15; i ++)
c . add (new JButton ( S t r i n g . v a l u e O f ( i ) ) ;
setSize (300 ,300);
s e t V i s i b l e ( true ) ;
}
}

Listato 5: Uso del Grid Layout

Fig. 7: Aspetto grafico del frame definito nel Listato 5.

6 Layout Management

17

Fig. 8: Le aree di una disposizione a bordi


BorderLayout `e il gestore di layout di default per la Lastra dei Contenuti di
un JFrame.
Il programmatore pu`o decidere in quale posizione aggiungere un controllo
utilizzando la variante presente nei Container:
public void add ( Component c , S t r i n g s ) ;

dove il primo parametro specica il componente da aggiungere e il secondo


indica la posizione. I valori validi per il secondo parametro sono le costanti:
BorderLayout .NORTH
BorderLayout .SOUTH
BorderLayout .CENTER
BorderLayout . EAST
BorderLayout .WEST

Si osservi lesempio nel Listato 6.


In aggiunta ai layout managers descritti in questa dispensa ne esistono
ulteriori che sono standard Java: 2
BoxLayout Questo manager pone i componenti in una singola riga o colonna.
Esso rispetta le dimensioni preferite e permette al programmatore di
decidere lallineamento
CardLayout Quando viene utilizzato questo layout manager, il container pu`o
contenere componente dierenti in momenti diversi. Per esempio `e
possibile utilizzare un Combo Box per determinare quale pannello/container interno viualizzare. In alternativa `e possibile utilizzare un
JTabbedPane (non descritto in questa dispensa), che fornisce una simile funzionalit`a utilizzando una interfaccia graca simile ad un cartella
di un archivio con diverse linguette.
2

Allindirizzo http://java.sun.com/docs/books/tutorial/uiswing/layout `e possibile leggere tutti i dettagli sul loro funzionamento

6 Layout Management

18

public c l a s s MyFrame extends JFrame


{
JButton nord = new JButton ( Nord ) ;
JButton c e n t r o = new JButton ( Centro ) ;
JButton o v e s t=new JButton ( Ovest ) ;

public MyFrame ( )
{
super ( Border Layout ) ;
C o n t a i n e r c = t h i s . getContentPane ( ) ;
c . s e t L a y o u t (new BorderLayout ( ) ) ;
c . add ( nord , BorderLayout .NORTH) ;
c . add ( c e n t r o , BorderLayout .CENTER) ;
c . add ( o v e s t , BorderLayout .WEST) ;
setSize (300 ,300);
s e t V i s i b l e ( true ) ;
}

Listato 6: Uso del Border Layout

GridBagLayout GridBagLayout `e un layout manager molto sosticato. Il suo


funzionamento `e simile al GridLayout ma con la possibilit`a che certi
widget occupino pi`
u celle della griglia.
SpringLayout SpringLayout `e molto essibile ed `e pensato soprattutto per essere utilizzato con tool automatici che permettono al programmatore di
disegnare nestra come in un programma di graca e automaticamente
generano i codice relativo. Esso permette di specicare precisamente
la relazione tra i bordi dei componenti. Ad esempio, `e possibile specicare che il bordo sinistro di un componente `e ad una certa distanza
dal bodo destro di un secondo componente.
Mentre chiunque pu`o denire un proprio layout semplicemente implementando linterfaccia LayoutManager, `e anche possibile disattivare ogni layout
manager `e utilizzare il cosiddetto posizionamento assoluto.
Sebbene sia possibile posizionare senza un layout manager, questa soluzione
dovrebbe essere evitata quanto pi`
u possibile. Un layout manager rende pi`
u
semplice modicare laspetto dei componenti, che pu`o in parte dipendere dalla piattaforma ospite, tenendo in conto la dimensione dei fonti, delle nestre.
Inoltre i layout manager sono in grado di riorganizzare automaticamente il
riposizionamento dei widget quando la nestra cambia di dimensione.

6 Layout Management

19

Tuttavia ci sono casi in cui `e consigliabile luso di un posizionamento senza


layout manager poich`e laspetto che si desidera ottenere non `e facilmente
realizzabile come combinazione di altri layout manager.
Per aggiungere componenti senza un manager occorre seguire i seguenti
passi:
1. Impostare il layout manager a chiamando:
mioContainer . s e t L a y o u t ( null ) ;

2. Per ogni componente che si desidera aggiungere, occorre specicare la


posizione in pixel dellangolo superiore sinistro, insieme con la larghezza
e laltezza desiderata. La posizione in pixel `e determinata relativamente
allangolo superiore sinistro del container nel quale si desidera aggiungere il componente. Il seguente esempio specica per il componente b1
una posizione a partire dalla coordinata (10,10), una larghezza di 30
pixel ed unaltezza di 40:
b1 . setBounds ( 1 0 , 1 0 , 3 0 , 4 0 ) ;

3. Aggiungere ogni componente al container come viene fatto solitamente.


In generale la posizione e la dimensione non `e mai data come valore assoluto ma `e sempre parametrica rispetto alle dimensione delle nestre/container
e degli altri controlli:
Dimension b 1 S i z e=b1 . g e t P r e f e r r e d S i z e ( ) ;
b1 . setBounds ( 1 0 , 1 0 , b 1 S i z e . width , b 1 S i z e . h e i g h t ) ;
Dimension b 2 S i z e=b2 . g e t P r e f e r r e d S i z e ( ) ;
b2 . setBounds (10+ b 1 S i z e . width , 1 0 , b 2 S i z e . width , b 2 S i z e . h e i g h t ) ;

In questo caso si desidera posizionare il widget b1 a partire dalla coordinata


(10,10) e farlo di una dimensione pari a quella preferibile. Inoltre, si vuole che
il b2 sia aancato a b1. Di conseguenza, la coordinata x della posizione di b2
sar`a pari a quella di b1 cui viene aggiunta una quantit`a pari alla dimensione
di b1.
Il Listato 7 mostra un esempio completo delluso di questo layout, il cui
risultato `e mostrato in Figura 9.
Comunque, `e ancora importante sottolineare che il posizionamento assoluto pu`o creare problemi quando la nestra viene ridimensionata. Per questa
ragione nel proseguo di questa dispensa ignoreremo sempre questo layout.

6 Layout Management

20

public c l a s s MyFrame
JButton b1 = new
JButton b2 = new
JButton b3 = new
JButton b4 = new

extends JFrame {
JButton ( uno ) ;
JButton ( n e ssuno ) ;
JButton ( c e n t o m i l a ) ;
JButton ( che a l t r o ?? ) ;

public MyFrame ( ) {
super ( N u l l Layout ) ;
C o n t a i n e r c = t h i s . getContentPane ( ) ;
c . s e t L a y o u t ( null ) ;
Dimension b 1 S i z e=b1 . g e t P r e f e r r e d S i z e ( ) ;
b1 . setBounds ( 1 0 , 1 0 , b 1 S i z e . width , b 1 S i z e . h e i g h t ) ;
Dimension b 2 S i z e=b2 . g e t P r e f e r r e d S i z e ( ) ;
b2 . setBounds (10+ b 1 S i z e . width , 1 0 , b 2 S i z e . width ,
b2Size . height ) ;
Dimension b 3 S i z e=b3 . g e t P r e f e r r e d S i z e ( ) ;
b3 . setBounds (10+ b 1 S i z e . width+b 2 S i z e . width , 1 0 ,
b 3 S i z e . width , b 3 S i z e . h e i g h t ) ;
Dimension b 4 S i z e=b4 . g e t P r e f e r r e d S i z e ( ) ;
b4 . setBounds ( 1 0 , 10+ b 1 S i z e . h e i g h t ,
b 1 S i z e . width+b 2 S i z e . width+b 3 S i z e . width ,
b4Size . height ) ;
c . add ( b1 ) ;
c . add ( b2 ) ;
c . add ( b3 ) ;
c . add ( b4 ) ;
setSize (300 ,300);
s e t V i s i b l e ( true ) ;
}

Listato 7: Uso del Null Layout

Fig. 9: Aspetto grafico del frame definito nel Listato 7.

7 Progettazione della GUI con le gerarchie di contenimento

21

Progettazione della GUI con le gerarchie di


contenimento

I gestori di layout permettono maggiore versatilit`a nellinserimento dei controlli nelle nestre. Tuttavia nella stragrande maggioranza delle situazioni
unintera nestra non pu`o seguire un unico layout. Si prenda, ad esempio,
in considerazione il frame in Figura 10. Non `e dicile convincersi che la
parte in alto contenente una sorta di tastierino numerico segue un GridLayout mentre i bottoni in basso seguono un FlowLayout centrato. Poich`e

Fig. 10: Un frame pi`


u complesso
ogni Container pu`o seguire uno ed un unico layout, occorre predisporre pi`
u
Container, uno per ogni zona che ha un layout dierente. Ovviamente ad
ogni Container possono essere aggiunti direttamente i controlli oppure altri
Container. La lastra dei contenuti `e il Container radice ed altri si possono
aggiungere allinterno. I Container (insieme con i componenti contenuti) aggiunti in un altro Container si comporteranno come ogni altro widget la cui
dimensione preferita `e quella minima necessaria a visualizzare tutti i componenti allinterno. Per creare nuovi Container conviene utilizzare la classe
javax.swing.JPanel che eredita da java.awt.Container.
La forza di questa soluzione `e data dallalta modularit`a: `e possibile usare
un layout per il pannello interno e un altro layout per il ContentPane. Il
pannello interno verr`a inserito nella nestra coerentemente con il layout della
lastra dei contenuti. Inoltre allinterno pannelli interni se ne possono inserire

7 Progettazione della GUI con le gerarchie di contenimento

22

altri con loro layout e cos` via, come nel gioco delle scatole cinesi. Il numero
di soluzioni diverse sono praticamente innite.
Ad esempio per il frame in Figura 10 `e possibile creare due contenitoripannelli JPanel: uno per contenere il tastierino numerico organizzato il
GridLayout ed un altro il FlowLayout per i tre bottoni in basso. La lastra
dei Contenuti viene lasciata in BorderLayout: al centro viene aggiunto il
pannello tastierino ed a sud il pannello con i tre bottoni.

7.1

Progettazione top down di interfacce grafiche

Durante la progettazione delle interfacce grache, pu`o essere utile ricorrere


a un approccio top down, descrivendo linsieme dei componenti a partire
dal componente pi`
u esterno per poi procedere a mano a mano verso quelli
pi`
u interni. Si pu`o sviluppare una GUI come quella dellesempio precedente
seguendo questa procedura:
1. Si denisce il tipo di top level container su cui si vuole lavorare (tipicamente un JFrame).
2. Si assegna un layout manager al content pane del JFrame, in modo da
suddividerne la supercie in aree pi`
u piccole.
3. Per ogni area messa a disposizione dal layout manager `e possibile
denire un nuovo JPanel. Ogni sotto pannello pu`o utilizzare un layout
manager dierente.
4. Ogni pannello identicato nel terzo passaggio pu`o essere sviluppato
ulteriormente, creando al suo interno ulteriori pannelli o disponendo
dei controlli.
Il risultato della progettazione pu`o essere rappresentato con un albero
della GUI. Lobiettivo di tale albero `e mostrare come i Container (a partire
dalla lastra dei contenuti) sono stati suddivisi per inserire i componenti o
altri Container.
Ogni componente (o Container) `e rappresentato da un nodo i cui gli sono
i componenti (o i Container) contenuti allinterno e il padre `e il componente
che lo contiene.
Una volta conclusa la fase progettuale, si pu`o passare a scrivere il codice
relativo allinterfaccia: in questo secondo momento, `e opportuno adottare un
approccio bottom up, realizzando dapprima il codice relativo ai componenti
atomici, quindi quello dei contenitori e inne quello del JFrame.

7 Progettazione della GUI con le gerarchie di contenimento

23

Fig. 11: Un frame desempio

JFrame

BorderLayout

nordPnl

FlowLayout

infoLbl

centroPnl

GridLayout (2,1)

sudPnl

FlowLayout

opz1chk opz2chk

okBtn
cancBtn

Fig. 12: Un frame pi`


u complesso
7.1.1

Esempio di Progettazione Top-Down

Un esempio nale viene mostrato per riassumere quanto `e stato nora detto.
Si inizier`a mostrando la progettazione top-down dellinterfaccia e si concludera mostrandone la realizzazione bottom-up. Si consideri la semplice nestra
in Figura 11. Lalbero della GUI corrispondente `e in Figura 12.
Il codice per mostrare tale nestra `e nel Listato 8. Si osservi linvocazione
del metodo pack() che sostituisce il metodo setSize(x,y) e che imposta la
dimensione della nestra alla minima necessaria a visualizzare tutti i controlli. Inoltre si osservi come le due istruzioni successive hanno lo scopo di
centrare la nestra sullo schermo. Il metodo getScreenSize() chiamato
sulla classe Singleton 3 Toolkit del package java.awt restituisce un riferimento
ad un oggetto java.awt.Dimension. Questo possiede due propriet`a Width e
Height che, nel caso specico, conterranno la dimensione dello schermo in
pixel. Listruzione successiva sposta la nestra al centro dello schermo con il
3

Una classe Singleton `e tale che di essa esista sempre al pi`


u una instanza.

7 Progettazione della GUI con le gerarchie di contenimento

24

public c l a s s MyFrame extends JFrame


{
JPanel nordPnl = new JPanel ( ) ;
JPanel c e n t r o P n l = new JPanel ( ) ;
JPanel sudPnl = new JPanel ( ) ;
JLabel i n f o L b l = new Labe l ( S e l e z i o n a r e : ) ;
JCheckBox opz1Chk = new JCheckBox ( Opz1 ) ;
JCheckBox opz2Chk = new JCheckBox ( Opz2 ) ;
JButton okBtn=new JButton ( OK ) ;
JButton cancBtn=new JButton ( Annulla ) ;
public MyFrame ( ) {
super ( Esempio ) ;
c e n t r o P n l . s e t L a y o u t (new GridLayout ( 2 , 1 ) ) ;
c e n t r o P n l . add ( opz1Chk ) ;
c e n t r o P n l . add ( opz2Chk ) ;
nordPnl . add ( i n f o L b l ) ;
sudPnl . add ( okBtn ) ;
sudPnl . add ( cancBtn ) ;
getContentPane ( ) . add ( nordPnl , BorderLayout .NORTH) ;
getContentPane ( ) . add ( c e n t r o P n l ,
BorderLayout .CENTER) ;
getContentPane ( ) . add ( sudPnl , BorderLayout .SOUTH) ;
pack ( ) ;
Dimension dim=
Toolkit . getDefaultToolkit ( ) . getScreenSize ( ) ;
s e t L o c a t i o n ( ( dim . getWidth () t h i s . getWidth ( ) ) / 2 ,
( dim . g e t H e i g h t () t h i s . g e t H e i g h t ( ) ) / 2 ) ;
s e t V i s i b l e ( true ) ;
}
}

Listato 8: Il codice della figura 11

8 Realizzare finestre di dialogo con JOptionPane

25

metodo setLocation(int x,int y) dove x, y sono le coordinate dellangolo


in alto a sinistra.

Realizzare finestre di dialogo con JOptionPane

La classe JOptionPane del package Swing permette di realizzare facilmente


nestra modali di input, di allarme o di conferma. Le API di JOptionPane
mettono a disposizione tre tipi di pannelli: Conrm, Input e Message Dialog.
Il primo viene usato quando si deve chiedere allutente di eettuare una scelta
tra un gruppo di possibilit`a, il secondo torna utile quando si deve richiedere
linserimento di una stringa di testo mentre il terzo viene usato per informare
lutente. La classe JOptionPane fornisce un gruppo di metodi statici che
permetto di creare facilmente queste nestre ricorrendo ad una sola riga di
codice. In questo paragrafo ci si concentrer`a sulluso di un sottoinsieme di
tali metodi, nella convinzione che essi permettano di risolvere la stragrande
maggioranza delle situazioni in modo compatto ed elegante.
Una nestra di conferma personalizzata si costruisce utilizzando il seguente
metodo statico che ammette due forme:
int showConfirmDialog ( Component parent , Object Message )
int showConfirmDialog ( Component parent , Object Message ,
S t r i n g t i t l e , int optionType , int messageType )

Il metodo restituisce uno dei seguenti valori a seconda se il bottone premuto


`e rispettivamente Si, No, Annulla, Ok :
JOptionPane . YES OPTION
JOptionPane . NO OPTION
JOptionPane .CANCEL OPTION
JOptionPane . OK OPTION

Una nestra di messaggio si costruisce con il seguente metodo statico che


ammette due forme:
void showMessageDialog ( Component parent , Object Message )
void showMessageDialog ( Component parent , Object Message ,
S t r i n g t i t l e , int messageType )

Una nestra di input si ottiene con il seguente metodo statico che ammette
tre forme:
Object show In pu tD ia lo g ( Component parent , Object Message )
Object show In pu tD ia lo g ( Component parent , Object Message ,
S t r i n g t i t l e , int messageType )
Object sh ow Inpu tD ia lo g ( Component parent , Object Message ,
S t r i n g t i t l e , int messageType , I c o n i c o n ,

8 Realizzare finestre di dialogo con JOptionPane

26

import j a v a x . swing . JOptionPane ;


public c l a s s A p p l i c a t i o n
{
public s t a t i c void main ( S t r i n g a r g s [ ] )
{
JOptionPane . showMessageDialog ( null , Ciao ! ,
Finestra di messaggio ,
JOptionPane .INFORMATION MESSAGE ) ;
int yn=JOptionPane . showConfirmDialog ( null ,
Salvare l e modifiche ? , Selezione ,
JOptionPane . YES NO CANCEL OPTION,
JOptionPane .QUESTION MESSAGE ) ;
i f ( yn==JOptionPane . NO OPTION)
return ;
S t r i n g prova ;
prova=( S t r i n g ) JOptionPane . showInputDialog ( null ,
stato di residenza : , Selezione ,
JOptionPane .INFORMATION MESSAGE, null , null ,
Italia );
S t r i n g x [ ] = { a , b , c , d , e , f , g } ;
prova=( S t r i n g ) JOptionPane . showInputDialog ( null ,
S c e g l i l opzione : , Selezione ,
JOptionPane .INFORMATION MESSAGE, null , x , x [ 3 ] ) ;
}
}

Listato 9: Esempi di uso delle finestre di dialogo

Object [ ] S e l e c t e d V a l u e s , Object i n i t i a l V a l u e )

Il metodo restituisce la stringa (come Object e quindi su cui `e necessario fare


il cast) inserita nellInputDialog oppure null se `e stato premuto il bottone
annulla. I parametri dei metodi appena illustrati sono descritti in Tabella 1.
La sezione si conclude con il Listato 9 dove vengono illustrati alcuni
esempi di uso delle nestre di dialogo.

9 La Gestione degli Eventi

27

La Gestione degli Eventi

Fig. 13: La gestione ad event delegation


La gestione degli eventi graci in Java segue il paradigma event delegation
(conosciuto anche come event forwarding). Ogni oggetto graco `e predisposto ad essere sollecitato in qualche modo dallutente e ad ogni sollecitazione
genera eventi che vengono inoltrati ad appositi ascoltatori, che reagiscono
agli eventi secondo i desideri del programmatore. Levent delegation presenta il vantaggio di separare la sorgente degli eventi dal comportamento a essi
associato: un componente non sa (e non `e interessato a sapere) cosa avverr`a
al momento della sua sollecitazione: esso si limita a noticare ai propri ascoltatori che levento che essi attendevano `e avvenuto, e questi provvederanno
a produrre leetto desiderato.
Ogni componente pu`o avere pi`
u ascoltatori per un determinato evento
o per eventi dierenti, come anche `e possibile installare uno stesso ascoltatore su pi`
u componenti anche diversi a patto che entrambi possono essere
sollecitati per generare levento. Loperazione di installare lo stesso ascoltatore su pi`
u controlli (che quindi si comporteranno nello stesso modo se sollecitati dallo stesso evento) `e frequente: si pensi alle voci di un menu che vengono replicate su una toolbar per un pi`
u facile accesso ad alcune funzionalit`a
frequenti

9.1

Implementazione dellevent delegation

Il gestore delle nestre pu`o generare un numero enorme di eventi (sono eventi
muovere il mouse nella nestra, spostare o ridimensionare una nestra, scri-

9 La Gestione degli Eventi

28

vere o abbandonare un campo di testo, . . . ). Molte volte si `e interessati a


pochi di questi eventi. Per fortuna, il programmatore pu`o considerare solo gli
eventi di cui ha interesse, gestendoli con un ascoltatore opportuno, ignorando completamente tutti gli altri che di default verranno gestiti come eventi
vuoti.
Quando accade un evento per il quale esiste un ricevitore, la sorgente dellevento chiama i metodi da voi forniti nell ricevitore, dando, in un oggetto di
una classe evento, informazioni pi`
u dettagliate sullevento stesso. In generale
sono coinvolte tre classi:
La classe del ricevitore. Implementa una particolare interfaccia del tipo XXXListener tipica degli eventi di una certa classe. I metodi dellinterfaccia
che la classe dellascoltatore implementa contengono il codice eseguito
allo scatenarsi degli eventi di una classe che lascoltatore intercetta.
Grazie al fatto che ogni ascoltatore `e caratterizzato da una particolare
interfaccia Java, qualsiasi classe pu`o comportarsi come un ascoltatore,
a patto che fornisca unimplementazione per i metodi deniti dalla
corrispondente interfaccia listener
il componente che genera levento che si vuole gestire
Lorigine dellevento. E
su cui si vuole installare lascoltatore. Ogni componente dispone per
ogni evento supportato di due metodi:
void addXXXListener ( XXXListener a s c o l t a t o r e ) ;
void removeXXXListener ( XXXListener a s c o l t a t o r e ) ;

La classe evento. Contiene le informazioni riguardanti le caratteristiche dellevento generato. Gli oggetti di questa classe sono istanziati direttamente dai componenti che noticano eventi agli ascoltatori. Formalmente sono parametri di input dei metodi dellinterfaccia implementata
dallascoltatore. Nel momento in cui il componente viene sollecitato, esso chiama gli ascoltatori in modalit`a callback, passando come parametro
della chiamata unapposita sottoclasse di Event. Queste classi contengono tutte le informazioni signicative per levento stesso e possono
essere utilizzate per modicare il comportamento dellascoltatore in
base alle informazioni sullevento scatenato
Le classi che permettono di gestire gli eventi generati dai componenti
Swing sono in gran parte gli stessi utilizzati dai corrispondenti componenti
AWT, e si trovano nel package java.awt.event. Alcuni componenti Swing,
tuttavia, non hanno un omologo componente AWT: in questo caso le classi
necessarie a gestirne gli eventi sono presenti nel package javax.swing.event.

9 La Gestione degli Eventi

9.2

29

Un esempio: elaborare gli eventi del mouse

Per chiarire il funzionamento, a prima vista complesso, della gestione degli


eventi Swing, verra fatto un esempio. In particolare, vogliamo realizzare un
meccanismo per spiare gli eventi del mouse su una nestra e stamparli.
Una classe che desidera catturare gli eventi del mouse deve implementare
linterfaccia
MouseListener denita nel package java.awt.event che `e cos` denita:
public i n t e r f a c e M o u s e L i s t e n e r
{
void mouseClicked ( MouseEvent e ) ;
void mouseEntered ( MouseEvent e ) ;
void mouseExited ( MouseEvent e ) ;
void mousePressed ( MouseEvent e ) ;
void mouseReleased ( MouseEvent e ) ;
}

dove MouseEvent `e la corrispondente classe evento il cui scopo `e dare informazioni aggiuntive sullevento del mouse. Infatti, questa denisce due
metodi che permettono di conoscere le coordinate del mouse allo scatenarsi dellevento (i primi due) e un terzo metodo che permette di determinare
quale bottone del mouse `e stato premuto:
int getX ( ) ;
int getY ( ) ;
int g e t M o d i f i e r s ( )

Nel codice del Listato 10 viene mostrata limplementazione della spia.


Si noti come la classe ascoltatore MouseSpy implementi linterfaccia
MouseListener e come la classe denisca due metodi vuoti: mouseEntered
e mouseExited. La denizione dei due metodi vuoti ha lo scopo di dichiarare
che gli eventi legati allentrata e alluscita del mouse dallarea della nestra
devono essere ignorati. In eetti, ignorare gli eventi `e gi`a il comportamento
di default e quindi tale dichiarazione `e superua. La ragione per cui `e stata
comunque dichiarata la gestione di tali eventi `e legata al linguaggio Java:
anche una classe implementi una interfaccia, tale classe deve implentare
tutti i metodi dellinterfaccia. E questo vale anche in questo caso: la classe
MouseSpy deve implementare tutti i metodi dellinterfaccia MouseListener
per poter dichiarare di implementarla.
Nella Tabella 2 sono riassunti gli ascoltatori pi`
u importanti. Per ulteriori
dettagli si consiglia di consultare la documentazione Java Swing.

9 La Gestione degli Eventi

30

import j a v a . awt . e v e n t . ;
import j a v a x . swing . ;
public c l a s s MouseSpy implements M o u s e L i s t e n e r
{
public void mouseClicked ( MouseEvent e ) {
System . out . p r i n t l n
( C l i c k su ( +e . getX ()+ , +e . getY ()+ ) ) ;
}
public void mousePressed ( MouseEvent e ) {
System . out . p r i n t l n
( Premuto su ( +e . getX ()+ , +e . getY ()+ ) ) ;
}
public void mouseReleased ( MouseEvent e ) {
System . out . p r i n t l n
( R i l a s c i a t o su ( +e . getX ()+ , +e . getY ()+ ) )
}
public void mouseEntered ( MouseEvent e ) {}
public void mouseExited ( MouseEvent e ) {}
}
public c l a s s MyFrame extends JFrame
{
public MyFrame ( )
{
super ( MouseTest ) ;
t h i s . addMouseListener (new MouseSpy ( ) ) ;
setSize (200 ,200);
s e t V i s i b l e ( true ) ;
}
}

Listato 10: Primo esempio della gestione degli eventi

9 La Gestione degli Eventi

31

public i n t e r f a c e WindowListener
{
public void windowOpened ( WindowEvent e ) ;
public void windowClosing ( WindowEvent e ) ;
public void windowClosed ( WindowEvent e ) ;
public void w i n d o w I c o n i f i e d ( WindowEvent e ) ;
public void w i n d o w D e i c o n i f i e d ( WindowEvent e ) ;
public void windowActivated ( WindowEvent e ) ;
public void windowDeactivated ( WindowEvent e ) ;
}

Listato 11: Linterfaccia MouseListener


c l a s s WindowClosedListener extends WindowAdapter {
public void windowClosed ( WindowEvent e )
{
// c o d i c e da e s e g u i r e a l l a c h i u s u r a d e l l a f i n e s t r a
}
}

Listato 12: Esempio delluso degli Adapter

9.3

Uso di adapter nella definizione degli ascoltatori

Alcuni componenti graci generano eventi cos` articolati da richiedere, per


la loro gestione, ascoltatori caratterizzati da pi`
u di un metodo. Si `e gi`a visto che MouseListener, ne denisce tre. Linterfaccia WindowListener (vedi
Listato 11) ne dichiara addirittura sette, ossia uno per ogni possibile cambiamento di stato della nestra. Si `e detto che se si desidera creare un ascoltatore
interessato a un solo evento (per esempio uno che intervenga quando la nestra viene chiusa), esso dovr`a comunque fornire unimplementazione vuota
anche dei metodi cui non `e interessato. Nei casi come questo `e possibile
ricorrere agli adapter: classi di libreria che forniscono unimplementazione
vuota di un determinato ascoltatore. Per esempio, il package java.awt.event
contiene la classe WindowAdapter, che fornisce unimplementazione vuota di
tutti i metodi previsti dallinterfaccia. Una sottoclasse di WindowAdapter,
pertanto, `e un valido WindowListener, con in pi`
u il vantaggio di una maggior
concisione. Si veda lesempio 12

10 La gestione degli eventi Azione

32

Fig. 14: La finestra ottenuta dal Listato 13

10

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 denisce un
unico metodo:
public i n t e r f a c e A c t i o n L i s t e n e r
{
public void a c t i o n P e r f o r m e d ( ActionEvent ae ) ;
}

Loggetto ActionEvent da informazioni aggiuntive sullevento generato. Ad


esempio, denisce il metodo Object getSource() che restituisce loggetto
che ha generato levento.
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. Si potrebbe obiettare che, la gestione del click di un bottone,
potrebbe essere fatta usando un mouseListener. Il vantaggio di utilizzare un
evento azione risiede nel fatto che `e possibile cliccare su un bottone anche con
la tastiera (anche se in quel caso non `e un vero click!): lutente con il tasto
TAB potrebbe spostarsi sulla schermata, mettere il focus su un bottone e
qui premere la barra spaziatrice per voler cliccare sul bottone.
possibile installare uno stesso ascoltatore, ad esempio, per un bottone
E
della toolbar e una voce di menu. Il vantaggio risiede nel fatto che sia la voce
del menu che il bottone hanno lo stesso ascoltatore invece che due ascoltatori
separati che fanno la stessa cosa.
Un primo esempio di uso degli eventi azione `e nel Listato 13. Il comportamento `e quello in Figura 14: quando lutente seleziona uno dei bottoni
della nestra MyFrame viene aperta una nestra di dialogo con il testo del
bottone premuto. Su ogni bottone `e installato lo stesso ascoltatore per gli
eventi azione. Alla pressione di un bottone viene eseguito il metodo action-

11 Accedere dallascoltatore agli oggetti di una finestra

33

Fig. 15: La finestra ottenuta dal Listato 14


Performed, il quale si fa restituire con il metodo getSource() il bottone
premuto (si noti il cast giacch`e il metodo lo restituisce come riferimento ad
Object). A partire dal bottone premuto, con il metodo getText() viene
restituito il testo visualizzato nel bottone.

11

Accedere dallascoltatore agli oggetti di una finestra

Lapproccio nora descritto funziona abbastanza bene anche se presenta ancora alcune problematiche. Il primo problema `e legato al fatto che la nestra
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 nestra. Essendo le due classi formalmente indipendenti tra loro, per
quanto detto, non `e possibile accedere dagli ascoltatori a tutti i componenti
della nestra (tranne il componente che ha generato levento).
Supponiamo di voler realizzare una nestra come quella in Figura 15.
Alla pressione del bottone OK il contenuto del campo di testo deve essere
visualizzato in un message dialog box. Si consideri il codice nel Listato 14.
Nellascoltatore deniamo un nuovo JTextField text. Questapproccio
non funziona perch`e il campo di testo denito `e un nuovo campo di testo e
non quello della nestra. Quindi la pressione del bottone mostrer`a sempre
una nestra dal contenuto vuoto perch`e di default un campo di testo viene
inizializzato con il contenuto vuoto.
Un primo approccio `e utilizzare una classe interna. Una classe interna
`e denita allinterno di unaltra classe e pu`o accedere a tutte le variabili e
i metodi di istanza della classe esterna (anche a quelli privati). Si consideri
il Listato 15 che fa uso della classi interne: listruzione text = txt nellascoltatore fa s` che il riferimento text punti allo stesso campo di testo
riferito da txt. Quindi quando nel codice successivo, lascoltatore chiede
text.getText() ottiene eettivamente il codice richiesto.
Questa tecnica `e accettabile se lascoltatore esegue poche operazioni e
pochi ascoltatori esistono. Altrimenti la classe MyFrame diventa eccessiva-

11 Accedere dallascoltatore agli oggetti di una finestra

public c l a s s MyFrame extends JFrame


{
private JButton uno = new JButton ( Uno ) ;
private JButton due = new JButton ( Due ) ;
private JButton t r e = new JButton ( Tre ) ;
private JButton q u a t t r o = new JButton ( Quattro ) ;
private JButton c i n q u e = new JButton ( Cinque ) ;
A s c o l t a t o r e l i s t e n e r = new A s c o l t a t o r e ( ) ;
public MyFrame ( )
{
...
C o n t a i n e r c = t h i s . getContentPane ( ) ;
c . add ( uno ) ;
uno . a d d A c t i o n L i s t e n e r ( l i s t e n e r ) ;
...
c . add ( c i n q u e ) ;
cinque . addActionListener ( l i s t e n e r ) ;
}
}
public c l a s s A s c o l t a t o r e implements A c t i o n L i s t e n e r
{
public void a c t i o n P e r f o r m e d ( ActionEvent e v e n t )
{
JButton b = ( JButton ) e v e n t . g e t S o u r c e ( ) ;
JOptionPane . showMessageDialog ( null ,
` s t a t o premuto +b . getText ( ) ) ;
E
}
}

Listato 13: Esempio uso di ActionPerformed

34

11 Accedere dallascoltatore agli oggetti di una finestra

public c l a s s MyFrame extends JFrame {


JPanel c e n t r o = new JPanel ( ) ;
JPanel sud = new JPanel ( ) ;
J T e x t F i e l d t x t = new J T e x t F i e l d ( 2 0 ) ;
JButton button = new JButton ( Premi ) ;
public MyFrame ( ) {
super ( Esempio ) ;
c e n t r o . add ( t x t ) ;
sud . add ( button ) ;
getContentPane ( ) . add ( c e n t r o , BorderLayout .CENTER) ;
getContentPane ( ) . add ( sud , BorderLayout .SOUTH) ;
button . a d d A c t i o n L i s t e n e r (new L i s t e n ( ) ) ;
...
}
}
c l a s s L i s t e n implements A c t i o n L i s t e n e r {
public void a c t i o n P e r f o r m e d ( ActionEvent e ) {
J T e x t F i e l d t e x t = new J T e x t F i e l d ( ) ;
JOptionPane . showMessageDialog (
null , t e x t . getText ( ) ) ;
}
}

Listato 14: Un esempio che non funziona

35

11 Accedere dallascoltatore agli oggetti di una finestra

public c l a s s MyFrame extends JFrame {


JPanel c e n t r o = new JPanel ( ) ;
JPanel sud = new JPanel ( ) ;
J T e x t F i e l d t x t = new J T e x t F i e l d ( 2 0 ) ;
JButton button = new JButton ( Premi ) ;
public MyFrame ( ) {
super ( Esempio ) ;
c e n t r o . add ( t x t ) ;
...
button . a d d A c t i o n L i s t e n e r (new L i s t e n ( ) ) ;
...
}

c l a s s L i s t e n implements A c t i o n L i s t e n e r
{
public void a c t i o n P e r f o r m e d ( ActionEvent e ) {
JTextField text = txt ;
JOptionPane . showMessageDialog (
null , t e x t . getText ( ) ) ;
}
}

Listato 15: Una soluzione che funziona con le classi interne

36

12 Condividere gli ascoltatori per pi`


u oggetti

37

mente grande. Inoltre il codice ottenuto diventa poco leggibile perch`e si


tende ad accorpare classi che rappresentano concetti diversi.
La soluzione preferibile `e quella in cui lascoltatore denisce un costruttore che prende come parametro un riferimento alla nestra con i componenti
richiesti. Il costruttore memorizza tale riferimento come variabile di classe.
La classe ascoltatore resta comunque esterna: essa accede ai widget della
nestra tramite tale riferimento e loperatore punto. Ovviamente i widget
non devono essere memorizzati privati. La soluzione `e descritta nel Listato 16. Nel codice viene anche mostrato una grande potenzialit`a degli eventi
azione: levento azione del campo di testo `e gestito nello stesso modo della nestra. Ne risulta, di conseguenza, che la pressione del bottone e la
pressione del tasto ENTER sul campo di testo vengono gestiti dallo stesso
ActionListener e quindi gestiti nello stesso modo.

12

Condividere gli ascoltatori per pi`


u oggetti

La tecnica pi`
u naive per gestire pi`
u eventi associati ad un controllo `e quello di
creare una classe ascoltatore per ogni oggetto e per ogni classe di eventi da
gestire per quello oggetto. Ci`o vorrebbe dire che se dovessimo gestire levento
di pressione di X bottoni, dovrebbe realizzare X classi che implementano
ActionListener.
Ovviamente `e nella pratica improponibile prevedere una classe per ogni
bottone, voce del menu e cos` via. Infatti, consideriamo per esempio unapplicazione con 5 nestre, ognuna con 5 bottoni. Supponiamo che una di tali
nestre abbia anche 3 voci del menu di 4 opzioni ciascuno. Ci`o signicherebbe
che avremmo 37 classi solo per gestire la pressione dei bottoni o della voci
del menu!
Lidea pi`
u elegante `e raggruppare gli oggetti su cui ascoltare in classi,
prevedendo un ascoltatore condiviso per tutti gli oggetti della stessa classe.
Da questo punto in poi consideriamo solo gli eventi actionListener senza
perdere di generalit`a. Lo stesso approccio si applica in tutti gli altri casi. I
componenti della stessa classe, ovviamente, condivideranno lo stesso metodo
actionPerformed. A meno che tutti i componenti della stessa classe si devo
comportare nello stesso modo allo scatenarsi dellevento azione, occore essere
in grado di capire quale oggetto ha generato levento. Questo al ne di
capire quale comportamente adottare.
Esistono due modi per capire chi ha generato levento:
1. Attraverso il metodo getSource() della classe ActionEvent che restituisce un riferimento alloggetto che ha scatenato levento. In questo

12 Condividere gli ascoltatori per pi`


u oggetti

public c l a s s MyFrame extends JFrame {


JPanel c e n t r o = new JPanel ( ) ;
JPanel sud = new JPanel ( ) ;
J T e x t F i e l d t x t = new J T e x t F i e l d ( 2 0 ) ;
JButton button = new JButton ( Premi ) ;
L i s t e n a s c o l t=new L i s t e n ( t h i s ) ;
public MyFrame ( ) {
super ( Esempio ) ;
c e n t r o . add ( t x t ) ;
...
button . a d d A c t i o n L i s t e n e r ( a s c o l t ) ;
txt . addActionListener ( a s c o l t ) ;
...
}
}
c l a s s L i s t e n implements A c t i o n L i s t e n e r {
MyFrame frame ;
public L i s t e n (MyFrame aFrame )
{ frame = aFrame ; }

public void a c t i o n P e r f o r m e d ( ActionEvent e ) {


J T e x t F i e l d t e x t = frame . t x t ;
JOptionPane . showMessageDialog (
null , t e x t . getText ( ) ) ;
}

Listato 16: La soluzione migliore

38

12 Condividere gli ascoltatori per pi`


u oggetti

39

modo `e possibile utilizzare un approccio di tipo switch/case: se il riferimento punta a X, allora chiama un metodo privato che corrisponde
alla gestione di pressione del bottone X; se il riferimento punta a Y,
allora chiama il metodo per Y. E cosi via. E ovviamente chiaro se questa approccio `e fattibile se `e possibile confrontare i riferimenti ottenuti
con il metodo getSource() con i riferimenti memorizzati internamente
alla classe che estende JFrame e che disegna la nestra con i bottoni
X, Y e gli altri. Questo approccio `e generalmente fattibile, quindi, con
le classi interne.
2. Utilizzando la propriet`a actionCommand, implementata per ogni componente, che permette di associare una stringa identicativa univoca
ad ogni componente che scatena un evento azione. A questo punto `e
possibile denire allinterno del metodo actionPerformed un approccio di tipo switch/case sugli actionCommand. Se lo actionCommand
delloggetto `e ACTX (che `e stato associato ad X) allora lascoltatore
esegue una certa porzione di codice, se lo actionCommand `e ACTY fa
unaltra cosa. E cos` via. In questo nuovo contesto non `e pi`
u necessario
per lascoltatore di avere accesso ai riferimenti.
Si consideri, ad esempio, la porzione di condice nel Listato 17 dove ci
sono tre voci di menu UpOpt, DownOpt, RandomOpt. Le tre voci del menu
hanno associati lo stesso ascoltatore con lo stesso metodo actionPerformed.
Allinterno del metodo la discriminazione su come lascoltatore si deve comportare in funzione del metodo invocato `e fatto analizzando i riferimenti.
Listruzione src=e.getSource() restituisce un riferimento alloggetto che
ha generato levento. Se tale riferimento `e uguale a UpOpt (entrambi puntano allo stesso oggetto corrispondente alla voce del menu Up, allora la
voce del menu scelta `e Up. Quindi viene eseguita la porzione di codice relativa a tale voce. Lo stesso per le altre voci. Lascoltatore `e realizzato come
classe interna in modo tale da poter realizzare luguaglianza tra i puntato e
vericare se coincidono.
Come si `e detto, lapproccio con le classi interne ha molti svantaggi. la
metodologia che vogliamo qui introdurre per capire chi ha generato levento
non deve assumere classi interne e, quindi, non pu`o basarsi sulluguaglianza
tra puntatori.
Quando si usano classi esterne, `e possibile capire il componente che ha
noticato levento associando ai diversi componenti un diverso valore della
propriet`a actionCommand.
Nellesempio nel Listato 18 i possibili valori per le propriet`a actionCommand sono memorizzate come costanti stringa, cio`e public final static,
della classe ascoltatore Listener. Ad ogni oggetto da predisporre per gestire

12 Condividere gli ascoltatori per pi`


u oggetti

public c l a s s MyFrame extends JFrame


{
...
JMenuItem UpOpt = new JMenuItem ( Up ) ;
JMenuItem DownOpt = new JMenuItem ( Down ) ;
JMenuItem RandomOpt = new JMenuItem ( Random ) ;
L i s t e n e r a s c o l t a t o r e = new L i s t e n e r ( ) ;
public MyFrame ( )
{
...
UpOpt . a d d A c t i o n L i s t e n e r ( a s c o l t a t o r e ) ;
DownOpt . a d d A c t i o n L i s t e n e r ( a s c o l t a t o r e ) ;
RandomOpt . a d d A c t i o n L i s t e n e r ( a s c o l t a t o r e ) ;
...
}

c l a s s L i s t e n e r implements A c t i o n L i s t e n e r
{
public void a c t i o n P e r f o r m e d ( ActionEvent e )
{
Object s r c = e . g e t S o u r c e ( ) ;
i f ( s r c == UpOpt)
{ c o d i c e d e l l a voce d e l menu Up }
e l s e i f ( s r c == DownOpt)
{ c o d i c e d e l l a voce d e l menu Down }
e l s e i f ( s r c == RandomOpt )
{ c o d i c e d e l l a voce d e l menu Random }
}
}

Listato 17: Ascoltatore condiviso come classe Interna

40

12 Condividere gli ascoltatori per pi`


u oggetti

41

public c l a s s MyFrame extends JFrame {


...
JMenuItem UpOpt = new JMenuItem ( Up ) ;
JMenuItem DownOpt = new JMenuItem ( Down ) ;
JMenuItem RandomOpt = new JMenuItem ( Random ) ;
L i s t e n e r a s c o l t = new L i s t e n e r ( ) ;
public MyFrame ( ) {
...
UpOpt . a d d A c t i o n L i s t e n e r ( a s c o l t ) ;
UpOpt . setActionCommand ( L i s t e n e r .UPOPT) ;
DownOpt . a d d A c t i o n L i s t e n e r ( a s c o l t ) ;
DownOpt . setActionCommand ( L i s t e n e r .DOWNOPT) ;
RandomOpt . a d d A c t i o n L i s t e n e r ( a s c o l t ) ;
RandomOpt . setActionCommand ( L i s t e n e r .RANDOMOPT)
...
}
}

Listato 18: Il JFrame per usare ascoltatori esterni

gli eventi azione viene associato un valore per la actionCommand presi tra
tali costanti stringa. Questo viene fatto grazie al metodo
void setActionCommand ( S t r i n g ) ;

Il metodo actionPerformed dellactionListener legge il valore della propriet`a actionCommand del componente che ha noticato levento e, in funzione
del valore letto, sceglie la porzione di codice da eseguire. Eventualmente `e
possibile associare lo stesso actionCommand a componenti gestiti dallo stesso ascoltatore se si desidera che questi gestiscano levento azione nello stesso
modo. Questo cosa `e molto utile quando si desidera replicare la voce di un
dato menu con un bottone da inserire nella toolbar della nestra: per fare s`
che sia la voce del menu che il bottone nella toolbar gestiscano levento nello
stesso modo `e suciente associare ad entrambi lo stesso actionCommand.
Torniamo indietro a considerare la nestra descritta nel Listato 18. Il suo
scheletro `e nel Listato 19. Si possono osservare sempre tre parti in questo
tipo di ascoltatori:
La parte che denisce le costanti stringa (le tre dichiarazioni di oggetti
pubblici, costanti4 e statici)
4

In Java `e possibile denire che il valore di un riferimento non pu`o cambiare dichiarandolo final. Questo ovviamente non `e suciente per dichiarare un oggetto costante. Tuttavia nel caso di oggetti String le due cose coincidono dato che gli oggetti stringa sono

12 Condividere gli ascoltatori per pi`


u oggetti

42

public c l a s s L i s t e n e r implements A c t i o n L i s t e n e r
{
public f i n a l s t a t i c S t r i n g UPOPT = up ;
public f i n a l s t a t i c S t r i n g DOWNOPT = down ;
public f i n a l s t a t i c S t r i n g RANDOMOPT = random ;
public void a c t i o n P e r f o r m e d ( ActionEvent e )
{
S t r i n g com = e . getActionCommand ( ) ;
i f ( com == UPOPT)
upOpt ( ) ;
e l s e i f ( s r c == DOWNOPT)
downOpt ( ) ;
e l s e i f ( s r c == RANDOMOPT)
randomOpt ( ) ;
}
private void upOpt ( )
{ ... }
private void randomOpt ( )
{ ... }

private void downOpt ( )


{ ... }

Listato 19: Ascoltatore condiviso come classe Interna


Il metodo actionPerformed che gestisce levento. Questo metodo in
questi casi non fa nientaltro che leggere il valore della propriet`a actionCommand delloggetto che ha generato levento. In funzione del
valore della propriet`a (e quindi delloggetto scatenante), viene attivata
una gestione separata che consiste nellinvocare metodi diversi.
I diversi metodi privati che eseguono eettivamente le operazioni associate allattivazione dei diversi controlli/widget.
immutabili: non pu`o cambiare il riferimento, n`e pu`o cambiare il valore delloggetto puntato

13 Conclusioni e Commenti Finali

13

43

Conclusioni e Commenti Finali

Questa dispensa ha descritto le funzionalit`a pi`


u comuni del pacchetto JavaSwing.
JavaSwing `e un pacchetto estremamente vasto, che va dalla denizione delle
interfacce e del disegno 2D, no alla gestione dellinterazione nei pi`
u minimi dettagli. Di conseguenza, `e praticamente impossibile toccare tutti gli
argomenti in questa dispensa.
` opportuno concludere la trattazione con alcune osservazioni su una corE
retta interazione tra le interfacce grache e la logica applicativa (ad es., la
realizzazione del diagramma delle classi). Una buon approccio metodologico
si basa su un concetto cardine: la logica applicativa non deve essere strettamente accoppiata con il codice delle classi delle interfacce grache. Quindi,
allinterno delle classi della logica applicativa non si devono avere riferimenti,
n`e avere accesso alle classi che descrivono le interfacce grache.
Daltronde, non `e possibile specicare una modalit`a di accesso private
per le variabili di istanza delle classi che ereditano da JFrame. Infatti, se
cos` fosse, gli ascoltatori non avrebbero accesso ai componenti delle nestre,
a meno di denire complessi e numerosi metodi getter/setter.
Un buon tradeo consiste nel mettere le classi delle interfacce e i relativi
ascoltatori in un package diverso dalla logica applicativa (ad esempio gui),
specicando un modalit`a di accesso package alle variabili di istanza delle
classi che ereditano da JFrame.5 In questo modo le classi della logica applicativa non hanno alcun accesso ai campi delle classi JFrame, dato che si trovano
i package diversi. Viceversa gli ascoltatori possono, poiche si trovano nello
stesso package. Infatti, gli ascoltatori sono gli unici che, da un lato, possono
accedere alla logica applicative e, dallaltro, possono aggiornare opportunamente laspetto e i contenuti delle nestre grache. In aggiunta `e possibile
denire per ogni nestra e tutti i listener associati ai widget della nestre
un opportuno sotto package (ad esempio gui.nomeFinestra) in modo da
limitare il raggio di azione dei diversi ascoltatori alla sola nestra.

Si ottiene la modalit`a di accesso package quando la descrizione del tipo delle variabili
distanza non `e preceduta da nessuna modalit`a di accesso: public, private o protected.

13 Conclusioni e Commenti Finali

parent

Message

title
messageType

44

Questo parametro serve a specicare il frame principale; esso


verr`a bloccato no al termine dellinterazione. Ponendo a null
questo parametro la nestra verr`a visualizzata al centro dello
schermo e risulter`a indipendente dal resto dellapplicazione.
Questo campo permette di specicare una stringa da visualizzare come messaggio oppure, in alternativa, una qualunque
sottoclasse di Component
Questo parametro `e utilizzato per specicare il titolo della
nestra modale.
Attraverso questo parametro `e possibile inuenzare laspetto
complessivo della nestra, per quanto riguarda il tipo di icona
e il layout. Il parametro pu`o assumere uno dei seguenti valori:
JOptionPane.ERROR MESSAGE
JOptionPane.INFORMATION MESSAGE
JOptionPane.WARNING MESSAGE
JOptionPane.QUESTION MESSAGE
JOptionPane.PLAIN MESSAGE

optionType

I Conrm Dialog possono presentare diversi gruppi di opzioni


per scegliere i bottoni da visualizzare nella nestra modale:
JOptionPane.YES NO OPTION
JOptionPane.YES NO CANCEL OPTION
JOptionPane.OK CANCEL OPTION

icon

SelectedValues

initialValue

Ogni istanza della classe Icon rappresenta unicona. In questo


caso loggetto passato sar`a visualizzato come icona di un Input Dialog. Nella maggior parte dei casi questo parametro
pu`o essere messo a null in modo tale che sia scelta licona di
default.
Questa parametro permette di impostare i possibili valori,
tipicamente stringhe, che possono selezionati attraverso lInput Dialog. I valori selezionabili sono inseriti in un ComboBox
in modo tale che nessun altro valore `e ammissibile. Se questo
parametro viene messo a null allora il ComboBox `e sostituito
da un campo di testo e qualsiasi valore `e ammesso.
Questo parametro permette di stabilire il valore di default,
tipicamente una stringa.

Tab. 1: Spiegazione dei parametri dei metodi di JOptionPane

13 Conclusioni e Commenti Finali

45

Denisce 1 metodo per ricevere eventi-azione


Denisce 4 metodi per riconoscere quando un
componente viene nascosto, spostato, mostrato o
ridimensionato
FocusListener
Denisce 2 metodi per riconoscere quando un
componente ottiene o perde il focus
KeyListener
Denisce 3 metodi per riconoscere quando viene
premuto, rilasciato o battuto un tasto
MouseMotionListener Denisce 2 metodi per riconoscere quando il mouse
e trascinato o spostato
MouseListener
Se ne `e gi`a parlato. . .
TextListener
Denisce 1 metodo per riconoscere quando cambia
il valore di un campo testo
WindowListener
Denisce 7 metodi per riconoscere quando un nestra viene attivata, chiusa, disattivata, ripristinata,
ridotta a icona, ecc.
ActionListener
ComponentListener

Tab. 2: Gli ascoltatori pi`


u importanti

Vous aimerez peut-être aussi