INTERFACCE GRAFICHE IN JAVA CON SWING - DISPENSE

Pagina creata da Enrico Ferri
 
CONTINUA A LEGGERE
INTERFACCE GRAFICHE
 IN JAVA CON SWING

      DISPENSE
INTRODUZIONE
L’interfaccia grafica del programma è composta dai cosiddetti componenti GUI (Graphics User
Interface); essi sono dei componenti che servono per l’input o per l’output, ed hanno un aspetto
grafico (esempio: menù, i bottoni, le label, ecc).
Tutti questi componenti saranno inserite in un contenitore principale che mi rappresenta la finestra;
questo è chiamato Frame.
Per questo le applicazioni con un'interfaccia grafica le chiameremo applicazioni “a finestre”.

Ogni GUI è quindi un pezzettino di interfaccia grafica; essi ricevono degli eventi e in base a questi
danno dei risultati.
Esempio: un bottone, esso può essere cliccato; il click è un evento sulla componente bottone;
saremo noi poi che dovremo gestire questo evento, ovvero si dovrà dire cosa succede quando il
bottone viene cliccato.
Ogni GUI avrà i suoi eventi associati che vanno gestiti.
Per capire quando si verifica un evento su un GUI (ese: click su un pulsante) si deve dichiarare un
ascoltatore (listener) dell’evento sul GUI: il listener è un oggetto che attende il verificarsi
dell’evento sul componente (GUI), e quando questo si verifica lo gestisce (indica che azioni fare).

Il package SWING
Per disegnare le interfacce grafiche utilizzeremo una libreria che Java mette a disposizione che è
chiamata Swing.

Questo significa che nella libreria Swing ci saranno le classi relative ai vari componenti GUI di cui
posso creare oggetti ed usarli.
Nulla mi vieta (anzi spesso è necessario in quanto le classi della libreria sono astratte) di estendere
queste classi con mie sottoclassi e personalizzarle.

In particolare la libreria SWING aggiunge funzionalità alla già preesistente e più vecchia libreria
AWT.
Sottintendiamo quindi che importeremo sempre il pacchetto delle librerie Swing: javax.swing.* e
AWT: java.awt.*.

La caratteristica dell'interfaccia Swing è quella di avere contenitori e contenuti. Un contenitore è un
componente Swing in grado di ospitare altri componenti.
Come si vede dall'immagine precedente in realtà tutti gli elementi sono potenzialmente sia
contenitori che componenti, nella pratica però utilizzeremo alcuni esclusivamente come componenti
altri come contenitori.
Con gli oggetti che utilizzeremo come contenitori utilizzeremo metodi ereditati dalla classe
Component e per quelli utilizzati come componenti quelli della classe Content.

Abbiamo in particolare che tutto il programma sarà incluso in un JFrame, ovvero una Finestra.
All'interno della finestra utilizzo dei pannelli JPanel in cui andrò poi a mettere i vari componenti
(pulsanti, scritte, etc).

Per ogni classe contenitore e contenuto posso istanziare oggetti e settarne poi le proprietà con i
relativi metodi. Mi conviene però in genere creare una mia classe che estende una di queste classi e
dentro di essa settarne le proprietà.

Esempio: potrei creare un oggetto della classe JFrame per creare una finestra, e poi settarne le
proprietà con i metodi che tale classe mi mette a disposizione (esempio: setta il titolo, le
dimensioni, la posizione). Mi conviene però definire una mia classe che fa l'extends di JFrame e
dentro di se setta le suddette proprietà.
JFrame
La prima cosa che bisogna fare è creare un Frame (la finestra) vuoto che poi conterrà le varie
componenti.
La classe della libreria Swing che bisogna utilizzare è JFrame.
In particolare bisogna definire una propria classe che estende la suddetta JFrame.

ESEMPIO O1
import javax.swing.*;

public class Finestra extends JFrame
{
      public Finestra( )
      {
            //indichiamo le dimensioni della finestra
            setSize(300, 200);
      }

       //creamo direttamente qui un main
       public static void main(String [] args){
             Finestra f = new Finestra();

               //indica che quando si chiude la finestra deve terminare l'applicaz
               f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

               //rende visibile la finestra che di partenza è invisibile
               f.setVisible(true);
       }
}

