|
I programmi in Java devono essere
indipendenti dalla risoluzione. Questo requisito, che è del
tutto naturale per un linguaggio multipiattaforma, ha un impatto sul
modo in cui sono scritti i programmi che utilizzano interfacce
grafiche. La libreria AWT ha introdotto la gestione geometrica delle
GUI: invece di disporre i componenti sulle form posizionandoli per
coordinate, si utilizzano i cosiddetti LayoutManager, che
posizionano i componenti dentro una form contenitore, e consentono di
ottenere interfacce grafiche ridimensionabili, che si autoadattano
alla dimensione del contenitore. Purtroppo gli ambienti Java della
prima generazione erano spesso un adattamento di ambienti
C++ per Windows, con editor visuali che generalmente non
permettevano di sfruttare visualmente i LayoutManager ma solo di
posizionare i componenti per coordinate. Per sviluppare codice
indipendente dalla risoluzione occorreva allora scrivere manualmente
il codice che descriveva l’interfaccia. JBuilder per fortuna di
utilizzare la gestione geometrica delle interfaccie, anche se prevede
un layout manager, l’XyLayout, che posiziona i componenti in un
contenitore per coordinate.
Facciamo dunque un breve riepilogo
della gestione geometrica. Il modello della AWT suddivide gli
elementi di interfaccia grafica in Component (come i Button,
i TexField eccetera) e Container; questi ultimi
possono essere finestre indipendenti (Frame, Window,
Dialog) oppure incorporati in altri container (Panel).
Un Container è un contenitore di componenti, che
dispone al suo interno utilizzando una classe accessoria
LayoutManager. Grazie ad essi un container può disporre
i suoi elementi in vari modi; quelli standard sono a griglia
(GridLayout), a croce (BorderLayout) oppure affiancati
(FlowLayout); ne esistono altri più sofisticati, ma
anche utilizzando solo questi si può fare molta strada. La
cosa importante è che i Container sono annidabili: per esempio
una finestra con un BorderLayout può contentere un
pannello che utilizza un FlowLayout e un altro con un
GridLayout. A loro volta i pannelli possono contenere altri
pannelli e così via. Questa struttura rende però
abbastanza difficile l’editing visuale. Non si tratta solo di
posizionare degli elementi su una griglia a certe coordinate: si
tratta si organizzare l’interfaccia in una gerarchia di
contenitori dentro altri contenitori.
Per facilitare l’editing,
Jbuilder mette a disposizione una rappresentazione ad albero, il
Component Tree, che viene visualizzata
contemporaneamente al preview dell’interfaccia risultante.

In Figura 3 è visibile un
esempio che dovrebbe chiarire il meccanismo. L’interfaccia
mostrata in figura è composta da un Frame con un
BorderLayout che contiene due pannelli, uno con un GridLayout
e l’altro con un FlowLayout. Questa struttura è
abbastanza comprensibile se si osserva la rappresentazione ad albero
in figura a sinistra. È importante notare che l’inserimento
dei componenti dell’interfaccia può essere fatto anche
cliccando sulla rappresentazione ad albero, e questo spesso è
un grande aiuto. Per esempio, per inserire un componente in un
determinato pannello occorre selezionare un componente dalla palette
e cliccare sul pannello dove lo si vuole inserire sull’albero.
Nonostanza sia disponibile l’albero
dei componenti, occorre stare attenti a non abusarne. Data la
particolare tecnica di composizione delle interfacce di Java, è
facile ritrovarsi con un albero di componenti molto annidato, che è
comunque difficoltoso da gestire. L’approccio migliore è
invece quello di suddividere i componenti di una form complessa in
varie form, editarli separatamente e poi assemblarli.

Per esempio in Figura 4 possiamo
vedere un TabsetPanel che contiene al suo interno diversi
pannelli, uno per ogni tab. Ogni pannello è editato come form
separata, creando una nuova classe basata su Panel invece che
su Frame. Il problema si pone quando si cerca di “assemblare”
il tutto. Siccome i nuovi componenti non sono disponibili
sulla palette, per inserire i propri pannelli si deve ricorrere
all’editing manuale del codice oppure all’aggiunta di un
nuovo componente in una toolbar. Mi sembra che il modo più
semplice sia il seguente.
Supponiamo di aver creato un
MovimentiPanel da Panel. Per inserirlo in un altro
contenitore, per esempio un TabsetPanel, la cosa migliore è
inserire un semplice Panel (rinominandolo per esempio
movimenti per trovarlo facilmente nel codice). Questo genera
gran parte del codice necessario per l’aggancio. A questo punto
si va su source, si cerca (control-F):
Panel movimenti
= new Panel();
e si cambia con:
MovimentiPanel
= new MovimentiPanel();
Tutto il resto del codice, che
inserisce il nuovo pannello al posto giusto (la parte più
noiosa da scrivere a mano) rimane valido. In alternativa si può
sempre andare su Tools | Configure (o cliccare col bottone
destro sulla toolbar e selezionare properties), e aggiungere
il nuovo pannello come componente alla toolbar, per poi inserirlo al
posto giusto con un semplice drag n drop.
Un problema frequente è quello
di produrre una decente “maschera” per l’input di
molti campi disposti in maniera regolare, problema tipico per le
schede di database, come quella visibile in Figura 4. La
soluzione ovvia è quello di utilizzare un GridLayout,
ma non va bene: questa soluzione ha il difetto di allargare i campi
di input alla intera larghezza della cella. Il risultato, orribile, è
che si ottengono campi di input che si allargano quando l’utente
ridimensiona la finestra invece di mantenere la loro dimensione.
Una soluzione migliore è invece
quella di utilizzare un GridLayout contentente dei FlowLayout
in ogni cella: in questo modo basta una cella per riga se si
sa come “allineare” i componenti contenuti in ogni cella.
Purtroppo le Label e i LabelControl inserite in un
FlowLayout vengono “schiacciate” alla loro
“preferred size”. Di conseguenza ogni label ha una
dimensione diversa a seconda del loro testo.
Cambiare la dimensione esplicitamente non risolve il problema, il
FlowLayout schiaccia sempre ogni componente alla
preferredSize. Questo rende impossibile allineare per
esempio Descrizione e Categoria come in Figura 4.
La migliore soluzione che ho trovato è ricorrere ad una classe
Label modificata come segue:
import java.awt.*;
public class FixedLabel extends Label {
public Dimension preferredSize() { return getSize(); }
}
Con questo trucco si riesce ad inserire in un FlowLayout delle Label
che mantengono la loro dimensione, operazione necessaria per riuscire
ad allineare semplicemente ed agevolmente le Label quando sono poste
in un FlowLayout. Tra l’altro la classe così modificata
può essere inserita nella toolbar. Sull’argomento
interfacce utenti si potrebbero dire molte altre cose, ma per il
momento ci fermiamo qui, per andare a esaminare l’aspetto
“accesso ai dati”.
|