Abstract
Nella progettazione di una user interface ben fatta, con un qualunque toolkit
non elementare, ha grande rilievo la conoscenza e l'uso in maniera adeguata
dei layout managers. In quest'articolo l'interesse non sarà rivolto ad un
solo toolkit in particolare. Gli esempi che verranno presentati saranno
realizzati sia con le Swing (il toolkit di Java 2) che con il GTK+
(uno fra i più famosi disponibili per il linguaggio C).
Data di stesura: 23/11/2002
Data di pubblicazione:
29/11/2002
Ultima modifica: 04/04/2006
Partiamo dal posizionamento fisso, ovvero la mancanza di un layout manager.
la disposizione dei componenti è determinata dalla posizione, in coordinate
relative alla finestra contenitore di ogni singolo widget (window gadget), e
dalle sue dimensioni.
Questo approccio comporta un layout bloccato e di difficile, o quanto
meno scomoda, gestione in caso di eventi di ridimensionamento della finestra
contenitrice.
Come possiamo vedere nella seguente illustrazione, un fixed positioning non
reagisce molto bene al cambiamento di dimensioni della dialog, questa è
un'operazione che arriva perfino a "mutilarne" il contenuto rendendo
inaccessibili alcuni elementi.
Figura 1: Esempi di posizionamento fisso in caso di ridimensionamento della finestra. In rosso è evidenziata la dimensione (fissa) per cui è stata progettata la finestra.
La progettazione più semplice, o naturale, per un utente alle prime armi
potrebbe sembrare quella a posizionamento fisso, poiché spesso, agli occhi
dello sviluppatore inesperto, i layout manager appaiono come entità
misteriose (forse perché sono - apparentemente - invisibili e complicati)
che operano in maniera "contorta" e spesso "capricciosa": "il bottone non
vuole stare giù!", "mi si allarga tutto!", "prima era li, ho spostato quello
ed è cambiato tutto!", ...
Il posizionamento fisso però si trascina una serie di svantaggi che già in
una UI (user interface) banale possono diventare critici. Primo fra tutti è
la necessità di bloccare le dimensioni della finestra per evitare di
incappare nelle problematiche sopra esposte. Ciò, in caso di risoluzioni
video elevate, impedisce di usare tutto lo spazio disponibile sullo schermo.
Il caso opposto è persino peggiore, quello cioè in cui le dimensioni dello
schermo sono nettamente inferiori a quelle adottate dall'applicazione (non
ridimensionabile).
Per evitare questi problemi si potrebbe ricorrere ad un ridimensionamento e
riposizionamento "al volo" di alcuni componenti ogni qualvolta le dimensioni
della finestra principale venissero alterate.
Ma il codice per fare questo è stato già scritto e possiamo usarlo
tranquillamente coi layout managers!
I Layout Managers sono griglie ordinate
O meglio, i layout managers sono algoritmi di ordinamento che nelle versioni
più comunemente utilizzate si occupano di distribuire dei widgets
all'interno di griglie.
I layout managers hanno proprio il compito di gestire tutti i componenti che
sono disposti sulla loro griglia in base anche a determinati suggerimenti che
il programmatore può passare loro (constraints).
I layout managers sono dei componenti basilari di una user interface, sono
fondamentalmente delle griglie con comportamenti predefiniti e più o meno
alterabili.
È possibile farne una sommaria classificazione prendendo come esempi quelli
disponibili in un paio di toolkits estremamente diffusi, le Swing di Java ed
il GTK+, utilizzabile dal linguaggio C ma anche da molti altri (Perl, Python,
...).
Tipologia
Layout Managers
Swing
GTK+
Griglie con tutte le celle di uguali dimensioni
GridLayout
HorizontalButtonBox, VerticalButtonBox
Griglie con celle di differenti dimensioni
BorderLayout, FlowLayout
HorizontalBox, VerticalBox
Tabelle
GridBagLayout
Table
Un uso tipico della prima classe di layout managers è sicuramente quello di
una barra di bottoni, permettendo di avere tutti gli elementi dimensionati in
base a quello al più grande fra questi.
Figura 2: GridLayout
Figura 3: FlowLayout
Sicuramente i bottoni principali di una dialog in Java sono disposti in
maniera ottimale usando un GridLayout, l'uso di un FlowLayout rischierebbe di
creare dominanze fra i bottoni contenenti descrizioni più lunghe finendo per
dare maggiore importanza visiva a operazioni che non necessariamente lo sono
anche in pratica.
Nella seconda riga di bottoni l'"Ok" è decisamente più
difficile da individuare e selezionare con il mouse degli altri in quanto
occupa un'area inferiore (fino al 50% più piccolo degli altri tre).
FlowLayout
Dispone i componenti lungo un flusso orizzontale (allineato a destra,
sinistra o centrato). Le dimensioni dei componenti non vengono
alterate, il layout manager si preoccupa di creare una nuova riga
subito sotto nel caso in cui non fosse possibile allocare spazio per
l'aggiunta di nuovi elementi alla propria sinistra.
GridLayout
È una griglia con celle tutte uguali fra di loro. Larghezza ed altezza
di ogni singola cella sono dipendenti dai componenti aventi maggiore
larghezza o altezza. Gli altri componenti vengono allargati in modo da
coprire tutto lo spazio della rispettiva cella.
Ovviamente nel caso di GTK+ cambiano i nomi ma la situazione è la stessa.
Figura 4: HorizontalButtonBox
Figura 5: HorizontalBox
Un esempio
Prenderemo ora in esame un paio di finestre d'esempio costruite con i
toolkits citati cercando analogie e differenze di comportamento.
Figura 6
Figura 7
Le due finestre realizzate per quest'esempio sono molto simili, ma la
gerarchia dei widgets è leggermente diversa:
Figura 8: GTK+
Figura 9: Swing
Una delle differenze più evidenti è il minor numero di componenti
necessarie per realizzare la stessa UI in GTK+.
In GTK+ non esiste una separazione netta fra contenitore e layout manager
come nelle Swing, bensì i layout managers (altresì noti come packers) sono
dei contenitori a tutti gli effetti.
Questo tuttavia dipende dalle scelte effettuate durante la
progettazione delle gerarchie di ereditarietà fra i vari widgets:
nelle Swing i layout managers sono derivati immediatamente da
java.lang.Object (non sono quindi componenti appartenenti agli elementi di UI
pur manipolandoli), mentre in GTK+ sono estensioni di GtkContainer.
I packers di GTK+ possiedono generalmente molte più opzioni
(constraints) per il layout di quante ne abbiano i layout "semplici" delle
Swing (BorderLayout, FlowLayout, GridLayout) finendo per somigliare più a
versioni ridotte del GridBagLayout.
Pro e contro per queste scelte ve ne sono per entrambi i toolkits, per
esempio avere più opzioni per un layout manager permette di ridurre il
numero di pannelli e layout managers accessori ma al contempo ne aumenta la
complessità d'uso.
Pensare e dividere il layout in blocchi (pannelli)
Una user interface va vista a strati (come le cipolle e gli orchi) quelli
più esterni conterranno gruppi di componenti e a loro volta ogni gruppo
potrà contenerne altri.
Figura 10
Pensando la finestra o il pannello principale come la base di una pila di
componenti, ogni livello inferiore conterrà l'opportuno layout manager per
disporre quelli immediatamente superiori e solo quelli poiché sarà compito
del livello superiore gestire quelli al livello a lui sovrastante, e così
via fino ad arrivare ai widgets.
Figura 11: Buttonbar in GTK+
L'HorizontalButtonBox ci permette di inserire dei bottoni equispaziati ed
equidimensionati, per allinearli a destra basta impostare il packing alla
fine del container (il lato destro).
Figura 12: Buttonbar con le Swing
Nel caso di Java serve un po' più di perizia per evitare di dover scomodare
il GridBagLayout. La button bar è realizzata con un pannello con un
GridLayout per garantire i bottoni tutti uguali, il pannello è ospitato
dentro un altro pannello ma con un FlowLayout allineato a destra.
Non bisogna aver paura di usare qualche pannello in più per disporre in
maniera opportuna dei widgets. Bisogna fondamentalmente tener presente il
comportamento dei singoli layout managers (non sono molti!), cercare quelli
che meglio si adattano a determinate problematiche ed eventualmente
combinarne più d'uno con sotto-pannelli ausiliari in modo da disporre le
aree nel migliore dei modi.
Conclusioni
In questo primo articolo di User Interface Design ho voluto evidenziare
quanto la conoscenza approfondita dei layout managers di un toolkit sia
estremamente importante per realizzare UI complesse nel migliore dei modi.
Vorrei inoltre sottolineare che:
la progettazione visuale con un GUI builder deve essere il passo
successivo ad una fase di analisi
Procedete quindi a realizzare la UI solo dopo aver steso almeno una bozza in
cui delineate le aree ed i layout managers in gioco per ognuna.
Costruire da zero una UI con un GUI builder senza avere le idee chiare sin
dall'inizio può solo portarvi, nel migliore dei casi, ad usare troppo
frequentemente le funzionalità di taglia e incolla!
Alcune delle critiche più frequenti all'uso di molte componenti "cosmetiche"
come pannelli e layout managers sono:
eccessiva complessità durante lo sviluppo e dell'insieme finale
spreco di memoria
La complessità è un fattore relativo, l'analisi preliminare servirà a
chiarire eventuali dubbi nella lettura a posteriori del codice.
Lo spreco di memoria è irrisorio, layout managers e pannelli sono componenti
"leggere", la memoria che viene realmente allocata per questi non è molta e
comunque non va mai considerata sprecata se risulta da una progettazione
ottimale e ben strutturata.
"Divide et impera" è molto spesso il modus operandi ottimale per la
soluzione di molti problemi complessi, non solo di UI.
Informazioni sull'autore
Marco Lamberto, laureato in Informatica presso la Statale di Milano, con diversi anni di esperienza sistemistica, di sicurezza e sviluppo prevalentemente in ambienti UNIX (Linux in primis) con linguaggi come C, Java, Perl, PHP, XML, HTML, ...