Java ha libreria di classi da usare in un programma per creare e gestire interfacce grafiche. Parte delle Java Foundation Classes (JFC), librerie di classi standard Java.
Due librerie:
Java AWT ha varieta' piu' limitata di componenti di interfaccia e meno sofisticati (es. bottoni possono avere etichette solo testuali).
Java Swing ha maggiore varieta' di compomenti di interfaccia e piu' sofisticati (es. bottoni possono essere etichettati con testo e/o immagini, ha funzioni avanzate per grafica 2D).
Noi vediamo Java Swing.
Essendo Java un linguaggio object-oriented, tutto e' classe:
Le classi sono organizzate in gerarchie di ereditarieta' secondo paradigma object-oriented.
Java AWT e Java Swing hanno due distinte gerarchie di classi per
i componenti.
Tutte le classi di componenti Swing hanno il nome che inizia per "J",
per distinguerle dalle corrispondenti classi AWT per lo
stesso componente (es. JButton classe per bottone in Swing rimpiazza
Button classe per bottone in AWT).
Non bisogna mai mischiare nella stessa interfaccia componenti
AWT e Swing.
Invece per le informazioni accessorie, gli eventi e i listener, Java Swing usa le stesse classi di AWT (con poche eccezioni).
Una finestra contenente
un bottone e un'etichetta posizionati uno sopra l'altro.
L'etichetta mostra il numero di volte in cui il bottone e' stato azionato.
Quando utente aziona il bottone, l'etichetta si incrementa di una unita'.
Inoltre quando utente clicca sul bottone di chiusura della finestra,
l'applicazione termina.
Usiamo le classi Java:
Le personalizziamo in questo modo:
E le dotiamo dei seguenti comportamenti aggiuntivi (callback):
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class SwingExample extends JFrame { private static String labelPrefix = "Number of clicks: "; private int numClicks = 0; JLabel label = null; JButton button = null; public SwingExample() { // crea i componenti label = new JLabel(labelPrefix + "0 "); button = new JButton("Click me!"); // stabilisce callback del bottone button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { numClicks++; label.setText(labelPrefix + numClicks); } }); // mette i componenti nel pannello di contenuto della finestra getContentPane().setLayout(new GridLayout(2, 1)); getContentPane().add(button); getContentPane().add(label); // stabilisce callback per chiusura della finestra addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); // stabilisce le dimensioni della finestra pack(); // la rende visibile setVisible(true); } public static void main(String[] args) { SwingExample app = new SwingExample(); } }
Risultato (dopo vari click):
Le JFC sono divise moduli chiamati package.
Moduli principali di AWT: java.awt e (sotto-modulo) java.awt.event.
Moduli di Swing: javax.swing (con sotto-moduli).
Swing usa alcune classi di AWT, per esempio quelle per eventi e
callback. Percio' bisogna importare anche AWT.
L'interfaccia e' realizzata dalla classe SwingExample, sottoclasse di JFrame, classe adatta per la finestra principale di una applicazione.
I punti 3,4 sono eseguiti nel costruttore della classe SwingExample.
Creare tutti i componenti (qui bottone e label) e posizionarli nel pannello di contenuto (content pane) della finestra, eventualmente usando contenitori intermedi. Stabilire il layout manager dei contenitori usati.
La relazione (gerarchia) di contenimento ha la seguente rappresentazione ad albero:
JFrame (finestra principale) | pannello di contenuto (contenitore) | +----+-----+ | | JButton JLabel (bottone ed etichetta)dove i primi due livelli sono fissi. Ogni JFrame ha sempre un pannello di contenuto; la parte di interfaccia che costruiamo noi va messa dentro questo pannello di contenuto.
In un'interfaccia piu' complicata potrei avere piu' livelli nella gerarchia di contenimento.
Il layout manager stabilisce come sono posizionati i componenti dentro a un contenitore. Usiamo layout a griglia con 2 righe e 1 colonna.
L'istruzione "pack" fa si' che il contenuto della finestra venga compattato per occupare il minimo spazio necessario.
Il bottone gestisce evento "attivazione" modificando l'etichetta della label. La finestra principale gestisce evento "chiusura" terminando l'applicazione.
In Java ogni tipo di evento XXX ha:
Per far si' che un componente reagisca ad un tipo evento XXX (previsto su quel tipo di componente) devo:
Per l'evento "attivazione" del bottone:
Definizione della classe che implementa l'interfaccia e creazione dell'oggetto sua istanza si possono fare contemporaneamente: l'istruzione usata crea un oggetto ActionListener dove il metodo ActionPerformed e' ridefinito nel modo indicato.
Per l'evento "chiusura" della finestra:
In teoria dovrei definire una classe che implementa tutti i metodi di WindowListener, anche quelli che non mi interessano, dando a questi ultimi un corpo vuoto.
Per comodita', ogni interfaccia XXXListener che prevede piu' di un
metodo ha una classe predefinita, chiamata XXXAdapter,
che implementa gia' tutti i metodi con corpo vuoto.
Basta definire una sotto-classe dell'adapter che re-implementi solo
i metodi che interessano.
Le classi listener con un metodo solo non hanno bisogno di adapter.
Il main crea un'istanza dell'interfaccia e la fa partire.
L'istruzione setVisible mappa sullo schermo la finestra e tutto il suo contenuto. Da questo momento si innesca automaticamente il "ciclo degli eventi".