Creamo quindi una classe Finestra che estende la classe JFrame.
Nel costruttore Finestra( ) settiamo le dimensioni della finestra utilizzando il metodo ereditato
setSize(...) che prende in input la larghezza e l'altezza.

Potremmo creare ora una classe esterna Main che ci crea un oggetto del tipo Finestra appena
definito. Per velocizzare definiamo però il metodo main direttamente nella classe Finestra e stesso
in esso creiamo un oggetto di tipo Finestra.

Creato l'oggetto, vengono utilizzati poi i metodi ereditati:
    – setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
    – setVisible(true);
Il primo indica che quando si chiude la finestra deve terminare anche l'applicazione; il secondo
rende visibile la finestra (perché all'inizio è creata invisibile).

Le caratteristiche di un frame
La classe Frame ovviamente mette a disposizione una serie di metodi per settarne le caratteristiche.

Costruttore
Già a partire dal costruttore abbiamo un overload del costruttore base da parte di un costruttore che
prende in input una stringa:
       JFrame(String titoloFinestra);
Imposta tale stringa come titolo della finestra.
Altre prorietà del Frame
dispose( )
chiude la finestra e rilascia le risorse del sistema utilizzate per la sua creazione.

setIconImage(Image img)
riceve un oggetto Image e lo utilizza come icona della finestra.

setTitle(String title)
modifica il titolo della finesta

setResizable(boolean resizable)
se riceve true come parametro rende la finestra ridimensionabile dall'utente.

setSize(int width, int height)
già l'abbiamo visto: setta le dimensioni della finestra. Se non viene chiamato all'inizio la finestra
sarà 0x0.

setLocation(int x, int y)
posiziona la finestra a x pixel orizzontalmente e y pixel verticalmente dall'angolo superiore sinistro
dello schermo

setVisible(boolean visible)
già l'abbiamo visto: se visible è true, rende visibile la finestra (che alla sua creazione è invisibile).

pack( )
ridimensiona la finestra tenendo conto delle dimensioni ottimali di ciascuno dei componenti
presenti all’interno.

setDefaultCloseOperation(int operation)
già l'abbiamo visto: imposta l’azione da eseguire alla pressione del bottone close.
Operation disponibili:
JFrame.DO NOTHING ON CLOSE (nessun effetto), JFrame.HIDE ON CLOSE (nasconde la
finestra), JFrame.DISPOSE ON CLOSE (chiude la finestra e libera le risorse di sistema) e
JFrame.EXIT ON CLOSE (chiude la finestra e conclude l’esecuzione del programma).

Frame con qualche proprietà settata in più
import javax.swing.*;
public class Finestra extends JFrame
{
      public static final int DEFAULT_WIDTH = 300;
      public static final int DEFAULT_HEIGHT = 200;

        public Finestra( )
        {
              //chiamo il costruttore della superclasse JFrame
              super("La Mia Finestra");
              setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
        }

        public static void main(String [] args){
              Finestra f = new Finestra();
              f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              f.setVisible(true);
        }
}
L'anatomia di un JFrame
Per poter lavorare con i Frame Swing è opportuno conoscere in linea e generale la struttura della
superficie.

La superficie di un frame Swing è coperta da quattro lastre:
Glass Pane:
La lastra di vetro è nascosta di default ed ha il compito di catturare gli eventi di input sulla
finestra. Normalmente è completamente trasparente a meno che venga implementato il metodo
paintComponent del GlassPane. Poichè è davanti a tutte le altre, qualsiasi oggetto è disegnato su
questa lastra nasconde qualsiasi altro è disegnato sulle altre .
Content Pane:
La lastra dei contenuti è la più importante perché è quella che ospita i componenti che volete
visualizzare nella finestra e la maggior parte dei programmatori Java lavora solo su questa .
Layered Pane:
Contiene la lastra dei contenuti ed eventualmente i menu. I menu, infatti, non vengono mai aggiunti
al Content Pane ma a questa lastra.
Root Pane:
La lastra radice ospita la lastra di vetro insieme con la lastra dei contenuti e i bordi della finestra.

Quello che interessa a noi è quindi principalmente il Content Pane.
Il Content Pane, come anche il JFrame stesso, estende la più generica classe Content in quanto
appunto è un contenitore.

Quindi la procedura da seguire è:
   1) Creo un JFrame;
   2) Prendo il suo Content Pane tramite l'apposito metodo getContentPane() della classe JFrame;
   3) Aggiungere i vari componenti al Content Pane.
