Paola Magillo, Univestita' di Genova, Corso di Interfacce Utente per Informatica, a.a. 2002-2003.

INTERFACCE GRAFICHE IN JAVA - III

[Per corso su Java vedere http://java.sun.com/docs/books/tutorial/]

Gestione degli eventi in Java

Parte di AWT, usate anche in Swing.

Classi di eventi

Gli eventi sono classificati a seconda della loro causa scatenante, che puo' essere:

Classi di eventi hanno nomi del tipo XXXEvent dove XXX e' la causa. Es: MouseEvent...

Ogni classe di componente e' una potenziale sorgente di certe classi di eventi. Es: un bottone puo' originare eventi di classe "azionamento" (classe ActionEvent).

Event listeners

Posso registrare un event listener per ricevere eventi di una certa classe da una certa componente dell'interfaccia.

Vi e' un'interfaccia event listener per ogni classe di evento:

L'interfaccia listener prevede uno o piu' metodi che scattano quando il listener riceve un evento di quella classe.
Essendo un listener interfaccia e non classe, l'implenentazione dei metodi non e' definita.

Stabilita la classe di eventi che voglio catturare, devo creare una classe che implementi il listener corrispondente, fornendo il codice dei suoi metodi.
Poi creo un oggetto di tale classe e lo associo alla componente sulla quale voglio catturare gli eventi.

Esempio: voglio catturare azionamento di un bottone.

Questo paradigma si chiama delegation perche' il bottone delega l'azione da eseguire ad un altro oggetto (il listener).

Posso associare lo stesso event listener a piu' componenti nell'interfaccia: questi reagiranno allo stesso evento nello stesso modo.

Posso associare piu' event listener allo stesso oggetto: questo avra' piu' reazioni.

Una classe puo' implementare piu' interfacce listener. Potra' essere usata per dare la stessa reazione a componenti sensibili a tipi diversi di eventi.
Esempio (ExQuit.java): bottone quit e chiusura di finestra, entrambi terminano applicazione.

import java.awt.*;
import java.awt.event.*;

public class ExQuit extends Frame
{
    Button quitButton;

    public ExQuit()
    {
        quitButton = new Button("Click me to quit!");
        add(quitButton);
        QuitListener listener = new QuitListener();
        quitButton.addActionListener(listener);
        addWindowListener(listener);
        pack();
    }

    class QuitListener extends WindowAdapter implements ActionListener
    {
       public void actionPerformed(ActionEvent e) {  System.exit(0);  }
       public void windowClosing(WindowEvent e) {  System.exit(0);  }
    }

    public static void main(String[] args)
    {
        ExQuit app = new ExQuit();
        app.setVisible(true);
    }
}

Implementare un listener

Tutti i metodi dei listener hanno parametri come segue:

public void nomeFunzione(XXXEvent e)
Nel body della funzione posso chiamare su e i metodi della classe di evento XXXEvent per reperire informazioni che servono per reagire all'evento. Esempio:

Listener e adapter

Il fatto che i listeners siano interfacce e non classi (cioe' che non implementino i metodi) implica che per i listener che hanno piu' metodi devo fornire un'implementazione di tutti i metodi, inclusi quelli che non mi interessano (l'implementazione di questi non fara' nulla: {}).

Per comodita' sono forniti adapters che forniscono implementazione standard di un listener (dove tutti i metodi non fanno nulla): creo sottoclasse dell'adapter ridefinendo solo i metodi che mi interessano.

Esempio: La classe WindowEvent corrisponde a eventi sulle finestre top-level: apertura / chiusura, iconificazione / deiconificazione...
La classe WindowListener ha un metodo per ciascuno degli eventi di cui sopra. Se voglio reagire solo all'evento di chiusura:

Eventi comuni a tutti i componenti

Classi di eventi che qualsiasi componente puo' catturare:

Per ciascuna di queste classe, alla componente posso associare un listener.

Nota: nelle varie sotto-classi di componenti alcuni di questi eventi sono gestiti internamente. Esempio: un campo di input testuale reagisce a guadagno / perdita del focus e a battitura di caratteri da tastiera in modo autonomo.

Eventi di componente

ComponentListener prevede i seguenti metodi di reazione ad eventi:

Focus della tastiera

FocusListener prevede i seguenti metodi:

Input da tastiera

KeyListener prevede i seguenti metodi:

La pressione di un carattere stampabile fa scattare tutti e tre i metodi, quella di un tasto non stampabile fa scattare i primi due.
Al carattere stampabile si accede con e.getKeyChar() (dove e e' l'evento di classe KeyEvent) e al codice del carattere con e.getKeyCode().

Eventi da mouse

La classe di eventi MouseEvent ha due listener. Sono separati gli eventi di movimento del mouse, che comportano molto lavoro per gestirli (ogni movimento di un pixel genera un evento) ed e' previsto un listener a parte per questi.

MouseListener reagisce a tutti gli eventi tranne quelli di movimento, con i metodi:

Se e e' l'evento di classe MouseEvent, la posizione del mouse all'interno del sistema di coordinate della componente si ottiene con e.getX(), e.getY()(in pixel).
Il click del mouse scatena tre reazioni: pressione, rilascio, click.
Il numero di pressioni e rilasci contenuti nel click si legge con e.getClickCount() (click semplice, doppio...).

MouseMotionListener reagisce agli eventi di movimento del mouse, con i metodi:

Eventi di finestra

I contenitori top-level (frame e dialoghi) possono catturare eventi di classe WindowEvent.

Il WindowListener corrispondente prevede i seguenti metodi di reazione ad eventi:

Eventi sui vari tipi di dispositivi

A seconda della classe di dispositivo. Li vedremo man mano.

Alcune classi di dispositivi

Etichette

Classe Label, JLabel e' etichetta testuale in AWT, testuale e/o grafica in Swing.

Creazione in AWT:

Label label = new Label("stringa di testo");

Creazione in Swing:

ImageIcon icon = new ImageIcon("immagine.gif");
label1 = new JLabel("solo testo");
label2 = new JLabel("testo e immagine",icon); 
label3 = new JLabel(icon); 
Puo' esserci un ultimo argomento: l'allineamento, che puo' essere JLabel.LEFT, JLabel.RIGHT, JLabel.CENTER, JLabel.LEADING, JLabel.TRAILING.

Ci sono altri metodi per stabilire allineamento orizzontale / verticale dell'etichetta, spaziatura.

Bottone di comando

Classe Button / JButton.

Creazione (in Swing):

button = new JButton("titolo");
button = new JButton("titolo",icona);
button = new JButton(icona);
La prima chiamata e' legale anche con Button (in AWT).

L'esempio ExJButtons.java mostra tre tipi di bottoni java. Serve anche il file immagine buttonicon.gif.

Bottone di comando puo' catturare evento di azionamento ActionEvent: l'utente ha azionato il bottone, in che modo dipende dalla piattaforma (di solito col mouse).

Il corrispondente ActionListener ha un solo metodo actionPerformed che scatta quando il bottone e' azionato.

In ciascuna finestra top-level posso definire al piu' un solo bottone "di default" che e' sensibile anche alla pressione del tasto "return" (il sistema distingue graficamente tale bottone). In Swing:

finestratop.getRootPane().setDefaultButton(bottone);

Bottoni a due stati (non mutuamente esclusivi)

Bottone a due stati (selezionato o non selezionato) o check box: Checkbox / JCheckBox.

Creazione:

Bottone a due stati puo' catturare evento di cambio stato ItemEvent. In Swing anche evento di azionamento ActionEvent (gia' visto per bottone di comando).

ItemListener prevede un solo metodo ItemStateChanged, che scatta quando il bottone cambia stato (da selezionato a deselezionato o viceversa).

Il programma legge il nuovo stato con e.getStateChange(), dove e e' l'evento, che ritorna uno fra ItemEvent.SELECTED e ItemEvent.DESELECTED.

boolean isSelected() ritorna true se componente e' selezionato
setSelected(boolean) forza selezione

Gruppi di bottoni radio

Gruppo di bottoni radio e' un gruppo di bottoni a due stati dove in ogni istante un solo bottone puo' essere selezionato.

La pressione di un bottone del gruppo provoca automaticamente il rilascio di tutti gli altri bottoni del gruppo. Gruppi di radio-bottoni servono a implementare scelte mutuamente esclusive.

In AWT:

Esempio:

CheckboxGroup cbg = new CheckboxGroup();
add(new Checkbox("uno", cbg, true));
add(new Checkbox("due", cbg, false));
add(new Checkbox("tre", cbg, false));
L'ultimo argomento booleano stabilisce se il bottone e' inizialmente selezionato (di default non e' selezionato).

In Swing:

Esempio:

rb1 = new JRadioButton("uno",true);
rb2 = new JRadioButton("due");
rb3 = new JRadioButton("tre");
ButtonGroup bg = new ButtonGroup();
bg.add(rb1);
bg.add(rb2);
bg.add(rb3);

Radio button cattura stessi eventi di check box.

In Swing, quando utente preme un bottone radio in un gruppo, si generano tre eventi:

  1. ItemEvent sul bottone premuto: suo stato cambia da non selezionato a selezionato
  2. ItemEvent sul bottone che era selezionato in precedenza: suo stato cambia da selezionato a non selezionato
  3. ActionEvent sul bottone premuto
In AWT si genera solo il primo dei tre.

Esempio

L'esempio ExButtons.java mostra i tre tipi di bottoni (di comando, check box, radio).

Provare ad aggiungere gestione degli eventi.

Menu' e voci di menu'

Menu' permette scelta di un'opzione da una lista di voci che e' presentata come una tendina a scomparsa.

Creazione di menu'

Un menu' puo' essere:

Esempio di menu' inseriti in barra:

bar = new MenuBar();
frame.setMenuBar(bar);
menu1 = new Menu("Primo menu'");
bar.add(menu1);
menu2 = new Menu("Altro menu'");
bar.add(menu2);

Analogamente si crea menu' pop-up:

pop = new PopupMenu("Menu' pop-up");
Codice analogo nel caso Swing.

Definizione delle voci di menu'

Ogni voce del menu' (menu item) ha caratteristiche analoghe a un bottone. Le classi di voci di menu' rispecchiano le classi di bottoni: MenuItem, CheckboxMenuItem in AWT, JMenuItem, JCheckBoxMenuItem, JRadioButtonMenuItem in Swing.

Creo le varie voci separatamente e poi le aggiungo ad un Menu / JMenu. Un Menu / JMenu puo' essere usato come voce di un altro menu', realizzando in tal modo un menu' a cascata.

Esempio:

menu1 = new Menu("Primo menu'");
menu1.add (new MenuItem("voce semplice"));
menu1.add (new CheckboxMenuItem("voce a due stati"));
menu2 = new Menu("sotto menu'");
menu1.add (menu2);
menu2.add("una voce");  
menu2.add("altra voce");
Crea un menu' (menu1) con tre voci: una semplice, una a check box e una costituita da un sottomenu' (menu2).

In piu' in Swing posso mettere testo e/o icone nelle voci di menu':

item = JMenuItem(stringa);
item = JMenuItem(icona);
item = JMenuItem(stringa,icona);
Analogo per JCheckBoxMenuItem con in piu' ultimo argomento booleano (opzionale) che specifica se voce e' inizialmente selezionata o no.

In piu' in Swing ho classe JRadioBoxMenuItem per realizzare voci a due stati mutuamente esclusive.

Eventi nei menu'

Quelle che reagiscono a eventi sono le singole voci dei menu'.

Le voci che sono sottomenu' le gestisce automaticamente il sistema (la loro reazione e' mostrare la tendina del sottomenu').

Menu' pop-up

Per assegnare un menu' pop-up ad un componente, si associa a quel componente un MouseListener che faccia apparire il menu' pop-up quando l'utente aziona il mouse.
Esempio (
PopFrame.java): frame con associato menu' pop-up

public class PopFrame extends Frame 
{
    PopupMenu popup;

    public PopFrame()
    {
        popup = new PopupMenu("A Popup Menu");
        add(popup);
        MenuItem mi1 = new MenuItem("aaa");
        popup.add(mi1);
        MenuItem mi2 = new MenuItem("bbb");
        popup.add(mi2);
        setSize(new Dimension(200,100));

        MouseListener listener = new PopupListener();
        addMouseListener(listener);
    }

    class PopupListener extends MouseAdapter 
    {
        public void mousePressed(MouseEvent e) 
        {    maybeShowPopup(e);
        }

        public void mouseReleased(MouseEvent e) 
        {   maybeShowPopup(e);
        }

        private void maybeShowPopup(MouseEvent e) 
        {
            if (e.isPopupTrigger())
                popup.show(e.getComponent(), e.getX(), e.getY());
        }
    }
}

Keyboard alternatives

Scorciatoie da tastiera.

Esistono appositi metodi per associare mnemonici e acceleratori alle voci di menu'.

Metodi generali sui componenti Java

Tutte le classi componenti hanno in comune un insieme di metodi per leggere / assegnare certe proprieta' generali.

Tutte le classi componenti hanno inoltre in comune un insieme di eventi che possono catturare (visti prima).

Queste caratteristiche comuni sono definite nella classe base Component (in AWT) e JComponent (in Swing). Nota: la classe JComponent deriva per ereditarieta' da Component, per cui JComponent ha tutti i metodi di Component ed altri in piu'.

Visibilita'

Mappatura di un componente su schermo:

Componenti NON sono mappati automaticamente. Occorre mappare i componenti top-level (frame, dialoghi), il che provoca automaticamente mappatura di tutti i sotto-componenti.

Abilitazione

Per abilita' si intende capacita' di un componente ad interagire con l'utente.

Per difetto sono abilitati.

Font

Esempio: comp.setFont(new Font("Helvetica",Font.PLAIN,14)) assegna font Helvetica normale a dimensione 14 punti.

Colori

Esempio: comp.setBackground(new Color(0.6,0.3,1.0)) assegna colore di sfondo mediante le sue componenti Red Green Blue.
Classe Color ha anche costanti che denotano i colori piu' diffusi, es: Color.red,...

Bordi (solo Swing)

I bordi servono non solo come ornamento ma anche per raggruppare assieme dispositivi logicamente correlati, eventualmente aggiungendo un titolo.

Ci vari tipi di bordi, in un bordo si possono inserire titoli e/o grafica. Esempi:

Messaggi in sovra-impressione (solo Swing)

Riga di spiegazione a comparsa e scomparsa. Associabile a qualsiasi componente. Appare quando utente si ferma col mouse sopra la componente.

Esempio: comp.setToolTipText("Questo e' il messaggio");