Sei sulla pagina 1di 10

In Windows la gestione del layout non è una questione fondamentale: in primo luogo si utilizza un

editor di finestre di dialogo per trascinare e incollare i componenti sulla superficie, poi si utilizzano
gli strumenti dell’editor per allineare i componenti, disporli in modo uniforme, centrarli e così via.
Inoltre se si lavora in un grosso progetto non ci si deve probabilmente occupare del layout dei
componenti, in quanto questa attività viene svolta da un esperto progettista di interfacce utente.

Il problema di questo genere di approccio è che il layout finale deve essere aggiornamento
manualmente se si modifica la dimensione dei componenti. Un utente potrebbe scegliere un font
più grande per le etichette dei pulsanti o per il testo delle altre finestre di dialogo ma i pulsanti
non possono crescere e i font più grandi vanno a riempire lo spazio precedente. Ad esempio la
parola tedesca per “Cancel” è “Abbrenchen”. Se si progetta il pulsante in modo che abbia spazio a
sufficienza per “Cancel”, la versione tedesca risulta interrotta e la stringa comando sarà
incompleta.

Il motivo per cui in Windows i pulsanti non possono crescere dipende dal fatto che la finestra di
dialogo si ricorda solo la posizione e la dimensione in pixel di ogni componente, ma non ha
istruzioni in merito alla direzione nella quale devono crescere.

Grid bag layout


Il layout a griglia è utile per disporre i componenti in una griglia. Tuttavia, le righe e le colonne
della griglia hanno dimensioni identiche, il che non è molto utile nella pratica. Per superare i limiti
del layout a griglia, la libreria AWT mette a disposizione il grid bag layout, che imposta sempre i
componenti in righe e in colonne ma, a differenza del GridLayout, presenta i seguenti vantaggi:

 Le dimensioni sono flessibili (le celle non hanno tutte la medesima grandezza).

 Si possono unire celle adiacenti per fare spazio a componenti più grandi.

 I componenti non devono riempire l’intera area della cella

 Infine è possibile specificare l’allineamento dei componenti all’interno delle celle.

Questo gestore di layout è decisamente il più complesso anche se offre il vantaggio della massima
flessibilità.
 Consideriamo l’esempio FontDialog. Questa finestra è costituita dai seguenti componenti:

o Due caselle combinate per specificare l’aspetto e le dimensioni del font

o Etichette per queste due caselle combinate

o Due caselle di controllo per selezionare grassetto e corsivo

o Un’area di testo per la stringa di esempio

Per descrivere il layout del gestore grid bag si deve seguire la seguente procedura:

1. Si crea un oggetto di tipo GridBagLayout. Non si dice quante righe e colonne ci sono nella
griglia sottostante; il gestore di layout tenta di indovinarne il numero dalle informazioni
fornite più avanti.

2. Si importa questo oggetto GridBagLayout come gestore di layout per il contenitore.

3. Per ogni componente si crea un oggetto di tipo GridBagConstraints. Si impostano i valori


dei campi di questo oggetto in modo da specificare come disporre i componenti all’interno
della griglia bag.

4. Infine si aggiunge il componente con i vincoli impostati utilizzando la chiamata


add(component, constraints);

 Di seguito è riportato un esempio di codice necessario:

GridBagLayout layout = new GridBagLayout();


panel.setLayout(layout);
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 100;
constraints.weighty = 100;
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 2;
constraints.gridheight = 1;
I parametri gridx, gridy, gridwidth, gridheight

Questi vincoli definiscono le modalità di posizione del componente nella griglia:

 Gli attributi gridx e gridy indicano le posizioni di colonna e di riga (indici) dell’angolo
superiore sinistro del componente

 Gli attributi gridwidth e gridheight stabiliscono quante colonne e righe sono occupate dal
componente.

Pesi (weightx e weighty)

Si devono sempre impostare i pesi per ogni area di un grid bag layout. Se si imposta un peso 0
l’area non aumenta né riduce oltre la sua dimensione iniziale nella direzione indicata. Nel codice
d’esempio le etichette hanno pesi 0 e per questo hanno larghezza costante quando si
ridimensiona la finestra. Da un punto di vista teorico, il problema dei parametri di peso è legato al
fatto che i pesi sono proprietà delle righe e delle colonne, non delle singole celle. E’ necessario
specificarli in termini di celle perché il grid bag layout non espone le righe e le colonne. I pesi delle
righe e delle colonne vengono calcolati in base al massimo dei pesi di cella in ogni riga o colonna.

