Paola Magillo, Univestita' di Genova, Corso di Programmazione II per SMID, a.a. 2008-2009.

Lezione 07:

INTERFACCE GRAFICHE IN JAVA - I

Librerie per interfacce in Java

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.

Metodo orientato a oggetti

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).

Esempio di interfaccia in Java

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):

Codice Java

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):

Schema generale

  1. Importare i package necessari
  2. Stabilire la finestra
  3. Stabilirne il contenuto
  4. Stabilire gestione degli eventi nelle componenti
  5. Far partire l'interfaccia

1) Importare i package

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.

2) Stabilire la finestra

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.

3) Stabilire il contenuto della finestra

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.

4) Stabilire gestione degli eventi

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.

5) Far partire l'interfaccia

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".