JToolBar
Nelle
moderne interfacce grafiche l'insieme dei controlli viene suddiviso principalmente
tra due luoghi: la MenuBar, che vedremo nel prossimo paragrafo, e la Tool
Bar, di cui ci occuperemo ora.
JToolBar
e' un contenitore che permette di raggruppare un insieme di controlli grafici
in una riga che, nella maggioranza dei casi, viene posizionata al di sotto
della barra dei menu. Sebbene sia utilizzata principalmente come contenitore
di pulsanti provvisti di icona, e' possibile inserire al suo interno qualunque
tipo di componente, come campi di testo o liste di selezione a discesa
Ricorrendo
al Drag & Drop e' possibile "staccare" una Tool Bar dalla sua posizione
originale e renderla fluttuante: in questo caso essa verra' visualizzata
in una piccola finestra separata dal Frame principale. Allo stesso modo
e' possibile "afferrare" una Tool Bar con il mouse e trascinarla in una
nuova posizione: questa possibilita' di personalizzazione e' presente nella
maggior parte dei programmi grafici.
Usare
JToolBar all'interno dei propri programmi non presenta particolari difficolta':
e' sufficiente crearla, aggiungervi i componenti nell'ordine da sinistra
a destra, e posizionarla all'interno del contenitore principale
JToolBar toolBar = new JToolBar();
ImageIcon icon = new ImageIcon("img.gif");
JButton b = new JButton(icon);
toolBar.add(b);
....
JFrame f = new JFrame();
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(BorderLayout.NORTH,toolBar);
Qui
di seguito presentiamo un riassunto dell'API JToolBar.
-
public
JToolBar(): Crea una Tool Bar.
-
public
Component add(Component c): Aggiunge un componente alla Tool Bar. L'ordine
di immissione dei componenti e' da sinistra a destra.
-
public
JButton add(Action a): Crea un pulsante corrispondente alla Action del
parametro e lo aggiunge alla Tool Bar
-
public
void addSeparator(): aggiunge un separatore alla Tool Bar
-
public
void setFloatable(boolean b): se si passa true come parametro, la Tool
Bar viene impostata come removibile, quindi puo' essere "staccata" dalla
sua posizione originale e trascinata in una nuova posizione.
JMenu
I
menu sono controlli che permettono di accedere ad un grande numero di opzioni
in uno spazio ridotto, organizzato gerarchicamente.Ogni programma grafico
dispone di una MenuBar organizzata per gruppi di funzioni: accesso al disco,
operazioni sulla clipboard, opzioni e cosi' via; ogni menu puo' essere
composto da elementi attivi (MenuItem) o da ulteriori menu nidificati.
|
Figura
1 - Gerarchia di JMenu
Su
Swing anche i menu si assemblano in maniera gerarchica, costruendo un oggetto
per ogni elemento e aggiungendolo al proprio contenitore. La gerarchia
delle classi in Figura 1 mostra che ogni sottoclasse di JComponent e' predisposta
a contenere Menu, capacita' che viene garantita dall'interfaccia MenuContainer;
le classi JMenu e JPopupMenu sono contenitori realizzati apposta per questo
scopo. La classe JMenuItem implementa il pulsante a menu di tipo
piu' semplice: essendo sottoclasse di AbstractButton, ne eredita l'interfaccia
di programmazione e il comportamento. JRadioButtonMenuItem e JCheckBoxMenuItem
sono MenuItems analoghi ai pulsanti JRadioButton e JCheckBox; oltre alla
parentela diretta con JMenuItem, essi hanno in comune con JMenu e JPopupMenu
l'interfaccia MenuElement, che accomuna tutti i componenti che possono
comparire all'interno di un menu.
Ecco
costruttori e metodi per le classi JMenuBar e JMenu
-
JMenuBar():
crea una JMenuBar
-
JMenu(String
text): crea un JMenu con l'etichetta specificata dal parametro
Alcuni
metodi comuni ad entrambe le classi
-
void add(JMenu
m): aggiunge un JMenu
-
void remove(JMenu
m): rimuove un JMenu
-
void removeAll():
rimuove tutti i JMenu
I
seguenti costruttori permettono di creare JMenuItem, JRadioButtonMenuItem
e JCheckBoxMenuItem in maniera simile a come si puo' fare con i JButton.
I parametri permettono di specificare l'etichetta, l'icona e stato.
-
JMenuItem(String
text)
-
JMenuItem(String
text, Icon icon)
-
JCheckBoxMenuItem(String
text, Icon icon, boolean b)
-
JCheckBoxMenuItem(String
text, boolean b)
-
JRadioButtonMenuItem(String
text, boolean selected)
-
JRadioButtonMenuItem(String
text, Icon icon, boolean selected)
Sebbene
sia possibile posizionare una JMenuBar ovunque all'interno di un'interfaccia
grafica, i Top Level Container JFrame, JApplet e JDialog riservano a questo
scopo un posto esclusivo, situato appena sotto alla barra del titolo. Possiamo
aggiungere un JMenu ad un JFrame, ad un JApplet o ad un JDialog usando
il metodo setJMenuBar(JMenuBar), come si vede nelle righe di esempio:
JMenuBar
menubar = new JMenuBar();
....
JFrame
f = new JFrame("A Frame");
f.setJMenuBar(menuBar)
Il
seguente esempio illustra la costruzione di un menu ricorrendo ad elementi
di ogni tipo; la figura 2 mostra il risultato, mentre la Figura 3 ne illustra
la gerarchia di contenimento.
import
javax.swing.*;
public
class JMenuExample extends JFrame {
public JMenuExample() {
// Imposta le proprieta' del Top Level Container
super("JMenuExample");
setBounds(10,35,250,250);
// Crea menu, sottomenu e menuitems
JMenuBar menubar = new JMenuBar();
JMenu menu = new JMenu("Menu");
JMenuItem simpleItem = new JMenuItem("SimpleItem");
JMenu checkSubMenu = new JMenu("CheckBoxes");
JCheckBoxMenuItem check1 = new JCheckBoxMenuItem("Check 1");
JCheckBoxMenuItem check2 = new JCheckBoxMenuItem("Check 1");
JMenu radioSubMenu = new JMenu("Radio");
JRadioButtonMenuItem radio1 = new JRadioButtonMenuItem("Radio 1");
JRadioButtonMenuItem radio2 = new JRadioButtonMenuItem("Radio 2");
ButtonGroup group = new ButtonGroup();
group.add(radio1);
group.add(radio2);
// Compone i menu
checkSubMenu.add(check1);
checkSubMenu.add(check2);
radioSubMenu.add(radio1);
radioSubMenu.add(radio2);
menu.add(simpleItem);
menu.addSeparator();//(new JSeparator());
menu.add(checkSubMenu);
menu.addSeparator();//.add(new JSeparator());
menu.add(radioSubMenu);
menubar.add(menu);
// Aggiunge la menubar al JFrame
setJMenuBar(menubar);
setVisible(true);
}
public
static void main(String argv[]) {
JMenuExample m = new JMenuExample();
}
}
|
Figura
2 - Esempio di Menu
|
Figura
3 - Gerarchia di contenimento relativa al programma
di
esempio
JPopupMenu
I
JPopupMenu implementano i menu contestuali presenti in quasi tutti i moderni
sistemi a finestre. La costruzione di JPopupMenu e' del tutto simile a
quella di JMenu; mentre diversa e' la modalita' di visualizzazione. Il
metodo
public
void show(Component invoker, int x, int y)
visualizza
il menu al di sopra del componente specificato dal parametro invoker, alle
coordinate x e y (relative ad invoker). Per associare un JPopupMenu alla
pressione del tasto destro del mouse su un oggetto grafico, e' necessario
registrare il componente interessato presso un MouseListener incaricato
di chiamare il metodo show() al momento opportuno. Dal momento che alcuni
sistemi a finestre mostrano il menu contestuale alla pressione del
tasto destro (evento mousePressed) , mentre altri lo mostrano al momento
del rilascio (evento mouseReleased), e' bene ascoltare entrambi gli eventi,
controllando la condizione isPopupTrigger() sull'evento MouseEvent: esso
restituisce true solamente se l'evento corrente e' quello che provoca il
richiamo del menu contestuale nella piattaforma ospite.
class
PopupListener extends MouseAdapter {
// tasto destro premuto (stile Motif)
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),e.getX(), e.getY());
}
}
// tasto destro premuto e rilasciato (stile windows)
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),e.getX(), e.getY());
}
}
}
Questo
accorgimento permette di creare programmi che rispecchiano il comportamento
della piattaforma ospite, senza ambiguita' che potrebbero disorientare
l'utente.
Il
seguente esempio crea un JTextField al quale aggiunge un MouseListener
che si occupa di visualizzare un JPopupMenu alla pressione del tasto destro.
import
java.awt.*;
import
java.awt.event.*;
import
javax.swing.*;
public
class JPopupMenuExample extends JFrame {
private JPopupMenu popup;
public JPopupMenuExample() {
super("JPopupMenuExample");
setBounds(10,35,350,120);
JTextField textField = new JTextField("Premi il tasto sinistro per vedere
un JPopupMenu");
textField.setEditable(false);
getContentPane().setLayout(new FlowLayout());
getContentPane().add(textField);
popup = new JPopupMenu();
JMenuItem popupItem1 = new JMenuItem("PopupItem 1");
JMenuItem popupItem2 = new JMenuItem("PopupItem 2");
JMenuItem popupItem3 = new JMenuItem("PopupItem 3");
popup.add(popupItem1);
popup.add(popupItem2);
popup.add(popupItem3);
// Aggiunge un MouseListener al componente che deve mostrare il menu
MouseListener popupListener = new PopupListener();
textField.addMouseListener(popupListener);
setVisible(true);
}
class PopupListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),e.getX(), e.getY());
}
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),e.getX(), e.getY());
}
}
}
public static void main(String[] args) {
JPopupMenuExample window = new JPopupMenuExample();
}
}
|
Figura
4 - Esempio di JPopupMenu
Gestione degli
Eventi
La
gestione degli eventi nei Menu e' del tutto simile a quella dei pulsanti:
ogni volta che si seleziona un JMenuItem, esso lancia un ActionEvent ai
suoi ascoltatori. Normalmente si usa ActionListener per i JMenuItem, ItemListener
per i JCheckboxMenuItem, mentre per JRadioButtonMenuItem possiamo usare
sia l'uno che l'altro.
Un modo alternativo
per gestire gli eventi: le Action
La
maggior parte dei programmi grafici permette di accedere ad una funzionalita'
in diverse maniere. I Word Processor, ad esempio, permettono di effettuare
un "Cut" su clipboard in almeno tre modi distinti: dal menu "Edit", tramite
il pulsante identificato dall'icona della forbice o tramite una voce del
menu contestuale. Questa ridondanza e' gradita all'utente, che ha la possibilita'
di utilizzare il programma secondo le proprie abitudini e il proprio grado
di esperienza, ma puo' rivelarsi complicato da implementare per il programmatore.
Su
Swing e' possibile risolvere questo genere di problemi ricorrendo alle
Action, oggetti che permettono di associare un particolare evento ad un
gruppo di controlli grafici, fornendo nel contempo la possibilita' di gestire
in modo centralizzato gli attributi e lo stato.
Descrizione dell'API
|
Figura
5 - Gerarchia di Action
L'interfaccia
Action, sottoclasse di ActionListener, eredita il metodo actionPerformed(ActionEvent
e) con il quale si implementa la normale gestione degli eventi.
Il
metodo setEnabled(boolean b) permette di abilitare una Action; la chiamata
a questo metodo provoca automaticamente l'aggiornamento dello stato di
tutti i controlli grafici ad essa associati.
La
coppia di metodi
Object getValue(String key)
void putValue(String key, Object value)
serve
a leggere o ad impostare coppie chiave-valore in cui la chiave e' una stringa
che descrive un attributo, e il valore e' l'attributo stesso. Tra le possibili
chiavi possiamo elencare
-
Action.NAME:
la chiave che specifica il nome dell'azione, che verra' riportato sul pulsante.
Il valore corrispondente deve essere di tipo String.
-
Action.SHORT_DESCRIPTION:
specifica la descrizione dell'azione, usata nei tooltip. Anche in questo
caso si richiede un valore di tipo String.
-
Action.SMALL_ICON:
la chiave che corrisponde all'icona di default associata a questa Action.
Il valore deve essere un oggetto di tipo Icon.
Per
impostare l'icona relativa ad una Action dobbiamo utilizzare l'istruzione
action.putValue(Action.SMALL_ICON
,new ImageIcon("img.gif"))
La
classe AbstractAction, implementazione dell'interfaccia Action, fornisce
alcuni costruttori che permettono di impostare le proprieta' in modo piu'
elegante
-
AbstractAction(String
name): definisce una Action con il nome specificato dal parametro.
-
AbstractAction(String
name, Icon icon): definisce una Action con l'icona e il nome specificati
dai parametri.
Esempio pratico
Possiamo
creare un oggetto Action estendendo la classe AbstractAction e fornendo
il codice del metodo actionPerformed(ActionEvent e), in modo simile a quanto
faremmo per un oggetto di tipo ActionListener.
class
MyAction extends AbstractAction {
private Icon myIcon = new ImageIcon("img.gif");
public MyAction() {
super("My Action",myIcon);
}
public void actionPerformed(ActionEvent e) {
// qui va il codice dell'ascoltatore
}
}
Anche
in questo caso e' possibile definire una Action come classe anonima
Action
myAction = new AbstractAction("My Action",
new ImageIcon("img.gif")) {
public void actionPerformed(ActionEvent e) {
// qui va il codice dell'ascoltatore
}
});
Per
abbinare una Action ai corrispondenti controlli grafici e' sufficiente
utilizzare il metodo add(Action a) presente su JMenu, JToolBar e JPopupMenu,
come si vede nelle seguenti righe:
Action
myAction = new MyAction();
JToolBar toolBar = new JToolBar();
JMenuBar menuBar = new JMenuBar();
JPopupMenu popup = new JPopupMenu();
// aggiunge un pulsante alla Tool Bar
toolBar.add(myAction);
// aggiunge un MenuItem alla Menu Bar
menuBar.add(myAction);
// aggiunge un MenuItem al Popup Menu
popup.add(myAction);
Dal
momento che il metodo add(Action) restituisce il componente che viene creato,
e' possibile cambiarne l'aspetto anche dopo che e' stato creato. Se vogliamo
aggiungere un MenuItem al menu, ma vogliamo che esso sia rappresentato
soltanto da una stringa di testo, senza icona, possiamo fare cosi':
JMenuItem
mi = menuBar.add(myAction);
mi.setIcon(null);
Se
durante l'esecuzione del programma vogliamo disabilitare i controlli abbinati
a MyAction, possiamo farlo ricorrendo all'unica istruzione
myAction.setEnabled(false);
che
provvedera' a disabilitare tutti i controlli legati a MyAction.
Approfondimento:
come funzionano le Action
La
semplicita' d'uso delle Action comporta un grande lavoro dietro le quinte,
che per fortuna viene svolto in tutto e per tutto dalle API di sistema.
La semplice aggiunta di una Action ad un JMenuBar o ad una JMenuBar comporta
le seguenti operazioni:
-
Viene
creato un componente adeguato al contenitore (un JButton caso di JToolBar
e un JMenuItem nel caso di JMenu)
-
Sul componente
appena creato vengono impostate le proprieta' della Action (icona,
stringa di testo e stato iniziale)
-
La Action
viene registrata come ascoltatore del componente
-
Viene
crearto un oggetto di tipo PropertyChangeListener abbinato al componente.
Il compito di questo oggetto e' quello di osservare l'Action e di notificarne
i cambiamenti al componente associato.
-
Il PropertyChangeListener
viene registrato come ascoltatore della Action.
Quando
il controllo viene azionato, la Action verra' invocata come un normale
ActionListener. Se durante l'esecuzione del programma viene invece alterata
una delle proprieta' della Action (come il nome, l'icona o lo stato), questo
cambiamento viene notificato a tutti i PropertyChangeListener registrati,
ognuno dei quali andra' a modificare l'aspetto dell'oggetto grafico ad
esso abbinato, per adeguarlo alla nuova situazione (chiamando i metodi
setText(), setIcon() o setEnabled()).
La
relazione circolare che si instaura tra un controllo grafico e una Action
puo' essere modellata come un'architettura Observer-Observable a due livelli,
visibile in Figura 6:
|
Figura
6 - Architettura Observer-Observable a due livelli
Ovviamente
il ricorso ad una struttura cosi' articolata comporta un lieve degrado
di performance rispetto alla gestione diretta degli eventi; il vantaggio
che ne deriva e' comunque di gran lunga superiore agli inconvenienti prestazionali,
e pertanto si consiglia di ricorrere senza indugio alle Action in tutti
i casi nei quali possono tornare utili.
Conclusioni
Arrivati
a questo punto siamo ormai in grado di maneggiare i piu' importanti elementi
delle interfacce grafiche. Nel prossimo articolo ci occuperemo di una serie
di controlli indispensabili per interazioni piu' complesse, come l'inserimento
di campi di testo o la scelta di elementi da una lista. |