Skip to content

5. Interfacce grafiche

In questa sede intendiamo illustrare come realizzare interfacce grafiche con JAVA. Vediamo innanzitutto quali sono le classi di base che ci consentono di costruire un'interfaccia grafica. In una prima fase non utilizzeremo alcun strumento di generazione automatica. Successivamente utilizzeremo JBuilder, uno strumento di sviluppo di Borland/Inprise che facilita lo sviluppo di applicazioni Java e in particolare la creazione di interfacce grafiche.

5.1. Nozioni di base sulle interfacce grafiche

5.1.1. Una finestra semplice

Consideriamo il codice seguente:


// classi importate
import javax.swing.*;
import java.awt.*;
    
    // la classe modulo
  public class form1 extends JFrame {
    
      // il costruttore
      public form1() {
            // titolo della finestra
            this.setTitle("Mon premier formulaire");
            // dimensioni della finestra
            this.setSize(new Dimension(300,100));
      }//costruttore
  
      // funzione di test
      public static void main(String[] args) {
            // viene visualizzato il modulo
            new form1().setVisible(true);
      }
  }//classe

L'esecuzione del codice precedente visualizza la seguente finestra:

Image

Un'interfaccia grafica deriva in genere dalla classe base JFrame:


  public class form1 extends JFrame {

La classe base JFrame definisce una finestra di base con pulsanti di chiusura, ingrandimento/riduzione, dimensioni regolabili, ecc... e gestisce gli eventi su questi oggetti grafici. Qui specializziamo la classe base assegnandole un titolo e specificandone la larghezza (300 pixel) e l’altezza (100 pixel). Ciò avviene nel suo costruttore:


      // il costruttore
      public form1() {
            // titolo della finestra
            this.setTitle("Mon premier formulaire");
            // dimensioni della finestra
            this.setSize(new Dimension(300,100));
      }//costruttore

Il titolo della finestra viene impostato dal metodo setTitle e le sue dimensioni dal metodo setSize. Questo metodo accetta come parametro un oggetto Dimension(largeur,hauteur), dove largeur e hauteur sono rispettivamente la larghezza e l’altezza della finestra espresse in pixel.

Il metodo main avvia l'applicazione grafica nel modo seguente:


            new form1().setVisible(true);

Viene quindi creato un modulo di tipo form1 (new form1()) e visualizzato (setVisible(true)), dopodiché l’applicazione si mette in ascolto degli eventi che si verificano sul modulo (clic, spostamenti del mouse, ...) ed esegue quelli gestiti dal modulo stesso. In questo caso, il nostro modulo non gestisce altri eventi oltre a quelli gestiti dalla classe base JFrame (clic sui pulsanti di chiusura, ingrandimento/riduzione, modifica delle dimensioni della finestra, spostamento della finestra, ...).

Quando si testa questo programma avviandolo da una finestra DOS con il comando:

java form1

per eseguire il file form1.class, si nota che quando si chiude la finestra visualizzata, non si “riacquista il controllo” nella finestra DOS, come se il programma non fosse terminato. È effettivamente così. L’esecuzione del programma avviene nel modo seguente:

  • inizialmente viene avviato un primo thread di esecuzione per eseguire il metodo main
  • quando questo crea il modulo e lo visualizza, viene creato un secondo thread per gestire specificamente gli eventi relativi al modulo
  • dopo tale creazione e, nel nostro esempio, il thread del metodo main termina e rimane quindi solo il thread di esecuzione dell’interfaccia grafica.
  • Quando si chiude la finestra, questa scompare ma non interrompe il thread in cui era in esecuzione
  • Per il momento siamo costretti a interrompere questo thread premendo Ctrl-C nella finestra DOS da cui è stata avviata l’esecuzione del programma.

Verifichiamo l’esistenza di due thread distinti, uno in cui viene eseguito il metodo main e l’altro in cui viene eseguita la finestra grafica:


// classi importate
import javax.swing.*;
import java.awt.*;
import java.io.*;

// la classe del modulo
 public class form1 extends JFrame {
    
// il costruttore
 public form1() {
   // titolo della finestra
   this.setTitle("Mon premier formulaire");
   // dimensioni della finestra
   this.setSize(new Dimension(300,100));
}//costruttore
  
// funzione di test
 public static void main(String[] args) {
   // monitoraggio
   System.out.println("Début du thread main");
   // viene visualizzato il modulo
   new form1().setVisible(true);
   // monitoraggio
   System.out.println("Fin du thread main");  
}//main
}//classe

L'esecuzione produce i seguenti risultati:

Image

Si può notare che il thread main è terminato mentre la finestra è ancora visualizzata. La chiusura della finestra non termina il thread in cui era in esecuzione. Per arrestare questo thread, si preme nuovamente Ctrl-C nella finestra DOS.

Per concludere questo esempio, si notino i pacchetti importati:

  • javax.swing per la classe JFrame
  • java.awt per la classe Dimension

5.1.2. Gestire un evento

Nell’esempio precedente, dovremmo gestire noi stessi la chiusura della finestra in modo che, quando si verifica, l’applicazione venga arrestata, cosa che per ora non avviene. A tal fine, dobbiamo creare un oggetto che “ascolti” gli eventi che si verificano sulla finestra e rilevi l’evento “chiusura della finestra”. Questo oggetto viene chiamato “listener” o gestore di eventi. Esistono diversi tipi di gestori per i vari eventi che possono verificarsi sui componenti di un’interfaccia grafica. Per il componente JFrame, il listener si chiama WindowListener ed è un’interfaccia che definisce i seguenti metodi (cfr. documentazione Java)

Riepilogo dei metodi
 
void
windowActivated(WindowEvent e)
          La finestra diventa la finestra attiva
 
void
windowClosed(WindowEvent e)
          La finestra è stata chiusa
 
void
windowClosing(WindowEvent e)
          L'utente o il programma ha richiesto la chiusura della finestra
 
void
windowDeactivated(WindowEvent e)
          La finestra non è più quella attiva
 
void
windowDeiconified(WindowEvent e)
          La finestra passa dallo stato ridotto allo stato normale
 
void
windowIconified(WindowEvent e)
          La finestra passa dallo stato normale allo stato ridotto
 
void
windowOpened(WindowEvent e)
          La finestra diventa visibile per la prima volta
 

Ci sono quindi sette eventi che possono essere gestiti. Tutti i gestori ricevono come parametro un oggetto di tipo WindowEvent che per ora ignoriamo. L'evento che ci interessa in questo caso è la chiusura della finestra, evento che dovrà essere gestito dal metodo windowClosing. Per gestire questo evento, è possibile creare un oggetto Windowlistener utilizzando una classe anonima nel modo seguente:


   // creazione di un gestore di eventi
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definizione win

Il nostro gestore di eventi che implementa l'interfaccia WindowListener deve definire i sette metodi di tale interfaccia. Poiché vogliamo gestire solo la chiusura della finestra, definiamo il codice solo per il metodo windowClosing. Quando si verificheranno gli altri eventi, ne saremo avvisati ma non faremo nulla. Cosa faremo quando ci verrà segnalato che la finestra sta per chiudersi (windowClosing)? Chiuderemo l’applicazione:


     public void windowClosing(WindowEvent e){System.exit(0);}

Abbiamo qui un oggetto in grado di gestire gli eventi di una finestra in generale. Come lo associamo a una finestra specifica? La classe JFrame dispone di un metodo addWindowListener(WindowListener win) che consente di associare un gestore di eventi «finestra» a una determinata finestra. Quindi, qui, nel costruttore della finestra scriveremo:


   // creazione di un gestore di eventi
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definizione win
    // questo gestore di eventi gestirà gli eventi della finestra corrente
   this.addWindowListener(win);

Il programma completo è il seguente:


// classi importate
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// la classe modulo
 public class form2 extends JFrame {
    
// il costruttore
 public form2() {
   // titolo della finestra
   this.setTitle("Mon premier formulaire");
   // dimensioni della finestra
   this.setSize(new Dimension(300,100));
   // creazione di un gestore di eventi
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definizione win
   // questo gestore di eventi gestirà gli eventi della finestra corrente
   this.addWindowListener(win);
}//costruttore
  
// funzione di test
 public static void main(String[] args) {
   // si visualizza il modulo
   new form2().setVisible(true);
}//main
}//classe

Il pacchetto java.awt.event contiene l'interfaccia WindowListener. Quando si esegue questo programma e si chiude la finestra che è apparsa, nella finestra DOS in cui è stato avviato il programma si nota la fine dell'esecuzione del programma, cosa che prima non accadeva.

Nel nostro programma, la creazione dell’oggetto incaricato di gestire gli eventi della finestra risulta un po’ macchinosa, in quanto si è costretti a definire metodi anche per eventi che non si desidera gestire. In questo caso, invece di utilizzare l’interfaccia WindowListener, è possibile utilizzare la classe WindowAdapter. Quest’ultima implementa l’interfaccia WindowListener, con sette metodi vuoti. Derivando dalla classe WindowAdapter e ridefinendo solo i metodi che ci interessano, otteniamo lo stesso risultato che avremmo con l’interfaccia WindowListener, ma senza dover definire i metodi che non ci interessano. La sequenza

  • definizione del gestore di eventi
  • associazione del gestore alla finestra

può essere eseguita nel modo seguente nel nostro esempio:


   // creazione di un gestore di eventi
   WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
   };//definizione win
   // questo gestore di eventi gestirà gli eventi della finestra corrente
   this.addWindowListener(win);

Qui utilizziamo una classe anonima che deriva dalla classe WindowAdapter e ridefinisce il suo metodo windowClosing. Il programma diventa quindi:


// classi importate
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// la classe modulo
 public class form2 extends JFrame {
    
// il costruttore
 public form2() {
   // titolo della finestra
   this.setTitle("Mon premier formulaire");
   // dimensioni della finestra
   this.setSize(new Dimension(300,100));
   // creazione di un gestore di eventi
   WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
   };//definizione win
   // questo gestore di eventi gestirà gli eventi della finestra corrente
   this.addWindowListener(win); }//costruttore
  
// funzione di test
 public static void main(String[] args) {
   // si visualizza il modulo
   new form2().setVisible(true);
}//main
}//classe

Fornisce gli stessi risultati del programma precedente, ma è più semplice da scrivere.

5.1.3. Un modulo con un pulsante

Aggiungiamo ora un pulsante alla nostra finestra:


// classi importate
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// la classe del modulo
 public class form3 extends JFrame {
 
     // un pulsante
  JButton btnTest=null;
  Container conteneur=null;
  
  // il costruttore
  public form3() {
     // titolo della finestra
    this.setTitle("Formulaire avec bouton");
     // dimensioni della finestra
    this.setSize(new Dimension(300,100));
    // creazione di un gestore di eventi
    WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
    };//definizione win
     // questo gestore di eventi gestirà gli eventi della finestra corrente
    this.addWindowListener(win);
     // si recupera il contenitore della finestra
    conteneur=this.getContentPane();
     // si sceglie un gestore di formattazione dei componenti in questo contenitore
    conteneur.setLayout(new FlowLayout());
    // si crea un pulsante
    btnTest=new JButton();
    // si imposta la sua etichetta
    btnTest.setText("Test");
    // si aggiunge il pulsante al contenitore
    conteneur.add(btnTest);
  }//costruttore
  
  // funzione di test
 public static void main(String[] args) {
    // si visualizza il modulo
   new form3().setVisible(true);
}//main
}//classe

Una finestra JFrame dispone di un contenitore in cui è possibile inserire componenti grafici (pulsanti, caselle di controllo, elenchi a discesa, ecc.). Questo contenitore è accessibile tramite il metodo getContentPane della classe JFrame:


  Container conteneur=null;
..........
     // si recupera il contenitore della finestra
    conteneur=this.getContentPane();

Ogni componente viene inserito nel contenitore tramite il metodo add della classe Container. Pertanto, per inserire il componente C nell'oggetto conteneur precedente, si scriverà:

conteneur.add(C)

Dove viene posizionato questo componente nel contenitore? Esistono diversi gestori di disposizione dei componenti denominati XXXLayout, con XXX uguale, ad esempio, a Border, Flow, ... Ogni gestore di layout presenta le proprie caratteristiche specifiche. Ad esempio, il gestore FlowLayout dispone i componenti in riga partendo dalla parte superiore del modulo. Una volta completata una riga, i componenti vengono posizionati nella riga successiva. Per associare un gestore di layout a una finestra JFrame, si utilizza il metodo setLayout della classe JFrame nella forma:

    setLayout(objet XXXLayout);

Pertanto, nel nostro esempio, per associare un gestore di tipo FlowLayout alla finestra, abbiamo scritto:


     // si sceglie un gestore di formattazione dei componenti in questo contenitore
    conteneur.setLayout(new FlowLayout());

È possibile non utilizzare un gestore di layout e scrivere:

    setLayout(null);

In questo caso, sarà necessario specificare le coordinate precise del componente all’interno del contenitore nella forma (x, y, larghezza, altezza), dove (x, y) sono le coordinate dell’angolo superiore sinistro del componente nel contenitore. È questo il metodo che useremo più spesso d’ora in poi.

Ora sappiamo come vengono posizionati i componenti nel contenitore (add) e dove (setLayout). Resta da conoscere i

componenti che è possibile inserire in un contenitore. In questo caso inseriamo un pulsante implementato dalla classe javax.swing.JButton:



  JButton btnTest=null;
..........
     // si crea un pulsante
    btnTest=new JButton();
    // si imposta la sua etichetta
    btnTest.setText("Test");
    // si aggiunge il pulsante al contenitore
    conteneur.add(btnTest);

Quando si esegue il programma, viene visualizzata la seguente finestra:

Image

Se si ridimensiona il modulo sopra riportato, viene automaticamente richiamato il gestore di layout del contenitore per riposizionare i componenti:

Image

Questo è il principale vantaggio dei gestori di layout: mantenere una disposizione coerente dei componenti al variare delle dimensioni del contenitore. Utilizziamo il gestore di layout null per vedere la differenza. Il pulsante viene ora posizionato nel contenitore tramite le seguenti istruzioni:


// si seleziona un gestore di formattazione dei componenti in questo contenitore
conteneur.setLayout(null);
    // si crea un pulsante
btnTest=new JButton();
    // si imposta il testo del pulsante
btnTest.setText("Test");
    // si impostano la posizione e le dimensioni
btnTest.setBounds(10,20,100,20);
     // si aggiunge il pulsante al contenitore
conteneur.add(btnTest);

Qui si posiziona esplicitamente il pulsante nel punto (10,20) del modulo e se ne impostano le dimensioni a 100 pixel di larghezza e 20 pixel di altezza. La nuova finestra diventa la seguente:

Image

Se si ridimensiona la finestra, il pulsante rimane nella stessa posizione.

Image

Se si fa clic sul pulsante Test, non accade nulla. Infatti, non abbiamo ancora associato alcun gestore di eventi al pulsante. Per conoscere il tipo di gestori di eventi disponibili per un determinato componente, è possibile cercare nella definizione della sua classe i metodi addXXXListener che consentono di associare un gestore di eventi al componente. La classe javax.swing.JButton deriva dalla classe javax.swing.AbstractButton, nella quale sono presenti i seguenti metodi:

Riepilogo dei metodi
 
void
addActionListener(ActionListener l)
 
void
addChangeListener(ChangeListener l)
 
void
addItemListener(ItemListener l)
 

In questo caso, è necessario consultare la documentazione per sapere quale gestore di eventi gestisce il clic sul pulsante. Si tratta dell'interfaccia ActionListener. Questa definisce un solo metodo:

Riepilogo dei metodi
 
void
actionPerformed(ActionEvent e)
 

Il metodo riceve un parametro ActionEvent che per il momento ignoreremo. Per gestire il clic sul pulsante btntest del nostro programma, gli associamo innanzitutto un gestore di eventi:


        btnTest.addActionListener(new ActionListener()
         {
                public void actionPerformed(ActionEvent evt){
                  btnTest_clic(evt);
              }
            }//classe anonima
    );//gestore di eventi

In questo caso, il metodo actionPerformed rimanda al metodo btnTest_clic, che definiamo come segue:


public void btnTest_clic(ActionEvent evt){
  // monitoraggio console
System.out.println("clic sur bouton");
}//btnTest_click

Ogni volta che l'utente fa clic sul pulsante Test, viene visualizzato un messaggio sulla console. È quanto mostra la seguente esecuzione:

Image

Il programma completo è il seguente:


// classi importate
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// classe modulo
 public class form4 extends JFrame {
 
     // un pulsante
  JButton btnTest=null;
  Container conteneur=null;
  
  // il costruttore
  public form4() {
     // titolo della finestra
    this.setTitle("Formulaire avec bouton");
     // dimensioni della finestra
    this.setSize(new Dimension(300,100));
    // creazione di un gestore di eventi
    WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
    };//definizione win
     // questo gestore di eventi gestirà gli eventi della finestra corrente
    this.addWindowListener(win);
     // si recupera il contenitore della finestra
    conteneur=this.getContentPane();
     // si sceglie un gestore di formattazione dei componenti in questo contenitore
    conteneur.setLayout(null);
    // si crea un pulsante
    btnTest=new JButton();
    // si imposta la sua etichetta
    btnTest.setText("Test");
    // si definiscono la posizione e le dimensioni
    btnTest.setBounds(10,20,100,20);
     // gli si associa un gestore di eventi
    btnTest.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent evt){
              btnTest_clic(evt);
          }
        }//classe anonima
    );//gestore di eventi
     // si aggiunge il pulsante al contenitore
    conteneur.add(btnTest);
  }//costruttore

    public void btnTest_clic(ActionEvent evt){
       // monitoraggio console
    System.out.println("clic sur bouton");
  }//btnTest_click
    
  // funzione di test
 public static void main(String[] args) {
    // viene visualizzato il modulo
   new form4().setVisible(true);
}//main
}//classe

5.1.4. I gestori di eventi

I principali componenti Swing che presenteremo sono le finestre (JFrame), i pulsanti (JButton), le caselle di controllo (JCheckBox), i pulsanti di opzione (JButtonRadio), i menu a tendina (JComboBox), gli elenchi (JList), i cursori (JScrollBar), le etichette (JLabel), le caselle di immissione a riga singola (JTextField) o a più righe (JTextArea), i menu (JMenuBar), gli elementi di menu (JMenuItem).

Le tabelle seguenti riportano un elenco di alcuni gestori di eventi e degli eventi a cui sono associati.

Gestore
Componente/i
Metodo di registrazione
Evento
ActionListener
JButton, JCheckbox, JButtonRadio, JMenuItem
public void addActionListener(ActionListener)
clic sul pulsante, sulla casella di controllo, sul pulsante di opzione, sull'elemento di menu
 
JTextField
ItemListener
JComboBox, JList
public void addItemListener(ItemListener)
L'elemento selezionato è cambiato
InputMethodListener
JTextField, JTextArea
public void addMethodInputListener(InputMethodListener)
il testo nel campo di immissione è cambiato oppure la posizione del cursore di immissione è cambiata
CaretListener
JTextField, JTextArea
public void addcaretListener(CaretListener)
Il cursore di immissione ha cambiato posizione
AdjustmentListener
JScrollBar
public void addAdjustmentListener(AdjustmentListener)
il valore del variatore è cambiato
MouseMotionListener
 
public void addMouseMotionListener(MouseMotionListener)
il mouse si è spostato
WindowListener
JFrame
public void addWindowlistener(WindowListener)
evento finestra
MouseListener
 
