Un'interfaccia
grafica, e' composta da un Top Level Container, ad esempio un JFrame,
al cui interno e' possibile disporre qualunque tipo di componente. Swing,
grazie alla sua struttura altamente modulare, permette di inserire qualunque
componente all'interno di qualunque altro: per esempio e' possibile, anche
se non particolarmente utile, creare un JButton contenente una JComboBox.
|
Figura
1 - Il design modulare di Swing permette anche di innestare un controllo
dentro ad un altro
Come
abbiamo visto, esistono alcuni componenti che hanno lo scopo di fungere
da contenitori per altri componenti. Il piu' usato di questi e' senza dubbio
JPanel, un pannello di uso estremamente generale. Come vedremo nell'esempio
successivo, possiamo creare un JPanel, disporre alcuni controlli grafici
al suo interno usando il metodo add(Component c), e quindi inserirlo in
un altro JPanel o nel ContentPane di un Top Level Container.
Il
seguente esempio permettera' di illustrare meglio i concetti appena illustrati,
e di introdurre i successivi.
import
javax.swing.*;
import
java.awt.*;
import
java.awt.Color.*;
import
java.awt.event.*;
public
class FirstExample
{
public
static void main(String argv[])
{
// imposta il Look&Feel di sistema
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {}
// Primo Componente
JTextField textField = new JTextField("Premi OK");
textField.setEditable(false);
// Secondo Componente
JLabel labelIcon = new JLabel(new ImageIcon("img.gif"));
labelIcon.setBorder(BorderFactory.createLineBorder(Color.black));
// Terzo Componente
JButton okButton = new JButton("OK");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try
{
System.exit(0);
}
catch (Exception ex)
{ }
}
});
// Quarto Componente
JButton cancelButton = new JButton("Cancel");
// Pannello NORTH
JPanel northPanel = new JPanel();
northPanel.setLayout(new GridLayout(1,0));
northPanel.setBorder(BorderFactory.createEmptyBorder(10,4,10,4));
northPanel.add(textField);
// Pannello CENTER
JPanel centralPanel = new JPanel();
centralPanel.setLayout(new BorderLayout());
CentralPanel.setBorder(
BorderFactory.createEmptyBorder(3,4,3,4));
centralPanel.add(BorderLayout.CENTER,labelIcon);
// Pannello SOUTH
JPanel southPanel = new JPanel();
southPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
southPanel.add(cancelButton);
southPanel.add(okButton);
// Top Level Container
JFrame f = new JFrame("Swing");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(BorderLayout.NORTH,northPanel);
f.getContentPane().add(BorderLayout.CENTER,centralPanel);
f.getContentPane().add(BorderLayout.SOUTH,southPanel);
f.pack();
f.setVisible(true);
}
}
|
Figura
2 - Un semplice programma di esempio
Come
si puo' vedere leggendo il sorgente, le interfacce grafiche vengono
assemblate dall'interno all'esterno: dapprima vengono creati i quattro
componenti piu' interni, quindi i tre pannelli destinati a contenerli e
infine un JFrame su cui questi tre pannelli vengono montati. Ogni pannello
stabilisce alcune regole valide al proprio interno, come un bordo o un
LayoutManager (vedremo i LayoutManager nel prossimo paragrafo). Vediamo
una rappresentazione ad albero della gerarchia dei componenti.
|
Figura
3 - Gerarchia di Contenimento del programma di esempio
Per
vedere la gerarchia di ogni JFrame o JDialog, basta clickare sul suo bordo
e quindi premere Control-Shift-F1. La console visualizzera' una lista di
tutta la gerarchia di contenimento.
Layout Management
Nel
disporre i componenti all'interno di un contenitore, possiamo ricevere
un grosso aiuto dai LayoutManager. I LayoutManager, situati nel package
java.awt, sono oggetti che si occupano dell'impaginazione automatica dei
componenti all'interno di un contenitore. Possiamo assegnare ad ogni container
un proprio LM, usando il metodo setLayoutManager(LayoutManager).
Sebbene
sia possibile posizionare i controlli dentro ad un contenitore indicandone
le coordinate assolute, l'uso dei LayoutManager risulta piu' rapido ed
elegante, e rende le interfacce grafiche meno soggette a problemi di visualizzazione
al cambio di dimensione o di Look & Feel: provando a deformare la finestra
dell'esempio, possiamo notare come i componenti restino posizionati elegantemente
al suo interno, mantenendo inalterate le posizioni relative.
|
Figura
4 - Deformando il frame si scoprono i vantaggi del Layout Management
Senza
la pretesa di esaurire l'argomento, presentiamo i tre LayoutManager di
uso piu' generale, che verranno utilizzati negli esempi presenti in questo
corso.
FlowLayout
Questo
semplice LM dispone i componenti da sinistra a destra e dall'alto al basso.
Ogni componente viene creato con la dimensione minima. Vediamo un esempio
JButton
b1 = new JButton("Primo");
JButton
b2 = new JButton("Secondo");
JButton
b3 = new JButton("Terzo");
JButton
b4 = new JButton("Quarto");
JButton
b5 = new JButton("Quinto");
JFrame
f = new JFrame();
f.getContentPane().setLayout(new
FlowLayout());
f.getContentPane().add(b1);
f.getContentPane().add(b2);
f.getContentPane().add(b3);
f.getContentPane().add(b4);
f.getContentPane().add(b5);
f.pack();
f.setVisible(true);
|
Figura
5 - Flowlayout
Al
termine di una riga, i componenti vengono inseriti nella successiva; su
ogni riga i componenti vengono centrati. Si noti come ad ogni oggetto venga
assegnata una dimensione diversa, corrispondente alla sua dimensione minima.
|
Figura
6 - FlowLayout
E'
possibile creare FlowLayout che allineano i componenti a destra o a sinistra,
invece che al centro. Basta usare il costruttore
public
FlowLayout(int alignment)
in
cui il parametro alignment puo' assumere i valori FlowLayout.LEFT, FlowLayout.CENTER,
o FlowLayout.RIGHT. Gli argomenti horizontalGap e verticalGap specificano
quanti pixel mettere tra i vari componenti.
GridLayout
GridLayout
suddivide il contenitore in una griglia di celle di uguale dimensione.
La dimensione della griglia vengono definite attraverso uno dei seguenti
costruttori
public
GridLayout(int rows, int columns)
public
GridLayout(int rows, int columns,int horizontalGap, int verticalGap)
public
GridLayout() // corrisponde a GridLayout(1,1,0,0)
in
cui rows e columns specificano rispettivamente le righe e le colonne, mentre
horizontalGap e verticalGap indicano quanti pixel disporre tra un componente
e l'altro.
JButton
buttons[] = new JButton[14];
for(int i=0;i<14;i++)
buttons[i] = new JButton(String.valueOf(i));
JFrame f = new JFrame("GridLayout");
f.getContentPane().setLayout(new GridLayout(4,4));
for(int i=0;i<14;i++)
f.getContentPane().add(buttons[i]);
f.pack();
f.setVisible(true);
|
Figura
7 - GridLayout
Se
gli argomenti row e column vengono posti uno a 0 e l'altro ad 1,
otterremo una riga o una colonna di componenti equidimensionati, diversamente
da FlowLayout che assegna una dimensione diversa ad ogni componente
|
Figura
8 - Layout Riga e Colonna usando GridLayout
BorderLayout
Il
BorderLayout suddivide il contenitore esattamente in cinque aree, disposte
a croce. Il costruttore permette, come al solito, di definire la spaziatura
in pixel tra i vari componenti
BorderLayout(int
horizontalGap, int verticalGap)
BorderLayout()
//corrisponde a BorderLayout(0,0)
Il
programmatore puo' decidere in quale posizione aggiungere un controllo
usando il metodo
add(component,String
)
dove
il secondo parametro deve assumere uno dei valori costanti BorderLayout.NORTH,
BorderLayout.SOUTH, BorderLayout.CENTER, BorderLayout.EAST o BorderLayout.WEST
Vediamo
l'esempio
JFrame
f = new JFrame("BorderLayout");
f.getContentPane().setLayout(new
BorderLayout());
f.getContentPane().add(new
Button("North"), BorderLayout.NORTH);
f.getContentPane().add(new
Button("South"), BorderLayout.SOUTH);
f.getContentPane().add(new
Button("East"), BorderLayout.EAST);
f.getContentPane().add(new
Button("West"), BorderLayout.WEST);
f.getContentPane().add(new
Button("Center"), BorderLayout.CENTER);
f.pack();
f.setVisible(true);
|
Figura
9 - BorderLayout
Ovviamente
non e' obbligatorio riempire tutti e cinque gli spazi.
|
Figura
10 - BorderLayout
BorderLayout
e' particolarmente indicato nel gestire l'impaginazione complessiva di
una finestra, poiche' la sua conformazione ripropone il layout di una tipica
applicazione a finestre, con la barra degli strumenti in alto, una barra
di stato in basso, un browser a sinistra o a destra e il pannello principale
al centro.
Progettazione
Top Down di Interfacce Grafiche
Durante
la progettazione di Interfacce Grafiche, torna utile ricorrere ad un approccio
Top Down, descrivendo il nostro insieme di componenti partendo da una visone
generale per poi scendere via via verso il dettaglio. Per sviluppare una
GUI come quella dell'esempio, si puo' procedere in questo modo:
-
Definiamo
il tipo di Top Level Container su cui vogliamo lavorare, in questo caso
un JFrame
-
Assegnamo
un LayoutManager al JFrame, in modo da suddividerne la superficie in aree
piu' piccole. Nell'esempio siamo ricorsi al BorderLayout.
-
Per ogni
area messa a disposizione dal Layout Manager possiamo definire un nuovo
JPanel.
-
Ogni sottopannello
puo' ricorrere ad un LayoutManager differente. Nell'area superiore abbiamo
usato un GridLayout per far si che il JTextField occupasse tutta l'area
disponibile in larghezza; in quella centrale il BorderLayout fa in modo
che il disegno sia sempre al centro dell'area disponibile; in basso un
FlowLayout garantisce che i pulsanti vengano sempre allineati a sinistra.
Si noti anche che ogni pannello definisce un proprio bordo.
-
Ogni pannello
identificato nel punto 3 potra' essere sviluppato ulteriormente, creando
al suo interno ulteriori pannelli, o disponendo dei controlli. Nell'esempio
abbiamo aggiunto al primo pannello un JTextField, nel secondo una JLabel
contenente un'icona e nel terzo due JButton.
Terminata
la fase progettuale, possiamo passare a scrivere il codice per i nostri
controlli. In questa seconda fase adotteremo l'approccio Bottom Up, cioe'
scriveremo per primo il codice dei componenti atomici, quindi quello dei
contenitori e infine quello del JFrame.
Uso della specializzazione
nel Design di Interfacce Grafiche
La
semantica dei linguaggi ad oggetti si sposa molto bene con la programmazione
di interfacce grafiche. Negli esempi presentati sopra abbiamo composto
le interfacce grafiche creando i singoli componenti e incastrandoli uno
dentro l'altro.
Il
ricorso alla specializzazione permette invece di trattare ogni sottopannello
come se si trattasse di un oggetto grafico personalizzato, specializzato
in una particolare funzione. Il codice dell'esempio puo' essere riscritto
in questa maniera, definendo la classe principale come sottoclasse di JFrame,
e realizzando per ognuno dei pannelli NorthPanel, CenterPanel e SouthPanel
una classe interna derivata da JPanel.
import
javax.swing.*;
import
java.awt.*;
import
java.awt.Color.*;
import
java.awt.event.*;
public
class SecondExample extends JFrame
{
class NorthPanel extends JPanel
{
private JTextField textField;
public NorthPanel()
{
textField = new JTextField("Premi OK");
textField.setEditable(false);
setLayout(new GridLayout(1,0));
setBorder(BorderFactory.createEmptyBorder(10,4,10,4));
add(textField);
}
}
class CenterPanel extends JPanel
{
private JLabel labelIcon;
public CenterPanel()
{
labelIcon = new JLabel(new ImageIcon("img.gif"));
labelIcon.setBorder(BorderFactory.createLineBorder(Color.black));
setLayout(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(3,4,3,4));
add(BorderLayout.CENTER,labelIcon);
}
}
class SouthPanel extends JPanel
{
private JButton okButton;
private JButton cancelButton;
public SouthPanel()
{
okButton = new JButton("OK");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try
{
System.exit(0);
}
catch (Exception ex)
{ }
}
});
cancelButton = new JButton("Cancel");
setLayout(new FlowLayout(FlowLayout.RIGHT));
add(cancelButton);
add(okButton);
}
}
private JPanel northPanel;
private JPanel centerPanel;
private JPanel southPanel;
public SecondExample()
{
super("Swing");
JPanel northPanel = new NorthPanel();
JPanel centerPanel = new CenterPanel();
JPanel southPanel = new SouthPanel();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(BorderLayout.NORTH,northPanel);
getContentPane().add(BorderLayout.CENTER,centerPanel);
getContentPane().add(BorderLayout.SOUTH,southPanel);
pack();
setVisible(true);
}
public
static void main(String argv[])
{
// imposta il Look&Feel di sistema
try
{
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {}
SecondExample se = new SecondExample();
}
}
Conclusioni
In
questo articolo abbiamo introdotto due concetti fondamentali della programmazione
grafica in java: la gerarchia di contenimento e la gestione del Layout.
Nel prossimo incontro affronteremo lo studio dei primi controlli grafici. |