MokaByte 51 - Aprile 2001
Foto dell'autore non disponibile
di
Andrea Gini
Corso di Swing
VIII parte: pannelli specializzati
Abbiamo visto come sia possibile creare interfacce grafiche innestando pannelli uno dentro l'altro. La scelta di pannelli disponibili su Swing non e' limitata al semplice JPanel, ma include pannelli specializzati nel trattamento di casi particolari.

JSplitPane
JSplitPane e' un pannello formato da due aree, separate da una barra mobile; al suo interno e' possibile disporre una coppia di componenti, affiancati lateralmente o uno sopra l'altro. Il divisore puo' essere trascinato per impostare l'area da assegnare a ciascuno dei due componenti, rispettando la dimensione minima dei componenti in esso contenuti. Usando JSplitPane in abbinamento a JScrollPane possiamo ottenere una coppia di pannelli ridimensionabili.
Il seguente programma crea una finestra con uno JSplitPane al suo interno: nel pannello superiore monta un'immagine JPEG, in quello inferiore una TextArea sulla quale si possono annotare commenti. Per avviarlo e' necessario specificare sulla riga di comando il percorso di un file JPEG o GIF (ad esempio java JSplitDemo immagine.jpg)
 
 


Figura 1 - Un esempio di Split Pane








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

public class JSplitDemo extends JFrame {

  public JSplitDemo(String fileName) {
    super("JSplitPane");
    setSize(300,250);

    // costruisce un pannello contenente un'immagine
    ImageIcon img = new ImageIcon(fileName);
    JLabel picture = new JLabel(img);
    JScrollPane pictureScrollPane = new JScrollPane(picture);

    // crea un pannello contenente una TextArea
    JTextArea comment = new JTextArea();
    JScrollPane commentScrollPane = new JScrollPane(comment);

    // Crea uno SplitPane verticale con i due 
    // pannelli al suo interno
    JSplitPane splitPane = new JSplitPane(
                       JSplitPane.VERTICAL_SPLIT,
                       pictureScrollPane,
                       commentScrollPane);
    splitPane.setOneTouchExpandable(true);
    splitPane.setDividerLocation(190);
    splitPane.setContinuousLayout(true);

    // aggiunge lo SplitPane al Frame principale
    getContentPane().add(splitPane);
    setVisible(true); 
  }

 public static void main(String argv[]) {
    if(argv.length==1) { 
      JSplitDemo b = new JSplitDemo(argv[0]);
    }
    else
      System.out.println("usage JSplitDemo <filename>");
 }
}

JSplitPane API
Nell'esempio siamo ricorsi ad un costruttore che permette di impostare le piu' importanti proprieta' dello SplitPane con un'unica istruzione. Ovviamente sono disponibili costruttori con un numero inferiore di parametri.
· public JSplitPane(int orientamento, Component leftComponent, Component rightComponent): Crea un JSplitPane con l'orientamento specificato dal primo parametro e i componenti specificati dal secondo e dal terzo. Il parametro orientamento puo' assumere i valori JSplitPane.HORIZONTAL_SPLIT o JSplitPane.VERTICAL_SPLIT. 

Per specificare l'orientamento o per posizionare la coppia di componenti all'interno dello SplitPane si puo'  anche ricorrere ai metodi

  • void setOrientation(int orientation): Imposta l'orientamento del divisore.
  • void setBottomComponent(Component comp): Imposta il componente inferiore. 
  • void setTopComponent(Component comp): Imposta il componente superiore.
  • void setRightComponent(Component comp): Imposta il componente di destra.
  • void setLeftComponent(Component comp): imposta il componente di sistra.


Un gruppo di tre metodi permette di specificare la posizione del divisore 

  • void setDividerLocation(int location): imposta la posizione assoluta del divisore.
  • void setDividerLocation(double proportionalLocation): imposta il divisore dividendo in modo proporzionale lo spazio disponibile tra i due componenti. Se il valore e' 0.5 il divisore verra' posto a meta' (questo metodo funziona correttamente solo se il pannello e' visibile)
  • void setResizeWeight(double): specifica come distribuire lo spazio che si viene a creare quando il componente viene ridimensionato; se si imposta un valore di 0.5 lo spazio in piu' viene diviso in maniera uguale tra i due componenti.