public void addMouselistener(MouseListener)
eventi del mouse (clic, ingresso/uscita dall'area di un componente, pulsante premuto, rilascio)
FocusListener
 
public void addFocuslistener(FocusListener)
evento focus (acquisito, perso)
KeyListener
 
public void addKeylistener(KeyListener)
evento tastiera (tasto digitato, premuto, rilasciato)
Componente
Metodo di registrazione dei gestori di eventi
JButton
public void addActionListener(ActionListener)
JCheckbox
public void addItemListener(ItemListener)
JCheckboxMenuItem
public void addItemListener(ItemListener)
JComboBox
public void addItemListener(ItemListener)
public void addActionListener(ActionListener)
Container
public void addContainerListener(ContainerListener)
JComponent
public void addComponentListener(ComponentListener)
public void addFocusListener(FocusListener)
public void addKeyListener(KeyListener)
public void addMouseListener(MouseListener)
public void addMouseMotionListener(MouseMotionListener)
JFrame
public void addWindowlistener(WindowListener)
JList
public void addItemListener(ItemListener)
JMenuItem
public void addActionListener(ActionListener)
JPanel
come contenitore
JScrollPane
come contenitore
JScrollBar
public void addAdjustmentListener(AdjustmentListener)
JTextComponent
public void addInputMethodListener(InputMethodListener)
public void addCaretListener(CaretListener)
JTextArea
come JTextComponent
JTextField
come JTextComponent
public void addActionListener(ActionListener)

Tutti i componenti, ad eccezione di quelli di tipo TextXXX, essendo derivati dalla classe JComponent, possiedono anche i metodi associati a tale classe.

5.1.5. I metodi dei gestori di eventi

La tabella seguente elenca i metodi che devono essere implementati dai diversi gestori di eventi.

Interfaccia
Metodi
ActionListener
public void actionPerformed(ActionEvent)
AdjustmentListener
public void adjustmentValueChanged(AdjustmentEvent)
ComponentListener
public void componentHidden(ComponentEvent)
public void componentMoved(ComponentEvent)
public void componentResized(ComponentEvent)
public void componentShown(ComponentEvent)
ContainerListener
public void componentAdded(ContainerEvent)
public void componentRemoved(ContainerEvent)
FocusListener
public void focusGained(FocusEvent)
public void focusLost(FocusEvent)
ItemListener
public void itemStateChanged(ItemEvent)
KeyListener
public void keyPressed(KeyEvent)
public void keyReleased(KeyEvent)
public void keyTyped(KeyEvent)
MouseListener
public void mouseClicked(MouseEvent)
public void mouseEntered(MouseEvent)
public void mouseExited(MouseEvent)
public void mousePressed(MouseEvent)
public void mouseReleased(MouseEvent)
MouseMotionListener
public void mouseDragged(MouseEvent)
public void mouseMoved(MouseEvent)
TextListener
public void textValueChanged(TextEvent)
InputmethodListener
public void InputMethodTextChanged(InputMethodEvent)
public void caretPositionChanged(InputMethodEvent)
CaretLisetner
public void caretUpdate(CaretEvent)
WindowListener
public void windowActivated(WindowEvent)
public void windowClosed(WindowEvent)
public void windowClosing(WindowEvent)
public void windowDeactivated(WindowEvent)
public void windowDeiconified(WindowEvent)
public void windowIconified(WindowEvent)
public void windowOpened(WindowEvent)

5.1.6. Le classi adattatrici

Come abbiamo visto per l'interfaccia WindowListener, esistono classi denominate XXXAdapter che implementano le interfacce XXXListener con metodi vuoti. Un gestore di eventi derivato da una classe XXXAdapter può quindi implementare solo una parte dei metodi dell’interfaccia XXXListener, ovvero quelli di cui l’applicazione ha bisogno.

Supponiamo di voler gestire i clic del mouse su un componente Frame f1. Si potrebbe associargli un gestore di eventi tramite:

    f1.addMouseListener(new gestionnaireSouris());

e scrivere:


    public class gestionnaireSouris implements MouseListener{
         // si scrivono i 5 metodi dell'interfaccia MouseListener
        // mouseClicked, ..., mouseReleased)
    }// fine classe

Poiché si desidera gestire solo i clic del mouse, è tuttavia preferibile scrivere:


public class gestionnaireSouris extends MouseAdapter{
        // si scrive un unico metodo, quello che gestisce i clic del mouse
        public void mouseClicked(MouseEvent evt){

        }
}// fine classe

La tabella seguente riporta le classi di adattatori dei diversi gestori di eventi:

Gestore di eventi
Adattatore
ComponentListener
ComponentAdapter
ContainerListener
ContainerAdapter
FocusListener
FocusAdapter
KeyListener
KeyAdapter
MouseListener
MouseAdapter
MouseMotionListener
MouseMotionAdapter
WindowListener
WindowAdapter

5.1.7. Conclusione

Abbiamo appena illustrato i concetti di base della creazione di interfacce grafiche in Java:

  • creazione di una finestra
  • creazione di componenti
  • associazione dei componenti alla finestra tramite un gestore di layout
  • associazione dei gestori di eventi ai componenti

Ora, invece di costruire interfacce grafiche “a mano” come abbiamo appena fatto, useremo JBuilder, uno strumento di sviluppo Java di Borland/Inprise nella sua versione 4, e supérieure.Nous utilizzeremo i componenti della libreria java.swing attualmente raccomandata da Sun, creatore di Java.

5.2. Creazione di un'interfaccia grafica con JBuilder

5.2.1. Il nostro primo progetto con JBuilder

Per familiarizzare con JBuilder, creiamo un'applicazione molto semplice: una finestra vuota.

  1. Avviate JBuilder e selezionate l’opzione File/Nuovo progetto. Verrà quindi visualizzata la prima pagina di una procedura guidata:

Image

  1. Compilate i seguenti campi:
Nome del progetto
Inizio
Questo comporterà la creazione di un file di progetto début.jpr nella cartella indicata nel campo "Nome della cartella del progetto"
Percorso radice
Indicare la cartella in cui verrà creata la cartella del progetto che si intende creare.
Nome della cartella del progetto
indicare il nome della cartella in cui verranno inseriti tutti i file del progetto. Questa cartella verrà creata nella directory indicata nel campo "Percorso radice"

I file sorgente (.java, .html, ...), i file di destinazione (.class, ...) e i file di backup potrebbero essere collocati in cartelle diverse. Lasciando vuoti i relativi campi, verranno collocati nella stessa cartella del progetto.

Inserite [suivant]

  1. Una schermata conferma le scelte effettuate nella fase precedente

Image

Selezionare [suivant]

  1. Una nuova schermata richiede di specificare le caratteristiche del progetto:

Image

Selezionare [terminer]

  1. Verificare che l'opzione Voir/projet sia selezionata. Nella finestra a sinistra dovrebbe essere visibile la struttura del progetto.

Image

  1. Ora creiamo un'interfaccia grafica in questo progetto. Selezionate l'opzione Fichier/Nouveau/Application:

Image

Nel campo classe, inserite il nome della classe che verrà creata. In questo caso abbiamo utilizzato lo stesso nome del progetto.

Eseguire [suivant]

  1. Viene visualizzata la schermata seguente:

Image

Classe
interfaceDébut
È il nome della classe corrispondente alla finestra che verrà creata
Titolo
È il testo che apparirà nella barra del titolo della finestra

Si noti che sopra abbiamo specificato che la finestra venga centrata sullo schermo all'avvio dell'applicazione.

Digitare [Terminer]

  1. È proprio ora che JBuilder si rivela utile.
  • Genera i file sorgente .java delle due classi di cui abbiamo specificato i nomi: la classe dell’applicazione e quella dell’interfaccia grafica. Questi due file compaiono nella struttura del progetto nella finestra a sinistra

Image

  • Per accedere al codice generato per le due classi, basta fare doppio clic sul file .java corrispondente. Torneremo in seguito sul codice generato.
  • Verificate che l’opzione Voir/Structure sia selezionata. Consente di visualizzare la struttura della classe attualmente selezionata (doppio clic sul file .java). Ecco, ad esempio, la struttura della classe début:

Image

Qui si possono vedere:

  • le librerie importate (imports)

  • che è presente un costruttore début()

  • che esiste un metodo statico main()

  • che esiste un attributo packFrame

Qual è l’utilità di avere accesso alla struttura di una classe?

  • si ha una visione d'insieme della stessa. Ciò è utile se la classe è complessa.

  • è possibile accedere al codice di un metodo cliccandoci sopra nella finestra della struttura della classe. Anche in questo caso è utile se la classe contiene centinaia di righe. Non è necessario scorrere tutte le righe per trovare il codice che si sta cercando.

Il codice generato da JBuilder è già utilizzabile. Selezionate Esegui/Esegui progetto oppure F9 e otterrete la finestra richiesta:

Image

Inoltre, si chiude correttamente quando si fa clic sul pulsante di chiusura della finestra. Abbiamo appena creato la nostra prima interfaccia grafica. Possiamo salvare il nostro progetto tramite l’opzione File/Chiudi progetti:

Image

Premendo Tous, tutti i progetti presenti nella finestra sopra riportata verranno selezionati. OK li chiuderà. Se siamo curiosi di accedere, tramite Esplora risorse di Windows, alla cartella del nostro progetto (quella indicata all’assistente all’inizio della configurazione del progetto), vi troveremo i seguenti file:

Image

Nel nostro esempio, avevamo richiesto che tutti i file (sorgente .java, destinazione .class, backup .jpr~) fossero nella stessa cartella del progetto .jpr.

5.2.2. I file generati da JBuilder per un'interfaccia grafica

Proviamo ora a dare un'occhiata ai file sorgente .java generati da JBuilder. È importante saper interpretare il codice generato, poiché nella maggior parte dei casi ci troveremo a dover aggiungere del codice a quello già generato. Iniziamo recuperando il nostro progetto: File/Apri progetto e selezioniamo il progetto début.jpr. Ritroviamo il progetto creato in precedenza.

5.2.2.1. La classe principale

Esaminiamo la classe début.java facendo doppio clic sul suo nome nella finestra che mostra l’elenco dei file del progetto. Abbiamo il seguente codice:

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

public class début {
  boolean packFrame = false;

   /**Creazione dell'applicazione*/
  public début() {
    interfaceDébut frame = new interfaceDébut();

     //Convalidare i frame con dimensioni predefinite
     //Comprimere i riquadri con informazioni sulle dimensioni preferite - ad es. in base alla loro disposizione
    if (packFrame) {
      frame.pack();
    }
    else {
      frame.validate();
    }
    //Centrare la finestra
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension frameSize = frame.getSize();
    if (frameSize.height > screenSize.height) {
      frameSize.height = screenSize.height;
    }
    if (frameSize.width > screenSize.width) {
      frameSize.width = screenSize.width;
    }
    frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
    frame.setVisible(true);
  }
   /**Metodo principale*/
  public static void main(String[] args) {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    new début();
  }
}

Commentiamo il codice generato:

  1. la funzione main imposta l'aspetto della finestra (setLookAndFeel) e crea un'istanza della classe début.
  2. Viene quindi eseguito il costruttore début(). Esso crea un'istanza frame della classe della finestra (new interfaceDébut()). Quest'ultima viene costruita ma non visualizzata.
  3. La finestra viene quindi ridimensionata in base alle informazioni disponibili per ciascuno dei suoi componenti (frame.validate). A questo punto inizia la sua esistenza autonoma, visualizzandosi e rispondendo alle richieste dell’utente.
  4. La finestra viene centrata sullo schermo, poiché ciò era stato richiesto durante la configurazione della finestra con l’assistente.

Vediamo cosa si otterrebbe se riducessimo il codice début.java al minimo indispensabile, come abbiamo fatto all’inizio del capitolo. Creiamo una nuova classe. Selezionate l’opzione File/Nuova classe:

Image

Assegnate alla nuova classe il nome début2 e modificate [Terminer]. Nel progetto compare un nuovo file:

Image

Il file début2.java viene ridotto alla sua forma più semplice:

public class début2 {
}

Completiamo la classe come segue:

public class début2 {
   // funzione principale
  public static void main(String args[]){
    // crea la finestra
    new interfaceDébut().show();    // oppure new interfaceDébut.setVisible(true);
  }//main
}//classe inizio2

La funzione main crea un'istanza della finestra interfaceDébut e la visualizza (show). Prima di eseguire il nostro progetto, è necessario segnalare che la classe contenente la funzione main da eseguire è ora la classe début2. Fare clic con il tasto destro del mouse sul progetto début.jpr e selezionare l’opzione propriétés, quindi la scheda Exécution:

21

Image

Qui è indicato che la classe principale è début (1). Premere il pulsante (2) per selezionare un’altra classe principale:

Image

Scegliere début2 e impostare [OK].

Image

Selezionare [OK] per confermare la scelta, quindi avviare l'esecuzione del progetto tramite Esegui/Esegui progetto o F9. Si ottiene la stessa finestra della classe début, tranne per il fatto che non è centrata poiché in questo caso non è stato richiesto. D'ora in poi non presenteremo più la classe principale generata da JBuilder poiché fa sempre la stessa cosa: creare una finestra. D'ora in poi ci concentreremo sulla classe di quest'ultima.

5.2.2.2. La classe della finestra

Diamo ora un’occhiata al codice generato per la classe interfaceDébut:

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

public class interfaceDébut extends JFrame {
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();

  /**Creare il frame*/
  public interfaceDébut() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(borderLayout1);
    this.setSize(new Dimension(400, 300));
    this.setTitle("Ma première interface graphique avec Jbuilder");
  }
   /**Sostituito, così possiamo uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }
}

5.2.2.2.1. Le librerie importate
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Si tratta delle librerie java.awt, java.awt.event e javax.swing. Le prime due erano le uniche disponibili per la creazione di interfacce grafiche con le prime versioni di Java. La libreria javax.swing è più recente. In questo caso, è necessaria per la finestra di tipo JFrame qui utilizzata.

5.2.2.2.2. Gli attributi
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();

JPanel è un tipo di contenitore in cui è possibile inserire componenti. BorderLayout è uno dei tipi di gestori di layout disponibili per posizionare i componenti all’interno del contenitore. In tutti i nostri esempi non utilizzeremo alcun gestore di formattazione e posizioneremo noi stessi i componenti in un punto specifico del contenitore. A tal fine, utilizzeremo il gestore di formattazione null.

5.2.2.2.3. Il generatore della finestra
   /**Creare il frame*/
  public interfaceDébut() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(borderLayout1);
    this.setSize(new Dimension(400, 300));
    this.setTitle("Ma première interface graphique avec Jbuilder");
  }
  1. Il costruttore inizia affermando che gestirà gli eventi sulla finestra (enableEvents), quindi avvia il metodo jbInit.
  2. Viene ottenuto (getContentPane) il contenitore (JPanel) della finestra (JFrame)
  3. Viene impostato il gestore di formattazione (setLayout)
  4. La dimensione della finestra è stata impostata (setSize)
  5. Il titolo della finestra è stato impostato (setTitle)

5.2.2.2.4. Il gestore degli eventi
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }

Il costruttore aveva specificato che la classe avrebbe gestito gli eventi della finestra. È il metodo processWindowEvent a svolgere questo compito. Inizia trasmettendo alla sua classe padre (JFrame) l’evento WindowEvent ricevuto; se quest’ultimo è l’evento WINDOW_CLOSING causato dal clic sul pulsante di chiusura della finestra, l’applicazione viene chiusa.

5.2.2.2.5. Conclusione

Il codice della classe della finestra è diverso da quello presentato nell’esempio all’inizio del capitolo. Se utilizzassimo uno strumento di generazione Java diverso da JBuilder, avremmo senza dubbio un codice ancora diverso. In pratica, accetteremo il codice prodotto da JBuilder per costruire la finestra, in modo da concentrarci esclusivamente sulla scrittura dei gestori di eventi dell’interfaccia grafica.

5.2.3. Disegnare un'interfaccia grafica

5.2.3.1. Un esempio

Nell’esempio precedente non avevamo inserito alcun componente nella finestra. Costruiamo ora una finestra con un pulsante, un’etichetta e un campo di immissione:

Image

I campi sono i seguenti:

nome
tipo
ruolo
1
lblSaisie
JLabel
una denominazione
2
txtSaisie
JTextField
un campo di immissione
3
cmdAfficher
JButton
per visualizzare in una finestra di dialogo il contenuto del campo di immissione txtSaisie

Procedendo come nel progetto precedente, create il progetto interface2.jpr senza inserire per il momento alcun componente nella finestra.

Image

Nella finestra sopra riportata, selezionate la classe interface2.java. A destra di questa finestra si trova un raccoglitore a schede:

Image

La scheda Source consente di accedere al codice sorgente della classe interface2.java. La scheda Conception consente di costruire visivamente la finestra. Selezionate questa scheda. Davanti a voi avete il contenitore della finestra, che accoglierà i componenti che vi trascinerete. Per il momento è vuoto. Nella finestra a sinistra è mostrata la struttura della classe:

Image

this
rappresenta la finestra
contentPane
il contenitore in cui verranno inseriti i componenti, nonché la modalità di disposizione di tali componenti all'interno del contenitore (BorderLayout per impostazione predefinita)
borderLayout1
un'istanza del gestore di formattazione

Selezionare l'oggetto this. La finestra delle proprietà apparirà quindi sulla destra:

Image

Alcune di queste proprietà meritano di essere segnalate:

background
per impostare il colore di sfondo della finestra
foreground
per impostare il colore degli elementi grafici nella finestra
JMenuBar
per associare un menu alla finestra
title
per assegnare un titolo alla finestra
resizable
per impostare il tipo di finestra
font
per impostare il carattere dei testi nella finestra

Con l'oggetto this ancora selezionato, è possibile ridimensionare il contenitore visualizzato sullo schermo trascinando i punti di ancoraggio situati attorno al contenitore:

Image

Ora siamo pronti per inserire dei componenti nel contenitore sopra indicato. Prima però, cambieremo il gestore di formattazione. Selezionare l’oggetto contentPane nella finestra della struttura:

Image

Quindi, nella finestra delle proprietà di questo oggetto, selezionate la proprietà layout e scegliete, tra i valori disponibili, il valore null:

Image

L'assenza di un gestore di formattazione ci consentirà di posizionare liberamente i componenti all'interno del contenitore. È ora il momento di selezionarli.

Quando è selezionato il riquadro Conception, i componenti sono disponibili in una cartella a schede nella parte superiore della finestra di progettazione:

Image

Per costruire l’interfaccia grafica, disponiamo dei componenti swing (1) e dei componenti awt (2). In questo caso utilizzeremo i componenti swing. Nella barra dei componenti sopra riportata, selezionate un componente JLabel (3), un componente JTextField (4) e un componente JButton (5) e posizionateli nel contenitore della finestra di progettazione.

Image

Ora personalizziamo ciascuno di questi 3 componenti:

  • l’etichetta (JLabel) jLabel1

Selezionate il componente per visualizzarne la finestra delle proprietà. In essa, modificate le seguenti proprietà: name: lblSaisie, text: Inserimento

  • il campo di immissione (JTextField) jTextfield1

Selezionare il componente per visualizzarne la finestra delle proprietà. In essa, modificare le seguenti proprietà: name: txtSaisie, text: lasciare vuoto

  • il pulsante (JButton): nome: cmdAfficher, testo: Visualizza

Ora abbiamo la seguente finestra:

Image

e la seguente struttura:

Image

Possiamo eseguire (F9) il nostro progetto per avere una prima anteprima della finestra in azione:

Image

Chiudete la finestra. Resta da scrivere la procedura associata al clic sul pulsante Afficher. Selezionate il pulsante per accedere alla sua finestra delle proprietà. Questa presenta due schede: propriétés e événements. Scegliete événements.

Image

La colonna di sinistra della finestra elenca gli eventi possibili relativi al pulsante. Un clic su un pulsante corrisponde all’evento actionPerformed. La colonna di destra contiene il nome della procedura chiamata quando si verifica l’evento corrispondente. Fare clic sulla cella a destra dell’evento actionPerformed:

Image

Jbuilder genera un nome predefinito per ogni gestore di eventi nel formato nomComposant_nomEvénement, in questo caso cmdAfficher_actionPerformed. È possibile cancellare il nome predefinito e inserirne uno diverso. Per accedere al codice del gestore cmdAfficher_actionPerformed è sufficiente fare doppio clic sul suo nome qui sopra. Si passa quindi automaticamente al riquadro del codice sorgente della classe, posizionato sullo scheletro del codice del gestore di eventi:

  void cmdAfficher_actionPerformed(ActionEvent e) {
 }

Non resta che completare questo codice. In questo caso, vogliamo visualizzare una finestra di dialogo contenente il contenuto del campo txtSaisie:

  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), 
"Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
 }

JOptionPane è una classe della libreria javax.swing. Consente di visualizzare messaggi accompagnati da un’icona o di richiedere informazioni all’utente. In questo caso, utilizziamo un metodo statico della classe:

Image

parentComponent
l’oggetto contenitore “genitore” della finestra di dialogo: in questo caso this.
messaggio
un oggetto da visualizzare. In questo caso il contenuto del campo di immissione
titolo
il titolo della finestra di dialogo
messageType
il tipo di messaggio da visualizzare. Determina l'icona che verrà visualizzata nella casella accanto al messaggio. Valori possibili:
INFORMATION_MESSAGE, QUESTION_MESSAGE, ERROR_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE

Eseguiamo la nostra applicazione (F9):

Image

Image

5.2.3.2. Il codice della classe della finestra

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


public class interface2 extends JFrame {
  JPanel contentPane;
  JLabel lblSaisie = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmdAfficher = new JButton();

  /**Creare il frame*/
  public interface2() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }
  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
  }
}

5.2.3.2.1. Gli attributi
  JPanel contentPane;
  JLabel lblSaisie = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmdAfficher = new JButton();

Si trovano il contenitore dei componenti di tipo JPanel e i tre componenti.

5.2.3.2.2. Il produttore
   /**Creare il frame*/
  public interface2() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }

Il costruttore interface2 è simile al costruttore della precedente interfaccia grafica analizzata. Le differenze si riscontrano nel metodo jbInit: il codice di costruzione della finestra dipende dai componenti che vi sono stati inseriti. È possibile riutilizzare il codice di jbInit inserendovi i propri commenti:

  private void jbInit() throws Exception  {
       // la finestra stessa (dimensioni, titolo)
this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
         // il contenitore dei componenti
    contentPane = (JPanel) this.getContentPane();
         // nessun gestore di formattazione per questo contenitore
    contentPane.setLayout(null);
         // l'etichetta lblSaisie (testo, posizione, dimensione)
         lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
        // il campo di immissione (posizione, dimensione)
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
         // il pulsante Visualizza (etichetta, posizione, dimensione)
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
         // il gestore degli eventi del pulsante
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
         // aggiunta dei 3 componenti al contenitore
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }//jbInit

Si notino due punti:

  • questo codice avrebbe potuto essere scritto a mano. Ciò significa che è possibile fare a meno di JBuilder per creare un'interfaccia grafica.
  • il modo in cui è impostato il gestore di eventi del pulsante cmdAfficher. Il gestore di eventi del componente cmdAfficher avrebbe potuto essere dichiarato tramite cmdAfficher.addActionListener(new gestore()) dove gestionnaire sarebbe una classe con un metodo pubblico actionPerformed incaricato di gestire il clic sul pulsante Afficher. In questo caso, JBuilder utilizza come gestore un'istanza di una classe anonima:
 new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }

Viene creata una nuova istanza dell'interfaccia ActionListener con il relativo metodo actionPerformed definito immediatamente dopo. Quest'ultimo si limita a chiamare un metodo della classe interface2. Tutto ciò non è altro che un espediente per definire, nella stessa classe della finestra, le procedure di gestione degli eventi dei componenti di tale finestra. Si potrebbe procedere in altro modo:

cmdAfficher.addActionListener(this)

il che fa sì che il metodo actionPerformed venga cercato in this, ovvero nella classe della finestra. Questo secondo metodo sembra più semplice, ma il primo presenta un vantaggio: consente di avere gestori diversi per pulsanti diversi, mentre il secondo metodo non lo permette. In quest'ultimo caso, infatti, l'unico metodo actionPeformed deve gestire i clic di pulsanti diversi e quindi deve prima identificare quale di essi ha generato l'evento prima di iniziare a funzionare.

5.2.3.2.3. I gestori di eventi

Ritroviamo quelli già studiati:

   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }

     // clic sul pulsante Visualizza
  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
  }

5.2.3.3. Conclusion

Dai due progetti esaminati, possiamo concludere che, una volta realizzata l’interfaccia grafica con JBuilder, il compito dello sviluppatore consiste nello scrivere i gestori degli eventi che desidera gestire per tale interfaccia grafica.

5.2.4. Cercare aiuto

Con Java, spesso si ha bisogno di aiuto, soprattutto a causa dell’elevatissimo numero di classi disponibili. Di seguito forniamo alcune indicazioni per trovare assistenza su una classe. Selezionate l’opzione Aiuto/Argomenti di aiuto dal menu.

Image

La schermata di aiuto presenta generalmente due finestre:

  • quella a sinistra, in cui si specifica ciò che si sta cercando. Presenta tre schede: Sommaire, Index e Chercher.
  • quella a destra, che mostra il risultato della ricerca

È disponibile una guida su come utilizzare la guida di JBuilder. Selezionate nella guida di JBuilder l’opzione Guida/Utilizzo della guida. Vi verrà quindi spiegato come utilizzare la guida. Vi verranno illustrate, ad esempio, le diverse componenti del visualizzatore della guida:

Image

Esaminiamo più da vicino le pagine Sommaire e Index.

5.2.4.1. Guida: Indice

Image

5.2.4.1.1. Indice: Introduzione a Java

Qui si trovano gli elementi di base di Java, ma non solo, come dimostra l’elenco degli argomenti trattati in questa opzione:

Image

5.2.4.1.2. Indice: Tutorial

Se selezioniamo l’opzione Tutorial dall’indice sopra riportato, la finestra a destra ci presenta un elenco dei tutorial disponibili:

Image

I tutorial di base sono particolarmente utili per iniziare a familiarizzare con JBuilder. Ne esistono molti altri oltre a quelli presentati sopra e, quando si desidera sviluppare un’applicazione, può essere utile verificare in anticipo se non ci sia un tutorial che possa aiutarci.

5.2.4.1.3. Indice: JDK

Selezionando l’opzione Java 2 JDK 1.3, si ha accesso a tutte le librerie di JDK. In genere non è qui che bisogna cercare se si hanno bisogno di informazioni su una classe specifica e non si sa in quale libreria si trovi. D’altra parte, questa opzione è utile se si desidera avere una visione d’insieme delle librerie di Java.

5.2.4.2. Guida: Indice

Selezionate la scheda Indice nella finestra a sinistra della guida. Questa opzione consente, ad esempio, di trovare informazioni su una classe. Supponiamo, ad esempio, che vogliate conoscere i metodi dei campi di immissione swing e JTextField. Digitate JTextField nel campo di immissione del testo da cercare:

Image

La guida restituirà le voci dell’indice che iniziano con il testo digitato:

Image

A questo punto basta fare doppio clic sulla voce che vi interessa, in questo caso JTextField class. La guida relativa a questa classe verrà quindi visualizzata nella finestra a destra:

Image

Viene quindi fornita una descrizione completa della classe.

5.2.5. Alcuni componenti Swing

Presentiamo ora diverse applicazioni che utilizzano i componenti swing più comuni, al fine di scoprirne i principali metodi e proprietà. Per ogni applicazione, presentiamo l'interfaccia grafica e il codice rilevante, in particolare quello dei gestori di eventi.

5.2.5.1. Componenti JLabel e JTextField

Abbiamo già incontrato questi due componenti. JLabel è un componente di testo e JTextField un componente campo di immissione. I loro due metodi principali sono

String getText()
per ottenere il contenuto del campo di immissione o il testo dell’etichetta
void setText(String unTexte)
per inserire unTexte nel campo o nell’etichetta

Gli eventi solitamente utilizzati per JTextField sono i seguenti:

actionPerformed
segnala che l'utente ha confermato (tasto Invio) il testo inserito
caretUpdate
segnala che l'utente ha spostato il cursore di inserimento
inputMethodChanged
segnala che l'utente ha modificato il campo di immissione

Ecco un esempio che utilizza l'evento caretUpdate per monitorare le modifiche apportate a un campo di immissione:

Image

n.
tipo
nome
ruolo
1
JTextField
txtSaisie
campo di immissione
2
JTextField
txtControle
visualizza il testo di 1 in tempo reale
3
JButton
cmdEffacer
per cancellare i campi 1 e 2
4
JButton
cmdQuitter
per uscire dall'applicazione

Il codice pertinente di questa applicazione è il seguente:

import java.awt.*;
....


public class Cadre1 extends JFrame {
  JPanel contentPane;
  JTextField txtSaisie = new JTextField();
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JTextField txtControle = new JTextField();
  JButton CmdEffacer = new JButton();
  JButton CmdQuitter = new JButton();

  /**Creare il frame*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    ....
    txtSaisie.addCaretListener(new javax.swing.event.CaretListener() {
      public void caretUpdate(CaretEvent e) {
        txtSaisie_caretUpdate(e);
      }
    });

...
    CmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        CmdEffacer_actionPerformed(e);
      }
    });
...
    CmdQuitter.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        CmdQuitter_actionPerformed(e);
      }
    });
....
}

   /**Sostituito, così possiamo uscire quando la finestra è chiusa*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_caretUpdate(CaretEvent e) {
         // il cursore di immissione si è spostato
    txtControle.setText(txtSaisie.getText());
  }

  void CmdQuitter_actionPerformed(ActionEvent e) {
        // si esce dall'applicazione
    System.exit(0);
  }

  void CmdEffacer_actionPerformed(ActionEvent e) {
        // si cancella il contenuto del campo di immissione
    txtSaisie.setText("");
  }
}

Ecco un esempio di esecuzione:

Image

5.2.5.2. componente JComboBox

Image

Image

Un componente JComboBox è un menu a tendina abbinato a un campo di immissione: l'utente può scegliere un elemento da (2) oppure digitare del testo in (1). Per impostazione predefinita, i componenti JComboBox non sono modificabili. Per renderli modificabili, è necessario richiamare esplicitamente il metodo setEditable(true). Per scoprire la classe JComboBox, digitare JComboBox nell'indice della guida.

L'oggetto JComboBox può essere creato in diversi modi:

new JComboBox()
crea un menu a tendina vuoto
new JComboBox (Object[] items)
crea un menu a discesa contenente un array di oggetti
new JComboBox(Vector items)
idem con un vettore di oggetti

Potrebbe sorprendere che un menu a discesa possa contenere oggetti, mentre solitamente contiene stringhe di caratteri. Dal punto di vista visivo, sarà proprio così. Se un JComboBox contiene un oggetto obj, visualizzerà la stringa obj.toString(). Ricordiamo che ogni oggetto dispone di un metodo toString ereditato dalla classe Object, che restituisce una stringa di caratteri «rappresentativa» dell’oggetto.

I metodi utili della classe JCombobox sono i seguenti:

void addItem(Object unObjet)
aggiunge un oggetto alla casella combinata
int getItemCount()
restituisce il numero di elementi del menu a discesa
Object getItemAt(int i)
restituisce l'oggetto n. i del menu a discesa
void insertItemAt(Object unObjet, int i)
inserisce unObjet nella posizione i del menu a tendina
int getSelectedIndex()
restituisce il numero dell'elemento selezionato nel menu a discesa
Object getSelectedItem()
restituisce l'oggetto selezionato nel menu a discesa
void setSelectedIndex(int i)
seleziona l'elemento i del menu a tendina
void setSelectedItem(Object unObjet)
seleziona l'oggetto specificato nel menu a tendina
void removeAllItems()
svuota il menu a tendina
void removeItemAt(int i)
rimuove l'elemento n. i dal menu a tendina
void removeItem(Object unObjet)
rimuove l'oggetto specificato dal menu a discesa
void setEditable(boolean val)
rende la casella combinata modificabile (val=true) o non modificabile (val=false)

Quando si seleziona un elemento dall'elenco a discesa, si verifica l'evento actionPerformed, che può quindi essere utilizzato per ricevere una notifica del cambiamento di selezione nel menu a discesa. Nell'applicazione seguente, utilizziamo questo evento per visualizzare l'elemento che è stato selezionato nell'elenco.

Image

Presentiamo solo il codice rilevante della finestra.

public class Cadre1 extends JFrame {
  JPanel contentPane;
  JComboBox jComboBox1 = new JComboBox();
  JLabel jLabel1 = new JLabel();

   /**Creazione del riquadro*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // elaborazione - si compila la casella combinata
    String[] infos={"un","deux","trois","quatre"};
    for(int i=0;i<infos.length;i++)
      jComboBox1.addItem(infos[i]);
  }
   /**Inizializza il componente*/
  private void jbInit() throws Exception  {
        ....

    jComboBox1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jComboBox1_actionPerformed(e);
      }
    });
       .... 
  }

  void jComboBox1_actionPerformed(ActionEvent e) {
        // è stato selezionato un nuovo elemento - visualizzarlo
    JOptionPane.showMessageDialog(this,jComboBox1.getSelectedItem(),
"actionPerformed",JOptionPane.INFORMATION_MESSAGE);
  }

}

5.2.5.3. componente JList

Il componente Swing JList è più complesso rispetto al suo omologo della libreria awt. Vi sono due differenze significative:

  • il contenuto della lista è gestito da un oggetto diverso dalla lista stessa. In questo caso utilizzeremo un oggetto DefaultListModel, che si utilizza come un Vector ma che, inoltre, avvisa l’oggetto JList non appena il suo contenuto cambia, in modo che anche la visualizzazione dell’elenco si aggiorni di conseguenza.
  • Per impostazione predefinita, l’elenco non è a scorrimento. È necessario inserire l’elenco in un contenitore ScrollPane che ne consenta lo scorrimento.

Nel codice sorgente, la definizione di un elenco può essere effettuata nel modo seguente:

      // il vettore dei valori dell'elenco 
DefaultlistModel valeurs=new DefaultListModel();
// l'elenco stesso a cui si associa il vettore dei suoi valori
  JList jList1 = new JList(valeurs);
     // il contenitore a scorrimento in cui si inserisce l'elenco per ottenere un elenco a scorrimento
  JScrollPane jScrollPane1 = new JScrollPane(jList1);

Per includere l'elenco jList1 nel contenitore jScrollPane1, il codice generato da JBuilder procede in modo diverso:

  • dichiarazione del contenitore negli attributi della finestra
  JScrollPane jScrollPane1 = new JScrollPane();
  • poi, nel codice di jbInit, l'elenco viene associato al contenitore
    jScrollPane1.getViewport().add(jList1, null);

Per aggiungere valori all’elenco JList1 sopra riportato, è sufficiente aggiungerli al relativo vettore di valori valeurs:

    // inizializza l'elenco
    for(int i=0;i<10;i++)
      valeurs.addElement(""+i);

e si ottiene quindi la seguente finestra:

Image

Come è strutturata questa interfaccia?

  • si seleziona un componente JScrollPane nella pagina «Contenitori Swing» dei componenti e lo si trascina nella finestra, regolandone le dimensioni in base alle proprie esigenze
  • si seleziona un componente JList nella pagina "Swing" dei componenti e lo si trascina nel contenitore JScrollPane, occupandone l'intero spazio.

Il codice generato da JBuilder deve essere leggermente modificato per ottenere il codice seguente:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  DefaultListModel valeurs=new DefaultListModel();
  JList jList1 = new JList(valeurs);
  JScrollPane jScrollPane1 = new JScrollPane();
  JLabel jLabel2 = new JLabel();


   /**Costruire il frame*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // elaborazione
     // si include l'elenco nel scrollPane
    // inizializzazione elenco
    for(int i=0;i<10;i++)
      valeurs.addElement(""+i);
  }

   /**Inizializzazione del componente*/
  private void jbInit() throws Exception  {
        ....
         // l'elenco jList1 è associato al contenitore jcrollPane1
    jScrollPane1.getViewport().add(jList1, null);
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
        ....
  }
}

Scopriamo ora i principali metodi della classe JList cercando JList nell'indice della guida. L'oggetto JList può essere creato in diversi modi:

Image

Un metodo semplice è quello che abbiamo utilizzato: creare un DefaultListModel V vuoto e poi associarlo alla lista da creare tramite new JList(V). Il contenuto della lista non è gestito dall’oggetto JList, ma dall’oggetto che contiene i valori della lista. Se il contenuto è stato creato utilizzando un oggetto DefaultListModel basato sulla classe Vector, per aggiungere, inserire ed eliminare elementi dall’elenco si potranno utilizzare i metodi della classe Vector. Un elenco può prevedere la selezione singola o multipla. Ciò viene stabilito dal metodo setSelectionMode:

Image

È possibile conoscere la modalità di selezione corrente con getSelectionMode:

Image

È possibile ottenere l’elemento o gli elementi selezionati tramite i seguenti metodi:

Image

È possibile associare un vettore di valori a un elenco utilizzando il costruttore JList(DefaultListModel). Viceversa, è possibile ottenere l'oggetto DefaultListModel da un elenco JList tramite:

Image

Ora sappiamo abbastanza per scrivere la seguente applicazione:

Image

I componenti di questa finestra sono i seguenti:

n.
tipo
nome
funzione
1
JTextField
txtSaisie
campo di immissione
2
JList
jList1
elenco contenuto in un contenitore jScrollPane1
3
JList
jList2
elenco contenuto in un contenitore jScrollPane2
4
JButton
cmd1To2
trasferisce gli elementi selezionati dall'elenco 1 all'elenco 2
5
JButton
cmd2To1
esegue l'operazione inversa
6
JButton
cmdRaz1
svuota l'elenco 1
7
JButton
cmdRaz2
svuota l'elenco 2

L'utente digita del testo nel campo (1) e lo conferma. A questo punto si verifica l'evento actionPerformed sul campo di immissione, che viene utilizzato per aggiungere il testo digitato all'elenco 1. Ecco il codice utile per questa prima funzione:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Creare il frame*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    ...
    txtSaisie.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtSaisie_actionPerformed(e);
      }
    });
        ...
         // Jlist1 viene inserito nel contenitore jScrollPane1
    jScrollPane1.getViewport().add(jList1, null);
        // Jlist2 viene inserito nel contenitore jScrollPane2
    jScrollPane2.getViewport().add(jList2, null);
        ...
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
     // il testo inserito è stato convalidato
     // viene recuperato senza gli spazi iniziali e finali
    String texte=txtSaisie.getText().trim();
     // se è vuoto, non lo vogliamo
    if(texte.equals("")){
      // messaggio di errore
      JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
        "Erreur",JOptionPane.WARNING_MESSAGE);
      // fine
      return;
    }//if
    // se non è vuoto, lo si aggiunge ai valori dell'elenco 1
    v1.addElement(texte);
     // e si svuota il campo di immissione
    txtSaisie.setText("");
  }/// txtSaisie_actionperformed
}//classe

Il codice per trasferire gli elementi selezionati da un elenco all'altro è il seguente:

  void cmd1To2_actionPerformed(ActionEvent e) {
    // trasferimento degli elementi selezionati dall'elenco 1 all'elenco 2
    transfert(jList1,jList2);
  }//cmd1To2

  void cmd2To1_actionPerformed(ActionEvent e) {
    // trasferimento degli elementi selezionati in jList2 verso jList1
      transfert(jList2,jList1);
  }//cmd2TO1

  private void transfert(JList L1, JList L2){
      // trasferimento degli elementi selezionati dall'elenco 1 all'elenco 2
       // si recupera la tabella degli indici degli elementi selezionati in L1
      int[] indices=L1.getSelectedIndices();
      // c'è qualcosa da fare?
      if (indices.length==0) return;
      // si recuperano i valori da L1
      DefaultListModel v1=(DefaultListModel)L1.getModel();
      // e quelli di L2
      DefaultListModel v2=(DefaultListModel)L2.getModel();
      for(int i=indices.length-1;i>=0;i--){
        // si aggiungono a L2 i valori selezionati in L1
        v2.addElement(v1.elementAt(indices[i]));
        // gli elementi di L1 copiati in L2 devono essere eliminati da L1
        v1.removeElementAt(indices[i]);
      }//per
  }//trasferimento

Il codice associato ai pulsanti Raz è semplicissimo:

  void cmdRaz1_actionPerformed(ActionEvent e) {
    // svuota elenco 1
    v1.removeAllElements();
  }//comando Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // svuota lista 2
    v2.removeAllElements();
  }///cmd Raz2

5.2.5.4. Caselle di selezione JCheckBox, pulsanti di opzione JButtonRadio

Ci proponiamo di scrivere la seguente applicazione:

Image

I componenti della finestra sono i seguenti:

n.
tipo
nome
funzione
1
JButtonRadio
jButtonRadio1
jButtonRadio2
jButtonRadio3
3 pulsanti di opzione appartenenti al gruppo buttonGroup1
2
JCheckBox
jCheckBox1
jCheckBox2
jCheckBox3
3 caselle di selezione
3
JList
jList1
un elenco in un contenitore jScrollPane1
4
ButtonGroup
buttonGroup1
componente non visibile - serve a raggruppare i 3 pulsanti di opzione in modo che, quando uno di essi si attiva, gli altri si disattivino.

Un gruppo di pulsanti di opzione può essere creato nel modo seguente:

  • si posizionano i singoli pulsanti di opzione senza preoccuparsi di raggrupparli
  • si inserisce nel contenitore un componente Swing ButtonGroup. Questo componente è non visibile. Non compare quindi nel designer della finestra. Compare tuttavia nella sua struttura:

Image

Come si vede sopra, nel ramo Autre sono riportati gli attributi non visivi della finestra. Una volta creato un gruppo di pulsanti di opzione, è possibile associarvi ciascuno dei pulsanti di opzione. A tal fine, selezionare le proprietà del pulsante di opzione:

Image

e nella proprietà buttonGroup del pulsante di opzione si inserisce il nome del gruppo in cui si desidera inserire il pulsante di opzione, in questo caso buttonGroup1. Si ripete questa operazione per tutti e 3 i pulsanti di opzione.

Il metodo principale per i pulsanti di opzione e le caselle di controllo è il metodo isSelected(), che indica se la casella o il pulsante è selezionato. Il testo associato al componente può essere recuperato con getText() e impostato con setText(String unTexte). La casella di controllo o il pulsante di opzione può essere selezionato con il metodo setSelected(boolean value).

Quando si fa clic su un pulsante di opzione o su una casella di controllo, viene attivato l’evento actionPerformed. Nel codice seguente, utilizziamo questo evento per monitorare le modifiche dei valori dei pulsanti di opzione e delle caselle di controllo:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JRadioButton jRadioButton1 = new JRadioButton();
  JRadioButton jRadioButton2 = new JRadioButton();
  JRadioButton jRadioButton3 = new JRadioButton();
  JCheckBox jCheckBox1 = new JCheckBox();
  JCheckBox jCheckBox2 = new JCheckBox();
  JCheckBox jCheckBox3 = new JCheckBox();
  ButtonGroup buttonGroup1 = new ButtonGroup();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel valeurs=new DefaultListModel();
  JList jList1 = new JList(valeurs);

   /**Costruire il frame*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    jRadioButton1.setSelected(true);
    jRadioButton1.setText("un");
    jRadioButton1.setBounds(new Rectangle(57, 31, 49, 23));
    jRadioButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });

    jRadioButton2.setBounds(new Rectangle(113, 30, 49, 23));
    jRadioButton2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });
    jRadioButton2.setText("deux");

    jRadioButton3.setBounds(new Rectangle(168, 30, 49, 23));
    jRadioButton3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });
    jRadioButton3.setText("trois");

// i pulsanti di opzione sono raggruppati
    buttonGroup1.add(jRadioButton1);
    buttonGroup1.add(jRadioButton2);
    buttonGroup1.add(jRadioButton3);

// caselle di controllo
    jCheckBox1.setText("A");
    jCheckBox1.setBounds(new Rectangle(58, 69, 32, 17));
    jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });

    jCheckBox2.setBounds(new Rectangle(112, 69, 40, 17));
    jCheckBox2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });
    jCheckBox2.setText("B");

    jCheckBox3.setText("C");
    jCheckBox3.setBounds(new Rectangle(170, 69, 37, 17));
    jCheckBox3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });

    ....
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
        ...
  }

  private void afficheRadioButtons(ActionEvent e){
     // visualizza i valori dei 3 pulsanti di opzione
    valeurs.addElement("boutons radio=("+jRadioButton1.isSelected()+","+
      jRadioButton2.isSelected()+","+jRadioButton3.isSelected()+")");
  }//afficheRadioButtons

  void afficheCases(ActionEvent e) {
     // visualizza i valori delle caselle di controllo
    valeurs.addElement("cases à cocher=["+jCheckBox1.isSelected()+","+
      jCheckBox2.isSelected()+","+jCheckBox3.isSelected()+")");
  }//afficheCases

}//ordina

Ecco un esempio di esecuzione:

Image

5.2.5.5. componente JScrollBar

Realizziamo la seguente applicazione:

Image

n.
tipo
nome
ruolo
1
JScrollBar
jScrollBar1
un variatore orizzontale
2
JScrollBar
jScrollBar2
un variatore verticale
3
JTextField
txtvaleurHS
visualizza il valore del variatore orizzontale 1 - consente anche di impostare tale valore
4
JTextField
txtvaleurVS
visualizza il valore del variatore verticale 2 - consente anche di impostare tale valore
  • Un variatore JScrollBar consente all'utente di selezionare un valore all'interno di un intervallo di valori interi rappresentato dalla "banda" del variatore su cui si sposta un cursore.
  • Per un variatore orizzontale, l’estremità sinistra rappresenta il valore minimo dell’intervallo, l’estremità destra il valore massimo, il cursore il valore attuale selezionato. Per un variatore verticale, il minimo è rappresentato dall’estremità superiore, il massimo dall’estremità inferiore. La coppia (min, max) è impostata di default su (0,100).
  • Un clic sulle estremità del variatore fa variare il valore di un incremento (positivo o negativo) a seconda dell’estremità cliccata, denominata unitIncrement, che per impostazione predefinita è 1.
  • Un clic su entrambi i lati del cursore fa variare il valore di un incremento (positivo o negativo) a seconda dell'estremità cliccata, denominata blockIncrement, il cui valore predefinito è 10.
  • Questi cinque valori (min, max, valore, unitIncrement, blockIncrement) possono essere ricavati tramite i metodi getMinimum(), getMaximum(), getValue(), getUnitIncrement(), getBlockIncrement(), che restituiscono tutti un numero intero e possono essere impostati tramite i metodi setMinimum(int min), setMaximum(int max), setValue(int val), setUnitIncrement(int uInc), setBlockIncrement(int bInc)

Ci sono alcuni aspetti da tenere presenti nell’utilizzo dei componenti JScrollBar. Innanzitutto, si trovano nella barra dei componenti Swing:

Image

Quando lo si trascina sul contenitore, per impostazione predefinita è verticale. È possibile renderlo orizzontale utilizzando la proprietà orientation riportata di seguito:

Image

Nella finestra delle proprietà sopra riportata si vede che si ha accesso alle proprietà minimum, maximum, value, unitIncrement, blockIncrement del JScrollbar. È quindi possibile impostarle in fase di progettazione. Quando si posiziona una barra di scorrimento sul contenitore, la sua “fascia di variazione” non appare:

Image

È possibile risolvere questo problema aggiungendo un bordo al componente. Ciò si ottiene tramite la proprietà border, che può assumere diversi valori:

Image

Ecco, ad esempio, il risultato ottenuto con RaisedBevel:

Image

Quando si clicca sull'estremità superiore di un cursore verticale, il suo valore diminuisce. Ciò può sorprendere l'utente medio, che normalmente si aspetta di vedere il valore "aumentare". Si risolve questo problema assegnando un valore negativo a unitIncrement e blockIncrement.

Come monitorare le variazioni di un cursore? Quando il suo valore cambia, si verifica l’evento adjustmentValueChanged. È sufficiente associare una procedura a questo evento per essere informati di ogni variazione del valore della barra di scorrimento.

Image

Il codice utile della nostra applicazione è il seguente:

....

public class cadreAppli extends JFrame {
  JPanel contentPane;
  JScrollBar jScrollBar1 = new JScrollBar();
  Border border1;
  JTextField txtValeurHS = new JTextField();
  JScrollBar jScrollBar2 = new JScrollBar();
  JTextField txtValeurVS = new JTextField();
  TitledBorder titledBorder1;

   /**Crea il riquadro*/
  public cadreAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializza il componente*/
  private void jbInit() throws Exception  {
...

// un bordo per le barre di scorrimento
    border1 = BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.white,Color.white,new Color(134, 134, 134),new Color(93, 93, 93));
        // nessun titolo lungo il bordo
    titledBorder1 = new TitledBorder("");

    jScrollBar1.setOrientation(JScrollBar.HORIZONTAL);
    jScrollBar1.setBorder(BorderFactory.createRaisedBevelBorder());
    jScrollBar1.setAutoscrolls(true);
    jScrollBar1.setBounds(new Rectangle(37, 17, 218, 20));
    jScrollBar1.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        jScrollBar1_adjustmentValueChanged(e);
      }
    });

    txtValeurHS.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtValeurHS_actionPerformed(e);
      }
    });

    jScrollBar2.setBounds(new Rectangle(39, 51, 30, 27));
    jScrollBar2.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        jScrollBar2_adjustmentValueChanged(e);
      }
    });
    jScrollBar2.setAutoscrolls(true);
    jScrollBar2.setUnitIncrement(-1);
    jScrollBar2.setBorder(BorderFactory.createRaisedBevelBorder());

    txtValeurVS.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtValeurVS_actionPerformed(e);
      }
    });

    ......
  }

   /**Sostituito, così possiamo uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void jScrollBar1_adjustmentValueChanged(AdjustmentEvent e) {
    // il valore della barra di scorrimento 1 è cambiato
    txtValeurHS.setText(""+jScrollBar1.getValue());
  }

  void jScrollBar2_adjustmentValueChanged(AdjustmentEvent e) {
    // il valore della barra di scorrimento 2 è cambiato
    txtValeurVS.setText(""+jScrollBar2.getValue());
  }

  void txtValeurHS_actionPerformed(ActionEvent e) {
    // si imposta il valore della barra di scorrimento orizzontale
    setValeur(jScrollBar1,txtValeurHS);
  }

  void txtValeurVS_actionPerformed(ActionEvent e) {
    // si imposta il valore della barra di scorrimento verticale
    setValeur(jScrollBar2,txtValeurVS);
  }

  private void setValeur(JScrollBar jS, JTextField jT){
     // fissa il valore della barra di scorrimento jS con il testo del campo jT
    int valeur=0;
    try{
      valeur=Integer.parseInt(jT.getText());
      jS.setValue(valeur);
    }
    catch (Exception e){
      // viene visualizzato l'errore
      afficher(""+e);
    }//try-catch
  }//setValeur

  void afficher(String message){
    // visualizza un messaggio in una finestra
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//visualizza

}

Ecco un esempio di esecuzione:

Image

5.2.5.6. componente JTextArea

Il componente JTextArea è un componente in cui è possibile inserire più righe di testo, a differenza del componente JTextField, in cui è possibile inserire solo una riga. Se questo componente viene inserito in un contenitore a scorrimento (JScrollPane), si ottiene un campo di immissione testo a scorrimento. Questo tipo di componente potrebbe essere presente, ad esempio, in un’applicazione di posta elettronica in cui il testo del messaggio da inviare verrebbe digitato in un componente JTextArea. I metodi più comuni sono String getText() per conoscere il contenuto della casella di testo, setText(String unTexte) per inserire unTexte nella casella di testo, append(String unTexte) per aggiungere unTexte al testo già presente nella casella di testo. Consideriamo la seguente applicazione:

Image

n.
tipo
nome
ruolo
1
JTextArea
txtTexte
un campo di testo multilinea
2
JButton
cmdAfficher
visualizza il contenuto di 1 in una finestra di dialogo
3
JButton
cmdEffacer
cancella il contenuto di 1
4
JTextField
txtAjout
testo aggiunto al testo di 1 quando viene confermato con il tasto Invio.
5
JScrollPane
jScrollPane1
container a scorrimento in cui è stata inserita l'area di testo 1 per ottenere un'area di testo a scorrimento.

Il codice da utilizzare è il seguente:

.....

public class cadreAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JButton cmdAfficher = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  JTextArea txtTexte = new JTextArea();
  JLabel jLabel2 = new JLabel();
  JTextField txtAjout = new JTextField();
  JButton cmdEffacer = new JButton();

  /**Creare il frame*/
  public cadreAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializza il componente*/
  private void jbInit() throws Exception  {

    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    txtAjout.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtAjout_actionPerformed(e);
      }
    });
    cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdEffacer_actionPerformed(e);
      }
    });
    .......
    jScrollPane1.getViewport().add(txtTexte, null);
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
        ........
  }

  void cmdAfficher_actionPerformed(ActionEvent e) {
    // visualizza il contenuto di TextArea
    afficher(txtTexte.getText());
  }

    void afficher(String message){
     // visualizza un messaggio in una finestra di dialogo
    JOptionPane.showMessageDialog(this,message,"Suivi",JOptionPane.INFORMATION_MESSAGE);
  }// visualizza

  void cmdEffacer_actionPerformed(ActionEvent e) {
    txtTexte.setText("");
  }//cmdEffacer_actionPerformed

  void txtAjout_actionPerformed(ActionEvent e) {
    // aggiungere testo
    txtTexte.append(txtAjout.getText());
     // cancellazione aggiunta
    txtAjout.setText("");
  }//
}

5.2.6. Eventi del mouse

Quando si disegna all'interno di un contenitore, è importante conoscere la posizione del mouse per, ad esempio, visualizzare un punto al momento del clic. Gli spostamenti del mouse generano eventi nel contenitore in cui si muove. Ecco, ad esempio, quelli offerti da JBuilder per un contenitore JPanel:

Image

mouseClicked
clic del mouse
mouseDragged
il mouse si sposta, tasto sinistro premuto
mouseEntered
il mouse è appena entrato nell'area del contenitore
mouseExited
il mouse ha appena lasciato l'area del contenitore
mouseMoved
il mouse si sta muovendo
mousePressed
Pressione del tasto sinistro del mouse
mouseReleased
Rilascio del tasto sinistro del mouse

Ecco un programma che permette di comprendere meglio in quali momenti si verificano i diversi eventi del mouse:

Image

n.
tipo
nome
ruolo
1
JTextField
txtPosition
per visualizzare la posizione del mouse nel contenitore (eventualmente MouseMoved)
2
JList
lstAffichage
per visualizzare gli eventi del mouse diversi da MouseMoved
3
JButton
cmdEffacer
per cancellare il contenuto di 2

Quando si esegue questo programma, ecco cosa si ottiene con un clic:

Image

Gli eventi vengono impilati dall’alto nell’elenco. Pertanto, la schermata sopra riportata indica che un clic genera tre eventi, nell’ordine:

  1. MousePressed quando si preme il pulsante
  2. MouseReleased quando lo si rilascia
  3. MouseClicked, che indica che la successione dei due eventi precedenti viene considerata come un clic. Potrebbe trattarsi di un doppio clic. Tuttavia, l’informazione clickCount=1 sopra riportata indica che si tratta di un semplice clic.

Ora, se si preme il pulsante, si sposta il mouse e si rilascia il pulsante:

Image

Qui si vedono i tre eventi:

  1. MousePressed quando si preme inizialmente il pulsante
  2. MouseDragged quando si sposta il mouse con il pulsante premuto
  3. MouseReleased quando si rilascia il pulsante

Nei due esempi sopra riportati, si nota che un evento del mouse porta con sé diverse informazioni, tra cui le coordinate (x, y) del mouse, ad esempio (408,65) nella prima riga sopra.

Proseguendo in questo modo, si scopre che l’evento MouseExited viene attivato non appena il mouse esce dal contenitore o passa su uno dei suoi componenti. In quest’ultimo caso, il contenitore riceve l’evento MouseExited e il componente l’evento MouseEntered. Si verificherà il contrario quando il mouse uscirà dal componente per tornare sul contenitore.

Cosa succede quando si esegue un doppio clic?

Image

Si verificano esattamente gli stessi eventi di un clic singolo. L’unica differenza è che l’evento riporta l’informazione clickCount=2 (vedi sopra), a indicare che si è trattato effettivamente di un doppio clic.

Il codice utile di questa applicazione è il seguente:

public class Cadre1 extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JTextField txtPosition = new JTextField();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel valeurs=new DefaultListModel();
  JList lstAffichage = new JList(valeurs);
  JButton cmdEffacer = new JButton();

  /**Costruire il frame*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {

    contentPane.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        contentPane_mouseMoved(e);
      }
      public void mouseDragged(MouseEvent e) {
        contentPane_mouseDragged(e);
      }
    });

    contentPane.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseEntered(MouseEvent e) {
        contentPane_mouseEntered(e);
      }
      public void mouseExited(MouseEvent e) {
        contentPane_mouseExited(e);
      }
      public void mousePressed(MouseEvent e) {
        contentPane_mousePressed(e);
      }
      public void mouseClicked(MouseEvent e) {
        contentPane_mouseClicked(e);
      }
      public void mouseReleased(MouseEvent e) {
        contentPane_mouseReleased(e);
      }
    });

    cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdEffacer_actionPerformed(e);
      }
    });

    jScrollPane1.getViewport().add(lstAffichage, null);

...............
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
    .........
  }

  void contentPane_mouseMoved(MouseEvent e) {
    txtPosition.setText("("+e.getX()+","+e.getY()+")");
  }

  void contentPane_mouseDragged(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseEntered(MouseEvent e) {
    afficher(e);
  }

  void afficher(MouseEvent e){
     // visualizza l'evento e nell'elenco lstAffichage
    valeurs.insertElementAt(e,0);
  }

  void contentPane_mouseExited(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mousePressed(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseClicked(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseReleased(MouseEvent e) {
    afficher(e);
  }

  void cmdEffacer_actionPerformed(ActionEvent e) {
     // cancella l'elenco
    valeurs.removeAllElements();
  }
}

5.2.7. Creare una finestra con menu

Vediamo ora come creare una finestra con menu utilizzando JBuilder. Creeremo la seguente finestra:

Image

Image

Create un nuovo progetto con una finestra vuota iniziale. Scegliete dall’elenco dei componenti “Contenitori Swing” il componente JMenuBar (cfr. figura 1 qui sotto) e trascinatelo sulla finestra in fase di progettazione.

Image

Nella finestra di progettazione non appare nulla, ma il componente JMenuBar compare nel pannello della struttura della finestra:

Image

Fare doppio clic sull’elemento jMenuBar1 sopra indicato per accedere al menu in modalità progettazione:

Image

1
inserire un elemento di menu
2
inserire un separatore
3
inserire un menu annidato
4
eliminare una voce di menu
5
disattivare una voce di menu
6
voce di menu da selezionare
7
invertire il pulsante di opzione

Per creare la tua prima voce di menu, digita "Opzioni A" nella casella A qui sopra, poi sotto, nell'ordine: A1, A2, separatore, A3, A4.

Image

Poi accanto:

Image

Utilizzate lo strumento 3 per indicare che B3 è un sottomenu annidato.

Man mano che si procede con la progettazione del menu, la struttura logica della nostra finestra si evolve:

Image

Se ora eseguiamo la nostra applicazione, vedremo una finestra vuota senza menu. Dobbiamo associare il menu creato alla nostra finestra. Per farlo, nella struttura della finestra, selezionate l'oggetto this:

Image

A questo punto avrete accesso alle proprietà di this:

Image

Una di queste è JMenuBar, che serve a definire il menu da associare alla finestra. Cliccate nella cella a destra di JMenuBar. Verranno quindi visualizzati tutti i menu creati. In questo caso, avremo solo jMenuBar1. Selezionatelo.

Avviare l’esecuzione dell’applicazione (F9):

Image

Ora abbiamo un menu, ma le opzioni, per il momento, non fanno nulla. Le opzioni del menu vengono trattate come componenti: hanno proprietà ed eventi. Nella struttura del menu, selezionate l’opzione jMenuItem1:

Image

A questo punto potrete accedere alle sue proprietà e ai suoi eventi:

Image

Selezionate la pagina degli eventi e cliccate sulla cella a destra dell’evento actionPerformed: si tratta dell’evento che si verifica quando si clicca su un elemento del menu. Per impostazione predefinita vi viene proposta una procedura di elaborazione. Fate doppio clic su di essa per accedere al suo codice:

Image

Scriveremo il seguente semplice codice:

  void jMenuItem1_actionPerformed(ActionEvent e) {
    afficher("L'option A1 a été sélectionnée");
  }

  void afficher(String message){
     // visualizza un messaggio in una finestra di dialogo
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//visualizza

Eseguire l'applicazione e selezionare l'opzione A1 per visualizzare il seguente messaggio:

Image

Il codice utile di questa applicazione è il seguente:

.....

public class Cadre1 extends JFrame {
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();
  JMenuBar jMenuBar1 = new JMenuBar();
  JMenu jMenu1 = new JMenu();
  JMenuItem jMenuItem1 = new JMenuItem();
  JMenuItem jMenuItem2 = new JMenuItem();
  JMenuItem jMenuItem3 = new JMenuItem();
  JMenuItem jMenuItem4 = new JMenuItem();
  JMenu jMenu2 = new JMenu();
  JMenuItem jMenuItem5 = new JMenuItem();
  JMenuItem jMenuItem6 = new JMenuItem();
  JMenu jMenu3 = new JMenu();
  JMenuItem jMenuItem7 = new JMenuItem();
  JMenuItem jMenuItem8 = new JMenuItem();
  JMenuItem jMenuItem9 = new JMenuItem();

  /**Crea il frame*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inizializza il componente*/
  private void jbInit() throws Exception  {
        // la finestra è associata a un menu
    this.setJMenuBar(jMenuBar1);

    jMenu1.setText("Options A");
    jMenuItem1.setText("A1");
    jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jMenuItem1_actionPerformed(e);
      }
    });
    jMenuItem2.setText("A2");
    jMenuItem3.setText("A3");
    jMenuItem4.setText("A4");
    jMenu2.setText("Options B");
    jMenuItem5.setText("B1");
    jMenuItem6.setText("B2");
    jMenu3.setText("B3");
    jMenuItem7.setText("B31");
    jMenuItem8.setText("B32");
    jMenuItem9.setText("B4");
    jMenuBar1.add(jMenu1);
    jMenuBar1.add(jMenu2);
    jMenu1.add(jMenuItem1);
    jMenu1.add(jMenuItem2);
    jMenu1.addSeparator();
    jMenu1.add(jMenuItem3);
    jMenu1.add(jMenuItem4);
    jMenu2.add(jMenuItem5);
    jMenu2.add(jMenuItem6);
    jMenu2.add(jMenu3);
    jMenu2.add(jMenuItem9);
    jMenu3.add(jMenuItem7);
    jMenu3.add(jMenuItem8);
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
....
  }

  void jMenuItem1_actionPerformed(ActionEvent e) {
    afficher("L'option A1 a été sélectionnée");
  }

  void afficher(String message){
     // visualizza un messaggio in una finestra di dialogo
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//visualizza

}

