MokaByte Numero  39  - Marzo 2000
Da AWT a Swing
di 
Michele Sciabarrà
Alcune considerazioni per convertire le vecchie interfacce grafiche fatte  con AWT in qualcosa di più moderno

Dopo aver tanto faticato a scrivere il nostro programma GUI in Java con AWT ecco che il rilascio di Swing sembra costringerci a rifare tutto da capo... 
Ma forse non tutto è  perduto: vediamo cosa dobbiamo conoscere per migrare facilmente da AWT a Swing

Introduzione
Non era facilmente prevedibile, anche se forse lo si poteva intuire, che il linguaggio introdotto col Netscape 2.0 per visualizzare animazioni, Java, potesse diventare un giorno addirittura un concorrente di Windows. La portabilità del linguaggio e le basi, ben fondate, che ne garantiscono l'indipendenza dal sistema operative hanno permesso di ampliare notevolmente le librerie standard del linguaggio. Oggi la libreria di Java è così ampia e completa da offrire l'opportunità di sviluppare applicazioni complesse senza ricorrere in alcun modo a chiamate dirette del sistema operativo. Naturalmente nessuno realmente pensa che Java potrà sostituire Windows, Unix o altri sistemi operativi: infatti in ogni caso la libreria Java sfrutta il sistema ospite. Ciò nonostante Java sembra destinato a diventare il fondamento di una nuova generazione di applicativi che girano indifferentemente su una ampia gamma di sistemi operativi mantenendo la portabilità a livello di codice eseguibile. Ormai il linguaggio e la sua libreria sembrano aver raggiunto una maturità sufficiente per consentire lo sviluppo di applicazioni professionali e complete. Fino alla versione 1.1, la principale limitazione della libreria di Java riguardava l'interfaccia grafica, limitazione che è stata definitivamente superata col rilascio della Java Foundation Classes di cui parliamo in questo articolo. Ci limitiamo a ricordare che la risposta di Microsoft non si è fatta attendere: le Window Foundation Classes per Java, una complessa e ricca libreria per Java, basata però su Win32. In ogni caso la JFC comprende varie API: noi ci occuperemo in particolare della libreria Swing che è un sottoinsieme della JFC.
 
 
 

Motivazioni della Swing
Sun ha gradualmente arricchito la libreria Java, portandola dai livelli minimali della versione 1.0, alla ricchezza e complessità della versione 1.1. Ciò nonostante, per quanto riguarda l'interfaccia grafica la 1.1 ha ancora soltanto l'Abrastact Window Toolkit. Il difetto principale di questa libreria è quello di essere piuttosto povera: le interfacce grafiche costruite con la AWT infatti risultano scarne e brutte, paragonabili come estetica e funzionalità addirittura alla versione 2.0 di Windows. La ragione di questa povertà è da ricercare nelle scelte di progetto della AWT. Tale libreria infatti si appoggia pesantemente al sistema operativo ospite, utilizzando i componenti di interfaccia grafica già presenti nel sistema. Per questo motivo la AWT segue la logica del "minimo comune multiplo", e fornisce al programmatore solo quei componenti di interfaccia grafica che sono comuni ai vari sistemi operativi che la AWT supporta, ovvero Windows, MacOS e Unix/Motif. Così abbiamo le MenuBar, ma non abbiamo, per esempio, le ToolBar che non sono "di sistema" sotto Unix o Mac. Quindi mantenendo questa impostazione, per garantire la portabilità delle applicazioni tra i vari sistemi, di meglio non si può fare.
Naturalmente gli utenti Java non sono affatto soddisfatti di questo stato di cose, e in effetti il fatto che i programmi in Java non sono stati nè particolarmente gradevoli all'occhio nè efficienti nell'interazione e ricchi di funzionalità dal punto di vista dell'utente, sono stati un chiaro freno alla diffusione di Java. Un programma Java sfigura ampiamente confrontato ad un programma nativo di Windows o Mac.
 
 
 