NB: in realtà il peso non fornisce le dimensioni relative delle colonne, bensì la percentuale di
spazio “libero” da allocare per ogni area se il contenitore supera la dimensione preferita.

I parametri fill e anchor

Se non si vuole che un componente si allarghi e riempia l’intera area (cella), si impostano i vincoli
fill. Questo parametro prevede quattro possibilità:

GridConstraints.NONE
GridConstraints.NORTH
GridConstraints.VERTICAL
GridConstraints.BOTH

Se il componente non riempie l’intera area, è possibile specificare la posizione della cella nella
quale si vuole impostare la casella anchor. I valori ammessi sono:

GridConstraints.CENTER (predefinito)
GridConstraints.NORTH
GridConstraints.NORTHEAST
GridConstraints.EAST

e così via.
Spaziatura esterna (external padding) e Spaziatura interna (internal padding)

E’ possibile circondare un componente con spazio vuoto aggiuntivo (external padding)


impostando la casella insets di GridBagConstraints. Si impostano i valori left, top, right e bottom
dell’oggetto Insets con la quantità di spazio che si vuole avere attorno al componente:

constraints.insets.left = 10;

Gli attributi ipadx e ipady si aggiungono alla larghezza e altezza minime del componente (internal
padding). In questo modo si garantisce che il componente non si riduca alla sua dimensione
minima.

constraints.ipadx = 10;

Metodo alternativo per specificare parametri gridx, gridy, gridwidth e gridheight

La documentazione AWT suggerisce che invece di impostare i valore gridx e gridy con posizioni
assolute si debbano impostare questi valori con la costante GridBagConstraints.RELATIVE. A
questo punto, si aggiungono i componenti secondo un ordine standard, da sinistra a destra nella
prima riga, poi spostandosi sulla riga successiva e così via.

Si deve ancora specificare il numero di righe e di colonne che si espandono, fornendo le caselle
gridheight e gridwidth appropriate. L’unica eccezione è quando il componente si estende fino
all’ultima riga o colonna, per cui non si richiede di indicare il numero effettivo ma la costante
GridBagConstraints.REMAINDER. In questo modo si dice al gestore di layout che il componente è
l’ultimo della riga.

Suggerrimenti: come costruire un grid bag layout.

1. Fare lo schizzo del layout del componente su un pezzo di carta individuando una griglia tale per
cui i componenti piccoli siano contenuti in una cella e quelli più grandi ne occupino di più.

2. Etichettare le righe e le colonne della griglia con 0, 1, 2, 3, . . . Ora si possono osservare i valori
gridx, gridy, gridwidth e gridheight delle singole righe e colonne.

3. Per ogni componente ci si deve chiedere se è necessario riempire (setFill) la sua cella in senso
orizzontale in verticale. Se non è necessario, quale allineamento (setAnchor) si vuole dare?

4. Si impostano tutti i pesi (weightx e weighty) a 100 tranne per quelle particolari righe o colonne
per le quali si vuole che rimangano nella propria dimensione predefinita.

5. Si scrive il codice, si compila, si esegue e si osservano i risultati.


Box Layout
Nel tentativo, infruttuoso, di progettare un gestore di layout che liberasse i programmatori dalla
tirannia del grid bag layout, i progettisti Swing hanno proposto un layout a casella. Questo layout
si limita a disporre una sequenza di componenti in senso orizzontale o verticale. Quando si
dispongono i componenti in senso orizzontale il layout a casella è simile al layout a flusso; tuttavia,
i componenti non seguono su una nuova riga quando la precedente è piena.

E’ presente anche un contenitore, la classe Box, il cui gestore di layout predefinito è BoxLayout, a
differenza della classe JPanel il cui gestore di layout predefinito è il FlowLayout. Inoltre la classe
Box contiene anche molti metodi statici che risultano utili per gestire i layout a casella.

 Per creare un nuovo contenitore con un layout a casella, si può semplicemente chiamare

Box b = Box.createHorizontalBox(); // o analogamente Box.createVerticalBox();

 A questo punto si aggiungono i componenti nel solito modo:

b.add(okButton);

In una casella orizzontale, i componenti vengono collocati da sinistra a destra, mentre in una
casella verticale vengono collocati dall’alto verso il basso.

Esaminiamo da vicino il layout orizzontale. Ogni componente prevede tre dimensioni:

1. La dimensione preferita: larghezza e altezza con le quali si vuole visualizzare il


componente.