Aggiungere componeti al JFrame
Come detto in precedenza non si inseriscono direttamente le componenti nel JFrame ma bisogna
prendere il suo Content Pane tramite il metodo getContentPane () ed utilizzare quello.

import javax.swing.*;
import java.awt.*;

public class Finestra extends JFrame
{
      public static final int DEFAULT_WIDTH = 300;
      public static final int DEFAULT_HEIGHT = 200;

       //creo un label (un'etichetta)
       JLabel jl = new Jlabel("Hello!!");

       public Finestra( )
       {
             //chiamo il costruttore della superclasse JFrame
             super("La Mia Finestra");
             setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

              //ottengo il content pane
              Container c = this.getContentPane();

              //aggiungo il label al content pane
              c.add(j1);
       }

       public static void main(String [] args){
             Finestra f = new Finestra();
             f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             f.setVisible(true);
       }
}

Abbiamo anticipato l'utilizzo di uno dei possibili componenti: Jlabel.
Lo rivedremo in seguito, per il momento anticipiamo che non è nient'altro che un'etichetta, una
stringa che è possibile visualizzare.

Per aggiungere una componente abbiamo visto che è possibile utilizzare il metodo:
       add(Component comp)

Questo metodo viene ereditato dalla classe Container e prende in input come parametro un oggetto
della classe Component.
I Pannelli: JPanel
Come detto in precedenza non si inseriscono direttamente le componenti nel JFrame ma bisogna
prendere il suo Content Pane tramite il metodo getContentPane () ed utilizzare quello.
In realtà in genere tali componenti non vengono inseriti direttamente sul Content Pane ma si creano
vari contenitori, i Pannelli (identificati dalla classe JPanel) e si inseriscono in essi.
Quindi in genere:
    1)   Creo un JFrame;
    2)   Prendo il suo Content Pane tramite l'apposito metodo getContentPane() della classe JFrame;
    3)   Creo un JPanel;
    4)   Inserisco gli elementi nel JPanel;
    5)   Aggiungo il JPanel al Content Pane del JFrame.

Per i Pannelli, come per JFrame, posso creare una classe che estende la classe JPanel e faccio
l'override dei suoi metodi per estenderne o personalizzare le sue funzionalità oppure creare ed
utlizziare direttamente un oggetto di tipo JPanel.

import javax.swing.*;
import java.awt.*;

public class Finestra extends JFrame
{
      public static final int DEFAULT_WIDTH = 300;
      public static final int DEFAULT_HEIGHT = 200;

         //creo un label (un'etichetta)
         JLabel jl = new JLabel("Hello!!");

         //creo un pannello
         Jpanel p1 = new JPanel();

         public Finestra( )
         {
               //chiamo il costruttore della superclasse JFrame
               super("La Mia Finestra");
               setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

                //ottengo il content pane
                Container c = this.getContentPane();

                //aggiungo il label al pannello
                p1.add(jl);

                //aggiungo il pannello al content pane
                c.add(p1);
         }

         public static void main(String [] args){
               Finestra f = new Finestra();
               f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               f.setVisible(true);
         }
}
LayoutManager: posizionamento dei componenti
I Layout Manager sono oggetti che si occupano di gestire la strategia di posizionamento
dei componenti all’interno di un contenitore.

Layout Manager è in realtà un'interfaccia che va implementata.
La libreria SWING fornisce un certo numero di classi che implementano tale interfaccia e che
quindi possiamo utilizzare.

Ogni container nasce con un certo Layout Manager ma è possibile assegnarne a piacimento con il
metodo:
       public void setLayout(LayoutManager m)

Vediamo le suddette classi che implementano LayoutManager che Swing mette a disposizione:

FlowLayout
Riempie il contenitore con le componenti disponendole su righe da sinistra verso destra; quando si
riempie la prima riga si passa alla successiva.
Tra i vari costruttori, i più importanti sono:
    – public FlowLayout();
    – public FlowLayout(int allin);
        Il parametro intero allin indica l'allineamento; possiamo utilizzare valori prestabiliti
        utilizzando le costanti:
        • FlowLayout.LEFT (allineamento a sinistra)
        • FlowLayout.CENTER (allineamento al centro)
        • FlowLayout.RIGHT (allineamento a destra)