5.3.1. Finestre di messaggio

Abbiamo già utilizzato la classe JOptionPane per visualizzare i messaggi. Pertanto, il codice seguente:

import javax.swing.*;

public class dialog1 {
  public static void main(String arg[]){
    JOptionPane.showMessageDialog(null,"Un message","Titre de la boîte",JOptionPane.INFORMATION_MESSAGE);
  }
}

genera la visualizzazione della seguente finestra:

Image

Quando si chiude questa finestra, essa scompare ma il thread di esecuzione in cui era in esecuzione non viene interrotto. Questo fenomeno normalmente non si verifica. Le finestre di dialogo vengono utilizzate all’interno di un’applicazione che, a un certo punto, utilizza un’istruzione System.exit(n) per arrestare tutti i thread. Ne terremo conto negli esempi che seguiranno, tutti basati sullo stesso modello. In DOS, l’applicazione può essere interrotta con Ctrl-C. Con JBuilder, si utilizzerà l’opzione Esegui/Reimposta il programma (Ctrl-F2). Inoltre, il primo argomento di showMessageDialog è in questo caso null. In genere non è così. Si tratta piuttosto di this o this, che indicano la finestra principale dell’applicazione.

5.3.2. Aspetto grafico

L’aspetto della finestra sopra riportata potrebbe essere diverso. È possibile configurarne l’aspetto tramite la classe javax.swing.UIManager. Quando abbiamo commentato il codice generato da JBuilder per la nostra prima finestra, abbiamo incontrato un’istruzione su cui non ci siamo soffermati:

      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

Il metodo setLookAndFeel della classe UIManager (UI=Interfaccia utente) consente di definire l’aspetto delle interfacce grafiche. La classe UIManager dispone di un metodo che permette di conoscere le possibili «aspettative» per le interfacce:

static UIManager.LookAndFeelInfo[]
getInstalledLookAndFeels()

Il metodo restituisce un array di elementi di tipo LookAndFeelInfo. Questa classe dispone di un metodo:

String
getClassName()

che restituisce il nome della classe che "implementa" un determinato tema. Proviamo il seguente programma:

import javax.swing.*;

public class LookAndFeels {
  // visualizza i look and feel disponibili
  public static void main(String[] args) {
    // Elenca i look and feel installati
    UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
    // visualizza
    for(int i=0;i<lf.length;i++){
      System.out.println(lf[i].getClassName());
    }//per
  }//main
}//classifica

Fornisce i seguenti risultati:

javax.swing.plaf.metal.MetalLookAndFeel
com.sun.java.swing.plaf.motif.MotifLookAndFeel
com.sun.java.swing.plaf.windows.WindowsLookAndFeel

Sembra quindi che ci siano tre "stili" diversi. Riprendiamo il nostro programma di visualizzazione dei messaggi provando i diversi stili possibili:

import javax.swing.*;

public class LookAndFeel2 {
  // visualizza i look and feel disponibili
  public static void main(String[] args) {
    // elenco dei look and feel installati
    UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
    // visualizzazione
    for(int i=0;i<lf.length;i++){
      // gestore dell'aspetto
      try{
        UIManager.setLookAndFeel(lf[i].getClassName());
      }catch(Exception ex){
        System.err.println(ex.getMessage());
      }//catch
      // messaggio
      JOptionPane.showMessageDialog(null,"Un message",lf[i].getClassName(),JOptionPane.INFORMATION_MESSAGE);
    }//for
  }//main
}//classe

L'esecuzione produce i seguenti risultati:

corrispondenti, da destra a sinistra, ai "look" Metal, Motif, Windows.

5.3.3. Finestre di conferma

La classe JOptionPane dispone di un metodo showConfirmDialog per visualizzare finestre di conferma con i pulsanti Oui, Non, Annuler. Esistono diversi metodi showConfirmDialog sovrascritti. Ne esaminiamo uno:

static int
showConfirmDialog(Component parentComponent, Object message, String title, int optionType)
parentComponent
il componente padre della finestra di dialogo. Spesso la finestra o il valore null
message
il messaggio da visualizzare
title
il titolo della finestra di dialogo
optionType
JOptionPane.YES_NO_OPTION: pulsanti Sì, No
JOptionPane.YES_NO_CANCEL_OPTION: pulsanti Sì, No, Annulla

Il risultato restituito dal metodo è:

JOptionPane.YES_OPTION
l'utente ha cliccato su sì
JOptionPane.NO_OPTION
l'utente ha cliccato su «No»
JOptionPane.CANCEL_OPTION
l'utente ha cliccato su «Annulla»
JOptionPane.CLOSED_OPTION
l'utente ha chiuso la finestra

Ecco un esempio:

import javax.swing.*;

public class confirm1 {
  public static void main(String[] args) {
    // visualizza le finestre di conferma
    int réponse;
    affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_OPTION));
    affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_CANCEL_OPTION));
  }//main

  private static void affiche(int réponse){
     // indica quale tipo di risposta si è ricevuta
    switch(réponse){
      case JOptionPane.YES_OPTION :
        System.out.println("Oui");
        break;
      case JOptionPane.NO_OPTION :
        System.out.println("Non");
        break;
      case JOptionPane.CANCEL_OPTION :
        System.out.println("Annuler");
        break;
      case JOptionPane.CLOSED_OPTION :
        System.out.println("Fermeture");
        break;
    }//switch
  }//display
}//classifica

Sulla console vengono visualizzati i messaggi Non e Annuler.

5.3.4. Campo di immissione

La classe JOptionPane offre anche la possibilità di effettuare un inserimento con il metodo showInputDialog. Anche in questo caso, esistono diversi metodi sovramodificati. Ne presentiamo uno:

static String
showInputDialog(Component parentComponent, Object message, String title, int messageType)

Gli argomenti sono quelli già incontrati più volte. Il metodo restituisce la stringa digitata dall'utente. Ecco un esempio:

import javax.swing.*;

public class input1 {
  public static void main(String[] args) {
    // inserimento
    System.out.println("Chaîne saisie ["+
      JOptionPane.showInputDialog(null,"Quel est votre nom","Saisie du nom",JOptionPane.QUESTION_MESSAGE )
      + "]");
  }//mano
}//classe

Visualizzazione della casella di immissione:

Image

Visualizzazione della console:

Chaîne saisie [dupont]

5.4. Caselle di selezione

Passiamo ora ad esaminare alcune caselle di selezione predefinite in Java 2:

JFileChooser
casella di selezione che consente di selezionare un file nella struttura ad albero dei file
JColorChooser
casella di selezione che consente di scegliere un colore

5.4.1. Casella di selezione JFileChooser

Realizzeremo la seguente applicazione:

Image

I controlli sono i seguenti:

N.
tipo
nome
ruolo
0
JScrollPane
jScrollPane1
Container scorrevole per la casella di testo 1
1
JTextArea all'interno di JScrollPane
txtTexte
testo digitato dall'utente o caricato da un file
2
JButton
btnSauvegarder
consente di salvare il testo di 1 in un file di testo
3
JButton
btnCharger
consente di caricare il contenuto di un file di testo in 1
4
JButton
btnEffacer
cancella il contenuto di 1

Viene utilizzato un controllo non visivo: jFileChooser1. Questo viene prelevato dalla tavolozza dei contenitori Swing di JBuilder:

Image

Inseriamo il componente nella finestra di progettazione, ma al di fuori del modulo. Appare nell’elenco dei componenti:

Image

Ora riportiamo il codice del programma per averne una visione d’insieme:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;

public class dialogues extends JFrame {
   // i componenti del riquadro
  JPanel contentPane;
  JButton btnSauvegarder = new JButton();
  JButton btnCharger = new JButton();
  JButton btnEffacer = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  JTextArea txtTexte = new JTextArea();
  JFileChooser jFileChooser1 = new JFileChooser();

  // i filtri dei file
  javax.swing.filechooser.FileFilter filtreTxt = null;

   //Costruire il frame
  public dialogues() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
       // altre inizializzazioni
      moreInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }

   // moreInit
  private void moreInit(){
     // inizializzazioni alla creazione della finestra
     // filtro *.txt
    filtreTxt = new javax.swing.filechooser.FileFilter(){
      public boolean accept(File f){
        // si accetta f?
        return f.getName().toLowerCase().endsWith(".txt");
      }//accettato
      public String getDescription(){
        // descrizione del filtro
        return "Fichiers Texte (*.txt)";
      }//getDescription
    };
     // si aggiunge il filtro
    jFileChooser1.addChoosableFileFilter(filtreTxt);
    // si desidera anche il filtro per tutti i file
    jFileChooser1.setAcceptAllFileFilterUsed(true);
    // si imposta la directory di partenza della cartella FileChooser come directory corrente
    jFileChooser1.setCurrentDirectory(new File("."));
  }

   //Inizializzare il componente
  private void jbInit() throws Exception  {
.......................
  }
   //Sostituito, così possiamo uscire quando la finestra viene chiusa
  protected void processWindowEvent(WindowEvent e) {
......................
    }
  }

  void btnCharger_actionPerformed(ActionEvent e) {
    // selezione di un file tramite un oggetto JFileChooser

     // si imposta il filtro iniziale
    jFileChooser1.setFileFilter(filtreTxt);
     // viene visualizzata la finestra di selezione
    int returnVal = jFileChooser1.showOpenDialog(this);
    // L'utente ha selezionato qualcosa?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // si inserisce il file nel campo di testo
       lireFichier(jFileChooser1.getSelectedFile());
    }//if
  }//btnCharger_actionPerformed

  // lireFichier
  private void lireFichier(File fichier){
     // visualizza il contenuto del file di testo «fichier» nel campo di testo

     // si cancella il campo di testo
    txtTexte.setText("");

     // alcuni dati
    BufferedReader IN=null;
    String ligne=null;
    try{
      // apertura del file in lettura
      IN=new BufferedReader(new FileReader(fichier));
      // si legge il file riga per riga
      while((ligne=IN.readLine())!=null){
        txtTexte.append(ligne+"\n");
      }//while
       // chiusura del file
      IN.close();
    }catch(Exception ex){
       // si è verificato un errore
      txtTexte.setText(""+ex);
    }//catch
  }

  // cancella
  void btnEffacer_actionPerformed(ActionEvent e) {
    // si cancella la casella di testo
    txtTexte.setText("");
  }

   // salva
  void btnSauvegarder_actionPerformed(ActionEvent e) {
     // salva il contenuto della casella di testo in un file

     // imposta il filtro iniziale
    jFileChooser1.setFileFilter(filtreTxt);
     // viene visualizzata la finestra di selezione per il salvataggio
    int returnVal = jFileChooser1.showSaveDialog(this);
    // L'utente ha selezionato qualcosa?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // si scrive il contenuto della casella di testo nel file
       écrireFichier(jFileChooser1.getSelectedFile());
    }//if
  }

   // lireFichier
  private void écrireFichier(File fichier){
     // scrive il contenuto della casella di testo in un file

     // alcuni dati
    PrintWriter PRN=null;
    try{
       // apertura del file in modalità scrittura
      PRN=new PrintWriter(new FileWriter(fichier));
      // si scrive il contenuto della casella di testo
      PRN.print(txtTexte.getText());
       // chiude il file
      PRN.close();
    }catch(Exception ex){
       // si è verificato un errore
      txtTexte.setText(""+ex);
    }//catch
  }

Non commenteremo il codice dei metodi btnEffacer_Click, lireFichier e écrireFichier, poiché non apportano nulla di nuovo rispetto a quanto già visto. Ci soffermeremo sulla classe JFileChooser e sul suo utilizzo. Questa classe è complessa, del tipo «complicatissima». Qui utilizziamo solo i seguenti metodi:

addChoosableFilter(FileFilter)
imposta i tipi di file proposti per la selezione
setAcceptAllFileFilterUsed(boolean)
indica se il tipo «Tutti i file» debba essere proposto o meno alla selezione
File getSelectedFile()
il file (File) selezionato dall'utente
int showSaveDialog()
metodo che visualizza la finestra di selezione per il salvataggio. Restituisce un risultato di tipo int. Il valore jFileChooser.APPROVE_OPTION indica che l'utente ha effettuato una scelta valida. In caso contrario, l'utente ha annullato la scelta oppure si è verificato un errore.
setCurrentDirectory
per impostare la directory iniziale da cui l’utente inizierà a esplorare il file system
setFileFilter(FileFilter)
per impostare il filtro attivo

Il metodo showSaveDialog visualizza una finestra di selezione simile alla seguente:

Image

1
elenco a discesa creato con il metodo addChoosableFilter. Contiene i cosiddetti filtri di selezione rappresentati dalla classe FileFilter. Spetta allo sviluppatore definire questi filtri e aggiungerli all'elenco 1.
2
cartella corrente, impostata dal metodo setCurrentDirectory se tale metodo è stato utilizzato; in caso contrario, la directory corrente sarà la cartella "Documenti" in Windows o la home directory in Unix.
3
nome del file selezionato o digitato direttamente dall’utente. Sarà disponibile con il metodo getSelectedFile()
4
Pulsanti Salva/Annulla. Se viene utilizzato il pulsante Enregistrer, il metodo showSaveDialog restituisce il risultato jFileChooser.APPROVE_OPTION

Come vengono creati i filtri dei file dell’elenco a discesa 1? I filtri vengono aggiunti all’elenco 1 utilizzando il metodo:

addChoosableFilter(FileFilter)
definisce i tipi di file proposti per la selezione

della classe JFileChooser. Resta da definire la classe FileFilter. Si tratta in realtà della classe javax.swing.filechooser.FileFilter, che è una classe astratta, c.a.d, una classe che non può essere istanziata ma solo derivata. È definita come segue:

FileFilter()
costruttore
boolean accept(File)
indica se il file f appartiene al filtro o meno
String getDescription()
stringa di descrizione del filtro

Facciamo un esempio. Vogliamo che il menu a tendina 1 offra un filtro per selezionare i file *.txt con la descrizione "File di testo (*.txt)".

  • dobbiamo creare una classe derivata dalla classe FileFilter
  • utilizzare il metodo boolean accept(File f) per restituire il valore true se il nome del file f termina con .txt
  • utilizzare il metodo String getDescription() per restituire la descrizione "File di testo (*.txt)"

Questo filtro potrebbe essere definito nella nostra applicazione nel modo seguente:

  javax.swing.filechooser.FileFilter filtreTxt = new javax.swing.filechooser.FileFilter(){
    public boolean accept(File f){
      // si accetta f?
      return f.getName().toLowerCase().endsWith(".txt");
    }//accettato
    public String getDescription(){
      // descrizione del filtro
      return "Fichiers Texte (*.txt)";
    }//getDescription
  };

Questo filtro verrebbe aggiunto all'elenco dei filtri dell'oggetto jFileChooser1 tramite l'istruzione:

     // si aggiunge il filtro *.txt
    jFileChooser1.addChoosableFileFilter(filtretxt);

Tutto ciò, insieme ad alcune altre inizializzazioni, viene eseguito nel metodo moreInit durante la creazione della finestra (cfr. il programma completo sopra riportato). Il codice del pulsante Sauvegarder è il seguente:

  void btnSauvegarder_actionPerformed(ActionEvent e) {
    // salva il contenuto della casella di testo in un file

     // si imposta il filtro iniziale
    jFileChooser1.setFileFilter(filtreTxt);
     // viene visualizzata la finestra di selezione per il salvataggio
    int returnVal = jFileChooser1.showSaveDialog(this);
    // L'utente ha selezionato qualcosa?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // si scrive il contenuto della casella di testo nel file
       écrireFichier(jFileChooser1.getSelectedFile());
    }//if
  }

La sequenza delle operazioni è la seguente:

  • si imposta il filtro attivo su *.txt per consentire all'utente di cercare preferibilmente questo tipo di file. È presente anche il filtro "Tutti i file", che è stato aggiunto nella procedura moreInit. Si hanno quindi due filtri.
  • Viene visualizzata la finestra di selezione per il salvataggio. A questo punto perdiamo il controllo, poiché l’utente utilizza la finestra di selezione per indicare un file dal sistema di file.
  • Quando l'utente esce dalla finestra di selezione, si verifica il valore restituito per stabilire se sia necessario o meno salvare la casella di testo. In caso affermativo, deve essere salvata nel file ottenuto tramite il metodo getSelectedFile.

Il codice relativo al pulsante «Carica» è molto simile a quello del pulsante «Salva».

  void btnCharger_actionPerformed(ActionEvent e) {
    // scelta di un file tramite un oggetto JFileChooser

     // si imposta il filtro iniziale
    jFileChooser1.setFileFilter(filtreTxt);
     // viene visualizzata la finestra di selezione
    int returnVal = jFileChooser1.showOpenDialog(this);
    // L'utente ha selezionato qualcosa?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // si inserisce il file nel campo di testo
       lireFichier(jFileChooser1.getSelectedFile());
    }//if
  }//btnCharger_actionPerformed

Ci sono due differenze:

  • per visualizzare la finestra di selezione dei file, si utilizza il metodo showOpenDialog anziché il metodo showSaveDialog. La finestra di selezione visualizzata è simile a quella visualizzata dal metodo showSaveDialog.
  • se l’utente ha selezionato correttamente un file, si chiama il metodo lireFichier anziché il metodo écrireFichier.

5.4.1.1. Finestre di selezione JColorChooser e JFontChooser

Continuiamo l’esempio precedente aggiungendo due nuovi pulsanti:

Image

N.
tipo
nome
ruolo
6
JButton
btnCouleur
per impostare il colore dei caratteri di TextBox
7
JButton
btnPolice
per impostare il carattere di TextBox

Il componente JColorChooser, che consente di visualizzare una finestra di selezione del colore, è presente nell'elenco dei componenti Swing di JBuilder:

Image

Inseriamo questo componente nella finestra di progettazione, ma al di fuori del modulo. Non è visibile nel modulo, ma è comunque presente nell’elenco dei componenti della finestra:

Image

La classe JColorChooser è molto semplice. Visualizziamo la finestra di selezione dei colori con il metodo int showDialog:

static Color
showDialog(Component component, String title, Color initialColor)
Component component
il componente padre della finestra di selezione, solitamente una finestra JFrame
String title
il titolo nella barra del titolo della finestra di selezione
Colore intialColor
colore inizialmente selezionato nella casella di selezione

Se l'utente sceglie un colore, il metodo restituisce un colore; in caso contrario, restituisce il valore null. Il codice associato al pulsante Couleur è il seguente:

  void btnCouleur_actionPerformed(ActionEvent e) {
     // scelta di un colore del testo tramite il componente JColorChooser
    Color couleur;
    if((couleur=jColorChooser1.showDialog(this,"Choix d'une couleur",Color.BLACK))!=null){
       // si imposta il colore dei caratteri della casella di testo
      txtTexte.setForeground(couleur);
    }//if
  }

