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

INTERFACCE GRAFICHE IN JAVA - II

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

Componenti Java

Una interfaccia e' fatta da tre tipi di componenti:

Tutti questi elementi (finestre top-level, contenitori, dispositivi) in Java sono detti componenti e sono classi che ereditano dalla classe base Component (oppure MenuComponent) in AWT, e da JComponent in Swing.

Le finestre top-level sono anch'esse contenitori, detti contenitori top-level. Un contenitore puo' contenere dispositivi o altri contenitori. Un contenitore intermedio e' contenuto in un altro contenitore, che puo' essere top-level o intermedio.

Ogni contenitore ha un layout manager che stabilisce in che modo gli oggetti devono essere dislocati al suo interno.

Gerarchia di contenimento

Descritta da foresta di alberi dove la relazione padre-figlio rispecchia la relazione di contenimento.
Tanti alberi quante le finestre top-level dell'interfaccia (es: 2 alberi se ho una finestra principale e un dialogo).

Nota bene: La "gerarchia" di contenimento rispecchia la gerarchia di annidamento tra le finestre che realizzano le varie componenti. Non ha niente a che vedere con la "gerarchia" delle classi nel paradigma object-oriented.

Contenitori Top-level

Realizzati come finestre top-level nell'ambiente in cui sara' eseguito il programma Java.

Contenitori intermedi

Contenitori di uso generale:

Contenitori per usi speciali:

Dispositivi in Java

Vari tipi di bottoni:

Vari tipi di menu':

E vari tipi di voci di menu', che rispecchiano i tipi di bottoni:

Altri dispositivi di controllo:

Dispositivi per mostrare informazioni, non sensibili a input:

Dispositivi per mostrare / acquisire informazioni formattate (eventualmente editabili):

Finestre (contenitori) top-level

Classe Frame / JFrame

Finestra top-level di tipo adatto ad essere usata come finestra primaria di applicazione.
Verra' mostrata con tutte le decorazioni del WM (window manager): bordo, barra del titolo, controlli (per chiusura, iconificazione ecc.).