okButton.setPreferredSize(new Dimension(..,..);

2. La dimensione massima: larghezza e altezza massima per il componente da visualizzare.

okButton.setMaximum Size(new Dimension(..,..);

3. La dimensione minima: larghezza e altezza minima per il componente da visualizzare.

okButton.setMinimumSize(new Dimension(..,..);
Di seguito sono riportati i dettagli che riguardano ciò che deve fare un gestore di layout a casella:

1. Calcola l’altezza massima del componente più alto.

2. Tenta di portare tutti i componenti a quell’altezza

3. Se un componente non si porta all’altezza richiesta, il suo allineamento verticale viene


assegnato mediante una chiamata del suo metodo getAlignmentY. Questo metodo
restituisce un numero con la virgola:

a. 0 allineamento in alto,

b. 1 allineamento in basso,

c. 0.5 allineamento al centro (valore predefinito nella classe Component)

4. Si ricava la dimensione preferita di ogni componente e si aggiungono tutte le larghezze


preferite.

5. Se la larghezza totale è inferiore alla larghezza della casella, i componenti vengono espansi
in quanto hanno la possibilità di crescere fino alla larghezza massima. Si collocano i
componenti, da sinistra a destra, senza spazi aggiuntivi tra un componente e il successivo.
Altrimenti Se la larghezza preferita totale è superiore della larghezza della casella, i
componenti vengono ridotti, se possibile fino alla larghezza minima ma non oltre. Se
ancora i componenti non si adattano alla larghezza minima, alcuni di questi non vengono
mostrati.
Riempitori

L’impostazione predefinita prevede che non ci sia spazio tra i componenti in un layout a casella.
Per spaziare i componenti si aggiungono dei riempitori (filler) invisibili. Ci sono tre tipi di
riempitori: montanti (strut), aree rigide(rigid area) e colla (glue).

Montante (strut): aggiunge semplicemente dello spazio tra i componenti; per esempio, di seguito
è mostrato come aggiungere 10 pixel di spazio tra i componenti in una casella orizzontale:

b.add(label);
b.add(Box.createHorizzontalStrut(10));
b.add(textField);

Per aggiungere spazio ai componenti si aggiunge un montante orizzontale in una casella


orizzontale, un montante verticale in una casella verticale. Se si aggiunge un montante verticale in
una casella orizzontale non si ha alcun effetto sul layout orizzontale; al contrario, ciò imposta
l’altezza minima della casella.

Area rigida (rigid area): è simile ad una coppia di montanti. Separa i componenti adiacenti ma
aggiunge anche un’altezza o larghezza minima nell’altra direzione alla casella. Si consideri il
seguente esempio:

b.add(Box.createRigidArea(new Dimension(5, 20));

Questa istruzione aggiunge un’area invisibile con una larghezza minima, preferita e massima di 5
pixel, un’altezza di 20 pixel e allineamento al centro. Se si aggiunge in una casella orizzontale,
funziona come montante di larghezza 5 che imposta anche un’altezza minima della casella di 20
pixel.

Colla (glue): separa i componenti per quanto possibile. La colla (invisibile) si espande per
consumare tutto lo spazio vuoto disponibile, allontanando i componenti tra loro. Di seguito si può
vedere come spaziare il più possibile due pulsanti in una casella:

b.add(button1);
b.add(Box.createGlue());
b.add(button2);

Un programma d’esempio: BoxLayoutTest

Il programma d’esempio BoxLayoutTest dispone un gruppo di etichette, casella di testo e pulsanti,


utilizzando un set di layout orizzontali e verticali. Ogni riga è collocata in un box orizzontale. I
montanti separano le etichette dalle caselle di testo, la colla allontana i due pulsanti l’uno
dall’altro. Le altre caselle orizzontali sono inserite in una casella verticale e la colla spinge la casella
del pulsante verso il fondo.
Spring Layout
Con lo spring layout si collegano delle “molle” (spring) a ogni componente. Una molla è un
dispositivo che permette di specificare le posizioni dei componenti e prevede:
 un valore minimo,
 un valore preferito,
 un valore massimo,
 un valore corrente (compreso tra il valore minimo e il valore massimo e il più vicino
possibile al valore preferito).

La classe Spring definisce un’operazione di somma che prende due molle e produce una nuova
molla che combina le caratteristiche delle singole molle. Quando si imposta un certo numero di
componenti in una riga, si collegano diverse molle in modo che la loro somma occupi l’intero
contenitore. Questa operazione deforma le singole molle. Viene impostato ogni valore delle molle
in modo che la deformazione di ogni molla coincida con la deformazione della somma. Impostati i
valori delle singole molle, il layout risulta determinato. Chi è interessato ai dettagli dei calcoli delle
estensioni, può approfondire lo studio della documentazione API relativa alla classe Spring.

Collegare le molle ai componenti

Si consideri ora un semplice esempio. Si vogliono impostare tre pulsanti in senso orizzontale:

Jbutton b1 = new JButton(“Yellow”);


Jbutton b2 = new JButton(“Yellow”);
Jbutton b3 = new JButton(“Yellow”);

Prima si imposta un oggetto SpringLayout come gestore del layout del frame e si aggiungono i
componenti:

panel.setLayout(new SpringLayout());
panel.add(b1);
panel.add(b2);
panel.add(b3);

A questo punto si costruisca una molla con una certa quantità di compressione. Il metodo statico
Spring.costant produce una molla con determinati valori minimo, preferito e massimo:

Spring spring = Spring.constant(0, 10000, 10000)

Ora si collega la molla dal bordo sinistro dal contenitore al bordo sinistro di b1, e a seguire si
collegano le altre molle per gli altri bottoni in maniera analoga:

layout.putConstraint(SpringLayout.WEST, b1, spring, SpringLayout.WEST, panel) ;

Il risultato è che le quattro molle vengono compresse alla stessa dimensione e i pulsanti vengono
spaziati in modo uniforme -> |~~~~b1~~~~b2~~~~b3~~~~|
In alternativa, è possibile modificare le spaziature. Si immagini di volere una distanza fissa tra i
componenti; si utilizza un montante (strut): una molla che non può essere espansa o compressa.

Si ricava una molla di questo genere con la versione a parametro singolo del metodo
Spring.constant:

Spring strut = Spring.constant(10);

Consideriamo l’esempio FontDialog, precedentemente trattato con il grid bag layout, e lavorare
utilizzando uno spring layout.I potizziamo di voler allineare i bordi sinistri delle due caselle
combinate, e quindi che entrambi i bordi sinistri inizino dopo l’etichetta più lunga (Font Face).

E’ possibile formare un massimo di due molle con il metodo Spring.max. Il risultato è una molla
lunga quanto la più lunga delle due molle.
 Si acquisisce il massimo dei due bordi destri come indicato di seguito:

Spring labelsEast = Spring.max(


layout.getConstraint(SpringLayout.EAST, faceLabel),
layout.getConstraint(SpringLayout.EAST, sizeLabel));

Si può osservare che il metodo getConstraint produce una molla che si porta dal bordo
sinistro del contenitore ai lati assegnati del componente.

 Si aggiunge un montante in modo che ci sia spazio tra le etichette e le caselle combinate.

Spring combosWest = Spring.sum(labelsEast, strut)

 Ora si può collegare questa molla al bordo sinistro di entrambe le caselle combinate. Il
punto iniziale coincide con l’inizio della molla labelsEast, cioè con l’inizio del contenitore:

layout.putConstraint(SpringLayout.WEST, face, comboWest, SpringLayout.West, panel);


layout.putConstraint(SpringLayout.WEST, size, comboWest, SpringLayout.West, panel);

Le due caselle combinate si allineano perché sono gestite dalla stessa molla.
Tuttavia c’è un piccolo difetto. Si preferirebbe avere le etichette allineate a destra. Si considerino
più dettagliatamente le molle orizzontali. Le molle verticali seguono la stessa logica.

Le molle orizzontali possono essere collegate in tre modi:

 Si collega il bordo sinistro del componente con il bordo sinistro del contenitore.

Spring west = layout.getConstraints(component).getX();

 Si percorre la larghezza del componente.

Spring width = layout.getConstraints(component).getWidth();

 Si collega il bordo destro del componente con il bordo sinistro del componente.

Spring east = layout.getConstraints(SpringLayout.EAST, component);

Ovviamemente le tre molle sono collegate tra loro: la somma di west e width deve coincidere con
east.

Il metodo getConstraints produce un oggetto di tipo SpringLayout.Constraints. Possiamo


immaginare questo oggetto come un rettangolo, a eccezione che i valori x e y, la larghezza e
altezza, sono molle, non numeri.
 Ad esempio è possibile ottenere la molla di sinistra anche nel seguente modo:

Spring west = layout.getConstraints(SpringLayout.WEST, component);

Quando si impostano per prima cosa i vincoli di un componente, la sua larghezza è definita con
una molla i cui parametri sono larghezza minima, preferita e massima del componente stesso.

L’esempio SpingLayoutTest mostra la finestra di dialogo dei font (l’esempio FontDialog) con le
molle. Se si esamina il codice si può convenire lo spring layout è un po’ meno intuitivo del grid bag
layout, ma si spera che un giorno verranno messi a disposizione strumenti che lo rendano più
semplice.

Potrebbero piacerti anche