Caratteristiche della Swing
Per rendere i programmi in Java ricchi dal punto di vista dell'interfaccia utente occorre rilasciare alcuni vincoli della AWT. Innanzitutto non ci si deve più appoggiare al sistema sottostante per costruire l'interfaccia utente: Java deve fare da sé. I componenti AWT sono definiti "heavyweight" (pesanti) proprio perché creano un "peer" (un aggancio al sistema sottostante) per ciascun componente. È il sistema operativo ospite che fornisce, per esempio, il bottone o la listbox. Nella versione 1.1 sono stati introdotti dei componenti "lightweight" (leggeri) che non creano "peer": questi nuovi componenti hanno in tutto e per tutto le proprietà degli altri componenti: in particolare sono in grado di gestire gli eventi, l'aspetto cruciale di una interfaccia grafica. Tuttavia questi componenti hanno la caratteristica di essere indipendenti dai componenti di interfaccia forniti dal sistema sottostante. I componenti lightweight sono disegnati interamente dal runtime di Java, e hanno numerose e interessanti proprietà: per esempio possono essere in parte trasparenti, o avere una forma non rettangolare (a differenza degli equivalenti heavyweight). Comunque non ci siano differenze sostanziali , dal punto di vista dell'utente, tra i due tipi: i componenti generati da Java sono indistinguibili da quelli generati dal sistema ospite.
I componenti lightweight sono dunque il fondamento per costruire nuovi componenti di interfaccia che il sistema operativo sottostante non mette a disposizione. Così facendo si può costruire una libreria di componenti, ricchi e sofisticati quanto si vuole, senza dover più sottostare ad eventuali restrizioni del sistema operativo ospite.
La versione 1.2 del JDK di Java ha nella Swing la sua più importante evoluzione: si tratta di una libreria di componenti grafici molto ricca e sofisticate, comprendente toolbar, tabbed pane, tooltip e tutti gli elementi presenti nelle interfaccie più moderni. Questi componenti sono disponibil anche su piattaforme che non li prevedono, o dove quelli presenti si comportano in maniera diversa. Come è facile immaginare, tutti i componenti della Swing sono lightweight e sono generati da Java: il sistema operativo ospite non è più chiamato in causa per quanto riguarda l'interfaccia utente. Chiaramente l'intento è quello di arrivare a programmi in Java che siano addirittura più accattivanti dal punto di vista dell'interfaccia utente rispetto a quelli nativi del sistema operativo.
Comunque c'è un problema: un aspetto fondamentale della AWT finora è stata la sua adattabilità al sistema: le interfacce grafiche hanno comunque un look and feel che è quello nativo del sistema ospite. Un utente Windows vede i bottoni tipici di Windows, e un utente Mac quelli tipici del Mac. Se la Swing disegna da sè i propri componenti di interfaccia, quale look and feel dovranno avere? Il problema non è di semplice soluzione: se le interfaccie non hanno l'aspetto a cui l'utente è abituato, quest'ultimo potrebbe non accettarle sentendo i programmi in Java come "estranei" al suo sistema. Il fatto che i programmi Microsoft hanno tutti un look and feel comune, per di più simile al look and feel nativo di Windows è stato uno dei motivi dello straordinario successo dei programmi Microsoft.
La soluzione scelta dai progettisti della Swing è stata quella di dotare la nuova libreria grafica di Java di un "pluggable look-and-feel". La Swing non definisce un look and feel dei suoi componenti: lascia la scelta all'utente. Ogni componente è astratto, e l'aspetto finale che avrà quando viene disegnato viene deciso da un UIFactory, la "fabbrica" dell'interfaccia utente. La Swing fornisce 3 look and feel: il look Windows, il look Motif e il look Metal. I primi due corrispondono ai look and feel familiari agli utenti Windows e Unix, mentre l'ultimo è un look multiplatform, ovvero il look and feel che sarà quello nativo della Java Platform. L'utente può quindi decidere se vuole che i propri programmi abbiano il look tipico del sistema a cui è abituato o vuole invece adottare il look standard dei programmi Java. In realtà è possibile aggiungere nuovi look and feel. In teoria ogni ditta può personalizzare i propri programmi creandosi un suo look and feel particolare e aggiungendolo a quelli disponibili.
 
 

FIGURA 1 Esempio di look-and-feel interscambiabile

In Figura 1 possiamo vedere le caratteristiche multi look delle Swing in azione. Si tratta di tre schermate di uno stesso programma che cambia dinamicamente il look and feel selezionando un radiobutton. Notare come al variare del look cambino dettagli come l'aspetto dei bottoni, il colore dello sfondo, il font utilizzato per le label , il cursore dell'elemento selezionato corrente.
I componenti forniti dalla JFC sono numerosi, e coprono praticamente tutte le esigenze di interfaccia grafica a cui ci hanno abituato i programmi più recenti. Descriverli tutti esula dai limiti di spazio riservati a questo articolo per cui ci limiteremo a descrivere le principali innovazioni per categorie; entreremo in dettaglio soltanto per descrivere le caratteristiche tecniche più interessanti per il programmatore.
 
 