Per modificare l'aspetto fisico del pannello sono diponibili le seguenti possibilita'
  • void setDividerSize(int): Imposta la dimensione in pixel della barra di divisione.
  • void setOneTouchExpandable(boolean): attivando questa proprieta', sulla barra apparira' una coppia di pulsanti a freccia che permettono di espandere o collassare il divisore con un semplice click 
  • void setContinuousLayout(boolean): specifica se si desidera che il pannello venga ridisegnato durante il posizionamento del divisore.


E' possibile inserire i JSplitPane uno dentro l'altro, nel caso si desideri avere piu' di due aree ridimensionabili. Ad esempio le seguenti istruzioni creano uno Split Pane orizzontale contenente una TextArea ed uno Split Pane verticale, il quale contiene a sua volta due TextArea.


Figura 2 - E' possibile creare SplitPane multipli 
inserendone uno dentro l'altro

JScrollPane scroll1 = new JScrollPane(new JTextArea());
JScrollPane scroll2 = new JScrollPane(new JTextArea());
JScrollPane scroll3 = new JScrollPane(new JTextArea());

JSplitPane internalSplit =
      new JSplitPane(JSplitPane.VERTICAL_SPLIT,scroll1, scroll2);
JSplitPane externalSplit =
      new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,scroll3, internalSplit );
 
 

JTabbedPane
JTabbedPane permette a diversi componenti di condividere lo stesso spazio sullo schermo; l'utente puo' scegliere su quale componente operare premendo il Tab corrispondente. Il tipico uso di questo  componente e' nei pannelli di controllo, nei quali si assegna ad ogni Tab la gestione di un insieme di funzioni differente.
Oltre all'innegabile utilita', questo componente presenta una modalita' di impiego straordinariamente semplice: e' sufficiente creare il pannello ed aggiungervi i vari componenti usando il  metodo addTab(String title,Component c), in cui il primo parametro specifica l'etichetta del tab, e il secondo passa il componente. Il passaggio da un tab all'altro viene ottenuto clickando con il mouse sul tab desiderato, senza bisogno di gestire gli eventi in modo esplicito.
Nell'esempio seguente viene creato un JTabbedPane al quale vengono aggiunti tre tab, ognuno dei quali contiene un componente grafico diverso. L'esempio mostra anche un esempio di gestione degli eventi: al cambio di tab viene aggiornato il titolo della finestra.
 
 


Figura 3 - Un semplice JTabbedPane






import javax.swing.*;
import javax.swing.event.*;

public class JTabbedPaneExample extends JFrame {
  private JTabbedPane tabbedPane;
  public JTabbedPaneExample() {
    super("JTabbedPaneExample");
    tabbedPane = new JTabbedPane();

    JTextField tf = new JTextField("Primo Tab");
    JButton b = new JButton("Secondo Tab");
    JSlider slider = new JSlider(JSlider.HORIZONTAL,0,60,15);
    tabbedPane.addChangeListener(new TabListener());
    tabbedPane.addTab("One", tf );
    tabbedPane.addTab("Two", b );
    tabbedPane.addTab("Three", slider);

    getContentPane().add(tabbedPane);
    pack();
    setVisible(true);
  }
  public class TabListener implements ChangeListener {
    public void stateChanged(ChangeEvent e) {
      int pos = tabbedPane.getSelectedIndex();
      String title = tabbedPane.getTitleAt(pos);
      setTitle(title);
    } 
  }
  public static void main(String[] args) {
    JTabbedPaneExample te = new JTabbedPaneExample();
  }
}

Nell'esempio abbiamo creato un JTabbedPane e vi abbiamo inserito tre Tab, ognuno dei quali contiene un componente. Ovviamente e' possibile inserire all'interno di un tab un intero pannello con tutto il suo contenuto: 

  ....
  JPanel panel = new JPanel();
  panel.setLayout(new BorderLayout());
  panel.add(BorderLayout.NORTH,new Button("Nord"));
  ....
  panel.add(BorderLayout.SOUTH,new Button("Sud"));
  tabbedPane.addTab("Pannello", panel);
  ....
 
 

JTabbedPane API
Per creare un JTabbedPane possiamo ricorrere ai seguenti costruttori:

  • JTabbedPane(): Crea un JTabbedPane.
  • JTabbedPane(int tabPlacement): Crea un JTabbedPane con i tab posizionati in alto, in basso, a destra o a sinistra secondo il valore del parametro:  JTabbedPane.TOP, JTabbedPane.BOTTOM, JTabbedPane.LEFT, o JTabbedPane.RIGHT.


Per aggiungere o togliere componenti abbiamo un gruppo di metodi, tra i quali:

  • void addTab(String title, Component component): aggiunge un componente su un tab con il titolo specificato.
  • void addTab(String title, Icon icon, Component component, String tip): aggiunge un componente con il titolo, l'icona e il ToolTip specificati dai parametri.
  • void remove(Component component): rimuove il Tab contenete il componente specificato.
  • void removeAll(): Rimuove tutti i tab.


Un gruppo di metodi permette di manipolare le proprieta' dei Tab

  • Component getSelectedComponent(): restituisce il componente attualmente selezionato 
  • void setSelectedComponent(Component c): imposta il Tab che contiene il componente specificato.
  • int getSelectedIndex(): restituisce l'indice del componente attualmente selezionato
  • void setSelectedIndex(int index): imposta il tab che si trova nella posizione specificata dal parametro.
  • int getTabCount(): restituisce il numero di Tab presenti nel Tabbed Pane.
  • int indexOfComponent(Component component): restituisce l'indice del componente passato come parametro.
  • String getTitleAt(int index): restituisce l'etichetta del tab nella posizione specificata.


La gestione degli eventi su JTabbedPane e' abbastanza limitata, dal momento che gli oggetti di tipo ChangeEvent non contengono nessuna informazione sul tipo di evento che li ha generati (in pratica e' impossibile, per un ascoltatore, distinguere tra un eventi di selezione, di aggiunta o di rimozione di tab). 
Possiamo aggiungere o rimuovere un ascoltatore usando i metodi 

  • void addChangeListener(ChangeListener l) 
  • void removeChangeListener(ChangeListener l) 


Gli ascoltatori di tipo ChangeListener richiedono l'implementazione del metodo void stateChanged(ChangeEvent e). Gli eventi di tipo ChangeEvent contengono il solo metodo Object getSource(), che permette di ottenere un riferimento all'oggetto che ha generato l'evento; per conoscere i dettagli dell'evento (numero di tab, titolo ecc..) e' necessario interrogare direttamente il componente sorgente. 
 
 
 

JDesktopPane e JInternalFrame
L'uso combinato di JDesktopPane e JInternalFrame apre, al programmatore java, un universo di possibilita': attraverso questi componenti e' possibile realizzare ambienti tipo desktop, dotati di finestre interne, icone ed oggetti grafici.
JInternalFrame e' un oggetto grafico molto simile, nell'aspetto, nel comportamento e nell'interfaccia di programmazione, ad un JFrame. La differenza piu' consistente tra questi due oggetti e' che JInternalFrame, non essendo un Top Level Container, non puo' comportarsi come radice di una gerarchia di contenimento; per visualizzare oggetti di questo tipo e' necessario creare un'istanza JDesktopPane e aggiungervi oggetti di tipo JInternalFrame usando il metodo add(component c).

    JInternalFrame frame = new JInternalFrame("Frame");
    desktop = new JDesktopPane();

    frame.setSize(120,80);
    desktop.add(frame);
    frame.setVisible(true);

Le finestre create all'interno di JDesktopPane si comportano come finestre di sistema, con l'unica differenza di essere confinate all'interno di questo pannello: l'utente puo' spostarle, allargarle, ridurle ad icona e chiuderle, ma non puo' in nessun caso trascinarle al di fuori dell'area a loro assegnata. Si noti che e' obbligatorio assegnare una dimensione al JInternalFrame, e che e' necessario chiamare il metodo setVisible(true) se si vuole rendere visibile la nuova finestra. E' consigliabile anche prevedere una politica di posizionamento, come nell'esempio a fine capitolo, altrimenti tutte le finestre interne verranno posizionate nel margine in alto a sinistra del JDesktopPane.
 

JDesktopPane API
Con pochi metodi possiamo creare oggetti di tipo JDesktopPane ed impostarne le proprieta'.
 

  • JDesktopPane(): crea un JDesktopPane. 
  • JInternalFrame[] getAllFrames() : restituisce tutti i JInternalFrames contenuti. 
  • JInternalFrame getSelectedFrame(): restituisce il JInternalFrame attivo, o null se nessun JInternalFrame e' attivo al momento. 
  • void setDragMode(int dragMode) : imposta lo stile di trascinamento per il desktop pane. Sono disponibili due scelte: JDesktopPane.LIVE_DRAG_MODE fa si' che il contenuto del frame rimanga visibile durante il trascinamento; JDesktopPane.OUTLINE_DRAG_MODE invece fa si' che durante il trascinamento venga spostata soltanto la sagoma del frame, permettendo un refresh piu' rapido. 
  • void setSelectedFrame(JInternalFrame f): imposta il JInternalFrame attivo. 