La classe Color è definita in java.awt.Color. In essa sono definite diverse costanti, tra cui Color.BLACK per il colore nero. La finestra di selezione visualizzata è la seguente

Image

Curiosamente, la libreria Swing non offre una classe per selezionare un tipo di carattere. Fortunatamente, ci sono le risorse Java disponibili su Internet. Effettuando una ricerca con la parola chiave «JFontChooser», che sembra un possibile nome per una classe di questo tipo, se ne trovano diverse. L’esempio che seguirà ci darà l’opportunità di configurare JBuilder in modo che utilizzi pacchetti non previsti nella sua configurazione iniziale.

Il pacchetto recuperato si chiama swingextras.jar ed è stato collocato nella cartella <jdk>\jre\lib\perso, dove <jdk> indica la directory di installazione del JDK utilizzato da JBuilder. Avrebbe potuto essere collocato in qualsiasi altra posizione.

Diamo un’occhiata al contenuto del pacchetto SwingExtras.jar:

Image

Qui si trova il codice sorgente Java dei componenti inclusi nel pacchetto. Sono presenti anche le classi stesse:

Image

Da questo archivio, si noti che la classe JFontChooser si chiama in realtà com.lamatek.swingextras.JFontChooser. Se non si desidera scrivere il nome completo, sarà necessario scrivere all’inizio del programma:

import com.lamatek.swingextras.*;

In che modo il compilatore e la macchina virtuale Java riusciranno a individuare questo nuovo pacchetto? Nel caso di un utilizzo diretto di JDK, ciò è già stato spiegato e il lettore potrà trovare le spiegazioni nel paragrafo dedicato ai pacchetti Java. Qui descriviamo in dettaglio il metodo da utilizzare con JBuilder. I pacchetti vengono configurati nel menu Opzioni/Configura JDK:

Image

1
Il pulsante Modifier (1) consente di indicare a JBuilder quale JDK utilizzare. In questo esempio, JBuilder aveva portato con sé JDK 1.3.1. Quando è stato rilasciato il JDK 1.4, è stato installato separatamente dal JBuilder e si è utilizzato il pulsante Modifier per indicare a JBuilder diutilizzare d'ora in poi la versione JDK 1.4, specificandone la directory di installazione
2
directory di base di JDK attualmente utilizzata da JBuilder
3
elenco degli archivi Java (.jar) utilizzati da JBuilder. È possibile aggiungerne altri tramite il pulsante Ajouter (4)
4
Il pulsante Ajouter (4) consente di aggiungere nuovi pacchetti che JBuilder utilizzerà sia per la compilazione che per l'esecuzione dei programmi. Qui lo abbiamo utilizzato per aggiungere il pacchetto SwingExtras.jar (5)

Ora JBuilder è configurato correttamente per poter utilizzare la classe JFontChooser. Tuttavia, per utilizzarla correttamente, dovremmo avere accesso alla definizione di questa classe. L'archivio swingextras.jar contiene file HTML che potremmo estrarre per sfruttarli. Ma non è necessario. La documentazione Java inclusa nei file .jar è direttamente accessibile da JBuilder. A tal fine, occorre configurare la scheda Documentation (6) sopra indicata. Si ottiene la seguente nuova finestra:

Image

Il pulsante Ajouter ci permette di specificare che il file SwingExtras.jar deve essere analizzato per la documentazione. Una volta completata questa operazione, si può constatare che si ha effettivamente accesso alla documentazione di SwingExtras.jar. Ciò si traduce in diversi vantaggi:

  • se si inizia a scrivere l’istruzione di importazione
import com.lamatek.swingextras.*;

si noterà che JBuilder fornisce un aiuto:

Image

Il pacchetto com.lamatek è stato trovato correttamente.

  • Se ora si passa al programma seguente:
import com.lamatek.swingextras.*;

public class test{
  JFontChooser jFontChooser1=null;
}  

eseguendo F1 sulla parola chiave JFontChooser, si ottiene una guida su questa classe:

Image

Nella barra di stato 1 si vede che viene effettivamente utilizzato un file HTML del pacchetto swingextras.jar. L'esempio sopra riportato è sufficientemente esplicito per poter scrivere il codice del pulsante Police della nostra applicazione:

  void btnPolice_actionPerformed(ActionEvent e) {
    // scelta di un carattere di testo tramite il componente JFontChooser
    jFontChooser1 = new JFontChooser(new Font("Arial", Font.BOLD, 12));
    if (jFontChooser1.showDialog(this, "Choix d'une police") == JFontChooser.ACCEPT_OPTION) {
      // si modifica il carattere della casella di testo
      txtTexte.setFont(jFontChooser1.getSelectedFont());
    }//if
  }

La finestra di selezione visualizzata dal metodo showDialog sopra riportato è la seguente:

Image

5.5. L'applicazione grafica IMPOTS

Riprendiamo l’applicazione IMPOTS già trattata due volte. Ora vi aggiungiamo un’interfaccia grafica:

Image

I controlli sono i seguenti

n.
tipo
nome
ruolo
1
JRadioButton
rdOui
spuntare se sposato
2
JRadioButton
rdNon
da spuntare se non sposato
3
JSpinner
spinEnfants
numero di figli del contribuente
Minimo=0, Massimo=20, Incremento=1
4
JTextField
txtSalaire
stipendio annuo del contribuente in F
5
JLabel
lblImpots
importo dell'imposta da versare
6
JTextField
txtStatus
campo dei messaggi di stato - non modificabile

Il menu è il seguente:

opzione principale
opzione secondaria
nome
ruolo
Imposte
   
 
Inizializza
mnuInitialiser
carica i dati necessari al calcolo da un file di testo
 
Calcola
mnuCalculer
calcola l'imposta da pagare quando tutti i dati necessari sono presenti e corretti
 
Cancella
mnuEffacer
riporta il modulo allo stato iniziale
 
Esci
mnuQuitter
chiude l'applicazione

Regole di funzionamento

  • il menu Calculer rimane disattivato finché il campo dello stipendio è vuoto
  • se, all’avvio del calcolo, si riscontra che lo stipendio non è corretto, viene segnalato l’errore:

Image

Il programma è riportato di seguito. Utilizza la classe impots creata nel capitolo dedicato alle classi. Una parte del codice generato automaticamente da JBuilder non è stata qui riprodotta.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;
import java.util.*;
import javax.swing.event.*;

public class frmImpots extends JFrame {
  // i componenti della finestra
  JPanel contentPane;
  JMenuBar jMenuBar1 = new JMenuBar();
  JMenu jMenu1 = new JMenu();
  JMenuItem mnuInitialiser = new JMenuItem();
  JMenuItem mnuCalculer = new JMenuItem();
  JMenuItem mnuEffacer = new JMenuItem();
  JMenuItem mnuQuitter = new JMenuItem();
  JLabel jLabel1 = new JLabel();
  JFileChooser jFileChooser1 = new JFileChooser();
  JTextField txtStatus = new JTextField();
  JRadioButton rdOui = new JRadioButton();
  JRadioButton rdNon = new JRadioButton();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSalaire = new JTextField();
  JLabel jLabel4 = new JLabel();
  JLabel jLabel5 = new JLabel();
  JLabel lblImpots = new JLabel();
  JLabel jLabel7 = new JLabel();
  ButtonGroup buttonGroup1 = new ButtonGroup();
  JSpinner spinEnfants=null;
  FileFilter filtreTxt=null;

   // gli attributi della classe
  double[] limites=null;
  double[] coeffr=null;
  double[] coeffn=null;
  impots objImpots=null;

   //Creare il frame
  public frmImpots() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // altre inizializzazioni
    moreInit();
  }

   // inizializzazione del modulo
  private void moreInit(){
     // menu «Calcola» disabilitato
    mnuCalculer.setEnabled(false);
    // campi di immissione disabilitati
    txtSalaire.setEditable(false);
    txtSalaire.setBackground(Color.WHITE);
    // campo di stato
    txtStatus.setBackground(Color.WHITE);
    // selettore "Figli" - da 0 a 20 figli
    spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);
     // filtro *.txt per la casella di selezione
    filtreTxt = new javax.swing.filechooser.FileFilter(){
      public boolean accept(File f){
        // si accetta la "f"?
        return f.getName().toLowerCase().endsWith(".txt");
      }//accettato
      public String getDescription(){
        // descrizione del filtro
        return "Fichiers Texte (*.txt)";
      }//getDescription
    };
     // si aggiunge il filtro *.txt
    jFileChooser1.addChoosableFileFilter(filtreTxt);
    // si desidera anche il filtro per tutti i file
    jFileChooser1.setAcceptAllFileFilterUsed(true);
    // si imposta la directory di partenza della cartella FileChooser
    jFileChooser1.setCurrentDirectory(new File("."));
  }//moreInit

   //Inizializzare il componente
  private void jbInit() throws Exception  {

................

  }

   //Sostituito, in modo da poter uscire quando la finestra viene chiusa
  protected void processWindowEvent(WindowEvent e) {
.....................
  }

  void mnuQuitter_actionPerformed(ActionEvent e) {
    // si esce dall'applicazione
    System.exit(0);
  }

  void mnuInitialiser_actionPerformed(ActionEvent e) {
    // si carica il file dei dati
     // che si seleziona con la casella di selezione JFileChooser1
    jFileChooser1.setFileFilter(filtreTxt);
    if (jFileChooser1.showOpenDialog(this)!=JFileChooser.APPROVE_OPTION)
      return;
     // si elabora il file selezionato
    try{
       // lettura dei dati
      lireFichier(jFileChooser1.getSelectedFile());
       // creazione dell'oggetto «imposte»
      objImpots=new impots(limites,coeffr,coeffn);
       // conferma
      txtStatus.setText("Données chargées");
       // lo stipendio può essere modificato
      txtSalaire.setEditable(true);
       // non sono più possibili modifiche
      mnuInitialiser.setEnabled(false);
    }catch(Exception ex){
       // problema
      txtStatus.setText("Erreur : " + ex.getMessage());
      // fine
      return;
    }//catch
  }

  private void lireFichier(File fichier) throws Exception {
     // le tabelle di dati
    ArrayList aLimites=new ArrayList();
    ArrayList aCoeffR=new ArrayList();
    ArrayList aCoeffN=new ArrayList();
    String[] champs=null;

     // apertura del file in lettura
    BufferedReader IN=new BufferedReader(new FileReader(fichier));
    // si legge il file riga per riga
     // sono della forma limite coeffr coeffn
    String ligne=null;
    int numLigne=0;           // numero della riga corrente
    while((ligne=IN.readLine())!=null){
      // un'altra riga
      numLigne++;
       // si scompone la riga in campi
      champs=ligne.split("\\s+");
       // 3 campi?
      if(champs.length!=3)
        throw new Exception("ligne " + numLigne + "erronée dans fichier des données");
       // si recuperano i tre campi
       aLimites.add(champs[0]);
       aCoeffR.add(champs[1]);
       aCoeffN.add(champs[2]);
    }//while
     // chiusura del file
    IN.close();
     // trasferimento dei dati in array delimitati
    int n=aLimites.size();
    limites=new double[n];
    coeffr=new double[n];
    coeffn=new double[n];
    for(int i=0;i<n;i++){
      limites[i]=Double.parseDouble((String)aLimites.get(i));
      coeffr[i]=Double.parseDouble((String)aCoeffR.get(i));
      coeffn[i]=Double.parseDouble((String)aCoeffN.get(i));
    }//for
  }

  void mnuCalculer_actionPerformed(ActionEvent e) {
    // calcolo dell'imposta
     // verifica dello stipendio
    int salaire=0;
    try{
      salaire=Integer.parseInt(txtSalaire.getText().trim());
      if(salaire<0) throw new Exception();
    }catch (Exception ex){
       // messaggio di errore
      txtStatus.setText("Salaire incorrect. Recommencez");
       // focus sul campo errato
      txtSalaire.requestFocus();
       // ritorno all'interfaccia
      return;
    }
     // numero di figli
    Integer InbEnfants=(Integer)spinEnfants.getValue();
     // calcolo dell'imposta
    lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),InbEnfants.intValue(),salaire));
     // cancellazione messaggio di stato
    txtStatus.setText("");
  }

  void txtSalaire_caretUpdate(CaretEvent e) {
     // lo stipendio è cambiato - si aggiorna il menu "Calcola"
    mnuCalculer.setEnabled(! txtSalaire.getText().trim().equals(""));
  }

  void mnuEffacer_actionPerformed(ActionEvent e) {
    // azzeramento del modulo
    rdNon.setSelected(true);
    spinEnfants.getModel().setValue(new Integer(0));
    txtSalaire.setText("");
    mnuCalculer.setEnabled(false);
    lblImpots.setText("");
  }
}

Qui abbiamo utilizzato un componente disponibile solo a partire dalla versione 1.4 di JDK, ovvero il JSpinner. Si tratta di un incrementatore, che in questo caso permette all’utente di impostare il numero di figli. Questo componente Swing non era disponibile nella barra dei componenti Swing di JBuilder 6 utilizzata per il test. Ciò non ne impedisce comunque l’utilizzo, anche se la procedura è leggermente più complessa rispetto a quella prevista per i componenti disponibili nella barra dei componenti. Infatti, non è possibile trascinare il componente JSpinner sul modulo durante la fase di progettazione. È necessario farlo in fase di esecuzione. Diamo un’occhiata al codice che lo realizza:

     // spinner Figli - inserire un numero compreso tra 0 e 20 figli
    spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);

La prima riga crea il componente JSpinner. Questo componente può essere utilizzato per diversi scopi e non solo come incrementatore di numeri interi, come in questo caso. L'argomento del costruttore JSpinner è in questo caso un modello di incrementatore di numeri che accetta quattro parametri (valore, min, max, incremento). Il componente ha la seguente forma:

Image

valeur
valore iniziale visualizzato nel componente
min
valore minimo visualizzabile nel componente
max
valore massimo visualizzabile nel componente
incrément
valore di incremento del valore visualizzato quando si utilizzano le frecce su/giù del componente

Il valore del componente si ottiene tramite il suo metodo getValue, che restituisce un tipo Object. Da qui derivano alcune conversioni di tipo per ottenere l'intero di cui abbiamo bisogno:

     // numero di figli
    Integer InbEnfants=(Integer)spinEnfants.getValue();
     // calcolo dell'imposta
    lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),
      InbEnfants.intValue(),salaire));

Una volta definito il componente JSpinner, questo viene inserito nella finestra:

    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);

Prima di tutto, l'utente deve utilizzare l'opzione di menu Initialiser che crea un oggetto impots con il costruttore

    public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception

della classe impots. Si ricorda che quest’ultima è stata definita a titolo di esempio nel capitolo dedicato alle classi. Si rimanda a tale capitolo se necessario. I tre array necessari al costruttore vengono popolati a partire dal contenuto di un file di testo con il seguente formato:

12620.0
0
0
13190
0,05
631
15640
0,1
1290,5
24.740
0,15
2072,5
31810
0,2
3309,5
39.970
0,25
4900
48360
0,3
6898,5
55790
0,35
9316,5
92970
0,4
12106
127.860
0,45
16754,5
151250
0,50
23147,5
172.040
0,55
30710
195.000
0,60
39312
0
0,65
49062

Ogni riga contiene tre numeri separati da almeno uno spazio. Questo file di testo viene selezionato dall'utente tramite un componente JFileChooser. Una volta creato l'oggetto di tipo impots, non resta che lasciare che l'utente fornisca le tre informazioni necessarie: stato civile (coniugato o no), numero di figli, stipendio annuo, e chiamare il metodo calculer della classe impots. L'operazione può essere ripetuta più volte. L'oggetto di tipo impots viene invece creato una sola volta, quando si utilizza l'opzione Initialiser.

5.6. Scrittura di applet

5.6.1. Introduzione

Una volta sviluppata un'applicazione con interfaccia grafica, è piuttosto semplice trasformarla in applet. Una volta salvata su un computer A, può essere scaricata tramite un browser web da un computer B collegato a Internet. L'applicazione iniziale viene così condivisa tra numerosi utenti e questo è il principale vantaggio della conversione di un'applicazione in applet. Tuttavia, non tutte le applicazioni possono essere convertite in questo modo: per non arrecare disturbo all'utente che utilizza un'applet nel proprio browser, l'ambiente dell'applet è regolamentato:

  • un’applet non può leggere né scrivere sul disco dell’utente
  • può comunicare solo con il computer da cui è stata scaricata dal browser.

Si tratta di restrizioni significative. Esse implicano, ad esempio, che un’applicazione che necessiti di leggere informazioni da un file o da un database dovrà richiederle tramite un’applicazione intermediaria situata sul server da cui è stata scaricata.

La struttura generale di una semplice applicazione web è la seguente:

Image

  • il client richiede un documento HTML al server Web, solitamente tramite un browser. Questo documento può contenere un’applet che funzionerà come un’applicazione grafica autonoma all’interno del documento HTML visualizzato dal browser del client.
  • Tale applet potrà accedere ai dati, ma solo a quelli presenti sul server web. Non avrà accesso né alle risorse del computer client su cui viene eseguita, né a quelle di altri computer della rete diversi da quello da cui è stata scaricata.

5.6.2. La classe JApplet

5.6.2.1. Définition

Un'applicazione può essere scaricata da un browser Web se è un'istanza della classe java.applet.Applet o della classe javax.swing.JApplet. Quest’ultima deriva dalla prima, che a sua volta deriva dalla classe Panel, derivata a sua volta dalla classe Container. Un'istanza di tipo Applet o JApplet, essendo di tipo container, potrà quindi contenere componenti (Component) quali pulsanti, caselle di controllo, elenchi, ecc. Forniamo alcune indicazioni sul ruolo dei diversi metodi:

Metodo
Funzione
public void destroy();
distrugge l’istanza dell’applet
public AppletContext getAppletContext();
recupera il contesto di esecuzione dell'applet (documento HTML in cui si trova, altre applet dello stesso documento, …)
public String getAppletInfo();
restituisce una stringa di caratteri contenente informazioni sull'applet
public AudioClip getAudioClip(URL url);
carica il file audio specificato da URL
public AudioClip getAudioClip(URL url, String name);
carica il file audio specificato da URL/name
public URL getCodeBase();
restituisce l'URL dell'applet
public URL getDocumentBase();
restituisce il URL del documento HTML contenente l'applet
public Image getImage(URL url);
recupera l'immagine specificata da URL
public Image getImage(URL url, String name);
recupera l'immagine specificata da URL/name
public String getParameter(String name);
recupera il valore del parametro name contenuto nel tag <applet> del documento HTML che contiene l'applet.
public void init();
questo metodo viene eseguito dal browser al primo avvio dell'applet
public boolean isActive();
Stato dell'applet
public void play(URL url);
riproduce il file audio specificato da URL
public void play(URL url, String name);
riproduce il file audio specificato da UR/name
public void resize(Dimension d);
imposta le dimensioni del riquadro dell'applet
public void resize(int width, int height);
idem
public final void setStub(AppletStub stub);
 
public void showStatus(String msg);
inserisce un messaggio nella barra di stato dell'applet
public void start();
questo metodo viene eseguito dal browser ogni volta che viene visualizzato il documento contenente l'applet
public void stop();
questo metodo viene eseguito dal browser ogni volta che il documento contenente l'applet viene chiuso per passare a un altro (cambio di URL da parte dell'utente)

La classe JApplet ha apportato alcuni miglioramenti alla classe Applet, in particolare la capacità di contenere componenti JMenuBar e c.a.d. nei menu, cosa che non era possibile con la classe Applet.

5.6.2.2. Esecuzione di un'applet: i metodi init, start e stop

Quando un browser carica un’applet, ne chiama tre metodi:

init
Questo metodo viene chiamato durante il caricamento iniziale dell’applet. Qui andranno inserite le inizializzazioni necessarie all’applicazione.
start
Questo metodo viene chiamato ogni volta che il documento contenente l'applet diventa il documento attivo del browser. Pertanto, quando un utente carica un'applet, i metodi init e start verranno eseguiti in questo ordine. Quando l'utente uscirà dal documento per visualizzarne un altro, verrà eseguito il metodo stop. Quando tornerà su quel documento in un secondo momento, verrà eseguito il metodo start.
stop
Questo metodo viene chiamato ogni volta che l'utente esce dal documento contenente l'applet.

