MokaByte Numero  44  - Settembre 2000
Corso di Swing
I parte
di 
Andrea Gini
Differenze tra Swing e AWT

Con il rilascio definitivo del JKD1.3, il package Swing ha ormai raggiunto la maturita'. Consegnati alla storia gli iniziali problemi di lentezza, consumo di memoria e difetti di visualizzazione, Swing si presenta ora come un'API robusta e affidabile, e viene utilizzata con successo su numerosi programmi commerciali.  Comprendere a fondo le caratteristiche e le potenzialita' di Swing rappresenta una carta vincente per ogni sviluppatore Java: questa corso vuole guidare l'utente alla scoperta di questo meravigliosa API

Introduzione
Come e' ormai noto, la piu' grossa sfida affrontata dai progettisti di java nello studio della JVM fu quello di realizzare un package grafico capace di funzionare con un buon livello di prestazioni su piattaforme molto differenti tra di loro. La soluzione proposta nel 1996 fu AWT, un package che mappava i componenti grafici del sistema ospite con una serie di classi scritte in gran parte in codice nativo.
Questa scelta limitava pesantemente la scelta dei componenti da includere nell'API, poiche' costringeva a prendere in considerazione solamente quel limitato insieme di controlli grafici che costituivano il "minimo comun denominatore" tra tutti i sistemi a finestre esistenti.
Inoltre comportava dei grossi problemi di visualizzazione, poiche' le interfacce grafiche realizzate su una JVM apparivano spesso con dei grossi difetti se eseguite su una JVM differente.

Il package Swing, al contrario, e' stato realizzato totalmente in codice java, senza ricorrere alle primitive di sistema. In questo modo sono stati risolti alla radice i problemi di compatibilita', dal momento che la stessa identica API viene utilizzata, senza alcuna modifica, su qualunque JVM. Dal momento che la Sun Microsystem ha pubblicato il codice delle API java, e' addirittura possibile studiarne i sorgenti e localizzare, ad esempio, i metodi che si occupano di disegnare i singoli componenti sullo schermo.
Liberi dal vincolo del "minimo comun denominatore", i progettisti di Swing hanno scelto di percorrere la via opposta, creando un package ricco di funzionalita' spesso non presenti nella piattaforma ospite.
Per risolvere definitivamente il problema della differenza di visualizzazione, viene offerta al programmatore la possibilita' di scegliere il Look & Feel con cui visualizzare le proprie GUI, al contrario di quanto avveniva su AWT, che era vincolato alla rappresentazione dei componenti della macchina ospite. 
 
 

 Figura 1 - Pluggable Look & Feel

 

Questa caratteristica, denominata "Pluggable Look & Feel", e' forse l'aspetto piu' sorprendente di Swing, in quanto e' totalmente trasparente al programmatore. Qualsiasi GUI realizzata in Swing puo' essere visualizzata in uno qualunque dei L&F disponibili sul sistema, senza che venga richiesta alcuna modifica a livello di codice sorgente. Non esistono problemi di compatibilita' nel passaggio tra un L&F e l'altro, dal momento che i componenti Swing sono stati progettati per mantenere una netta separazione tra la rappresentazione grafica di un componente, le informazioni che contiene e il controllo che implementa. Questo design, definito "Model Control View" (MCV), risulta, nella maggior parte dei casi, del tutto trasparente al programmatore, che puo' utilizzare i componenti Swing nello stesso modo in cui era abituato con quelli AWT. Nel momento in cui ci fosse la necessita' di progettare componenti personalizzati, si potranno sfruttare pienamente le funzionalita' offerte da questo design.

La presenza di queste  features non va ad incidere sulla caratteristica piu' richiesta da un programmatore: la facilita' d'uso. Chi avesse gia' maturato una buona conoscenza di AWT, si trovera' a proprio agio con Swing, data la sostanziale compatibilita' a livello sorgente. Chi invece fosse alla ricerca di nuove modalita' operative, trovera' soddisfazione nell'esplorare le caratteristiche piu' avanzate di questa straordinaria API grafica
 
 
 
 

