|
Java Tutorial - Parte 2 0.1
Un tutorial per esempi
|
I programmi che abbiamo scritto nella prima parte di questo tutorial sono programmi cosidetti CLI (Command Line Interface = interfaccia a riga di comando) la cui caratteristica principale è quella di essere procedurali: il programma inizia con la prima istruzione del metodo main e termina quando il metodo main finisce.
Schematicamente, questo è un programma CLI, ricordate?
Una applicazione GUI, invece, non è procedurale; il suo metodo main crea la interfaccia grafica e poi ... non fa null'altro. Le operazioni propedeutiche della applicazione vengono eseguite solo se intervengono degli eventi che, solitamente, vengono innescati dal utente quando preme un bottone, o sceglie un elemento da una lista e seleziona un comando da un menù.
Il paradigma di una GUI è il cosidetto event driven programming (programmazione guidata dagli eventi); gli venti che non si limitano alla interazione con l'utente: un evento può essere innescato, per esempio, quando si ricevono dati su un canale di comunicazione.
Per creare una applicazione GUI non possiamo più usare i metodi che abbiamo usato in passato: il metodo System.out.println che stampava un messaggio sul terminale, in una GUI non esiste perchè ... non esiste alcun terminale! Anche l'oggetto System.in che rappresenta lo standard input (la tastiera) e che abbiamo usato in passato per ottenere l'input dall'utente, non esiste in una GUI.
Certo è che non possiamo gestire l'output sullo schermo pilotando direttamente l'hardware soprattutto perchè:
Quindi anche in una GUI vengono messe a disposizione del programmatore le funzioni per eseguire l'I/O da e verso le periferiche: queste funzioni sono spesso scritte in linguaggio "C" e sono note col nome di Application Program Interface (in breve, API).
Ogni sistema operativo grafico possiede le proprie specifiche API ed esse non sono compatibili tra i sistemi: Windows™ ha le sue specifiche API mentre su altre piattaforme, come per esempio Linux, ogni ambiente grafico ha le sue proprie API: l'ambiente grafico KDE, per esempio, possiede API diverse da GNOME (un altro ambiente grafico Linux) ed entrambe sono diverse dal sistema grafico unix che è conosciuto col nome di "X11".
Aprite il file hellow.c che trovate nella sottocartella /javatutor2/projects/hellow/. Si tratta di un sorgente in linguaggio "C" che visualizza una finestra di dialogo modale. Questo è il contenuto del file:
Aprite il terminale e spostatevi nella cartella citata più sopra e compilate il sorgente usando il compilatore MinGW (se non lo avete installato rileggete il capitolo Gli strumenti di sviluppo). Compilate il sorgente col seguente comando; fate attenzione a specificare la opzione -mwindows e poi mandatelo in esecuzione:
\javatutor2\projects\hellow>gcc -o hellow.exe hellow.c -mwindows \javatutor2\projects\hellow>hellow.exe \javatutor2\projects\hellow>
Otterrete una finestra con il messaggio "Hello World". Per chiuderla, clikkate sul bottone di comando "OK":
Quella che avete eseguito è una applicazione GUI minimale che visualizza una message box di tipo modale. Voglio attirare la vostra attenzione sulla prima riga del listato sorgente:
Si tratta di una direttiva del preprocessore del linguaggio "C" che ingloba nel sorgente un intero file: il file windows.h che rappresenta il file header. Questo file contiene le dichiarazioni dei tipi di dato e delle funzioni specifiche per il sistema operativo Windows: quello che voglio farvi capire è che il listato sorgente scritto poc'anzi è specifico per Windows e non potrà mai compilare su una piattaforma GUI diversa da Windows.
Per superare questa limitazione sono nati diversi ambienti cosidetti cross platform (=multi piattaforma) per mezzo dei quali è possibile scrivere una applicazione GUI che compila su diverse piattaforme come per esempio Windows, Linux e persino Apple.
Questi ambienti sono normalmente basati su linguaggi object-oriented come il "C++" o il "C#" dal momento che questi linguaggi riescono ad astrarre le funzioni a basso livello specifiche della API. Esistono molti ambienti di questo tipo ma i più famosi (e più usati) sono, al momento attuale e per quanto ne so, tre:
Il linguaggio Java non ha bisogno di ambienti di sviluppo multi-piattaforma poichè è il linguaggio stesso ad essere multi-piattaforma. Java non compila in modo nativo: esso crea un codice intermedio detto bytecode che viene interpretato da una Java Virtual Machine (JVM).
Pertanto è sufficiente implementare una JVM per la specifica piattaforma hardware/software ed il codice creato dal compilatore Java funzionerà su qualsiasi piattaforma indipendentemente dal linguaggio nativo usato su detta piattaforma: e questo si applica sia alle applicazioni CLI che alle applicazioni GUI.
Java fornisce nella propria libreria standard un framework (=struttura) potente e flessibile chiamato Swing costituito da decine e decine di classi Java che incapsulano tutti gli aspetti di programmazione di una GUI.
Swing fà parte del modulo java.desktop e consiste di diversi packages che gestiscono la GUI mediante classi dedicate; Swing è il successore di AWT (Abstract Window Toolkit) un framework Java per lo sviluppo di applicazioni GUI desktop sul quale, peraltro, lo stesso Swing si basa.
Entrambi i toolkit (AWT e Swing) fanno parte del modulo java.desktop. Per maggiori informazioni vedi il modulo java.desktop.
Aprite il file HelloWorldSwing.java che trovate nella cartella javatutor2/projects/hellow.
Compilate il sorgente e mandatelo in esecuzione:
javatutor2\projects\hellow>javac HelloWorldSwing.java javatutor2\projects\hellow>java -ea HelloWorldSwing
otterrete la vostra prima applicazione GUI scritta in Java:
Per chiudera la applicazione clikkate sul bottone di chiusura in alto a destra.
Analizziamo il codice che abbiamo scritto:
Queste righe sono abbastanza facili: abbiamo importato le classi che rappresentano:
JFrame: il frame principale della finestra; questo componente grafico possiede alcune caratteristiche speciali rispetto agli altri componenti grafici; esso può avere un bordo, una barra del titolo e alcuni bottoni speciali come il bottone di chiusura della finestra. Il frame può anche possedere una barra del menù. JPanel: questa classe rappresenta un elemento GUI generico che non ha bordo nè decorazioni speciali (che invece il frame possiede); caratteristica particolare di questo componente è che può contenere altri componenti e che esso viene impostato come contenuto di un frame Jlabel: questa classe rappresenta un componente speciale; una label (=etichetta). Le labels non hanno alcuna funzionalità speciale (come per invece hanno i buttons) se non quella di visualizzare una stringa di testo o una icona. Nel nostro caso la stringa "Hello World"Passiamo ora ad analizzare il main:
Nel metodo principale vediamo un costrutto piuttosto particolare: abbiamo richiamato il metodo statico invokeLater della classe SwingUtilities. Questo metodo accetta come parametro un oggetto di classe Runnable o meglio, un oggetto di una classe che implementi Runnable (ebbene si, questa non è una classe ma una interfaccia).
Poichè Runnable è una interfaccia qualsiasi classe che la implementi deve definire tutti i metodi dichiarati nella interfaccia stessa (che vengono anche chiamati metodi astratti). L'unico metodo dichiarato in Runnable è il metodo run che non ha parametri e ritorna void.
Pertanto dovremo scrivere una classe che implementa Runnable e che dovrebbe essere passata come argomento al metodo invokeLater:
Allo scopo di rendere il codice più conciso, Java permette al programmatore di definire le cosidette classi anonime. Si tratta in sostanza di sostituire al costrutto new MyRunnable scritto proprio qui sopra, la definizione stessa della classe MyRunnable omettendone il nome dal momento che esso non è nemmeno più necessario.
La classe così definita non ha un nome proprio ed è per questo che viene chiamata classe anonima.
Proseguendo nella analisi del codice arriviamo al metodo createAndShowGUI:
La prima riga del codice costruisce un frame passando come argomento aò costruttore la stringa "HelloWorldSwing" che rappresenta il titolo del frame; esso viene visualizzato nella barra del titolo.
La seconda riga di codice imposta la azione che deve essere compiuta quando l'utente clikka il bottone di chiusura della finestra (la "X" che appare solitamente nell'angolo in alto a destra nella barra del titolo). La costante EXIT_ON_CLOSE specifica che la azione da eseguire è la chiusura della applicazione; questo è il comportamento normale per tutte le applicazioni.
Proseguendo nella analisi del metodo createAndShowGUI troviamo le righe che creano il pannello dei contenuti che rappresenta l'area interna del frame.
Dovete sapere che il frame non ha alcun contenuto: un frame è il bordo della finestra e le due barre: del titolo e del eventuale menù ma tutto ciò che vi è all'interno è assolutamente trasparente; senza un pannello dei contenuti la vostra applicazione visualizzerebbe solo il bordo e le barre e all'interno voi vedreste solo tutto ciò che già si trovava sullo schermo.
Ecco perchè dobbiamo creare un content pane (= annello dei contenuti) nel quale possiamo aggiungere i componenti da visualizzare: le etichette, i bottoni di comando, i bottoni radio e le check box, le liste, etc.
La classe che rappresenta un pannello dei contenuti è la JPanel che può essere costruita senza alcun argomento: per aggiungere i componenti si usa il metodo add. Ed è proprio con questo metodo che aggiungiamo la JLabel che visualizza la stringa.
Il content pane deve essere aggiunto al JFrame; è possibile usare il metodo add a questo scopo oppure anche il metodo setContentPane.
Il metodo createAndShowGUI termina con le righe che impostano la dimensione del frame a 400 x 200 pixels e lo rende visibile sullo schermo.
E' necessario impostare una dimensione del frame in una applicazione? La risposta è: NO, anzi, è meglio lasciare calcolare la dimensione del content-pane alla libreria Java e dimensionare il frame di conseguenza.
Questa operazione si può ottenere con il metodo pack della clssse JFrame (che nel listato è stata commentata). Per vederne l'effetto non divete fare altro che commentare la riga che imposta le dimensione del frame e togliere il commento alla riga che contiene l'istruzione frame.pack().
Poichè la label che contiene la stringa "HelloWorld" è di dimensioni molto ridotte otterrete una finestra minuscola sullo schermo; ma questo è proprio il risultato del metodo pack: dimensionare il frame in modo che esso contenga solo i componenti da visualizzare.
Vi chiederete per quale oscuro motivo abbiamo usato questo metodo per richiamare il metodo createAndShowGUI. Non potevamo semplicemente scrivere?
Ebbene, no! Il motivo per cui abbiamo dovuto usare invokeLater vi è al momento di difficile comprensione ma sappiate che una applicazione GUI si compone di due threads distinti:
main Voglio attirare la attenzione del lettore sul fatto che il disegno di un componente della GUI, compreso lo stesso frame, rappresenta un evento e che, di conseguenza, DEVE essere eseguito nel EDT.
Torneremo su questo argomento in Versione 0.5: il giocatore A.I. nel quale vi renderete conto che qualsiasi evento che ha impatto sulla GUI deve essere inoltrato al EDT; in caso contrario il nostro codice non avrà alcun effetto visivo.