JInternalFrame API
L'interfaccia di programmazione di JInternalFrame e' abbastanza complessa, e ricalca il prototipo dell'API JFrame. Rispetto a quest'ultima, l'API JInternalFrame fornisce un maggior controllo sulle proprieta' della barra del titolo e sui controlli che essa deve contenere per chiudere, espandere, ridurre ad icona e ridimensionare la finestra; il seguente costruttore permette di impostare in un'unica istruzione tutte queste proprieta':

  • JInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable): crea un  JInternalFrame con le proprieta' specificate dai parametri.
I seguenti metodi sono equivalenti a quelli presenti su JFrame.
  • Container getContentPane(): restituisce il content pane di questo JInternalFrame.
  • void setContentPane(Container c): imposta il content pane. 
  • void pack(): fa in modo che i componenti interni vengano impostati alla dimensione ideale. 
  • void setDefaultCloseOperation(int operation):  imposta l'azione da eseguire alla pressione del tasto "close". Sono disponibili le seguenti scelte:
    • WindowConstants.DO_NOTHING_ON_CLOSE: nessun effetto.
    • WindowConstants.HIDE_ON_CLOSE: nasconde la finestra (e' l'impostazione di default)
    • WindowConstants.DISPOSE_ON_CLOSE: distrugge la finestra.
  • void setJMenuBar(JMenuBar m) : imposta la JMenuBar.
  • void setTitle(String title): imposta il titolo del frame interno JInternalFrame.
Questi metodi invece sono presenti esclusivamente su JInternalFrame:
  • void setSelected(boolean selected) : seleziona o deseleziona il JInternalFrame. 
  • JDesktopPane getDesktopPane(): restituisce il JDesktopPane che contiene questo JInternalFrame. 
  • Component getFocusOwner() : restituisce il JInternalFrame che ha il fuoco.
  • void setFrameIcon(Icon icon): imposta l'icona della titlebar.
  • void toBack(): spinge "in sotto" questo JInternalFrame.
  • void toFront(): "tira su'" il JInternalFrame. 
  •  void addInternalFrameListener(InternalFrameListener l): aggiunge a questo JInternalFrame un InternalFrameListener.
  • void removeInternalFrameListener(InternalFrameListener l): rimuove un InternalFrameListener da questo JInternalFrame.


Modello degli eventi di JInternalFrame
La gestione degli eventi e' affidata ad oggetti di tipo InternalFrameListener ,  che offrono un controllo totale degli eventi che possono capitare ad un JInternalFrame.

: Figura 4 - Il meccanismo di gestione degli eventi di JInternalFrame

Di seguito ecco i metodi dell'interfaccia InternalFrameListener con la descrizione del tipo di evento che ne provoca la chiamata.

  • void internalFrameActivated(InternalFrameEvent e): un internal frame e' stato attivato attivato.
  • void internalFrameClosed(InternalFrameEvent e): un internal frame e' stato chiuso.
  • void internalFrameClosing(InternalFrameEvent e): un internal frame sta' per essere chiuso.
  • void internalFrameDeactivated(InternalFrameEvent e): un internal frame e' stato disattivato.
  • void internalFrameDeiconified(InternalFrameEvent e): un internal frame e' stato de-iconificato.
  • void internalFrameIconified(InternalFrameEvent e): un internal frame e' stato ridotto ad icona.
  • void internalFrameOpened(InternalFrameEvent e): un internal frame e' stato aperto.


Un esempio pratico
Ecco ora un esempio completo: una fabbrica di JInternalFrame. Questo programma presenta un JDesktopPane e una JToolBar, nella quale sono presenti i controlli che permettono di creare dei JInternalFrame dopo averne impostate le proprieta'. La politica di posizionamento viene implementata con delle formule matematiche nel metodo createFrame(), che calcola le coordinate tenendo conto della dimensione del frame, del desktop e dalla posizione dell'ultima finestra visualizzata.