Il padre di tutti i componenti Swing: JComponent
Il package Swing contiene piu' di 70 classi, quasi tutte sottoclassi di JComponent, una classe astratta derivata da Component e da Container. Al pari di Component, JComponent e' un'oggetto grafico che ha una posizione e una dimensione sullo schermo, al cui interno e' possibile disegnare, scrivere o ricevere eventi dal mouse e dalla tastiera. Come Container invece, offre la possibilita' di disporre altri componenti al suo interno, nonche' il supporto ai LayoutManager, che facilitano l'impaginazione dei componenti.
 
 

Figura 2 - JComponent eredita il proprio comportamento sia da JComponent che da Container. Di nuovo aggiunge la capacita' di aggiungere bordi e Tooltip 

 

La classe JComponent fornisce in esclusiva alcune funzionalita', che vengono ereditate da qualunque componente Swing. Tra queste possiamo segnalare:

  • Tool tips: il metodo setToolTipText(String) permette di aggiungere un ToolTip, un messaggio testuale che compare sul componente dopo che l'utente vi ha lasciato fermo il mouse per qualche istante.
  • Bordi: ogni componente Swing puo' essere dotato di un bordo grazie al metodo setBorder(Border). La classe BorderFactory fornisce strumenti per creare diversi tipi di bordo, da semplici contorni a sofisticate decorazioni
  • Pluggable look and feel: e' la possibilita' offerta da ogni JComponent di essere visualizzato in maniera differente a seconda del Look & Feel impostato dall'utente.
  • Double buffering: il Double Buffering rende il refresh delle interfacce grafiche Swing molto piu' naturale di quanto avvenisse con i componenti AWT.


Classificazione funzionale dei componenti Swing
E' possibile classificare i componenti Swing in base all'uso che se ne fa, secondo una classificazione che non corrisponde necessariamente all'organizzazione gerarchica delle classi all'interno del package; questa classificazione funzionale e' indispensabile per stabilire un ordine di esposizione

La prima categoria e' quella dei "Top Level Containers", indispensabili per realizzare qualunque interfaccia grafica. Sono gli unici componenti  Swing che non derivano da JComponent, essendo sottoclassi dei rispettivi componenti AWT.  Il piu' usato di questi e' JFrame, corrispondente ad una finestra di sistema. JDialog implementa invece una finestra modale, cioe' una finestra che "blocca" il frame principale in attesa di un evento utente. Meno usati sono invece JWindow, che implementa una finestra priva di pulsanti di controllo (utile nella realizzazione di Splash Screen) e JApplet, l'implementazione Swing dell'Applet AWT.

La seconda categoria e' quella dei Contenitori, che sono componenti entro i quali e' possibile collocare altri componenti, secondo il Layout definito dal programmatore. Alcuni contenitori forniscono delle funzionalita' aggiuntive: ecco una rassegna di quelli di uso piu' comune

  • JPanel: e' il contenitore di base per controlli grafici. 
  • JScrollPane : e' un JPanel dotato di scrollbar, che puo' contenere superfici piu' grandi dell'area visibile.
  • JSplitPane: contiene due pannelli separati da una barra di split riposizionabile
  • JTabbedPane: permette di creare un insieme di pannelli richiamabili attraverso tab
  • JToolBar: e' la caratteristica pulsantiera presente ormai su qualunque programma grafico


I seguenti contenitori meritano di essere trattati a parte, dal momento che sono destinati ad un uso piu' specialistico.

  • JLayeredPane: permette di disporre componenti su livelli sovrapposti, visibili in trasparenza.
  • JDesktopPane: e' un pannello che offre la capacita' di creare ambienti tipo Desktop, con oggetti grafici e finestre al suo interno.
  • JInternalFrame: e' un contenitore simile ad una finestra, realizzato apposta per essere visualizzato all'interno di JDesktopPane.


Su Swing troviamo qualunque tipo di Controllo Grafico presente in un moderno sistema a finestre, come pulsanti, menu o checkbox. Rispetto agli analoghi componenti AWT possiamo segnalare fin da subito la possibilita' di inserire icone e label all'interno di qualunque componente, sia esso un pulsante, un menu o un ComboBox. 

Il package Swing contiene di serie alcuni strumenti per lavorare su strutture dati complesse, come JTable che permette di generare report formattati in stile Spreadsheet, o  JTree che fornisce una vista su strutture ad albero come ad esempio i FileSystem.

Per navigare tra i colori di sistema o attraverso il File System, sono stati realizzati i componenti JColorChooser e  JFileChooser.

Per finire, un set completo di Text Components permette di affrontare qualunque situazione in cui sia necessario manipolare dei testi.

  • JTextField  implementa il classico campo di testo a singola riga.
  • JPasswordField permette di inserire in un campo di testo informazioni riservate, invisibili ad occhi indiscreti
  • JTextArea e' un componente di testo ideale per lavorare su documenti di media lunghezza privi di formattazione
  • JEditorPane e' un componente di testo in grado di manipolare documenti formati. Dispone, per default, della capacita' di trattare RTF o HTML, ma e' possibile crearne delle estensioni per renderlo adatto a trattare qualunque altro formato.
  • JTextPane infine permette di lavorare su qualunque tipo di testo, utilizzando qualunque combinazione di font, stile, colore e persino di aggiungere immagini o collegamenti ipertestuali. 


I Top Level Container
 

Figura 3 - I Top Level Container Swing derivano dai corrispondenti componenti AWT 

 

I Top Level Container sono i componenti all'interno dei quali si creano le interfacce grafiche: ogni programma grafico ne possiede almeno uno, di solito un JFrame, che rappresenta la finestra principale.
Come si puo' vedere dalla gerarchia delle classi, i Top Level Container sono l'interfaccia tra il mondo AWT e quello Swing. Diverse proprieta' dei controlli grafici non sono presenti nei Top Level Container: in particolare il cambio di Look & Feel non va ad incidere sull'aspetto delle finestre, che rimangono sempre e comunque uguali alle altre finestre di sistema.
Dal punto di vista implementativo, i Top Level Container presentano una struttura a strati piuttosto complessa, che verra' illustrata piu' avanti, dopo aver introdotto alcuni importanti concetti. L'unica implicazione degna di nota per l'utente comune e' la presenza di un "Pannello di Contenimento" (Content Pane), accessibile attraverso il metodo getContentPane(): questo pannello, e non il JFrame, verra' utilizzato come contenitore base per tutti gli altri controlli . Questo dettaglio e' l'unica vistosa differenza a cui il programmatore AWT deve abituarsi nel passaggio a Swing: se su AWT si era abituati a scrivere

Frame f  = new Frame(); 
f.setLayout(new FlowLayout());
f.add(new Button("OK));


ora e' necessario scrivere

JFrame jf  = new JFrame();
Container contentPane = jf.getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.add(new Button("OK));
o piu' brevemente
 
JFrame jf  = new JFrame();
jf.getContentPane().setLayout(new FlowLayout());
jf.getContentPane().contentPane.add(new Button("OK));


JFrame

Possiamo creare una finestra usando i seguenti costruttori

public JFrame():crea una finestra 
public JFrame(String title): crea una finestra con il titolo specificato dal parametro 

I seguenti metodi permettono di lavorare su alcune proprieta' dell'oggetto
 

  • public void setSize(int width, int height): specifica la larghezza e l'altezza della finestra.
  • public void setResizable(boolean b): permette di stabilire se si desidera che la finestra sia ridimensionabile.
  • public void pack(): ridimensiona la finestra tenendo conto della dimensione ottimale dei componenti posti al suo interno.
  • public void setBounds(int x, int y, int width, int height): oltre alle dimensioni, permette di specificare anche le coordinate dell'angolo in alto a sinistra
  • public void setDefaultCloseOperation(int operation): Seleziona cosa deve accadere alla pressione del tasto "close", dopo aver provveduto alla notifica degli ascoltatori. Sono disponibili le seguenti scelte:
    • WindowConstants.DO_NOTHING_ON_CLOSE: nessun effetto.
    • WindowConstants.HIDE_ON_CLOSE: nasconde la finestra (e' l'impostazione di default)
    • WindowConstants.DISPOSE_ON_CLOSE: distrugge la finestra, che non potra' piu' essere aperta.
    • JFrame.EXIT_ON_CLOSE: (introdotto nel JDK1.3) chiude la finestra e termina l'esecuzione del programma.
  • public void setTitle(String title) imposta il titolo della finestra.
  • public void setVisible(boolean b) Rende la finestra visibile o invisibile, secondo il valore del parametro.
  • public void setJMenuBar(JMenuBar menubar) aggiunge una JMenuBar al Frame.
  • public Container getContentPane() restituisce il pannello di contenimento del JFrame, al cui interno e' possibile aggiungere i componenti.


Il seguente programma di esempio crea un JFrame, gli da un titolo, una posizione, una dimensione, ne stabilisce il comportamento in chiusura e lo rende visibile.

import javax.swing.*;

public class JFrameExample {
    public static void main(String argv[]){
      JFrame j = new JFrame();
      j.setTitle("JFrameExample");
      j.setBounds(10,10,300,200);
      j.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      j.setVisible(true);
    }
}
 
 
 

Figura 4 - Un JFrame vuoto

 

JDialog
Le finestre di dialogo vengono usate per l'inserimento di valori, o per segnalare all'utente una situazione anomala. Ogni finestra di dialogo appartiene ad un'altra finestra; se definiamo come modale un JDialog,  alla sua comparsa esso blocchera' il frame di appartenenza, in modo da costringere l'utente a portare a termine l'interazione.  Possiamo creare finestre di dialogo con i seguenti costruttori

  • JDialog(Dialog owner, String title, boolean modal): crea un JDialog modale o non modale, con un titolo e con lo specificato Dialog come proprietario.
  • JDialog(Frame owner, String title, boolean modal): crea un JDialog modale o non modale, con un titolo e con lo specificato Frame come proprietario.


Altri costruttori permettono di specificare un numero inferiore di parametri. I metodi presentati su JFrame sono validi anche su JDialog. Ovviamente non e' possibile selezionare  l'opzione EXIT_ON_CLOSE con il metodo setDefaultCloseOperation().
 
 
 

Conclusione
Questa veloce panoramica ha permesso di esplorare i contenuti del Package Swing, e di impadronirsi dell'uso dei principali Top Level Container. Dal prossimo articolo entreremo nel vivo della progettazione di interfacce grafiche analizzando il concetto di Gerarchia di Contenimento e di Layout Management.
 
 
 

Appendice: Dove si trovano i sorgenti delle API del JDK
Uno studio anche occasionale dei sorgenti delle API permette di capire molte cose sul linguaggio java, sul suo funzionamento e sullo stile di programmazione. Soprattutto aiuta a capire che le classi di libreria, per quanto grandi e complesse, sono pur sempre programmi java, e che con il giusto impegno e' possibile raggiungere lo stesso livello di efficienza nella scrittura del codice.

Potete trovare i sorgenti di tutte le API java nel file src.jar, presente nella directory principale del JDK. Potete aprirla trascinando l'icona del fine src.jar dentro WinZip, oppure lanciando dalla directory del JDK il comando 

jar xvf src.jar

che estrare tutti i sorgenti nella directory src.

 

Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it