[Per corso su Java vedere http://java.sun.com/docs/books/tutorial/]
Per abilita' si intende capacita' di un componente ad interagire con l'utente.
I bordi servono non solo come ornamento ma anche per raggruppare assieme dispositivi logicamente correlati, eventualmente aggiungendo un titolo.
Ci vari tipi di bordi, in un bordo si possono inserire titoli e/o grafica. Esempi:
crea bordo a linea semplice, nero, senza titolo
comp.setBorder(BorderFactory.createLineBorder(Color.black));
crea bordo come prima piu' titolo posizionato al centro sotto
il riquadro incorniciato dal bordo
b1 = BorderFactory.createLineBorder(Color.black);
b2 = BorderFactory.createTitledBorder(b1,"titolo",
TitleBorder.CENTER,TitleBorder.BELOW_BOTTOM);
comp.setBorder(b2);
Riga di spiegazione a comparsa e scomparsa. Associabile a qualsiasi componente. Appare quando utente si ferma col mouse sopra la componente.
Esempio: comp.setToolTipText("Questo e' il messaggio");
Gli eventi sono classificati a seconda della loro causa scatenante, che puo' essere:
Classi di eventi hanno nomi del tipo XXXEvent dove XXX e' la causa. Es: MouseEvent...
Ogni classe di componente e' una potenziale sorgente di certe classi di eventi. Es: un bottone puo' originare eventi di classe "azionamento" (classe ActionEvent).
Posso registrare un event listener per ricevere eventi di una certa classe da una certa componente dell'interfaccia.
Vi e' un'interfaccia event listener per ogni classe di evento:
L'interfaccia listener prevede uno o piu' metodi che scattano quando
il listener riceve un evento di quella classe.
Essendo un listener interfaccia e non classe,
l'implenentazione dei metodi non e' definita.
Stabilita la classe di eventi che voglio catturare, devo creare
una classe che implementi il listener corrispondente,
fornendo il codice dei suoi metodi.
Poi creo un oggetto di tale classe e lo
associo alla componente sulla quale voglio catturare gli
eventi.
Esempio: voglio catturare azionamento di un bottone.
Questo paradigma si chiama delegation perche' il bottone delega l'azione da eseguire ad un altro oggetto (il listener).
Riprendiamo l'esempio AWTExample / SwingExample modificandolo un po'.
La finestra contiene due bottoni b1 e b2 e un'etichetta (label)
collocati organizzando la finestra come griglia a tre righe e
una colonna.
La scritta sull'etichetta cambia a seconda di quale
bottone e' stato premuto per ultimo:
Questo si puo' ottenere in vari modi.
Attenzione: la classe e' sempre ListenerExample ma
i file si chiamano con 1, 2, 3, 4.
Ricalca esempio AWTExample / SwingExample gia' visto, qui usiamo AWT.
Ciascuno dei due bottoni ha il suo listener ottenuto da una classe
creata al volo.
File ListenerExample1.java
class ListenerExample extends Frame { Button b1; Button b2; Label l; public ListenerExample() { b1 = new Button("bottone b1"); b2 = new Button("bottone b2"); l = new Label("---"); setLayout(new GridLayout(3,1)); add(b1); add(b2); add(l); b1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { l.setText("uno"); } }); b2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { l.setText("due"); } }); pack(); setVisible(true); } public static void main(String[] args) { ListenerExample app = new ListenerExample(); } }
Ma qui abbiamo due classi listener praticamente identiche, cambia
solo la stringa che viene scritta nell'etichetta.
Compilando produce ListenerExample.class e
altri due file .class per i listener.
Definiamo una sola classe listener parametrica sulla stringa
da scrivere nell'etichetta.
La classe listener e' classe interna di ListenerExample,
in questo modo puo' accedere agli attributi di ListenerExample.
File ListenerExample2.java
class ListenerExample extends Frame { Button b1; Button b2; Label l; // classe listener interna class MyListener implements ActionListener { String s; // la stringa da scrivere MyListener(String ss) { s = ss; } public void actionPerformed(ActionEvent e) { l.setText(s); } } public ListenerExample() { b1 = new Button("bottone b1"); b2 = new Button("bottone b2"); l = new Label("---"); setLayout(new GridLayout(3,1)); add(b1); add(b2); add(l); b1.addActionListener(new MyListener("uno")); b2.addActionListener(new MyListener("due")); pack(); setVisible(true); } public static void main(String[] args) { ListenerExample app = new ListenerExample(); } }
Abbiamo una solae classe listener di cui creiamo due
istanze, con diversa stringa.
Compilando produce ListenerExample.class e un
altro file .class per la classe listener.
Definiamo una sola classe listener come prima, ma questa volta,
invece di memorizzare la stringa, guardiamo da quale dei due bottoni
arriva l'evento e in base a quello decidiamo che stringa scrivere.
File ListenerExample3.java
class ListenerExample extends Frame { Button b1; Button b2; Label l; // classe listener interna class MyListener implements ActionListener { public void actionPerformed(ActionEvent e) { if (e.getSource()==b1) l.setText("uno"); else /* deve essere b2 */ l.setText("due"); } } public ListenerExample() { b1 = new Button("bottone b1"); b2 = new Button("bottone b2"); l = new Label("---"); setLayout(new GridLayout(3,1)); add(b1); add(b2); add(l); ActionListener lis = new MyListener(); b1.addActionListener(lis); b2.addActionListener(lis); pack(); setVisible(true); } public static void main(String[] args) { ListenerExample app = new ListenerExample(); } }
Abbiamo una solae classe listener di cui creiamo una sola istanza.
Invece di definire una classe listener interna,
e' la nostra classe ListenerExample (sotto-classe di Frame) a
fungere da listener, implementando l'interfaccia ActionListener.
File ListenerExample4.java
class ListenerExample extends Frame implements ActionListener { Button b1; Button b2; Label l; // implementazione della funzione public void actionPerformed(ActionEvent e) { if (e.getSource()==b1) l.setText("uno"); else /* deve essere b2 */ l.setText("due"); } public ListenerExample() { b1 = new Button("bottone b1"); b2 = new Button("bottone b2"); l = new Label("---"); setLayout(new GridLayout(3,1)); add(b1); add(b2); add(l); b1.addActionListener(this); b2.addActionListener(this); pack(); setVisible(true); } public static void main(String[] args) { ListenerExample app = new ListenerExample(); } }
Compilando otteniamo il solo file ListenerExample.class.
Posso associare lo stesso event listener a piu' componenti nell'interfaccia: questi reagiranno allo stesso evento nello stesso modo.
Posso associare piu' event listener allo stesso oggetto: questo avra' piu' reazioni.
Una classe puo' implementare piu' interfacce listener.
Potra' essere usata per dare la stessa reazione a componenti
sensibili a tipi diversi di eventi.
Esempio (ExQuit.java):
bottone quit e chiusura di finestra, entrambi terminano applicazione.
public class ExQuit extends Frame { Button quitButton; public ExQuit() { quitButton = new Button("Click me to quit!"); add(quitButton); QuitListener listener = new QuitListener(); quitButton.addActionListener(listener); addWindowListener(listener); pack(); } class QuitListener extends WindowAdapter implements ActionListener { public void actionPerformed(ActionEvent e) { System.exit(0); } public void windowClosing(WindowEvent e) { System.exit(0); } } public static void main(String[] args) { ExQuit app = new ExQuit(); app.setVisible(true); } }
Tutti i metodi dei listener hanno parametri come segue:
public void nomeFunzione(XXXEvent e)Nel body della funzione posso chiamare su e i metodi della classe di evento XXXEvent per reperire informazioni che servono per reagire all'evento. Esempio:
Il fatto che i listeners siano interfacce e non classi (cioe' che non implementino i metodi) implica che per i listener che hanno piu' metodi devo fornire un'implementazione di tutti i metodi, inclusi quelli che non mi interessano (l'implementazione di questi non fara' nulla: {}).
Per comodita' sono forniti adapters che forniscono implementazione standard di un listener (dove tutti i metodi non fanno nulla): creo sottoclasse dell'adapter ridefinendo solo i metodi che mi interessano.
Esempio:
La classe WindowEvent corrisponde a eventi sulle finestre
top-level: apertura / chiusura, iconificazione / deiconificazione...
La classe WindowListener ha un metodo per ciascuno
degli eventi di cui sopra.
Se voglio reagire solo all'evento di chiusura:
Classi di eventi che qualsiasi componente cattura:
Nota: nelle varie sotto-classi di componenti alcuni di questi eventi sono gestiti internamente. Esempio: un campo di input testuale reagisce a guadagno / perdita del focus e a battitura di caratteri da tastiera in modo autonomo.
ComponentListener prevede i seguenti metodi di reazione ad eventi:
FocusListener prevede i seguenti metodi:
KeyListener prevede i seguenti metodi:
La classe di eventi MouseEvent ha due listener. Sono separati gli eventi di movimento del mouse, che comportano molto lavoro per gestirli (ogni movimento di un pixel genera un evento) ed e' previsto un listener a parte per questi.
MouseListener reagisce a tutti gli eventi tranne quelli di movimento, con i metodi:
MouseMotionListener reagisce agli eventi di movimento del mouse, con i metodi:
I contenitori top-level (frame e dialoghi) possono catturare eventi di classe WindowEvent.
Il WindowListener corrispondente prevede i seguenti metodi di reazione ad eventi:
A seconda della classe di dispositivo. Li vedremo man mano.
Classe -- descrizione -- eventi -- listener.
Classe Label, JLabel e' etichetta testuale in AWT,
testuale e/o grafica in Swing.
Ha metodi per stabilire allineamento orizzontale / verticale
dell'etichetta, spaziatura.
Non cattura eventi.
Classe Button / JButton. In AWT il bottone mostra una stringa, in Swing puo' mostrare una stringa, un'icona, oppure entrambe.
L'esempio ExJButtons.java mostra
tre tipi di bottoni java.
Serve anche il file immagine buttonicon.gif.
Bottone di comando cattura evento di azionamento
ActionEvent:
l'utente ha azionato il bottone,
in che modo dipende dalla piattaforma (di solito col mouse).
In ciascuna finestra top-level posso definire al piu' un solo
bottone "di default" che e' sensibile anche alla pressione del tasto
"return" (il sistema distingue graficamente tale bottone).
Il corrispondente ActionListener ha un solo metodo actionPerformed che scatta quando il bottone e' azionato.
Classe Checkbox / JCheckBox.
Interazione con utente cambia lo stato del bottone da selezionato a non
selezionato e viceversa.
Ha funzioni
boolean isSelected() ritorna true se componente e' selezionato
setSelected(boolean) forza selezione da programma.
Bottone a due stati cattura evento di cambio stato
ItemEvent.
Se e e' un item event,
leggo il nuovo stato con
e.getStateChange(), che ritorna
uno fra ItemEvent.SELECTED e ItemEvent.DESELECTED.
In Swing cattura anche evento di azionamento
ActionEvent (gia' visto per bottone di comando).
ItemListener prevede un solo metodo ItemStateChanged, che scatta quando il bottone cambia stato (da selezionato a deselezionato o viceversa).
Gruppo di bottoni radio e' un gruppo di bottoni a due stati dove in ogni istante un solo bottone puo' essere selezionato.
La pressione di un bottone del gruppo provoca automaticamente il rilascio di tutti gli altri bottoni del gruppo. Gruppi di radio-bottoni servono a implementare scelte mutuamente esclusive.
In AWT:
Esempio:
CheckboxGroup cbg = new CheckboxGroup(); add(new Checkbox("uno", cbg, true)); add(new Checkbox("due", cbg, false)); add(new Checkbox("tre", cbg, false));L'ultimo argomento booleano stabilisce se il bottone e' inizialmente selezionato (di default non e' selezionato).
In Swing:
Esempio:
rb1 = new JRadioButton("uno",true); rb2 = new JRadioButton("due"); rb3 = new JRadioButton("tre"); ButtonGroup bg = new ButtonGroup(); bg.add(rb1); bg.add(rb2); bg.add(rb3);
In Swing, quando utente preme un bottone radio in un gruppo, si generano tre eventi:
L'esempio ExButtons.java mostra i
tre tipi di bottoni (di comando, check box, radio).
Provare ad aggiungere gestione degli eventi.
Testo su una linea (campo) o su piu' linee (area).
Hanno super-classe TextComponent / JTextComponent,
molti metodi si trovano nella super-classe.
Metodi utili (il primo anche su text field, gli altri solo su area):
Per default, l'area di testo non va a capo, ma mostra tutto
su linea unica e, se inserita in
scroll pane, permette di scorrere orizzontalmente.
In Swing posso controllare le andate a capo:
Text component cattura
evento ActionEvent quando utente preme il tasto "return".
Inoltre anche:
Classe JComboBox.
Puo' essere non editabile o editabile
(di default non editabile).
Combo box appare come un bottone (se non editabile) o come
un campo di testo con un piccolo bottone accanto (se editabile).
Se utente agisce sul bottone compare una tendina con lista di scelte,
ciascuna scelta puo' essere stringa di testo o immagine.
In combo box editabile, l'utente puo' anche digitare direttamente
digitare nel campo di testo.
Per creare una combo box, si passa in argomento un array di stringhe (o di icone), e si assegna la stringa (icona) inizialmente selezionata:
Combo box sono sensibili a eventi ActionEvent e
ItemEvent.
Di solito e' sufficiente un ActionListener per gestire
una combo box.
Combo box editabile genera un ActionEvent
quando l'utente sceglie un elemento dalla tendina e anche
quando l'utente preme tasto "enter" nel campo di testo.
Una lista mostra una serie di elementi disposti in colonna da cui l'utente puo' scegliere.
La lista ammette selezione di righe singole o multiple. Swing distingue anche fra selezione multipla limitatamente ad un intervallo di righe contigue oppure multipla con piu' intervalli.
Contenuto di una lista puo' essere cambiato dinamicamente.
Per creare una lista in AWT la creo e le aggiungo le righe:
List list = new List(4); list.add("una"); list.add("due"); list.add("tre"); list.add("quattro");L'argomento del crostruttore (4) determina lista con 4 linee visibili.
Per creare una lista in Swing devo passare l'array di righe:
String[] scelte = {"una", "due", "tre", "quattro"}; JList list = new JList(scelte);
Per stabilire modo di selezione in AWT scelgo tra singolo e multiplo:
list.setMultipleMode(true/false);
Per stabilire modo di selezione in Swing ho tre possibilita':
list.setSelectionMode(modo);dove il modo e' uno fra
Lista e' sensibile a eventi di classe ListSelectionEvent. Il corrispondente ListSelectionListener prevede il metodo valueChanged che scatta quando utente esegue una selezione.
Dall'evento di selezione posso ottenere:
Classe JSlider. Consente di introdurre un valore numerico compreso fra un minimo ed un massimo. Barra con cursore mobile. La barra corrisponde ad un intervallo di valori, il cursore ad un valore corrente nell'intervallo.
Per creare uno slider, stabilisco l'orientamento (orizzontale o verticale) e i valori minimo, massimo e corrente (interi):
slider = new JSlider(direzione,minimo,massimo,corrente);La direzione puo' essere JSlider.HORIZONTAL o JSlider.VERTICAL.
Per leggere il valore corrente dello slider usare getValue(). Per sapere se utente sta ancora muovendo il cursore o ha finito, chiamare getValueIsAdjusting().
Slider puo' visualizzare righello con due ordini di tacche: maggiore e minore. Ciascun ordine di tacche puo' essere numerato oppure no.
Slider e' sensibile a eventi di classe ChangeEvent. Il corrispondente ChangeListener prevede il metodo stateChanged che scatta quando utente muove il cursore dello slider.
Esempio che reagisce solo se utente ha terminato l'interazione:
public void stateChanged(ChangeEvent e) { JSlider source = (JSlider)e.getSource(); if (!source.getValueIsAdjusting()) ... fare qualcosa con source.getValue() ... }
Menu' permette scelta di un'opzione da una lista di voci che e' presentata come una tendina a scomparsa.
Un menu' puo' essere:
CLassi:
Esempio di menu' inserito in barra:
bar = new MenuBar(); frame.setMenuBar(bar); Menu m = new Menu("un menu'"); bar.add(m);
PopupMenu pop = new PopupMenu("pop-up");
Ogni voce del menu' (menu item) ha caratteristiche analoghe a un bottone. Le classi di voci di menu' rispecchiano le classi di bottoni:
Esempio che crea un menu' (menu1) con tre voci: una semplice, una a check box e una costituita da un sottomenu' (menu2).
menu1 = new Menu("Primo menu'"); menu1.add (new MenuItem("voce semplice")); menu1.add (new CheckboxMenuItem("voce a due stati")); menu2 = new Menu("sotto menu'"); menu1.add (menu2); menu2.add("una voce"); menu2.add("altra voce");
Quelle che reagiscono a eventi sono le singole voci dei menu'.
Per assegnare un menu' pop-up ad un componente, si associa a quel componente
un MouseListener che faccia apparire il menu' pop-up
quando l'utente aziona il mouse.
Esempio (PopFrame.java): frame con
associato menu' pop-up
public class PopFrame extends Frame { PopupMenu popup; public PopFrame() { popup = new PopupMenu("A Popup Menu"); add(popup); MenuItem mi1 = new MenuItem("aaa"); popup.add(mi1); MenuItem mi2 = new MenuItem("bbb"); popup.add(mi2); setSize(new Dimension(200,100)); MouseListener listener = new PopupListener(); addMouseListener(listener); } class PopupListener extends MouseAdapter { public void mousePressed(MouseEvent e) { maybeShowPopup(e); } public void mouseReleased(MouseEvent e) { maybeShowPopup(e); } private void maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger()) popup.show(e.getComponent(), e.getX(), e.getY()); } } }
Scorciatoie da tastiera.