Creare un frame (vediamo Frame, e' uguale con JFrame):

Il titolo apparira' sulla barra del titolo aggiunta dal WM. Viene creato vuoto, gli dovro' aggiungere componenti (dispositivi con eventuali contenitori intermedi).

Un frame puo' avere una barra di menu' (classe MenuBar o JMenuBar). Aggiungere la barra di menu' ad un frame:

Alla barra di menu' poi aggiungero' menu'...

Classe Dialog / JDialog

Finestra top-level di tipo adatto ad essere usata come finestra di dialogo (finestra secondaria di un'applicazione che serve per mostrare informazioni e richiere input).
E' intesa come finestra temporanea. Puo' avere tutte o un sottoinsieme delle decorazioni, dipende dal WM.

Dipende funzionalmente da un'altra (tipicamente da un frame).
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.

Creare un dialogo dipendente da una finestra t (vediamo Dialog, e' uguale con JDialog):

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 di un frame o di un dialogo

Un frame o dialogo quando viene creato e' vuoto. Per riempirlo devo aggiungere componenti.
Prima devo aver stabilito il layout manager, che derermina come posizionare le componenti aggiunte.

Per quanto riguarda i dialoghi, esistono anche dialoghi pronti all'uso, con contenuto gia' predisposto.

Dimensioni

Assegnare dimensioni specificate:

Questa istruzione e' applicabile a tutte le classi di componenti.

Assegnare dimensioni minime sufficienti a mostrare tutto il contenuto della finestra:

Questa istruzione vale solo per contenitori top-level, ed e' in genere preferibile. Va chiamata dopo aver aggiunto tutti i componenti.

Mappatura

Mappare / demappare finestra top-level su schermo:

Prima di mapparla devo averne stabilite le dimensioni con setSize o pack. Altrimenti le dimensioni saranno nulle!

Layout Management

Processo di stabilire automaticamente dimensioni e posizione delle componenti all'interno di un contenitore. Ogni contenitore (sia top-level che intermedio) ha un layout manager.

Potrei lavorare senza layout manager, ma allora dovrei fornire posizione assoluta di ogni componente all'interno del contenitore, ed avrei problemi quando il contenitore top-level viene redimensionato dall'utente.

Tutti i contenitori hanno un metodo per stabilire il layout manager:

c.setLayout(layout);

Tutti i contenitori hanno un metodo per aggiungere componenti nel contenitore:

c.add(componente);
c.add(componente,posizione);
A seconda del tipo di layout manager presente, va usata l'una o l'altra forma del metodo add.

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

Classi di layout manager

FlowLayout

I componenti sono messi uno dopo l'altro orizzontalmente finche' ci stanno, poi si va a capo e si forma un'altra linea, ecc.

layout = new FlowLayout();
layout = new FlowLayout(allineamento);
layout = new FlowLayout(spazX,spazY);
layout = new FlowLayout(allineamento,spazX,spazY); 
L'allineamento puo' essere FlowLayout.LEFT, FlowLayout.RIGHT oppure FlowLayout.CENTER (componenti allineate a sinistra, a destra o centrate).
Le due spaziature stabiliscono il numero di pixel inseriti fra due componenti contigue in orizzontale e in verticale.

Si usa la forma di add senza posizione, le componenti sono messe in fila nello stesso ordine in cui sono aggiunte.

BorderLayout

Il contenitore e' diviso in 5 zone:

+--------------------------+
|          NORTH           |
+------+------------+------+
| EAST |   CENTER   | WEST |
+------+------------+------+
|          SOUTH           |      
+--------------------------+

layout = new BorderLayout();
layout = new BorderLayout(spazX,spazY);

Si usa la forma di add con la posizione, che e' una fra BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.EAST, BorderLayout.WEST, BorderLayout.CENTER
Il componente viene aggiunto a quella specificata tra le 5 zone, non mettere piu' di un componente per zona! E' permesso lasciare vuote alcune zone.

GridLayout

Il contenitore e' organizzato a tabella con un certo numero di righe e colonne.

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

Si usa la forma di add senza posizione, le componenti sono prese nell'ordine e vanno a riempire la griglia riga dopo riga.

+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
| 5 | 6 | 7 | 8 |
+---+---+---+---+

Esempio

FlowLayout, BorderLayout, GridLayout sono i piu' usati. FlowLayout e' quello di default.

L'esempio ExLayout.java li mostra in azione. Il programma accetta Flow, Border, o Grid come opzioni da command line. Crea 5 etichette con sfondo di colori diversi e le dispone secondo il layout specificato.

Inizialmente impacchetta la finestra nel minimo spazio sufficiente. Quando l'utente cambia le dimensioni alla finestra, il layout manager ne riadatta il contenuto.

Altri layout manager

Impacchettamento

I contenitori top-level hanno metodo pack, che assegna le dimensioni della finestra e di tutto il suo contenuto secondo la gerarchia di contenimento.

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

Usa i layout manager di tutti i contenitori presenti nella gerarchia di contenimento che ha per radice la mia finestra top-level.

Dimensioni e allineamenti

Componenti possono dare suggerimenti al layout manager, ma l'ultima decisione spetta a questo.
Suggerimenti sono relativi a: dimensioni e allineamento.

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 hanno valori di default (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. Per assegnarli l'unico modo e' ridefinire la funzione "get" corrispondente.
Java Swing fornisce anche metodi per assegnarle (nome che inizia per "set").

Altri metodi relativi alle dimensioni:

Alcune classi di contenitori intermedi

Pannelli a scomparto unico senza barre di scorrimento

Classe Panel / JPanel.

panel = new Panel();
panel = new Panel(layout);
Se non specificato, il layout manager e' FlowLayout.

Pannelli a scomparto unico con barre di scorrimento

Classe ScrollPane / JScrollPane.
Consente di visualizzare (una parte alla volta) un contenuto piu' grande dell'area occupata sullo schermo dal pannello.

In AWT:

panel = new ScrollPane(); 
panel = new ScrollPane(scrolling);

Il criterio usato per decidere se mostrare le barre (parametro scrolling) puo' essere uno tra:

La componente contenuta si aggiunge con "add".

In Swing:

panel = new JScrollPane(); 
panel = new JScrollPane(comp);
panel = new JScrollPane(scrollY,scrollX);
panel = new JScrollPane(comp,scrollY,scrollX); 
comp e' la componente da collocare all'interno del pannello.
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:

panel = new JSplitPane();
panel = new JSplitPane(direzione);
panel = new JSplitPane(true/false);
panel = new JSplitPane(direzione,comp1,comp2);
panel = 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).

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 forma senza specifica della posizione (la prima componente aggiunta e' quella in alto / a sinistra), oppure con setTopComponent(Component) e analogo con Bottom, Left, Right.

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:

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

Esercizio

Creare finestra con barra di menu', un'area di testo grande centrale provvista di barre di scorrimento, un'area messaggi in fondo, una fila di bottoni sotto la barra di menu', una lista (che potrebbe essere l'indice dei documenti consultabili nell'area centrale) lateralmente a sinistra, sempre a sinistra, sotto la lista una maschera per la ricerca.

Posso usare: