530 likes | 685 Views
Introduzione a JFC/Swing. Progetto di Basi di Dati 2007/2008 Domenico Lembo, Luigi Dragone, Marzo Ruzzi e Daniele Calisi Queste slide saranno online: http://www.dis.uniroma1.it/ ~calisi (teaching) . Sommario. Introduzione al modello a componenti JavaBeans Modello JavaBeans
E N D
Introduzione a JFC/Swing Progetto di Basi di Dati 2007/2008 Domenico Lembo, Luigi Dragone, Marzo Ruzzi e Daniele Calisi Queste slide saranno online: http://www.dis.uniroma1.it/~calisi (teaching)
Sommario • Introduzione al modello a componenti JavaBeans • Modello JavaBeans • Eventi e listener • Elementi di base della libreria JFC/Swing • Esempi di componenti • Struttura di un'applicazione • Esempi
Modello JavaBeans (1) • La libreria JFC/Swing è organizzata in base al modello JavaBeans • Un componente è un particolare tipo di dato (classe/istanza) caratterizzato da un insieme di proprietà, che comunica con gli altri componenti attraverso il meccanismo degli eventi. • Un evento è un oggetto che descrive il verificarsi di una particolare situazione: viene generato da un componente ed elaborato da altri
Modello JavaBeans: esempio window1 (component) - width (property) - height (property) resizeEvent (event) imageInWindow1 (component) - width (property) - height (property) - image (property)
Modello JavaBeans (2) • Esistono componenti in grado di contenerne degli altri. Il ciclo di vita di un componente è generalmente strettamente contenuto in quello del suo contenitore (esempio: finestra, widget interni) • Le proprietà di un componente ne descrivono lo stato, a differenza delle variabili membro, possono essere di sola lettura, di sola scrittura o di lettura/scrittura
Modello JavaBeans (3) • In Java, un componente è denominato bean ed è caratterizzato da: • Un costruttore pubblico, possibilmente senza argomenti • Metodi pubblici per impostare il valore delle proprietà (setter) • Metodi pubblici per consultare il valore delle proprietà (getter) • Eventualmente metodi pubblici per eseguire particolari compiti (verb) • Le proprietà sono gestite da variabili private del componente • I metodi di impostazione possono implementare logiche di validazione dei dati
Modello JavaBeans: esempio di codice public class MyBean { private String v1; private int v2; public MyBean(String v1) { this.v1 = v1; v2 = 0 } public String getV1() { return v1; } public void setV2(int v2) { this.v2 = v2; } public int getV2() { return v2; } }
Eventi e ascoltatori (1) • Un componente è in grado di produrre eventi di qualche natura • es.: il valore di qualche proprietà è stato modificato • Permette agli altri componenti di registrarsi come ascoltatori (listener) agli eventi in questione • Quando si verifica la situazione associata all'evento, il produttore dell'evento lo notifica (firing) a tutti gli ascoltatori che si sono registrati per quell'evento (meccanismo di callback).
Eventi e ascoltatori (2) • In Java un listener è un oggetto che implementa una specifica interfaccia, generalmente con dei metodi di callback che hanno come argomento l'oggetto di tipo evento a cui devono rispondere. • L'oggetto di tipo evento porta con sé informazioni sull'evento stesso: • L'origine dell'evento, ovvero l'oggetto che l'ha generato • Lo stato dell'evento (se è già stato consumato, se è valido, ecc.) • Le caratteristiche specifiche dell'evento (dipendenti dalla tipologia)
Eventi e ascoltatori: esempio resizeEvent (event) - source (property) - newSize (property) window1 (component) - width (property) - height (property) firing resizeEventListener (listener) - process(ResizeEvent) (callback) imageInWindow1 (component) - width (property) - height (property) - image (property)
Eventi e ascoltatori: esempio window1 (component) - width (property) - height (property) firing resizeEvent (event) - source (property) - newSize (property) imageInWindow1 (component + listener) - width (property) - height (property) - image (property) - process(ResizeEvent) (callback)
Pattern “observer” • Observer: due componenti: • Modello: mantiene la struttura dati e consente (agli altri componenti) di manipolare il contenuto • Vista: visualizza (ad es.: in una finestra) tale struttura dati • Per evitare inutili aggiornamenti, il componente “vista” si registra come ascoltatore degli eventi di modifica del modello e aggiorna la vista solo in risposta a tali eventi • Disaccoppiamento tra struttura dati e visualizzazione (possono esistere molteplici visualizzazioni della stessa struttura dati)
Eventi e ascoltatori: esempi di codice (1) • Un evento (ad es., EventX) è definito mediante un’opportuna classe associata all’interfaccia con il metodo di callback: public class EventX { public Object getSource() { ... } ... } interface EventXListener { public void process(EventX e); }
Eventi e ascoltatori: esempi di codice (2) • Un componente che puo sollevare tale tipo di evento espone dei metodi per il controllo dei listener ed il firing dello stesso: class Component { public void addEventXListener(EventXListener l) { ... } public void removeEventXListener(EventXListener l) { ... } void fireEventX(EventX e) { ... } }
Eventi e ascoltatori: esempi di codice (3) • Supponendo che il componente mantenga in una collezione listener l’elenco dei listener registrati, una possibile implementazione del metodo di firing è: void fireEventX(EventX e) { for(Iterator iter = listeners.iterator(); iter.hasNext(); ) { ((EventXListener)iter.next()).process(e); } }
Eventi e ascoltatori: esempi di codice (4) • Un listener e un oggetto di una classe implementante una specifica interfaccia che viene registrato presso il componente di cui si intende processare gli eventi. class MyListener implements EventXListener { public void process(EventX e) { ... } } ... Component c = new Component(); EventXListener l = newMyListener(); c.addEventXListener(l);
Eventi e ascoltatori: esempi di codice (5) • Per comodità di scrittura, i listener vengono generalmente implementati con classi anonime definite all'interno del codice. Esempio: BaseType b = new BaseType() { public void processEventX(EventX e) { … } }); myContainer.addXListener(new BaseType() { public void processEventX(EventX e) { // ... } });
Componenti visuali • Il modello a componenti e stato proposto in diversi scenari applicativi, tuttavia si presta particolarmente bene alla progettazione ed implementazione di GUI. • Il modello dei JavaBeans e stato definito inizialmente per l’originaria libreria Java per lo sviluppo di GUI, denominata AWT, ma si è poi evoluto in modo indipendente (ad es., gli Enterprise JavaBeans sono componenti non visuali che incapsulano funzioni e dati in applicazioni distribuite).
Componenti in JFC/Swing (1) • Un insieme di componenti che realizzano controlli (control) visuali (es.: etichette, campi di testo, liste di selezione, ecc.) • Un insieme di eventi (event) significativi per l'interazione con l'utente (es.: spostamento del mouse, pressione di un tasto, cambiamento di stato di un oggetto, ecc.) • Un insieme di componenti che realizzano oggetti di tipo contenitore (container) che aggregano visivamente e funzionalmente altri componenti
Componenti in JFC/Swing (2) • Un insieme di moduli che gestiscono strategie di impaginazione dei componenti all'interno dei contenitori (layout manager), di formattazione visuale (border renderer, formatter, ecc.) e di colloquio (standard dialog: avvisi, apertura/salvataggio file, ecc.) • Un insieme di tipi di dato di base (listener e data model) da estendere opportunatamente per implementare il comportamento desiderato
Event dispatcher thread • La libreria JFC/Swing definisce ed implementa, inoltre, un opportuno event dispatch thread che intercetta gli eventi e li smista ai moduli interessati dal processamento • La logica applicativa viene eseguita, quindi, all’interno del thread del dispatcher che non e il thread principale dell’applicazione • Le applicazioni che usano JFC/Swing sono sempre multi-threaded • Le operazioni svolte dai listener devono essere di breve durata, per non bloccare il thread di gestione degli eventi ovvero l’intera GUI (congelamento); • Eventuali operazioni di lunga durata vanno eseguite in un thread separato, il listener si limita all’inizializzazione ed avvio dello stesso.
I controlli di JFC/Swing • I controlli disponibili JFC/Swing e sono caratterizzati: • Da un insieme di proprietà • Dall'aspetto visivo • Dal tipo di interazione e di eventi che possono essere generati
Eventi di interfaccia e semantici • Gli eventi possono essere: • di interfaccia, ovvero operazioni di basso livello (es.: pressione di un tasto, trascinamento, click del mouse, ecc.) • applicativi/semantici, ovvero associati a una o più combinazioni di eventi di interfaccia e denotano operazioni di alto livello (avvio di un'operazione, modifica di un valore, ecc.) • La distinzione può aumentare la modularità e quindi la portabilità
Esempio di controllo • Controllo JButton • Permette di implementare “pulsanti” che l'utente può “premere” • La proprietà di base del controllo e il testo visualizzato (text); • L’aspetto visivo è un rettangolo che visualizza tale testo • L’evento applicativo associato alla scelta da parte dell’utente è actionEvent.
Look&Feel (1) • La visualizzazione può essere adattata in modo trasparente all'applicazione • In JFC/Swing è prevista una funzionalità per specificare in maniera indipendente: • Il look: specifiche di visualizzazione • Il feel: specifiche di interazione
Altri esempi di controlli Swing • Le etichette (label) realizzate dalla classe JLabel, permettono di specificare un testo da visualizzare, generalmente utilizzato per descrivere un altro controllo; • I campo di testo implementati dalle classe JTextField e JTextArea, per specificare un’area in cui l’utente può inserire/modificare valori espressi come testo (formattato o meno, ad es., numeri); • Le check-box implementate dalla classe JCheckBox, per gestire opzioni di tipo booleano; • I radio-button implementati dalle classi JRadioButton e ButtonGroup, per gestire opzioni a scelta multipla esclusiva • Le liste di selezione, implementate da diverse classi (JListBox, JComboBox, ecc.) per la scelta (eventualmente multipla) di opzioni da elenchi generici
Esempi di controlli Swing Un elenco visuale di tutti i controlli Swing si trova a questa URL: http://java.sun.com/docs/books/tutorial/ui/features/components.html
Contenitori (1) • Contenitori di primo livello (top-level container) • Contenitori che non sono contenuti in altri contenitori, in genere sono finestre o finestre di dialogo (window, dialog box)
Contenitori (2) • Contenitori di livello intermedio (intermediate container) • Contenitori che sono contenuti all'interno di altri contenitori
Struttura di un'applicazione con Swing (1) • Sebbene possibile, in genere le applicazioni non definiscono dei propri controlli visuali, ma si appoggiano a quelli della libreria. • Al contrario è prassi corrente definire dei contenitori specifici per l’applicazione. • La specializzazione è guidata dalla specifiche funzionali dell’applicazione. • Ad es., se è richiesta la visualizzazione di un certo insieme di dati in una finestra si definirà una sotto-classe del container specializzata per la visualizzazione di tali dati.
Struttura di un'applicazione con Swing (2) • I passi sono i seguenti: • si definisce una classe che estende la classe del contenitore della libreria (ad es., JFrame se si tratta di una finestra, JPanel se e un semplice pannello); • per ogni elemento contenuto all’interno del contenitore si definisce una variabile membro privata della classe del tipo opportuno; • si definiscono uno o piu metodi che inizializzano i vari elementi; • si definisce un metodo che inizializza l’oggetto in questione; • si associano le operazioni di inizializzazione al costruttore della classe.
Struttura di un'applicazione con Swing (3) • La procedura di inizializzazione di ogni controllo si articola nei seguenti passi: • istanziazione dell’oggetto; • impostazione delle proprieta dell’oggetto; • registrazione di eventuali event listener; • aggiunta dell’oggetto al contenitore. • Alcuni contenitori, in particolare i pannelli, che sono in genere inclusi all’interno di finestre o dialog box, richiedono che in fase di inizializzazione sia specificata la politica di posizionamento degli oggetti contenuti (layout).
Applicazione minimale (1) • In linea di massima un’applicazione JFC/Swing minimale è strutturata nel seguente modo: • un insieme di tipi dato ottenuti mediante estensione delle classi dei container che definiscono la struttura delle finestre/maschere dell’applicazione • un insieme di tipi dato ottenuti mediante l’implementazione delle interfacce dei listener che definiscono il comportamento dell’applicazione • La main class dell’applicazione si limita a istanziare il componente della finestra principale dell’applicazione (da cui l’utente può accedere alle diverse funzionalità) ed a visualizzarlo.
Applicazione minimale (2) • Il thread principale termina ed il controllo passa all’event dispatcher. • L’applicazione termina quando viene chiusa la finestra principale (dall’utente o mediante API) oppure si invoca l’istruzione di chiusura (System.exit(int)). • L’insieme dei tipi di evento è in genere quello previsto dalla libreria, in quanto eventuali nuovi tipi non sarebbero processabili dal dispatcher. • Ciò non impedisce, tuttavia, di estendere l’insieme per ereditarietà (specializzazione).
Esempio completo (1) • Si supponga di voler implementare una calcolatrice che permetta di eseguire le quattro operazioni aritmetiche. • Deve essere possibile specificare i due operandi, indicare l’operazione e quindi leggere il risultato.
Esempio completo (2) • Il modulo puo essere realizzato con un componente di tipo frame, CalcFrame che estende la classe JFrame, introducendo: • 2 campi per l’imputazione degli operandi, realizzati con dei controlli JTextField; • 4 pulsanti (controlli JButton) per selezionare l’operazione desiderata; • 1 campo per la visualizzazione del risultato, con un controllo di tipo JTextField non modificabile dall’utente (editabile).
Esempio completo (3) import javax.swing.*; import java.awt.event.*; import java.awt.*; public class CalcFrame extends JFrame { private JPanel jContentPane = null; private JTextField firstOpTextField = null; private JTextField secondOpTextField = null; private JButton sumButton = null; ... private JTextField resultTextField = null;
Esempio completo (4) public CalcFrame() { super(); initialize(); } private void initialize() { this.setSize(300, 200); this.setContentPane(getJContentPane()); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setTitle("Calc"); } ... }
Esempio completo (5) • La proprieta defaultCloseOperation serve a specificare che l’applicazione deve terminare quando la finestra in questione viene chiusa. • L’inizializzazione del modulo e basata sull’inizializzazione dei diversi controlli e la loro registrazione nel panello predefinito (jContentPane) del frame. private JPanel getJContentPane() { if (jContentPane == null) { jContentPane = new JPanel(); jContentPane.setLayout( new FlowLayout()); jContentPane.add(getFirstOpTextField(), null);
Esempio completo (6) jContentPane.add(getSecondOpTextField(), null); jContentPane.add(getSumButton(), null); ... jContentPane.add(getResultTextField(), null); } return jContentPane; } • La proprieta layout permette di impostare il layout manager, in questo caso si e optato per un semplice flow layout, che dispone I controlli in righe successive (simile all’impaginazione di un testo).
Esempio completo (7) • L’inizializzazione di un controllo, ad es., un campo viene eseguita da un metodo specifico: private JTextField getFirstOpTextField() { if(firstOpTextField == null) { firstOpTextField = new JTextField(); firstOpTextField.setColumns(10); } return firstOpTextField; } private JTextField getResultTextField() { if(resultTextField == null) { resultTextField = new JTextField();
Esempio completo (8) resultTextField.setColumns(10); resultTextField.setEditable(false); } return resultTextField; } • La proprieta columns specifica l’ampiezza del campo (numero di caratteri), mentre la proprieta editable indica se il contenuto del campo e modificabile dall’utente. • I campi non editabili sono utilizzati per presentare informazioni in sola lettura (ad es., il risultato di un’elaborazione). Le etichette sono utilizzate per presentare informazioni statiche (che non cambiano nel corso dell’esecuzione).
Esempio completo (9) • Per quanto riguarda le operazioni, i metodi di inizializzazione dei controlli specificano il listener con una classe anonima interna: private javax.swing.JButton getSumButton() { if(sumButton == null) { sumButton = new JButton(); sumButton.setText("+"); sumButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("actionPerformed()"); try { int firstOp = Integer.parseInt(getFirstOpTextField().getText());
Esempio completo (10) int secondOp = Integer.parseInt(getSecondOpTextField().getText()); int result = firstOp + secondOp; getResultTextField().setText( Integer.toString(result)); } catch (NumberFormatException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(null, ex, "Error", JOptionPane.ERROR_MESSAGE); } } // public void actionPerformed(e) });
Esempio completo (11) } // if (sumButton == null) return sumButton; } • L’evento di tipo action corrisponde all’azione di attivazione del pulsante indipendentemente dalla modalita con cui questa e stata indicata dall’utente (ad es., click del mouse, pressione di un tasto, ecc.). • La classe JOptionPane implementa la struttura di base di un dialogo modale ed offre i metodi per visualizzare messaggi di avviso/conferma (showXXX).
Esempio completo (12) • Il metodo main del modulo si limita all’istanziazione e visualizzazione dello stesso: public static void main(String[] args) { JFrame frame = new CalcFrame(); frame.validate(); frame.setVisible(true); }
I modelli (1) • I controlli piu semplici, ad es., i campi di testo, hanno una rappresentazione del dato generalmente basata su una proprietà strutturalmente da intendersi come atomica. • Si tratta di un’applicazione del pattern observer.
I modelli (3) • Nel caso di controlli piu complessi, ad es., liste di selezione è necessario definire modelli dati piu sofisticati, ad es., è necessario rappresentare l’insieme delle voci e la selezione corrente. • Per fare ciò viene introdotto il concetto di data model, ovvero di un componente non visuale che adatta le strutture dati specifiche dell’applicazione con il modello dati tipico del controllo visuale. • Ogni controllo prevede un particolare tipo di data model, generalmente definito mediante un’interfaccia o una classe base astratta.