GridLayout
Suddivide il contenitore in una griglia in celle di uguali dimensioni.
Le dimensioni della griglia vengono definite mediante il costruttore:
   – public GridLayout(int rows, int columns)

I componenti all’interno della griglia assumono automaticamente la stessa dimensione, dividendo
equamente lo spazio disponibile.
Man mano che inserisco gli oggetti mi riempiono in ordine la griglia.

BorderLayout
Suddivide il contenitore esattamente in cinque aree, disposte a croce, come in figura:
                               Ogni zona può contenere uno ed un solo componente (che di fatto
                               però può essere un contenitore a sua volta).
                               Se una o più zone non vengono riempite, allora i componenti nelle
                               altre zone sono estesi a riempire le zone vuote.
                               Il programmatore può decidere in quale posizione aggiungere un
                               controllo utilizzando la variante del metodo add sempre presente
                               nella classe Containe r:
                                      public void add(Component c, String s);
                               dove come secondo parametro vado a passargi l'indicazione di dove
                               inserire il componente utilizzabili le costanti:
                               BorderLayout.NORTH, BorderLayout.SOUTH,
BorderLayout.CENTER, BorderLayout.EAST e BorderLayout.WEST .
Esempio con LayoutManager di tipo FlowLayout

import javax.swing.*;
import java.awt.*;

public class Finestra2 extends JFrame
{
      public static final int DEFAULT_WIDTH = 300;
      public static final int DEFAULT_HEIGHT = 200;

      //creo   dei label (un'etichetta)
      JLabel   l1 = new JLabel("uno");
      JLabel   l2 = new JLabel("due");
      JLabel   l3 = new JLabel("tre");
      JLabel   l4 = new JLabel("quattro");
      JLabel   l5 = new JLabel("cinque");

      //creo un pannello
      JPanel p1 = new JPanel();

      public Finestra2( )
      {
            //chiamo il costruttore della superclasse JFrame
            super("La Mia Finestra 2");
            setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);

             //ottengo il content pane
             Container c = this.getContentPane();

             //setto il layout del pannello
             p1.setLayout(new FlowLayout());

             //aggiungo i label al pannello
             p1.add(l1);
             p1.add(l2);
             p1.add(l3);
             p1.add(l4);
             p1.add(l5);

             //aggiungo il pannello al content pane
             c.add(p1);
      }

      public static void main(String [] args){
            Finestra2 f = new Finestra2();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
      }
}
Paranoramica di alcune componenti

I nomi delle classi per la maggior parte dei componenti dell’interfaccia utente Swing iniziano con la
lettera J.

JTextField
E' un campo per insierire del testo.
Bisogna fornire al costruttore, come parametro, l’ampiezza; cioè il numero approssimato di caratteri
che vi aspettate verranno inseriti dall’utente (gli utenti possono comunque inserire più caratteri,
però verranno visualizzati il numero di caratteri indicato).
        JTextField xField=new JTextField(5);

JLabel
Semplici etichette di testo. Al costruttore posso passare il testo da visualizzare.
       JLabel xField=new Jlabel("testo");

JButton
Sono dei bottoni. Al costruttore può essere fornita una stringa che fungerà da etichetta,
un’immagine come icona o entrambe .
Progettazione di un'interfaccia grafica
In genere è utile, in fase progettuale, ricorrere descrivere l’insieme dei componenti a partire dal
componente più esterno per poi procedere a mano a mano verso quelli più interni:
   1) Si definisce il top level container, il JFrame, su cui si vuole lavorare ;
   2) Si assegna un layout manager al content pane del JFrame, in modo da suddividerne la
      superficie in aree più piccole.
   3) Per ogni area messa a disposizione dal layout manager è possibile definire un JPanel. Ogni
      sotto pannello pò utilizzare un layout manager differente.
   4) Ogni pannello identificato nel terzo passaggio può essere sviluppato ulteriormente, creando
      al suo interno ulteriori pannelli o disponendo dei controlli.
Una volta conclusa la fase progettuale, si può passare a scrivere il codice relativo all’interfaccia: in
questo secondo momento è utile realizzare dapprima il codice relativo ai componenti atomici,
quindi quello dei contenitori e infine quello del JFrame.
Puoi anche leggere