Principali componenti Swing

Ci riferiremo ai vari componenti chiamandoli con il nome che viene dato loro nella Swing. Una caratteristica importante è che tutti i componenti di interfaccia grafica della Swing hanno un nome che comincia per J. La ragione tecnica di questa scelta è di distinguere i componenti Swing da quelli AWT (che hanno molto spesso lo stesso nome), e consentire una semplice migrazione dalla AWT. Ogni sforzo è stato fatto perché ogni nuovo componente della Swing corrisponda ad un componente esistente nella AWT, e perché esso si comporti allo stesso modo. In questo modo avendo una applicazione AWT è possibile convertirla in applicazione Swing semplicemente rinominando i componenti per aggiungervi una J in testa.
 

Figura 2:  i molti aspetti dei bottoni Swing

Entriamo ora in dettaglio e descriviamo i principali componenti Swing. Le label, innanzitutto, sono stringhe di testo che vengono inserite nelle interfacce utente. Logicamente sono relate ai bottoni e ne condividono molte caratteristiche: in un certo senso una label è un bottone che non può essere cliccato. Sono disponibili quattro tipi di bottoni: i tradizionali bottoni, i checkbox (bottoni selezionabili e deselezionabili), i radiobutton (bottoni che agiscono in gruppo, in modo che selezionandone uno si deseleziona automaticamente qualunque altro bottone del gruppo) e i nuovi bottoni "toggle", che assumono e mantengono un nuovo stato ogni volta che vengono premuti. A differenza della AWT tutti i bottoni e le label Swing hanno caratteristiche grafiche avanzate: innanzitutto ciascuno di questi componenti può visualizzare sia una stringa di testo che una immagine (correttamente chiamata icona). Le icone possono essere inserite prima o dopo il testo, ed è possibile impostare l'allineamento della icona rispetto al testo in orizzontale e in verticale. Le icone sono gestite dalla classe JIcon, che supplisce alle limitazioni della classe Image usata nella AWT per gestire le immagini. La classe Image ha un uso abbastanza complesso (occorre gestire gli eventi per disegnarle) e una procedura di caricamento piuttosto fastidiosa. Tutte queste complessità vengono risolte da JIcon, che è di uso semplicissimo: il programmatore deve soltanto specificare una proprietà che indica il file da cui deve venire caricata l'icona. Altra innovazione importante è l'introduzione degli "acceleratori" di tastiera, ovvero i tasti che possono essere premuti per attivare il bottone senza ricorrere al mouse (e che vengono visualizzati come una lettera sottolineata); naturalmente si può definire un acceleratore per ogni tipo di bottone.
Esaminiamo adesso i menù della Swing. Innanzitutto è possibile inserirli in qualsiasi componente Swing, mentre in AWT si potevano legare soltanto ad un Frame. Come si vede in figura 3, possiamo incorporare un menu all'interno di una finestra. 
 
 
 
 

Figura 3 I menù della Swing possono essere inseriti anche all'interno