Per molte applet è necessario solo il metodo init. I metodi start e stop sono necessari solo se l'applicazione avvia attività (thread) che girano in parallelo e in modo continuo, spesso all'insaputa dell'utente. Quando l'utente esce dal documento, la parte visibile dell'applicazione scompare, ma queste attività in background continuano a funzionare. Spesso ciò è superfluo. Si approfitta quindi della chiamata del browser al metodo **stop per arrestarli. Se l’utente torna al documento, si approfitta della chiamata del browser al metodo **start per riavviarli.

Prendiamo ad esempio un’applet che presenta un orologio nella propria interfaccia grafica. Questo viene gestito da un’attività in background (thread). Quando l’utente esce dalla pagina dell’applet nel browser, è superfluo mantenere l’orologio che è diventato invisibile: nel metodo stop dell’applet, si interromperà il thread che gestisce l’orologio. Nel metodo start, lo si riavvierà in modo che, quando l’utente torna alla pagina dell’applet, ritrovi un orologio aggiornato.

5.6.3. Trasformazione di un'applicazione grafica in un'applet

Si suppone qui che tale trasformazione sia possibile, ovvero che rispetti le restrizioni di esecuzione delle applet. Un’applicazione viene avviata tramite il metodo main di una delle sue classi:


public class application{
    
    public static main void(String arg[]){
        
    }
    
}// fine classe

Un'applicazione di questo tipo viene trasformata in un'applet nel modo seguente:

import java.applet.JApplet;

public class application extends JApplet{
    
    public void init(){
        
    }
    
}// fine classe

Si notino i seguenti punti:

  1. la classe application deriva ora dalla classe JApplet
  2. il metodo main è sostituito dal metodo init.

A titolo di esempio, torniamo su un'applicazione già esaminata: la gestione delle liste.

Image

I componenti di questa finestra sono i seguenti:

n.
tipo
nome
funzione
1
JTextField
txtSaisie
campo di immissione
2
JList
jList1
elenco contenuto in un contenitore jScrollPane1
3
JList
jList2
elenco contenuto in un contenitore jScrollPane2
4
JButton
cmd1To2
trasferisce gli elementi selezionati dall'elenco 1 all'elenco 2
5
JButton
cmd2To1
esegue l'operazione inversa
6
JButton
cmdRaz1
svuota l'elenco 1
7
JButton
cmdRaz2
svuota l'elenco 2

La struttura dell'applicazione era la seguente:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Creare la tabella*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Inizializzare il componente*/
  private void jbInit() throws Exception  {
    ...
    txtSaisie.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtSaisie_actionPerformed(e);
      }
    });
        ...
         // Jlist1 viene inserito nel contenitore jScrollPane1
    jScrollPane1.getViewport().add(jList1, null);
        // Jlist2 viene inserito nel contenitore jScrollPane2
    jScrollPane2.getViewport().add(jList2, null);
        ...
  }
   /**Sostituito, in modo da poter uscire quando la finestra viene chiusa*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
.............
}//classe
  void cmd1To2_actionPerformed(ActionEvent e) {
..........
  }//trasferimento
  void cmdRaz1_actionPerformed(ActionEvent e) {
    // svuota lista 1
    v1.removeAllElements();
  }//comando Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // svuota lista 2
    v2.removeAllElements();
  }///cmd Raz2

Per trasformare la classe appli in applet, occorre procedere come segue:

  • modificare la classe precedente interfaceAppli in modo che non derivi più da JFrame ma da JApplet:
public class interfaceAppli extends JApplet {
  • eliminare l'istruzione che definisce il titolo della finestra JFrame. Un'applet JApplet non ha una barra del titolo
    // this.setTitle("JList");
  • modificare il costruttore in metodo init e in questo metodo eliminare la gestione degli eventi della finestra (WindowListener, ...). Un'applet è un contenitore che non può essere ridimensionato né chiuso.
   /**Costruire il quadro*/
  public init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli
  • Il metodo processWindowEvent deve essere eliminato o messo in commento
   /**Sostituito, in modo da poter uscire quando la finestra è chiusa*/
  //protected void processWindowEvent(WindowEvent e) {
  //  super.processWindowEvent(e);
  //  if (e.getID() == WindowEvent.WINDOW_CLOSING) {
  //    System.exit(0);
  //  }
  // }

A titolo di esempio, riportiamo il codice completo dell'applet, ad eccezione della parte generata automaticamente da JBuilder

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

public class interfaceAppli extends JApplet {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Costruire la cornice*/
  public void init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Inizializza il componente*/
  private void jbInit() throws Exception  {
    //setIconImage(Toolkit.getDefaultToolkit().createImage(interfaceAppli.class.getResource("[Votre icône]")));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(400, 308));
    // this.setTitle("JList");
    jScrollPane1.setBounds(new Rectangle(37, 133, 124, 101));
    jScrollPane2.setBounds(new Rectangle(232, 132, 124, 101));
.............
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
     // il testo inserito è stato convalidato
     // viene recuperato senza gli spazi iniziali e finali
    String texte=txtSaisie.getText().trim();
     // se è vuoto, non lo si accetta
    if(texte.equals("")){
      // messaggio di errore
      JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
        "Erreur",JOptionPane.WARNING_MESSAGE);
      // fine
      return;
    }//if
    // se non è vuoto, lo si aggiunge ai valori dell'elenco 1
    v1.addElement(texte);
     // e si svuota il campo di immissione
    txtSaisie.setText("");
  }

  void cmd1To2_actionPerformed(ActionEvent e) {
    // trasferimento degli elementi selezionati dall'elenco 1 all'elenco 2
    transfert(jList1,jList2);
  }//cmd1To2

  private void transfert(JList L1, JList L2){
      // trasferimento degli elementi selezionati dall'elenco 1 all'elenco 2
       // si recupera la tabella degli indici degli elementi selezionati in L1
      int[] indices=L1.getSelectedIndices();
      // c'è qualcosa da fare?
      if (indices.length==0) return;
      // si recuperano i valori da L1
      DefaultListModel v1=(DefaultListModel)L1.getModel();
      // e quelli di L2
      DefaultListModel v2=(DefaultListModel)L2.getModel();
      for(int i=indices.length-1;i>=0;i--){
        // si aggiungono a L2 i valori selezionati in L1
        v2.addElement(v1.elementAt(indices[i]));
        // gli elementi di L1 copiati in L2 devono essere eliminati da L1
        v1.removeElementAt(indices[i]);
      }//per
  }//trasferimento

  private void affiche(String message){
    // visualizza messaggio
      JOptionPane.showMessageDialog(this,message,
        "Suivi",JOptionPane.INFORMATION_MESSAGE);
  }// visualizza

  void cmd2To1_actionPerformed(ActionEvent e) {
    // trasferimento degli elementi selezionati da jList2 a jList1
      transfert(jList2,jList1);
  }//cmd2TO1

  void cmdRaz1_actionPerformed(ActionEvent e) {
    // svuota elenco 1
    v1.removeAllElements();
  }//comando Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // svuota elenco 2
    v2.removeAllElements();
  }//comando Raz2

}//classe

È possibile compilare il codice sorgente di questa applet. Lo facciamo qui con il JDK, senza il JBuilder.

E:\data\serge\Jbuilder\interfaces\JApplets\1>javac.bat interfaceAppli.java

E:\data\serge\Jbuilder\interfaces\JApplets\1>dir
12/06/2002  16:40                6 148 interfaceAppli.java
12/06/2002  16:41                  527 interfaceAppli$1.class
12/06/2002  16:41                  525 interfaceAppli$2.class
12/06/2002  16:41                  525 interfaceAppli$3.class
12/06/2002  16:41                  525 interfaceAppli$4.class
12/06/2002  16:41                  525 interfaceAppli$5.class
12/06/2002  16:41                4 759 interfaceAppli.class

L'applicazione può essere testata con il programma AppletViewer di JDK, che consente di eseguire applet o un browser. A tal fine, è necessario creare il documento HTML appli.htm che conterrà al suo interno l'applet:

<html>
    <head>
        <title>listes swing</title>
    </head>
    <body>
        <applet 
        code="interfaceAppli.class"
      width="400"
      height="300">
        </applet>
    </body>
</html>

Si tratta di un documento HTML classico, se non fosse per la presenza del tag applet. Quest'ultimo è stato utilizzato con tre parametri:

code="interfaceAppli.class"
nome della classe JAVA compilata da caricare per eseguire l'applet
width="400"
larghezza del riquadro dell’applet nel documento
height="300"
altezza del riquadro dell'applet nel documento

Una volta creato il file appli.htm, è possibile caricarlo con il programma appletviewer contenuto in JDK oppure con un browser. Digitare in una finestra DOS il seguente comando nella cartella del file appli.htm:

appletviewer appli.htm

Si suppone che la directory dell’eseguibile appletviewer.exe si trovi nella directory PATH della finestra DOS; in caso contrario, occorrerebbe specificare il percorso completo dell’eseguibile appletviewer.exe. Si ottiene la seguente finestra:

Image

Si interrompe l’esecuzione dell’applet con l’opzione Applet/Quitter. Proviamo ora l’applet con un browser. Quest’ultimo deve utilizzare una macchina virtuale Java 2 per poter visualizzare i componenti Swing. Fino a poco tempo fa (2001), questo requisito rappresentava un problema poiché Sun produceva JDK che gli editori di browser adottavano solo con notevole ritardo. Alla fine Sun ha risolto questo ostacolo mettendo a disposizione degli utenti un’applicazione chiamata «Java plugin» che consente ai browser Internet Explorer e Netscape Navigator di utilizzare le versioni più recenti di JRE prodotte da Sun (JRE = Java Runtime Environment). I test che seguono sono stati effettuati con IE 5.5 dotato del plugin Java 1.4.

Un primo modo per testare l’applet interfaceAppli.class consiste nel caricare il file HTML appli.htm direttamente nel browser facendo doppio clic su di esso. Il browser visualizzerà quindi la pagina HTML e la relativa applet senza l’ausilio di un server Web:

Image

Come si evince da URL (1), il file è stato caricato direttamente nel browser. Nell’esempio seguente, il file appli.htm è stato richiesto a un server Web Apache in esecuzione sulla porta 81 del computer locale:

Image

5.6.4. L'opzione di formattazione <applet> in un documento HTML

Abbiamo visto che all'interno di un documento HTML, l'applet era indicata dal comando di formattazione (tag) <applet>. Questo comando può avere diversi parametri:

<applet
    code=
    width=
    height=
    codebase=
    align=
    hspace=
    vspace=
alt=
>
    <param name1=nom1 value=val1>
    <param name2=nom2 value=val2>
    texte
>
</applet>

Il significato dei parametri è il seguente:

Parametro
Significato
code
obbligatorio - nome del file .class da eseguire
width
obbligatorio - larghezza dell'applet nel documento
height
obbligatorio - altezza …
codebase
facoltativo - URL della directory contenente l'applet da eseguire. Se codebase non è specificato, l'applet verrà cercata nella directory del documento HTML che la richiama
align
facoltativo - posizionamento (LEFT, RiGHT, CENTER) dell'applet nel documento
hspace
facoltativo - margine orizzontale che circonda l'applet espresso in pixel
vspace
facoltativo - margine verticale che circonda l'applet, espresso in pixel
alt
facoltativo - testo visualizzato al posto dell'applet se il browser non è in grado di caricarla
param
facoltativo - parametro passato all'applet che specifica il suo nome (name) e il suo valore (value). L'applet potrà recuperare il valore del parametro nome1 tramite val1=getParameter("nome1")
È possibile utilizzare tutti i parametri che si desidera
texte
facoltativo - verrà visualizzato da qualsiasi browser incapace di eseguire un'applet, ad esempio perché privo di una macchina virtuale Java.

Prendiamo un esempio per illustrare il passaggio dei parametri a un'applet:

<html>
    <head>
        <title>paramètres applet</title>
    </head>
    <body>
        <applet code="interfaceParams.class" width="400" height="300">
      <param name="param1" value="val1">
      <param name="param2" value="val2">      
        </applet>
    </body>
</html>

Il codice Java dell'applet interfaceParams è stato generato come indicato in precedenza. È stata creata un'applicazione JBuilder, quindi sono state apportate le poche modifiche menzionate sopra:

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

public class interfaceParams extends JApplet {
  JPanel contentPane;
  JScrollPane jScrollPane1 = new JScrollPane();
  JLabel jLabel1 = new JLabel();
  DefaultListModel params=new DefaultListModel();
  JList lstParams = new JList(params);

  //Costruire il quadro
  public void init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // altre inizializzazioni
    moreInit();
  }

   // inizializzazioni
  public void moreInit(){
     // visualizza i valori dei parametri dell'applet
    params.addElement("nom=param1 valeur="+getParameter("param1"));
    params.addElement("nom=param2 valeur="+getParameter("param2"));
  }//ulteriori inizializzazioni

   //Inizializza il componente
  private void jbInit() throws Exception  {
............
    this.setSize(new Dimension(205, 156));
    //this.setTitle("Parametri di un'applet");
    jScrollPane1.setBounds(new Rectangle(19, 53, 160, 73));
............
 }
}

Esecuzione con AppletViewer:

Image

5.6.5. Accedere a risorse remote da un'applet

Molte applicazioni devono utilizzare informazioni presenti in file o database. Abbiamo già indicato che un'applet non ha accesso alle risorse del computer su cui viene eseguita. Si tratta di una misura di buon senso. Altrimenti basterebbe scrivere un'applet per "spiare" il disco di chi la carica. L'applet ha tuttavia accesso alle risorse del server da cui è stata scaricata, ad esempio ai file. È proprio questo che vedremo ora.

5.6.5.1. La classe URL

Qualsiasi applicazione Java può leggere un file presente su una macchina della rete grazie alla classe java.net.URL (URL = Uniform Resource Locator). Un URL identifica una risorsa di rete, il computer su cui si trova, nonché il protocollo e la porta di comunicazione da utilizzare per recuperarla nella forma:

protocollo:porta//macchina/file

Pertanto, l'URL http://www.ibm.com/index.html indica il file index.html presente sul computer www.ibm.com. È accessibile tramite il protocollo http. La porta non è specificata: il browser utilizzerà la porta 80, che è la porta predefinita del servizio http.

Vediamo in dettaglio alcuni degli elementi della classe URL:

public URL(String spec)
crea un'istanza di URL a partire da una stringa del tipo "protocollo:porta//macchina/file" - genera un'eccezione se la sintassi della stringa non corrisponde a una URL
  
public String getFile()
consente di ottenere il campo «file» della stringa «protocollo:porta//macchina/file» dell’oggetto URL
  
public String getHost()
consente di ottenere il campo "macchina" dalla stringa "protocollo:porta//macchina/file" dell'URL
  
public String getPort()
consente di ottenere il campo porta della stringa "protocollo:porta//macchina/file" da URL
  
public String getProtocol()
consente di ottenere il campo "protocollo" dalla stringa "protocollo:porta//macchina/file" di URL
  
public URLConnection openConnection()
apre la connessione con la macchina remota getHost() sulla sua porta getPort() secondo il protocollo getProtocol() al fine di leggere il file getFile(). Genera un'eccezione se non è stato possibile aprire la connessione
  
public final InputStream openStream()
abbreviazione di openConnection().getInputStream(). Consente di ottenere un flusso di input dal quale sarà possibile leggere il contenuto del file getFile(). Genera un'eccezione se non è stato possibile ottenere il flusso
  
public String toString()
visualizza l'identità dell'istanza di URL

Ci sono due metodi che ci interessano:

  • public URL(String spec) per specificare il file da elaborare
  • public final InputStream openStream() per elaborarlo

5.6.5.2. Un esempio da console

Scriveremo un programma Java, senza interfaccia grafica, incaricato di visualizzare sullo schermo il contenuto di un URL che gli viene passato come parametro. Un esempio di chiamata potrebbe essere il seguente:

    DOS> java urlcontenu http://istia.univ-angers.fr/index.html

Il programma è relativamente semplice:


import java.net.*;             // per la classe URL
import java.io.*;                // per i flussi (stream)

public class urlcontenu{

// visualizza il contenuto di URL passato come argomento
// questo contenuto deve essere testo per poter essere leggibile

  public static void main (String arg[]){
    // verifica degli argomenti
    if(arg.length==0){
      System.err.println("Syntaxe pg url");
      System.exit(0);
    }
    
    try{
      // creazione di URL      
      URL url=new URL(arg[0]);
      // lettura del contenuto
      try{
        // creazione del flusso di input
        BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));
        try{
          // lettura delle righe di testo nel flusso di input
          String ligne;
          while((ligne=is.readLine())!=null)
            System.out.println(ligne);
        } catch (Exception e){
            System.err.println("Erreur lecture : " +e);
        }
        finally{
          try { is.close(); } catch (Exception e) {}
        }
      } catch (Exception e){
          System.err.println("Erreur openStream : " +e);
      }
    } catch (Exception e){
      System.err.println("Erreur création URL : " +e);
    }
  }// fine main
}// fine classe

Dopo aver verificato che fosse presente un argomento, si tenta di creare un URL con esso:


// creazione di URL      
      URL url=new URL(arg[0]);

La creazione potrebbe non andare a buon fine se l’argomento non rispetta la sintassi dei URL protocole:port//machine/fichier. Se si dispone di un URL sintatticamente corretto, si tenta di creare un flusso di input dal quale sarà possibile leggere righe di testo. Il flusso di input fornito da una funzione URL URL.openStream() fornisce un flusso di tipo InputStream, ovvero un flusso orientato ai caratteri: la lettura avviene carattere per carattere ed è necessario formare le righe manualmente. Per poter leggere le righe di testo, è necessario utilizzare il metodo readLine delle classi BufferedReader o DataInputStream.On; in questo caso è stata scelta BufferedReader. Non resta che trasformare il flusso InputStream di URL in un flusso BufferedReader. Ciò avviene creando un flusso intermedio InputStreamReader:


BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));

La creazione di questo flusso potrebbe non andare a buon fine. Si gestisce l’eccezione corrispondente. Una volta ottenuto il flusso, non resta che leggerne le righe di testo e visualizzarle sullo schermo.


            String ligne;
         while((ligne=is.readLine())!=null)
                System.out.println(ligne);

Anche in questo caso la lettura può generare un'eccezione che deve essere gestita. Ecco un esempio di esecuzione del programma che richiede una URL locale:

E:\data\serge\Jbuilder\interfaces\JApplets\3>java urlcontenu http://localhost
<html>

<head>


<title>Bienvenue dans le Serveur Web personnel</title>
</head>

<body bgcolor="#FFFFFF" link="#0080FF" vlink="#0080FF"
topmargin="0" leftmargin="0">

<table border="0" cellpadding="0" cellspacing="0" width="100%"
bgcolor="#000000">
............
                </table>
                </center></div></td>
            </tr>
        </table>
        </td>
    </tr>
</table>
</body>
</html>

5.6.5.3. Un esempio grafico

L'interfaccia grafica sarà la seguente:

Image

Numero
Nome
Tipo
Ruolo
1
txtURL
JTextField
URL da leggere
2
btnCharger
JButton
pulsante per avviare la riproduzione di URL
3
JScrollPane1
JScrollPane
pannello scorrevole
4
lstURL
JList
elenco che mostra il contenuto dell'URL richiesto

All'esecuzione, otteniamo un risultato analogo a quello del programma da console:

Image

Il codice rilevante dell'applicazione è il seguente:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.net.*;
import java.io.*;

public class interfaceURL extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JTextField txtURL = new JTextField();
  JButton btnCharger = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel lignes=new DefaultListModel();
  JList lstURL = new JList(lignes);

  //Costruire il frame
  public interfaceURL() {
..............
  }

   //Inizializzazione del componente
  private void jbInit() throws Exception  {
..............
  }

   //Sostituito, così possiamo uscire quando la finestra viene chiusa
  protected void processWindowEvent(WindowEvent e) {
...................
  }

  void txtURL_caretUpdate(CaretEvent e) {
    // imposta lo stato del pulsante Carica
    btnCharger.setEnabled(! txtURL.getText().trim().equals(""));
  }

  void btnCharger_actionPerformed(ActionEvent e) {
    // visualizza il contenuto di URL nell'elenco
    try{
      afficherURL(txtURL.getText().trim());
    }catch(Exception ex){
       // visualizza l'errore
      JOptionPane.showMessageDialog(this,"Erreur : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
    }//try-catch
  }

  private void afficherURL(String strURL) throws Exception {
    // visualizza il contenuto di URL strURL nell'elenco
     // nessuna eccezione viene gestita in modo specifico. Ci si limita a segnalarle

     // creazione di URL
    URL url=new URL(strURL);
     // creazione del flusso di input
    BufferedReader IN=new BufferedReader(new InputStreamReader(url.openStream()));
    // lettura delle righe di testo nel flusso di input
    String ligne;
    while((ligne=IN.readLine())!=null)
      lignes.addElement(ligne);
     // chiusura del flusso di lettura
    IN.close();
  }
}