Figura 4 - Una fabbrica di JInternalFrame





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

public class InternalFrameExample extends JFrame {
  private JDesktopPane desktop;
  private int frameNumber = 0;
  private int xPos = 0;
  private int yPos = 0;
  private JTextField titleTextField;
  private JCheckBox resizableCheckBox;
  private JCheckBox closableCheckBox;
  private JCheckBox maximizableCheckBox;
  private JCheckBox iconifiableCheckBox;

  public InternalFrameExample() {
    super("InternalFrameExample");
    setSize(640,210);
    JToolBar toolbar = createToolBar();
    desktop = new JDesktopPane();
    getContentPane().add(BorderLayout.WEST,toolbar);
    getContentPane().add(BorderLayout.CENTER,desktop);
    setVisible(true);
  }

  protected JToolBar createToolBar() {
    JToolBar tb = new JToolBar(JToolBar.VERTICAL);
    JPanel titlePanel = new JPanel();
    titlePanel.setLayout(new FlowLayout());
      JLabel titleLabel = new JLabel("Titolo");
      titleTextField = new JTextField("Frame 0",10);
    titlePanel.add(titleLabel);
    titlePanel.add(titleTextField);
    resizableCheckBox = new JCheckBox("Ridimensionabile");
    closableCheckBox = new JCheckBox("Richiudibile");
    maximizableCheckBox = new JCheckBox("Massimizzabile");
    iconifiableCheckBox = new JCheckBox("Iconificabile");
    JButton generateButton = new JButton("Genera
                                         un JInternalFrame");

    ActionListener listener = new GenerateButtonActionListener();
    generateButton.addActionListener(listener);
    titleTextField.addActionListener(listener);

    tb.add(titlePanel);
    tb.add(resizableCheckBox);
    tb.add(closableCheckBox);
    tb.add(maximizableCheckBox);
    tb.add(iconifiableCheckBox);
    tb.add(generateButton);
    return tb;
  } 
  protected JInternalFrame createFrame(String title,boolean resizable,boolean closable,boolean maximizable,boolean iconifiable) {
    // Crea il Frame secondo i parametri
    JInternalFrame frame = new JInternalFrame(title,
                                         resizable,
                                         closable,
                                         maximizable,
                                         iconifiable);
    frame.setSize(120,80);
    // Aggiunge una Label al suo interno
    JLabel titleLabel = new JLabel(title,JLabel.CENTER);
    frame.getContentPane().add(titleLabel);

    // Calcola la posizione del nuovo InternalFrame
    int xSize = desktop.getWidth()-frame.getWidth(); 
    int ySize = desktop.getHeight()-frame.getHeight();
    int xStep = desktop.getWidth()/10;
    int yStep = desktop.getHeight()/10;
    xPos=(xPos+xStep)%xSize;
    yPos=(yPos+yStep)%ySize;
    frame.setLocation(xPos,yPos);

    return frame;
  }

  class GenerateButtonActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String title = titleTextField.getText();
      boolean resizable = resizableCheckBox.isSelected();
      boolean closable = closableCheckBox.isSelected();
      boolean maximizable = maximizableCheckBox.isSelected();
      boolean iconifiable = iconifiableCheckBox.isSelected();
      JInternalFrame frame = createFrame(title,
                                         resizable,
                                         closable,
                                         maximizable,
                                         iconifiable);
      // aggiunge al JDesktopPane
      desktop.add(frame);
      // lo mette in cima agli altri JInternalFrame
      frame.moveToFront();
      // lo rende visibile
      frame.setVisible(true);
      titleTextField.setText("Frame "+String.valueOf(
                                      frameNumber++));
     }
  }

  public static void main(String[] args) {
    InternalFrameExample frame = new InternalFrameExample();
  }
}
 

Conclusioni
Questa carrellata sui principali contenitori specializzati presenti su Swing chiude il ciclo di articoli dedicata ai componenti grafici. Nel prossimo articolo vedremo come abbellire i componenti Swing usando bordi, icone e tooltips.

L'esempio descritto in questo articolo può essere trovato qui
 

Vai alla Home Page di MokaByte
Vai alla prima pagina di questo mese


MokaByte®  è un marchio registrato da MokaByte s.r.l.
Java®, Jini®  e tutti i nomi derivati sono marchi registrati da Sun Microsystems; tutti i diritti riservati
E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a
mokainfo@mokabyte.it