I menu Swing sono notevolmente più potenti di quelli AWT: è possibile associare immagini ad ogni voce di menù (concettualmente i JMenuItem sono pure dei bottoni, quindi hanno le stesse proprietà relative all'uso di icone) e gli acceleratori di tastiera. Sono state aggiunte (finalmente!) le toolbar, che sono forse una della più sentite mancanze della AWT tanto sono comuni nelle GUI moderne. Nelle toolbar possono essere inseriti componenti di qualunque tipo (normalmente bottoni ma anche campi di input o liste di selezione a discesa) e se la interfaccia utente sottostante la supporta, possono essere fluttuanti, ovvero sganciabili e posizionabili liberamente.
Una interessante innovazione estetica della JFC, che non ha equivalenti in altre interfaccie utente, è l'estensivo supporto per i bordi. Ogni componente può avere un bordo, e più bordi possono essere annidati uno intorno all'altro. In figura 4 è possibile vedere una schermata che illusta il meccanismo: abbiamo un componente JLabel a cui sono stati applicati alcuni dei vari bordi disponibili. 
 
 
 

Figura 4: ogni componente può avere un bordo

Sono presenti innanzitutto i bordi semplici come le linee, ma anche quelli più "alla moda", che consentono di dare un look 3d alle applicazioni. I bordi possono presentare anche dei titoli, possono essere posizionati e allineati come si desidera e si può selezionare il font. Notare che il bordo può essere composto da una immagine ripetuta, più o meno come avviene con lo sfondo dei desktop. Effetti sofisticati sono ottenibili componendo più bordi: per esempio combinando un bordo a bassorilievo con una opportuna figura si può ottenere un simpatico effetto di cornice. Altri componenti degli di nota sono le slider (barre simili ai regolatori del volume negli impianti ad alta fedeltà), che consentono di selezionare un valore in un certo intervallo numerico, e le progress bar (barre che mostrano il punto in cui si è arrivati nell'esecuzione di una operazione che richiede un certo tempo).
 
 
 

Pannelli e Layout
Finora abbiamo visto componenti della Swing. Comunque Java presenta aspetti caratteristici nel modo in cui visualizza e dispone gli elementi in una interfaccia grafica. Siccome il linguaggio è multipiattaforma, deve essere in grado di generare interfacce utente indipendenti dalla risoluzione video. L'approccio scelto dai progettisti della AWT e mantenuto nella Swing, è quello di definire una serie di componenti contenitori. Ogni contenitore può contenere dei componenti e disporli al suo interno non per coordinate ma con specifiche geometriche. Il posizionamento dei componenti all'interno di un container è eseguito da classi ausiliarie, dette Layout Manager, il cui compito è appunto quello di di disporre gli elementi dentro un contenitore. La Swing estende la AWT con nuovi tipi di contenitori e di Layout Manager.
 
 
 

Figura  5: componenti scrollabili con "ruler" personalizzabili

I contenitori AWT e Swing sono di due tipi: finestre e pannelli. Le finestre sono dei contenitori che hanno una autonomia: creano appunto una nuova finestra. I pannelli invece sono dei particolari contenitori che devono essere contenuti in una finestra, e visualizzano in modo particolare i componenti che contengono. Tanto per fare un esempio la toolbar Swing è un particolare tipo di contenitore. La AWT mette a disposizione due soli tipi di pannello (il Panel e lo ScrollPane), mentre la Swing è molto più ricca.
 
 
 

Figura  6:  Multiple Document Interface con Swing

Consideriamo ora qualcuno dei nuovi componenti della Swing. Innanzitutto abbiamo il JTabbedPane, un pannello che consente la selezione dei componenti che contiene tramite linguetta. Questo pannello è visibile per esempio in figura 3. Un altro interessante pannello è il JScrollPane, che consente ai componenti che contiene (tipicamente immagini) una visualizzazione parziale e scrollabile. La AWT 1.1 possiede un componente analogo, lo ScrollPane, ma quello della Swing è più ricco in quanto consente di personalizzare i righelli e di controllare in maniera più accurata l'area visualizzata. Il JSplitPane infine consente di dividere una finestra con un divisore: al contenitore possono essere aggiunti due componenti che vengono ridimensionati a seconda di come l'utente sposta il divisore. È simile in un certo senso ai frame delle pagine Web.
Al set dei LayoutManager standard della AWT (BorderLayout, GridLayout, FlowLayout, eccetera) la principale aggiunta è il BoxLayout. Questo Layout Manager consente di allineare i componenti in un container specificando l'allineamento e lo spacing. Grazie ad esso si risolvono molti annosi problemi che rendono abbastanza difficoltosa la produzione di interfacce grafiche della AWT con allineamenti precisi dei componenti.

Windows ci ha abituati da anni alle finestre "annidate", ovvero finestre contenute dentro altre finestre. Si tratta della cosiddetta interfaccia MDI (Multiple Document Interface), che per anni non ha avuto eguali nelle altre interfaccie grafiche come per esempio il Mac o Motif. Swing mette a disposizione anche la possibilità di costruire applicazioni MDI grazie all'uso del JInternalFrame

Finora abbiamo visto molti dei componenti ma ancora non abbiamo esaminato una aspetto importante che differenza sostanzialmente le finestre AWT (Frame) da quelle JFC (JFrame). Mentre è possibile con la AWT aggiungere elementi direttamente ad un Frame, ogni JFrame contiene due pannelli sovrapposti, uno detto rootPane e l'altro glassPane, e bisogna aggiungere esplicitamente i componenti di una finestra al rootPane. In pratica invece di scrivere jframe.add(button) occorre usare il codice jframe.getContentPane().addButton(). Questa caratteristica è giustificata dalla necessità di supportare componenti che si sovrappongono all'interfaccia utente abituale. Per esempio i menù popup o i tooltip. Grazie alla scelta di sovrapporre due pannelli in ogni finestra si risolve elegantemente in problema. Ogni elemento che fa parte dello sfondo della finestra viene disegnato nel rootPane, mentre ogni altro elemento che appare provvisoriamente e poi scompare sfrutta il glassPane.
 
 

Modello Documento-Vista

La Swing ci mette a disposizione alcuni componenti abbastanza complessi: per la precisione abbiamo gli "outline" (rappresentazione grafica di alberi) e le griglie (rappresentazione grafica di matrici). Per l'utilizzo di questi componenti diviene fondamentale sfruttare un altro importante aspetto della JFC che non è stato ancora esaminato: l'architettura Documento Vista. Questa architettura è una delle caratteristiche principali del sistema GUI del linguaggio Smalltalk, dove prende il nome di Model View Control. Questa archiettura è stata in seguito ripresa in altre librerie come le MFC di Visual C++, ed infine incorporata nelle Swing di Java. Una descrizione dettagliata di come funzioni questo modello esula dai limiti di questo articolo. 
 
 
 

Figura 7: Rich Text Format modificabile con Swing

Ci limitiamo a descrivere cosa comporta e quali vantaggi ha per il programmatore. Per ogni componente è possibile separare la struttura dati visualizzata da un componente dal componente stesso. In generale per componenti semplici questo non ha grande importanza, mentre ne ha per componenti complessi quali le tabelle o gli alberi. Supponiamo quindi di voler visualizzare in una tabella il risultato di una interrogazione di un database. Senza il modello MVC, occorrerebbe prendere i risultati dal database e inserirli nel componente uno per uno per presentarli all'utente; qualora l'utente modifichi la tabella, occorre esaminare il contenuto del componente e riportarlo nel database. Sfruttando MVC e seguendo gli standard della Swing è possibile definire una struttura dati e associarla al componente: la modifica di questa struttura dati si ripercuote automaticamente sul componente, e viceversa la modifica del componente si ripercuote sulla struttura dati. In questo modo è possibile separare nettamente la logica di interazione con l'utente (che è essenzialmente gestione di eventi) dalla logica di modifica dei dati. Come esempio consideriamo la visualizzazione di una griglia che contiene dati di un database come in Figura 8. 
 
 

Figura 8: una tabella con collegamento a database 

Il programmatore separa il codice che gestisce l'interazione col database (che rappresenta il modello) dal codice che gestisce l'interazione con l'utente (la vista). In pratica occorre sviluppare una classe che implementa l'AbstractTableModel: questa classe si occuperà di aprire la connessione e memorizzare i risultati. L'aggancio con la griglia avviene semplicemente impostanto il modello che la griglia visualizza (con setModel). Notare che in questo modo il codice che gestisce l'interfaccia rimane integralmente valido anche se il programmatore decide di cambiare la fonte dati, utilizzando un file invece che un database, e occorre soltanto cambiare il modello. Analogamente si può riutilizzare il codice del modello nell'interazione col database agganciandolo ad una interfaccia utente completamente diversa.
 
 

Conclusioni
La Swing era talmente necessaria che il suo uso è cominciato ben prima del rilascio del JDK 1.2. Usandola i programmi hanno un aspetto gradevole, ma soprattutto la sua architettura risente dell'esperienza di molti altri toolkit per interfaccia grafica ed è progettata in maniera elegante e potente. I pezzi si incastrano tra di loro in maniera molto efficace, e le feature all'ultima moda si sprecano: per esempio il supporto multi-look e il modello documento-vista incorporato in ogni componente. Insomma un toolkit all'altezza delle aspettative di quello che deve diventare lo standard GUI di Java.
 
 

 

Michele Sciabarrà è autore, consulente e formatore. Quando non scrive o tiene corsi coordina lo sviluppo software della Prometeus, ditta di cui è titolare. Il suo ultimo libro è "Linux e Programmazione Web" (McGraw-Hill). Si occupa di Java dalla versione alpha, di Linux dalla versione 0.99 e di programmazione dai tempi del Commodore 64
Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it