5.6.5.4. Un'applet

L'applicazione grafica precedente viene trasformata in un'applet, come già illustrato più volte. L'applet viene inserita in un documento HTML appliURL.htm:

<html>
  <head>
    <title>Applet lisant une URL</title>
  </head>
  <body>
    <h1>Applet lisant une URL</h1>
    <applet
      code="appletURL.class"
      width="400"
      height="300"
    >
    </applet>
  </body>
</html>

Image

Nell'esempio sopra riportato, il browser ha richiesto l'http://localhost:81/Japplets/2/appliURL.htm URL a un server web apache in esecuzione sulla porta 81. L'applet è stata quindi visualizzata nel browser. All’interno di essa, è stato richiesto nuovamente l’URL http://localhost:81/Japplets/2/appliURL.htm per verificare che venisse effettivamente recuperato il file appliURL.htm che avevamo creato. Ora proviamo a caricare un file URL che non appartiene al computer che ha generato l’applet (in questo caso localhost):

Image

Il caricamento del file URL è stato rifiutato. Qui si riscontra il vincolo legato alle applet: esse possono accedere solo alle risorse di rete del computer da cui sono state scaricate. Esiste un modo per l’applet di aggirare questa limitazione, ovvero delegare le richieste di rete a un programma server situato sul computer da cui è stata scaricata. Questo programma effettuerà le richieste di rete al posto dell’applet e le restituirà i risultati. Si tratta di un programma relay.

5.7. L’applet IMPOTS

Ora trasformeremo l’applicazione grafica IMPOTS in un’applet. Ciò presenta un certo interesse: l’applicazione diventerà disponibile per qualsiasi utente di Internet dotato di un browser e di un plugin Java 1.4 (grazie al componente JSpinner). La nostra applicazione diventa così globale... Questa modifica richiederà qualcosa di più della semplice trasformazione da applicazione grafica ad applet, ormai diventata un classico. L’applicazione utilizza un’opzione di menu Initialiser il cui scopo è leggere il contenuto di un file locale. Tuttavia, se immaginiamo un’applicazione web, ci rendiamo conto che questo schema non è più valido:

Image

È evidente che i dati che definiscono le aliquote fiscali si troveranno sul server web e non su ciascuna delle macchine client. Il file da leggere sul server web sarà collocato nella stessa cartella dell’applet e l’opzione Initialiser dovrà recuperarlo da lì. Gli esempi precedenti hanno mostrato come farlo. Inoltre, per selezionare il file dei dati localmente, era stato utilizzato un componente JFileChooser che qui non è più necessario.

Il documento HTML contenente l’applet sarà il seguente:

<html>
  <head>
    <title>Applet de calcul d'impôts</title>
  </head>
  <body>
    <h2>Calcul d'impôts</h2>
    <applet
      code="appletImpots.class"
      width="320"
      height="270"
    >

<param name="data" value="impots.txt">


    </applet>
  </body>
</html>

Si noti il parametro data, il cui valore è il nome del file contenente i dati della tabella delle aliquote fiscali. Il documento HTML, la classe appletImpots, la classe impots e il file impots.txt si trovano tutti nella stessa cartella del server Web:

E:\data\serge\web\JApplets\impots>dir
12/06/2002  13:24                  247 impots.txt
13/06/2002  10:17                  654 appletImpots$2.class
13/06/2002  10:17                  653 appletImpots$3.class
13/06/2002  10:17                  653 appletImpots$4.class
13/06/2002  10:17                  651 appletImpots$5.class
13/06/2002  10:06                  651 appletImpots$6.class
13/06/2002  10:17                8 655 appletImpots.class
13/06/2002  10:17                  657 appletImpots$1.class
13/06/2002  10:24                  286 appletImpots.htm
13/06/2002  10:17                1 305 impots.class

Il codice dell'applet è il seguente (abbiamo evidenziato solo le modifiche rispetto all'applicazione grafica):

...........
import java.net.*;
import java.applet.Applet;

public class appletImpots extends JApplet {
  // i componenti della finestra
  JPanel contentPane;
............................

   // gli attributi della classe
  double[] limites=null;
  double[] coeffr=null;
  double[] coeffn=null;
  impots objImpots=null;
  String urlDATA=null;

  //Creazione del frame
  public void init() {
    //enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // altre inizializzazioni
    moreInit();
  }

   // inizializzazione del modulo
  private void moreInit(){
     // menu «Calcola» disabilitato
    mnuCalculer.setEnabled(false);
................
     // si recupera il nome del file della tabella delle imposte
    String nomFichier=getParameter("data");
    // errore?
    if(nomFichier==null){
       // messaggio di errore
      txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
       // si blocca l'opzione Inizializza
      mnuInitialiser.setEnabled(false);
      // fine
      return;
    }//if
     // si imposta il URL dei dati
    urlDATA=getCodeBase()+"/"+nomFichier;
  }//moreInit

   //Inizializza il componente
  private void jbInit() throws Exception  {
...................
  }

  void mnuQuitter_actionPerformed(ActionEvent e) {
    // si esce dall'applicazione
    System.exit(0);
  }

  void mnuInitialiser_actionPerformed(ActionEvent e) {
    // caricamento del file di dati
    try{
       // lettura dei dati
      lireDATA();
       // creazione dell'oggetto «imposte»
      objImpots=new impots(limites,coeffr,coeffn);
................
  }

  private void lireDATA() throws Exception {
     // tabelle di dati
    ArrayList aLimites=new ArrayList();
    ArrayList aCoeffR=new ArrayList();
    ArrayList aCoeffN=new ArrayList();
    String[] champs=null;

     // apertura del file in modalità di lettura
    BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));
    // si legge il file riga per riga
....................
  }

  void mnuCalculer_actionPerformed(ActionEvent e) {
    // calcolo dell'imposta
......................
  }

  void txtSalaire_caretUpdate(CaretEvent e) {
..........
  }

  void mnuEffacer_actionPerformed(ActionEvent e) {
...............
  }
}

Qui ci limitiamo a commentare le modifiche derivanti dal fatto che il file di dati da leggere si trova ora su una macchina remota anziché su una macchina locale:

Il nome del file di dati da leggere, URL, viene ricavato dal seguente codice:

     // recupero del nome del file della tabella delle imposte
    String nomFichier=getParameter("data");
    // errore?
    if(nomFichier==null){
       // messaggio di errore
      txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
       // si blocca l'opzione Inizializza
      mnuInitialiser.setEnabled(false);
      // fine
      return;
    }//if
     // si imposta il URL dei dati
    urlDATA=getCodeBase()+"/"+nomFichier;

Ricordiamo che il nome del file "impots.txt" è stato passato nel parametro data dell'applet:

    <param name="data" value="impots.txt">

Il codice sopra riportato inizia quindi recuperando il valore del parametro data, gestendo un eventuale errore. Se il valore di URL del documento HTML contenente l’applet è http://localhost:81/JApplets/impots/appletImpots.htm, il URL del file impots.txt sarà http://localhost:81/JApplets/impots/impots.txt. Dobbiamo costruire il nome di questo URL. Il metodo getCodeBase() dell'applet fornisce il URL della cartella da cui è stato recuperato il documento HTML contenente l'applet, quindi, nel nostro esempio, http://localhost:81/JApplets/impots. L’istruzione seguente consente quindi di costruire l’URL del file di dati:

    urlDATA=getCodeBase()+"/"+nomFichier;

Nel metodo lireFichier(), che legge il contenuto di URL e urlData, si trova la creazione del flusso di input che consentirà di leggere i dati:

     // apertura del file in lettura
    BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));

A questo punto, non è più possibile distinguere se i dati provengano da un file remoto piuttosto che da uno locale. Ecco un esempio di esecuzione dell'applet:

Image

5.8. Conclusione

Questo capitolo ha presentato

  • un'introduzione alla creazione di interfacce grafiche con JBuilder
  • i componenti Swing più comuni
  • la creazione di applet

Ricordiamo che

  • il codice generato da JBuilder può essere scritto a mano. Una volta ottenuto questo codice in un modo o nell'altro, basta un semplice JDK per eseguirlo e JBuilder non è quindi più indispensabile.
  • l'utilizzo di uno strumento come JBuilder può comportare notevoli aumenti di produttività:
    • se è possibile scrivere a mano il codice generato da JBuilder, ciò può richiedere molto tempo e presenta scarso interesse, poiché la logica dell’applicazione risiede in genere altrove.
    • Il codice generato può essere istruttivo. Leggerlo e studiarlo è un buon modo per scoprire alcuni metodi e proprietà dei componenti che si utilizzano per la prima volta.
    • JBuilder è multipiattaforma. Si mantengono quindi le competenze acquisite passando da una piattaforma all’altra. La possibilità di scrivere programmi che funzionano sia su Windows che su Linux è ovviamente un fattore di produttività molto importante. Tuttavia, ciò è dovuto a Java stesso e non a JBuilder.

5.9. JBuilder su Linux

Tutti gli esempi precedenti sono stati testati su Windows 98. Ci si può chiedere se i programmi scritti siano portabili così come sono su Linux. A condizione che la macchina Linux in questione disponga delle classi utilizzate dai vari programmi, lo sono. Se, ad esempio, avete installato JBuilder su Linux, le classi necessarie sono già presenti sulla vostra macchina. Ecco, ad esempio, il risultato dell’esecuzione di uno dei nostri programmi sul componente JList con JBuilder 4 su Linux:

Di seguito illustriamo l’installazione di JBuilder 4 Foundation su un computer Linux. La sua installazione su un computer Win9x non presenta alcun problema ed è analoga alla procedura che verrà ora descritta. L’installazione delle versioni successive è probabilmente diversa, ma questo documento può comunque fornire alcune informazioni utili.

JBuilder è disponibile sul sito di Inprise all'indirizzo http://www.inprise.com/jbuilder

Image

In questa pagina sono presenti i link, in particolare per Windows e Linux. Descriviamo ora l’installazione di JBuilder su un computer Linux dotato dell’interfaccia grafica KDE.

Seguendo il link «Jbuilder 4 for Linux», viene visualizzato un modulo. Una volta compilato, si ottengono due file:

jb4docs_fr.tar.gz: la documentazione e gli esempi di Jbuilder4

jb4fndlinux_fr.tar.gz: JBuilder 4 Foundation. Si tratta di una versione limitata del JBuilder 4 commerciale, ma sufficiente in un contesto didattico.

Una chiave di attivazione del software vi verrà inviata via e-mail. Essa vi consentirà di utilizzare JBuilder 4 e vi verrà richiesta al primo utilizzo. Se perdete questa chiave, potete tornare all’URL sopra indicato e seguire il link “get your activation key”. Diamo per scontato che ora siate root e che vi troviate nella directory dei due file tar.gz sopra indicati. Decomprimete i due file:

[tar xvzf jb4fndlinux_fr.tar.gz]
[tar xvzf jb4docs_fr.tar.gz]
[ls -l]
drwxr-xr-x    3 nobody   nobody       4096 oct 10  2000 docs
drwxr-xr-x    3 nobody   nobody       4096 déc  5 13:00 foundation
[ls -l foundation]
-rw-r--r--    1 nobody   nobody       1128 déc  5 13:00 deploy.txt
-rwxr-xr-x    1 nobody   nobody   69035365 déc  5 13:00 fnd_linux_install.bin
drwxr-xr-x    2 nobody   nobody       4096 déc  5 13:00 images
-rw-r--r--    1 nobody   nobody      15114 déc  5 13:00 index.html
-rw-r--r--    1 nobody   nobody      23779 déc  5 13:00 license.txt
-rw-r--r--    1 nobody   nobody      75739 déc  5 13:00 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 déc  5 13:00 whatsnew.html
[ls -l docs]
-rw-r--r--    1 nobody   nobody       1128 oct 10  2000 deploy.txt
-rwxr-xr-x    1 nobody   nobody   40497874 oct 10  2000 doc_install.bin
drwxr-xr-x    2 nobody   nobody       4096 oct 10  2000 images
-rw-r--r--    1 nobody   nobody       9210 oct 10  2000 index.html
-rw-r--r--    1 nobody   nobody      23779 oct 10  2000 license.txt
-rw-r--r--    1 nobody   nobody      75739 oct 10  2000 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 oct 10  2000 whatsnew.html

Nelle due cartelle generate, il file .bin è il file di installazione. Inoltre, aprite con un browser il file index.html presente in ciascuna delle due cartelle. Questi file forniscono le istruzioni da seguire per installare entrambi i prodotti. Iniziamo con l'installazione di JBuilder Foundation:

[cd foundation]
[./fnd_linux_install.bin]

Viene visualizzata la prima schermata dell’installazione. Ne seguiranno molte altre:

Image

Confermare. Segue una schermata esplicativa:

Image

Eseguire [suivant].

Image

Accettare il contratto di licenza ed eseguire [suivant].

Image

Accetta la posizione proposta per JBuilder (prendine nota, ti servirà in seguito) e digita [suivant]. L'installazione richiede poco tempo.

Image

Siamo pronti per un primo test. In KDE, avviate il gestore dei file per accedere alla directory degli eseguibili di JBuilder: /opt/jbuilder4/bin (se avete installato JBuilder in /opt/jbuilder4):

Image

Fare clic sull’icona jbuilder qui sopra. Poiché è la prima volta che si utilizza JBuilder, sarà necessario inserire la chiave di attivazione:

Image

Compilate i campi Nom e Société. Selezionare [Ajouter] per inserire la chiave di attivazione. Si ricorda che questa dovrebbe esservi stata inviata via e-mail e che, se l’avete smarrita, potete recuperarla all’indirizzo URL da cui avete scaricato JBuilder 4 (cfr. inizio dell’installazione). Una volta accettata la chiave di attivazione, vi verranno ricordate le condizioni di licenza:

Image

Digitando OK, tornerete alla finestra di inserimento dei dati già vista in precedenza:

Image

Digitare OK per completare questa fase, che viene eseguita solo al primo avvio. Successivamente apparirà l’ambiente di sviluppo di JBuilder:

Image

Chiudete JBuilder per procedere all’installazione della documentazione. Questa operazione installerà una serie di file che verranno utilizzati nella guida di JBuilder, che al momento è incompleta. Tornate nella directory in cui avete decompresso i file tar.gz di JBuilder. Il programma di installazione si trova nella directory docs. Accedeteci:

[cd docs]
[ls -l]
-rw-r--r--    1 nobody   nobody       1128 oct 10  2000 deploy.txt
-rwxr-xr-x    1 nobody   nobody   40497874 oct 10  2000 doc_install.bin
drwxr-xr-x    2 nobody   nobody       4096 oct 10  2000 images
-rw-r--r--    1 nobody   nobody       9210 oct 10  2000 index.html
-rw-r--r--    1 nobody   nobody      23779 oct 10  2000 license.txt
-rw-r--r--    1 nobody   nobody      75739 oct 10  2000 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 oct 10  2000 whatsnew.html

Il file di installazione è doc_install.bin, ma non è possibile eseguirlo immediatamente. Se lo si fa, l'installazione fallisce senza che se ne capisca il motivo. Leggere il file index.html con un browser per una descrizione dettagliata della procedura di installazione. Il programma di installazione richiede una macchina virtuale Java, ovvero un programma denominato java, che deve trovarsi nella directory PATH del proprio computer. Si ricorda che PATH è una variabile Unix il cui valore, nella forma rep1:rep2:...:repn, indica le directory repi che devono essere esplorate nella ricerca di un eseguibile. In questo caso, il programma di installazione richiede l'esecuzione di un programma denominato java. È possibile che esistano diverse versioni di questo programma a seconda del numero di macchine virtuali Java installate qua e là. Logicamente, useremo quella fornita da Jbuilder4 e che si trova in /opt/jbuilder4/jdk1.3/bin. Per inserire questa directory nella variabile PATH:

[echo $PATH]
/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
[export PATH=/opt/jbuilder4/jdk1.3/bin/:$PATH]
[echo $PATH]
/opt/jbuilder4/jdk1.3/bin/:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin

Ora possiamo avviare l'installazione della documentazione:

[./doc_install.bin]
Preparing to install...

Si tratta di un'installazione grafica:

Image

È molto simile a quella di JBuilder Foundation. Pertanto non la descriveremo in dettaglio. C'è una schermata da non perdere:

Image

Normalmente il programma di installazione dovrebbe individuare automaticamente la posizione in cui è stato installato JBuilder Foundation. Pertanto, non modificate le impostazioni predefinite, a meno che non siano errate.

Una volta installata la documentazione, siamo pronti per un nuovo test di JBuilder. Avviate l’applicazione come illustrato in precedenza. Una volta avviato JBuilder, selezionate l’opzione Aiuto/Argomento della guida. Nel riquadro a sinistra, cliccate sul link Tutoriels:

Image

JBuilder viene fornito con una serie di tutorial che vi permetteranno di muovere i primi passi nella programmazione Java. Di seguito abbiamo seguito il tutorial “Creazione di un’applicazione” sopra riportato.

Image

In JBuilder, selezionate File/Nuovo progetto. Una procedura guidata vi mostrerà 3 schermate:

Image

Creeremo una semplice finestra con il titolo "Ciao a tutti". Chiameremo questo progetto coucou. L’esempio è stato eseguito da root. JBuilder propone quindi di raggruppare tutti i progetti in una cartella jbproject all’interno della cartella di connessione di root. Il progetto coucou verrà collocato nella cartella coucou (campo «Nome della cartella del progetto») di jbproject. Eseguire [suivant].

Image

Questa seconda schermata è un riepilogo dei vari percorsi del progetto. Non è necessario modificare nulla. Eseguire [suivant].

Image

La terza schermata vi chiede di personalizzare il vostro progetto. Compilate i campi e selezionate [Terminer].

Ora esegua Fichier/Nouveau:

Image

Scegliete Application e selezionate OK. Una nuova procedura guidata vi mostrerà 2 schermate:

Image

Il campo paquet riporta il nome del progetto. Il campo «classe» richiede il valore nom, che verrà assegnato alla classe principale del progetto. Prendete il nome sopra indicato e inserite [suivant]:

Image

La nostra applicazione comprende una seconda classe Java per la finestra dell'applicazione. Assegnate un nome a questa classe. Il campo Titre corrisponde al titolo che desiderate assegnare a questa finestra. Il riquadro options offre diverse opzioni per la vostra finestra. Selezionatele tutte e inserite [Terminer]. Si tornerà quindi all’ambiente JBuilder, che è stato arricchito con le informazioni fornite per il progetto:

Image

Nella finestra in alto a sinistra (1) è presente l’elenco dei file che compongono il progetto. Tra questi figurano in particolare le due classi .java a cui abbiamo appena assegnato un nome. Nella finestra a destra (2) è presente il codice Java della classe coucouCadre. Nella finestra in basso a sinistra è visibile la struttura (classi, metodi, attributi) del progetto. Il progetto è pronto per essere eseguito. Premere il pulsante 4 in alto oppure selezionare Esegui/Esegui progetto oppure selezionare F9. Dovrebbe apparire la seguente finestra:

Image

Cliccando sull’opzione Aide, dovreste ritrovare le informazioni che avete fornito alle procedure guidate di creazione. Non andremo oltre. È ora il momento di immergervi nei tutorial.

Prima di concludere, mostriamo semplicemente che è possibile utilizzare non JBuilder e la sua interfaccia grafica, ma il JDK che ha portato con sé e che ha collocato in /opt/jbuilder4/jdk1.3. Compilate il seguente file essai1.java:

import java.io.*;

public class essai1{
    public static void main(String args[]){
        System.out.println("coucou");
        System.exit(0);
    }
}

Compiliamolo ed eseguiamolo con il JDK di JBuilder:

[ls -l *.java]
-rw-r--r--    1 root     root          135 mai  9 21:57 essai1.java
[/opt/jbuilder4/jdk1.3/bin/javac essai1.java]
[/opt/jbuilder4/jdk1.3/bin/java essai1]
coucou