Sei sulla pagina 1di 13

Questo capitolo spiega il funzionamento del modello di eventi della libreria AWT di Java.

Nell’ambiente di programmazione Java, entro i limiti imposti dagli eventi riconosciuti da AWT, è
possibile controllare completamente come gli eventi sono trasmessi dalle sorgenti di eventi (es.
pulsanti e barre di scorrimento), ai listener (o recettori) di eventi. E’ possibile impostare un oggetto
qualsiasi come listener di eventi; in pratica si seleziona un oggetto che possa fornire la risposta
desiderata a un determinato evento.
In un linguaggio orientato agli oggetti come Java è lecito aspettarsi che le informazioni che
riguardano gli eventi siano incapsulate in un oggetto evento. In java tutti gli oggetti evento derivano
dalla classe java.util.EventObject. Ovviamente ci sono sottoclassi per ogni tipo di evento (es.
ActionEvent e WindowEvent): diverse sorgenti di eventi possono produrre generi di eventi
differenti; per esempio, un pulsante può inviare oggetti ActionEvent, mentre una finestra può
inviare oggetti WindowEvent.

Di seguito è riportata una panoramica del funzionamento in AWT della gestione di eventi:

 Un oggetto listener è un’istanza di una classe che implementa un’interfaccia speciale


denominata ovviamente interfaccia di listener;

 Una sorgente di eventi è un oggetto che registra gli oggetti listener e invia oggetti evento.
Un bottone ad esempio può essere una sorgente di eventi;

 Quando si verifica un evento, la sorgente di eventi invia gli oggetti evento a tutti i listener
associati;

 Gli oggetti listener utilizzano le informazioni dell’oggetto di eventi per stabilire la reazione
all’evento.

 L’associazione dell’oggetto listener con l’oggetto sorgente è stabilita da righe di codice che
seguono il modello qui indicato:
“oggettoSorgenteEvento.addEventoListener(oggettoListenerEvento);”

ActionListener listener = “. . .”;


JButton button = new JButton(“OK”);
button.addActionListener(listener);

L’oggetto listener viene notificato ogni volta che si verifica nel pulsante un “evento
azione”. Nel caso dei pulsanti è lecito aspettarsi che un evento azione corrisponda a un click
del pulsante stesso.
Il codice come il precedente richiede che la classe alla quale appartiene il listener implementi
l’interfaccia appropriata, che nell’esempio è l’interfaccia ActionListener, Come in tutte le
interfacce di Java, l’implementazione di un’interfaccia significa ridefinire i suoi metodi.
 La classe che implementa ActionListener deve avere un metodo void di nome
actionPerformed che riceve un oggetto ActionEvent come parametro:

public class MyListener implements ActionListener {


...
public void actionPerformed(ActionEvent event) {
// qui le istruzioni di reazioni a un click sul pulsante
}
}

Ogni volta che l’utente fa click sul pulsante, l’oggetto JButton crea un oggetto ActionEvent e
chiama il listener associato, actionPerformed(event), passano l’oggetto dell’evento. E’ possibile
aggiungere più oggetti ai listener di una sorgente di evento. In questo caso ogni volta che l’utente fa
click sul pulsante, il pulsante chiama i metodi di tutti i listener.

 Una strategia che si può adottare è quella di utilizzare classi interne come listener che
implementano l’interfaccia opportuna oppure per semplificare ulteriormente il codice è
possibile impostare una classe anonima:

public void makeButton(String name) {


JButton firstButton = new JButton(name);
add(button);
button.addActionListener(new
ActionListener() {
public void actionPerformed(ActionEvent event) {
// istruzioni reazione
}
});
}
Un’altra strategia che si può applicare è quella di individuare il componente che deve cambiare
a seguito di un evento e si fa in modo che questo implementi l’interfaccia ActionListener,
aggiungendo un metodo actionPerformed.

 Nell’esempio che si sta considerando si può’ modificare un pannello in un listener di azioni:

public class ButtonPanel extends JPanel implements ActionListener {


...
firstButton.addActionListener(this);
secondButton.addActionListener(this);
...
public void actionPerformed(ActionEvent event) {
// istruzioni reazione
}
}

A questo punto il pannello imposta se stesso come listener di tutti i pulsanti che contiene. Si
noti che ora i pulsanti non hanno listener individuali, ma condividono lo stesso oggetto
listener, cioè il pannello che li contiene. Di conseguenza actionPerformed deve sapere su
quale pulsante è stato fatto il click.

Individuare la sorgente dell’evento in un oggetto listener condiviso da più sorgenti

