INTERFACCE GRAFICHE IN JAVA - III

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

Generalita' 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.

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'.

Metodi comuni a tutti i componenti

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.

Colori e font

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:
comp.setBorder(BorderFactory.createLineBorder(Color.black));
crea bordo a linea semplice, nero, senza titolo

b1 = BorderFactory.createLineBorder(Color.black);
b2 = BorderFactory.createTitledBorder(b1,"titolo",
             TitleBorder.CENTER,TitleBorder.BELOW_BOTTOM);
comp.setBorder(b2);
crea bordo come prima piu' titolo posizionato al centro sotto il riquadro incorniciato dal bordo.

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");

Dimensioni e allineamenti

Ogni componente ha tre dimensioni (minima, massima, preferita) di cui il layout manager del contenitore in cui la componente si trova puo' tenere conto.

Ogni componente ha anche due allineamenti (orizzontale, verticale) di cui il layout manager puo' tenere conto.

Queste dimensioni e allineamenti sono stabiliti dal sistema in modo generalmente sensato (es. per un bottone la dimensione preferita e' quella grande a sufficienza per mostrare tutta l'etichetta e non piu' grande del necessario).

Java AWT fornisce metodi solo per leggere queste dimensioni e allineamenti (nome che inizia per "get"), mentre non posso assegnarli esplicitamente.
Java Swing fornisce anche metodi per assegnarle (nome che inizia per "set").

Altri metodi relativi alle dimensioni:

Eventi comuni a tutti i componenti

Classi di eventi che qualsiasi componente puo' catturare: Per ciascuna di queste classi, 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.

Tutti i metodi dei listener hanno parametri come segue:

public void nomefunzione(XXXEvent e)
Posso chiamare su e i metodi della classe di evento XXXEvent per reperire informazioni che servono per reagire all'evento. Esempio:

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. Si e' ritenuto opportuno separare gli eventi di movimento del mouse, che comportano molto lavoro per gestirli (ogni movimento di un pixel genera un evento) e definire 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:

Finestre (contenitori) top-level

Classe Frame / JFrame

Finestra top-level con tutte le decorazioni del WM (window manager): bordo, barra del titolo, controlli (per chiusura, iconificazione ecc.). Serve come finestra primaria di un'applicazione.

Creazione

Frame myframe = new Frame();
Frame myframe = new Frame("titolo");
e uguale con JFrame.
Il titolo apparira' sulla barra del titolo aggiunta dal WM.

Barra di menu'

Un frame puo' avere una barra di menu' (classe MenuBar o JMenuBar). Per aggiungere la barra di menu' ad un frame:
mymenubar = new JMenuBar();
myframe.setMenubar(mymenubar);
Alla barra di menu' poi aggiungero' menu' (ved.
piu' avanti).

Contenuto di un frame

Oltre alla barra di menu' (se presente), un frame e' vuoto. Per riempirlo devo aggiungere componenti Istruzioni su aggiunta di componenti in un contenitore sono
piu' avanti.

Classe Dialog / JDialog

Finestra top-level dipendente funzionalmente da un'altra (tipicamente da un frame), intesa come finestra temporanea. Puo' avere tutte o un sottoinsieme delle decorazioni, dipende dal WM.
Usata come finestra secondaria di un'applicazione per mostrare informazioni e richiere input (finestra di dialogo).

Quando distruggo un frame, distruggo anche tutti i suoi dialoghi. Quando iconifico / deiconifico il frame, spariscono / riappaiono i suoi dialoghi. Questo comportamento e' gestito automaticamente dal sistema.

Creazione

mydialog = new Dialog(myframe);
mydialog = new Dialog(myframe, "titolo");
mydialog = new Dialog(myframe, true/false);
mydialog = new Dialog(myframe, "titolo", true/false);
e uguale con JDialog.

Crea dialogo dipendente da myframe (al posto di un frame potrei avere un altro dialogo). Il titolo apparira' sulla barra aggiunda da WM (se la aggiunge) alla finestra del dialogo. La costante true/false indica se il dialogo sara' modale o no (per default non e' modale).

Proprieta' dei dialoghi

Contenuto

Il dialogo creato e' vuoto. Per definirne il contenuto devo aggiungere componenti, nello stesso modo in cui si fa per i frame (cioe' diverso in AWT e Swing, ved.
sopra).

Dialoghi pronti all'uso

In Swing esistono option panels, tipi di pannelli predefiniti da mostrare dentro un dialogo, che contengono: Creo un JOptionPane e da questo un dialogo che lo contiene. Esempio:
JOptionPane myoption = new JOptionPane ("messaggio",
         JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION); 
JDialog mydialog = myoption.createDialog(myframe,"titolo");
mydialog.pack();
crea dialogo che dipende funzionalmente da myframe avente come pannello di contenuto myoption, che si presenta cosi':

I due bottoni presenti nel pannello hanno automaticamente l'effetto di chiudere la finestra di dialogo, quando premuti.

Classe JOptionPane ha metodi per mostrare dialoghi modali associati ad un frame: showMessageDialog (con un bottone), showOptionDialog (con numero di bottoni a piacere), ed altri.

Esempio 1:

JOptionPane.showMessageDialog(myframe, "messaggio", "titolo",
                              JOptionPane.PLAIN_MESSAGE);
Il messaggio e' mostrato nella finestra, il titolo apparira' sulla barra del titolo aggiunta dal WM (se sara' aggiunta). PLAIN_MESSAGE significa che non e' presente l'icona, alternative: WARNING_MESSAGE, ERROR_MESSAGE, INFORMATION_MESSAGE...

Esempio 2:

Object[] options = {"questa", "quella", "l'altra"};
int n = JOptionPane.showOptionDialog(myframe, "Quale delle tre?", "titolo",
        JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
        new ImageIcon("myicon.gif"), options, options[2]);
Mostra dialogo con tre bottoni (YES_NO_CANCEL_OPTION), le cui etichette sono le tre stringhe contenute in options, e l'opzione di default e' la terza, l'icona e' presa dal file myicon.gif:

Esistono anche dialoghi predefiniti che consentono immissione di una stringa.

Quale bottone e' stato azionato nel dialogo

Premere uno dei bottoni presenti sul dialogo ne provoca la chiusura.

I metodi ShowXXXDialog ritornano una costante indicante il bottone premuto: JDialog.OK_OPTION, JDialog.YES_OPTION, JDialog.NO_OPTION, JDialog.CANCEL_OPTION (se 3 bottoni). Oppure JDialog.CLOSE_OPTION se dialogo chiuso agendo sul bordo della finestra.

L'optione selezionata in un JOptionPane si legge con:
int n = (Integer)myoption.getValue().intValue();

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:

Metodi

Metodi utili comuni a tutte le finestre top-level: Prima di mappare una finestra, occorre sempre stabilirne la dimensione con setSize o pack.

Uso dei contenitori

Tutti i contenitori hanno un metodo che stabilisce il layout manager e un metodo che consente di aggiungere componenti nel contenitore.

Layout manager

Per default, il layout manager e' sempre FlowLayout.

Per cambiare il layout manager (prima di aggiungere componenti):

mycontainer.setLayout(mylayout);
Il metodo per creare il layout manager dipende dalla classe. Parametri comuni sono:

Classe FlowLayout

mylayout = new FlowLayout();
mylayout = new FlowLayout(allineamento);
mylayout = new FlowLayout(spazX,spazY);
mylayout = new FlowLayout(allineamento,spazX,spazY); 
dove l'allineamento puo' essere FlowLayout.LEFT, FlowLayout.RIGHT oppure FlowLayout.CENTER (componenti allineate a sinistra, a destra o centrate).

Classe GridLayout

mylayout = new GridLayout(numrighe,numcolonne);
mylayout = new GridLayout(numrighe,numcolonne,spazX,spazY);

Classe BorderLayout

        
mylayout = new BorderLayout();
mylayout = new BorderLayout(spazX,spazY);
Non vediamo GridBagLayout, la cui gestione e' piuttosto complicata.

Classe BoxLayout

Solo in Swing, organizza componenti in riga o colonna.
mylayout = new BoxLayout(contenitore,direzione);
Dove contenitore e' quello a cui verra' associato il layout manager, direzione puo' essere BoxLayout.X_AXIS oppure BoxLayout.Y_AXIS.
BoxLayout ha metodi particolari per controllo accurato di allineamento, spaziatura, dimensioni delle componenti al suo interno.

Aggiunta di componenti

Il metodo add della classe contenitore consente di aggiungere oggetti:
mycontainer.add(componente);
mycontainer.add(componente,posizione);
Se il contenitore ha layout manager FlowLayout o GridLayout (o BoxLayout), allora si usa la prima forma di add: Se il contenitore ha layout manager BorderLayout, allora si usa la seconda forma di add, dove la posizione e' una fra BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.EAST, BorderLayout.WEST, BorderLayout.CENTER
+--------------------------+
|          NORTH           |
+------+------------+------+
| EAST |   CENTER   | WEST |
+------+------------+------+
|          SOUTH           |      
+--------------------------+
Il contenitore e' diviso in 5 zone, ciascun componente va aggiunto ad una zona specifica, non mettere piu' di un componente per zona! E' permesso lasciare vuote alcune zone.

Suggerimenti al layout manager

Componenti possono dare suggerimenti al layout manager, ma l'ultima decisione spetta a questo.

Suggerimenti sono relativi a: dimensioni minime, massime, preferite, e allineamento.

Impacchettamento

I contenitori top-level hanno metodo pack, che assegna le dimensioni della finestra e di tutto il suo contenuto tenendo conto delle dimensioni preferite della finestra e del layout delle sue sottocomponenti.

Da chiamare dopo aver aggiunto tutte le componenti e sottocomponenti alla finestra e prima di mapparla.

Rimozione di componenti

Posso aggiungere e anche togliere componenti da un contenitore a run-time. Simmetrico al metodo add esiste metodo remove.

Alcune classi di contenitori intermedi

Pannelli a scomparto unico

Classe Panel / JPanel

Senza barre di scorrimento. Creazione:
mypanel = new Panel();
mypanel = new Panel(mylayout);
Analogo con JPanel. Se non specificato, il layout manager e' FlowLayout.

Classe ScrollPane / JScrollPane

Con barre di scorrimento. Consente di visualizzare (una parte alla volta) un contenuto piu' grande dell'area occupata sullo schermo dal pannello.

Creazione in AWT:

mypanel = new ScrollPane(); 
mypanel = new ScrollPane(scrolling);
Dove scrolling determina il criterio usato per decidere se ci sono le barre, e puo' essere uno tra ScrollPane.SCROLLBARS_ALWAYS (barre sempre), ScrollPane.SCROLLBARS_AS_NEEDED (solo se il contenuto e' piu' grande del contenitore), ScrollPane.SCROLLBARS_NEVER (mai).
Se non specificato, per default e' "as needed". La componente dentro lo scrolled pane si aggiunge con "add".

Creazione in Swing:

mypanel = new JScrollPane(); 
mypanel = new JScrollPane(comp);
mypanel = new JScrollPane(scrollY,scrollX);
mypanel = new JScrollPane(comp,scrollY,scrollX); 
Dove comp e' la componente da collocare all'interno del pannello. I parametri scrollY e scrollX controllano separatamente la presenza / assenza delle barre e possono valere JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS (e idem per HORIZONTAL).

Aggiungere la componente con "add" non sembra funzionare.

Importante impostare le dimensioni del pannello (con setPreferredSize), altrimenti per default il pannello si dimensiona grande quanto la componente contenuta al suo interno.

Pannelli bipartiti

Pannello bipartito con linea di divisione orizzontale o verticale.

Contiene due componenti dalle parti opposte del divisorio. L'utente puo' trascrinare col mouse il divisorio per stabilire quanto spazio va dato a ciascuna delle due parti.

Solo Swing:

mypanel = new JSplitPane();
mypanel = new JSplitPane(direzione);
mypanel = new JSplitPane(true/false);
mypanel = new JSplitPane(direzione,comp1,comp2);
mypanel = new JSplitPane(direzione,true/false,comp1,comp2);
La direzione e' una fra JSplitPane.HORIZONTAL_SPLIT (defalult) e JSplitPane.VERTICAL_SPLIT.
La costante booleana indica se il contenuto dei due scomparti viene ridisegnato in tempo reale mentre l'utente sposta il divisorio (per default no).
I parametri comp1, comp2 sono le due componenti da mettere nei due scomparti (la prima e' quella in alto / a sinistra).

Contenuto

Uno split pane puo' contenere solo due componenti e non ha bisogno di specificare layout manager.

Se non ho specificato le due componenti alla creazione, Posso aggiungerle dopo con add nella sua prima forma (la prima componente aggiunta e' quella in alto / a sinistra), oppure con setTopComponent(Component) e analogo con Bottom, Left, Right.

Posizione del divisorio

Posso cambiare la direzione del divisorio con setOrientation(direzione).

Per default il pannello determina la posizione del divisorio in base alle dimensioni preferite dei due componenti che contiene.

Per spostare il divisorio:

mypanel.setDividerLocation(percentuale);
mypanel.setDividerLocation(pixel);
Le posizioni ammissibili per il divisorio risentono delle dimensioni minime dei due componenti che contiene.

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, spaziatura.

Bottoni

Creazione

In AWT il creatore prende come argomento una stringa (etichetta da visualizzare).
In Swing prende una stringa, un'icona, oppure una stringa e un'icona.

Esempi in Swing:

mybutton = new JButton("titolo");
mybutton = new JButton("titolo",icona);
mybutton = new JButton(icona);
Stessa cosa si puo' fare con JCheckBox, con in piu' un ultimo argomento booleano opzionale che stabilisce se il bottone e' inizialmente selezionato o no (di default non e' selezionato).

La prima chiamata e' legale anche con Button o Checkbox (AWT).

Reazione a eventi

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);
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 int getStateChange() che ritorna uno fra ItemEvent.SELECTED e ItemEvent.DESELECTED.

Alcune funzioni utili su bottoni a due stati

Per selezionare / controllare se e' selezionato:

boolean isSelected() ritorna se componente selezionato
setSelected(boolean) forza selezione
boolean isSelected() ritorna true se componente 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();
b1 = new Checkbox("uno", cbg, true);
b2 = new Checkbox("due", cbg, false);
b3 = 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 eventi ActionEvent e ItemEvent.
Quando utente preme un bottone radio in un gruppo, si generano tre 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:
mybar = new MenuBar();
myframe.setMenuBar(mybar);
menu1 = new Menu("Primo menu'");
mybar.add(menu1);
menu2 = new Menu("Altro menu'");
mybar.add(menu2);
Analogamente si crea menu' pop-up:
mypop = 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:
public class PopFrame extends Frame 
{
    PopupMenu popup;

    public PopFrame()
    {
        Label label = new Label("Try bringing up a popup menu!");
        add(label);

        popup = new PopupMenu("A Popup Menu");
        add(popup);
        MenuItem mi1 = new MenuItem("aaa");
        popup.add(mi1);
        MenuItem mi2 = new MenuItem("bbb");
        popup.add(mi2);

        MouseListener listener = new PopupListener();
        addMouseListener(listener);
        label.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'.