1. Esiste per questo il metodo getSource della classe EventObject, il quale restituisce un
riferimento alla sorgente dell’evento:

Object source = event.getSource();


if (source == firstButton) . . .

Ovviamente, questo approccio richiede che I riferimenti ai pulsanti siano campi istanza
del pannello circostante.

2. In alternativa la classe ActionEvent ha un metodo getActionCommand che restituisce


la stringa di comando associata all’azione: se ActionEvent ha origine da un bottone, la
stringa di comandi è uguale all’etichetta del pulsante, a meno che non venga modificata
dal metodo setActionCommand:

firstButton.setActionCommand(“CommandFirstButton”)
String command = event.getActionCommand();
if (command.equals(“CommandFirstButton”) . . .
Rilevare gli eventi della finestra
Non tutti gli eventi sono semplici da gestire come i click sui pulsanti. Per esempio quando l’utente
chiude il frame si può inserire una finestra di dialogo che segnali che si sta per perdere il lavoro non
memorizzato e che si può uscire dal programma solo dopo una conferma da parte dell’utente.
Quando l’utente del programma tenta di chiudere una finestra del frame, l’oggetto JFrame diventa
la sorgente di un evento WindowEvent.

 Se si vuole rilevare questo evento, si deve realizzare un oggetto listener appropriato da


aggiungere all’elenco dei listener della finestra:

WindowListener listener = “. . .”;


frame.addWindowListener(listener);

Il listener della finestra deve essere un oggetto di una classe che implementa l’interfaccia
WindowListener. Ci sono 7 metodi nell’interfaccia WindowListener. Il frame li chiama in
risposta a sette eventi distinti che si possono verificare in una finestra:

public Terminator implements WindowListener {


public void windowOpened(WindowEvent e) {. . . }
public void windowClosing(WindowEvent e) {. . . } //chiusura
public void windowClosed(WindowEvent e) {. . . }
public void windowIconified(WindowEvent e) {. . . }
public void windowDeiconified(WindowEvent e) {. . . }
public void windowActivated(WindowEvent e) {. . . }
public void windowDeactivated(WindowEvent e) {. . . }
}
Classi Adapter
Scrivere codice per sei metodi che non devono fare nulla è il genere di lavoro che nessuno ama. Per
semplificare questo lavoro, in AWT ogni interfaccia di listener che ha più di un metodo definisce
una corrispondente classe adapter che implementa tutti i metodi dell’interfaccia in modo che non
facciano nulla. Per esempio:

public Terminator extends WindowAdapter {


public void windowClosing(WindowEvent e) {
// istruzioni reazione
}
}

Ora si può registrar un oggetto di tipo Terminator come listener di evento:

frame.addWindowEvent(new Terminator());

Oppure, per semplificare ancora, si può costruire la classe listener in una classe anonima del frame:

frame.addWindowEvent(new
WindowAdapter() {
public void windowClosing(WindowEvent e) {
// istruzioni reazione
});
La Tabella seguente illustra le interface di listener, gli eventi e le sorgenti di eventi più importanti
della libreria AWT

Interfaccia Metodi Parametro/Accessori Eventi generati


da
ActionEvent AbstractButton
ActionListener actionPerformed  getActionCommand JComboBox
 getModifiers JTextField
Timer
AdjustmentEvent
AdjustmentListener adjustmentValueChanged  getAdjustable JScrollBar
 getAdjustmentType
 getValue
ItemEvent
ItemListener itemStateChanged  getItem AbstractButton
 getItemSelectable JComboBox
 getStateChange
FocusListener focusGained FocusEvent Component
(vedi paragrafi successivi) focusLost  isTemporary
keyPressed KeyEvent
 getKeyChar
KeyListener keyReleased  getKeyCode
(vedi paragrafi successivi)  getKeyModifiersText Component
keyTyped  getKeyText
 isActionKey
mousePressed MouseEvent
mouseRelased  getClickCount
MouseListener mouseEntered  getX Component
(vedi paragrafi successivi) mouseExited  getY
mouseClicked  getPoint
 TranslatePoint
MouseMotionListener mouseDragged MouseEvent Component
(vedi paragrafi successivi) mouseMoved
MouseWheelEvent
MouseWheelListener mouseWheelMoved  getWheelRotation Component
 getScrollAmount
windowClosing *Opened WindowEvent
WindowListener *Iconified *Deiconified  getWindow Window
*Closed *Activated
*Deactivated
WindowFocusListener windowGainedFocus WindowEvent Window
windowLostFocus  getOppositeWindow
WindowEvent
WindowStateListener windowStateChanged  getOldState Window
 getNewState
Eventi della tastiera
Quando un utente preme un tasto, si genera un KeyEvent con ID_PRESSED, mentre quando lo
rilascia si genera un KeyEvent con KEY_RELEASE. Questi eventi sono intercettati dai metodi
keyPressed e keyReleased di qualsiasi classe che implementi l’interfaccia KeyListener. Esiste poi
un terzo metodo, keyTyped, che combina i primi due riportando i caratteri generati dalla pressione
dei tasti

Il modo migliore per vedere ciò che succede consiste nello studiare un esempio. Prima conviene
però introdurre alcuni nuovi termini importanti. Java distingue tra caratteri e codici virtuali dei
tasti:
 I codici virtuali dei tasti sono indicati con un prefisso VK_’X’, per esempio VK_A o
VK_SHIFT. Questi codici corrispondono ai tasti presenti sulla tastiera. Non esiste il codice
virtuale per i tasti in minuscolo proprio perché la tastiera non prevede tasti contrassegnati
con lettere minuscole.

Per trovare lo stato corrente dei tasti MAIUSC, CTRL, ALT si può ovviamente rilevare lo
stato dei tasti premuti VK_SHIFT, VK_CONTROL_ VK_ALT, ma questa procedura è
abbastanza noiosa. Conviene utilizzare i metodi isShiftDown, isControlDown, isAltDown.

Per lavorare con i metodi keyPressed e keyReleased si deve prima verificare il codice del
tasto come nel seguente esempio:

public void keyPressed(KeyEvent event) {


int keyCode = event.getKeyCode();
// controlla se l’utente ha premuto la combinazione di tasti MAIUSC + 
if (keyCode == KeyEvent.VK_RIGHT && event.isShiftDown()) . . .
}

Il codice del tasto equivale a una delle costanti definite nella classe KeyEvent, le cui
definizioni sono piuttosto mnemoniche.
Es.
VK_A . . . VK_Z oppure VK_0 . . . VK_9

 La procedura keyTyped, invece, riporta il carattere che è stato digitato (“A” o “a”).

Nel metodo keyTyped si chiama il metodo getKeyChar per ricavare il carattere che è stato
effettivamente digitato. Non tutte le combinazioni di tasti danno un risultato in keyTyped: è
possibile rilevare solo le combinazioni di tasti che generano un carattere Unicode.
Eventi del mouse
Non è necessario gestire gli eventi del mouse in modo esplicito se si vuole che l’utente possa solo
fare click su un pulsante o in un menu. Queste operazioni del mouse sono gestite interamente da
diversi componenti dell’interfaccia utente e poi tradotti negli eventi semantici appropriati. Tuttavia,
se si vuole consentire all’utente di disegnare con il mouse, è necessario intercettare gli eventi di
spostamento del mouse, dei click e del trascinamento.

Interfaccia MouseListener
Quando l’utente fa click con un pulsante del mouse, si chiamano tre metodi di listener:
mousePressed quando si preme il pulsante del mouse, mouseReleased quando si rilascia il
pulsante del mouse e, infine, mouseClicked.

 Se si utilizzano i metodi getX e getY sull’argomento MouseEvent, si possono ottenere le


coordinate x e y del puntatore del mouse in corrispondenza dell’istante in cui si fa clic sul
mouse.

 Per distinguere tra click singoli, doppi e tripli si utilizza il metodo getClickCount.

 Ci sono altri due metodi di eventi del mouse: mouseEntered e mouseExited. Questi metodi
vengono chiamati quando il mouse entra o esce da un componente.

Interfaccia MouseMotionListener
Se l’utente, invece, preme un pulsante del mouse mentre il mouse è in movimento
(trascinamento), vengono generate chiamate mouseDragged al posto di chiamate mouseMoved.

Modficare la forma del cursore


L’operazione che modifica la forma del cursore è eseguita dal metodo setCursor. Il metodo statico
getPredefinedCursor della classe Cursor prende come parametro una delle costanti relative ai
diversi cursori resi disponibili dalla classe Cursor:

DEFAULT_CURSOR CROSSHAIR_CURSOR HAND_CURSOR


WAIT_CURSOR TEXT_CURSOR MOVE_CURSOR

Esempio:
setCursor(Cursor.getPredefinedCursor(Cursos.CROSSHAIR_CURSOR)

In alternativa il metodo createCustomCursor della classe Toolkit permette di definire cursori


personalizzati. Il metodo prende in ingresso 3 parametri:
 Image img: immagine del cursore da visualizzare quando è attivo
 Point hotSpot: l’hotSpot del cursore. Esempio punta di una freccia o centro di una croce
 String name: descrizione testuale del cursore
Eventi focus

Quando si utilizza un mouse, è possibile puntare un oggetto qualsiasi sullo schermo, mentre quando
si digita, le combinazioni di tasti devono puntare un oggetto specifico sullo schermo. Il gestore delle
finestre, per esempio Windows, dirige tutte le combinazioni di tasti nella finestra attiva. La finestra
attiva si distingue dalla barra del titolo in evidenza. E’ possibile avere una sola finestra attiva. Si
supponga ora che la finestra attiva sia controllata da un programma Java. La finestra Java riceve le
combinazioni di tasti e a sua volta le dirige verso un particolare componente. Si dice che questo
componente ha un focus (o punto focale). La maggior parte dei componenti Swing segnala in modo
visuale la presenza del focus: una casella di testo ha un cursore lampeggiante, un pulsante mostra un
rettangolo attorno all’etichetta e così via.
In una finestra solo un componente può avere il focus. Un componente perde il focus se l’utente fa
click su un altro componente, che a sua volta guadagna il focus. L’utente può anche utilizzare il
tasto TAB per spostare il focus su ciascun componente.

 Alcuni componenti, le etichette o i pannelli, non acquisiscono il focus per default, perché si
assume che vengano utilizzati solo per le decorazioni o per raggruppare diversi elementi. E’
comunque possibile farglielo acquisire semplicemente tramite la chiamata del seguente
metodo:

panel.setFocusable(true);
Azioni
Spesso si hanno più modi per attivare lo stesso comando. L’utente può selezionare una determinata
funzione mediante un menu, una combinazione di tasti oppure un pulsante su una barra di
strumenti. Questa situazione è facile da realizzare nel modello di eventi AWT collegando tutti gli
eventi a uno stesso listener.
Il pacchetto Swing propone un meccanismo molto utile per incapsulare i comandi e per collegarli a
diverse sorgenti di evento: l’interfaccia Action. Un’ azione è un oggetto che incapsula:

 una descrizione del comando, ovvero una stringa di testo e una icona facoltativa

 i parametri necessari per eseguire il comando, per esempio un colore

La tabella seguente mostra i metodi dell’interfaccia Action:

Metodo Descrizione
È il consueto metodo dell’interfaccia
void actionPerformed(ActionEvent event) ActionListener poiché l’interfaccia
Action estende l’interfaccia
ActionListener.
Attiva/Disattiva l’azione. Quando
un’azione è collegata a un menu
void setEnable(boolean b) oppure a una barra degli strumenti ed
è disattivata, l’opzione corrispondente
appare in grigio.

boolean isDisable() Verifica se l’azione è attualmente


attiva.

void putValue(String key, Object value) Permettono di memorizzare e rilevare


coppie arbitrarie nome/valore
Object getValue(String key) nell’azione dell’oggetto.

void Permettono di modificare ad altri


addPropertyChangeListener(PropertyChangeListener oggetti, in particolare i menu o le
list) barre degli strumenti che
attivano/disattivano l’azione, ogni
void volta che cambiano le proprietà
removePropertyChangeListener(PropertyChangeListener dell’azione stessa.
list) Es. il menu diventa grigio.

Dato che Action è un’interfaccia, ogni classe che implementa questa interfaccia deve implementare
i 7 metodi appena descritti. Fortunatamente è stata messa a disposizione una classe AbstractAction
che implementa tutti i metodi a eccezione di actionPerformed. Questa classe si occupa della
memorizzazione di tutte le coppie nome-valore e di gestire i listener di modifica delle proprietà. E’
sufficiente estendere AbstractAction e fornire un metodo actionPerformed.
La tabella seguente mostra I nomi delle principali azioni predefinite:

Nome Valore
Il nome dell’azione visualizzato sui pulsanti e sulle voci
NAME dei menu.

Una posizione per memorizzare una piccola icona da


SMALL_ICON visualizzare in un pulsante, una voce di menu o una
barra degli strumenti.
Una breve descrizione dell’icona da visualizzare in un
SHORT_DESCRIPTION suggerimento.

Un’abbreviazione mnemonica da visualizzare nelle voci


MNEMONIC_KEY del menu. Permette di navigare nella gerarchia di un
menu.
ACCELLERATOR_KEY Memorizza una combinazione di tasti senza passare per
la gerarchia del menu (power user).

Per esempio, si consideri che ColorAction sia un listener di azioni il cui metodo actionPerformed
modifichi il colore dello sfondo. Colleghiamo quindi questo oggetto ai relativi pulsanti:

public class ColorAction extends AbstractAction {


public ColorAction(String name, Icon icon, Color c) {
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);
putValue(“color”, c);
putValue(Action.SHORT_DESCRIPTION, “Set panel color to “ +
name);
}
public void actionPerformed(ActionEvent event) {
Color c = (Color) getValue(“color”);
setBackground(c);
}
}

La creazione di oggetti di questa classe avviene come nel seguente esempio:

Action blueAction = new ActionColor(“blue”, new ImageIcon(“blu.gif”),


Color.BLUE);

A questo punto si può associare facilmente l’azione con un pulsante, dato che si può utilizzare un
costruttore JButton che rileva l’oggetto Action:

JButton blueButton = new JButton(blueAction);

Questo costruttore legge il nome e l’icona dell’azione, assegna la breve descrizione come
suggerimento e l’azione come listener.
Combinazioni di tasti
Adesso, vogliamo aggiungere gli oggetti di azione alle combinazioni di tasti in modo che le azioni
possano essere eseguite quando l’utente digita i comandi da tastiera. I progettisti di Swing hanno
proposto una soluzione comoda per risolvere tale problema.

 Per associare le azioni con le combinazioni di tasti è necessario prima generare gli oggetti
della classe KeyStroke, la quale incapsula la descrizione di un tasto.

 Per generare un oggetto KeyStroke non si deve chiamare un costruttore ma si utilizza il


metodo statico getKeyStroke della classe KeyStroke e si specifica una descrizione della
combinazione di tasti cioè una sequenza di stringhe delimitate da spazi

KeyStroke ctrlBKey = KeyStroke.getKeyStroke(“ctrl B”);

Ogni JComponent ha tre mappe di input, ciascuna delle quali mappa gli oggetti KeyStroke con le
azioni associate. Le mappe di input corrispondono a tre condizioni diverse, come mostra la tabella
seguente:

Flag Condizione che richiama l’azione


WHEN_FOCUSED Quando questo componente ha il focus
della tastiera.
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT Quando questo componente contiene il
componente che ha il focus della
tastiera.
Quando questo componente è contenuto
WHEN_INF_FOCUSED_WINDOW nella stessa finestra del componente che
ha il focus della tastiera.

Una mappa di input del componente si ricava con il metodo getInputMap; per esempio:

InputMap imap = panel.getInputMap(JComponent.WHEN_FOCUSED);

InputMap non mappa direttamente gli oggetti KeyStroke in oggetti Action. La mappatura avviene
in oggetti arbitrari e una seconda mappa, implementata dalla classe ActionMap, mappa questi
oggetti oggetti in Action. In questo modo è più facile condividere le stesse azioni tra combinazioni
di tasti che derivano da diverse mappe di input.
In definitiva, ogni componente ha tre mappe di input e una mappa di azioni. Per collegare tra
loro le mappe è necessario arrivare ai nomi delle azioni. Di seguito è indicato come è possibile
collegare un tasto a un’azione:

imap.put(KeyStroke.getKeyStroke(“ctrl Y”), “panel.yellow”);


ActionMap amap = panel.getActionMap();
amap.put(“panel.yellow”, yellowAction);
Riepilogo

Per riassumere, ecco ciò che si deve fare per eseguire una stessa azione in risposta a un pulsante ,
una voce di menu oppure una combinazione di tasti.

1. Implementare una classe che estende la classe AbstractAction. Si può utilizzare la stessa
classe per più azioni collegate.

2. Costruire un oggetto della classe di azioni.

3. Costruire un pulsante o una voce di menu dall’oggetto azione. Il costruttore legge il testo
dell’etichetta e l’icona dall’oggetto azione.

4. Nel caso di azioni che possono essere attivate da combinazioni di tasti, si devono impostare
dei passaggi ulteriori. Prima si individua il componente top-level della finestra, per esempio
un pannello che contiene tutti gli altri componenti.

5. In seguito acquisisce la mappa di input


WHEN_ANCESTOR_OF_FOCUSED_COMPONENT del componente top-level. Si
costruisce un oggetto KeyStroke per la combinazione di tasti che si vuole definire. Si
costruisce un oggetto che costituisce la chiave dell’azione, per esempio una stringa che la
descrive. Si aggiunge la coppia KeyStroke chiave dell’azione nella InputMap.

6. Infine si acquisisce la mappa di azioni del componente top-level e si aggiunge la coppia


(chiave dell’azione, oggetto azione) nella mappa.

Potrebbero piacerti anche