Java Interfaccia Grafica • Testi di consultazione: a) core Java 1.1 (Volume I – Fundamentals) Cay S. Horstmann, Gary Cornell, Prentice Hall, 1997. b) Java 1.2 Unleashed, Jamie Jaworski, Sams Publishing, 1998. c) Programmazione di applicazioni grafiche in Java, S. Mazzanti e V. Milanese, 2006. • Un programma interattivo classico controlla l'interazione con l'utente quando il flusso di controllo lo esige. • Con una interfaccia grafica (GUI) normalmente l'applicazione attende che l'utente generi eventi agendo sull'interfaccia (azioni e movimenti del mouse, bottoni, ecc...) • Si può parlare di interazione guidata dagli eventi, a cui corrispondono delle opportune procedure/funzioni/metodi per gestirli. • Un sistema operativo che supporti un'interfaccia grafica deve controllare continuamente l'ambiente per verifica se sono avvenute azioni (ad esempio 'la pressione di un tasto sulla tastiera' o un 'click' sul mouse), che generano eventi corrispondenti. Tali eventi vengono quindi comunicati all'applicazione che li gestisce. Vi sono vari approcci. Approcci classici di alto e basso livello Approccio stile Visual Basic • Corrispondenza diretta eventi-procedure (event procedures) • Si scrive codice per manipolare ciascun evento e si mette tale codice nella corrispondente procedura. Ad esempio: per HelpButton si scrive la procedura HelpButton_Click per gestire l'evento 'click' del mouse. Approccio stile C: • Un unico ciclo che controlla continuamente la coda degli eventi, con un enorme 'case' per testare tutte le possibilità. Per ogni evento può essere invocata la routine opportuna. Java e gli altri • L'approccio del Visual Basic consente di trattare una classe limitata di Eventi, quelli previsti dal linguaggio, però il codice corrispondente è molto più conciso, semplice ed elegante. • L'approccio del C permette una maggiore flessibilità e di trattare più eventi. • Il codice Java deve essere indipendente dalla piattaforma, quindi definisce un modello astratto che contiene i concetti comuni ai vari ambienti grafici. Di conseguenza si pone in una posizione intermedia come flessibilità e semplicità tra l'esempio del Visual Basic e del C. • Package per la gestione dell'ambiente grafico: AWT (Abstract Window Toolkit). java.awt (4 sotto-package: java.awt.datatransfer, java.awt.event, java.awt.image, java.awt.peer). 105 classi, 22 interfacce Java 1.2 • Extended JFC (Java Foundation Classes) JFC 1.1 ---- AWT, SWING, JAVA 2D, Drag and Drop, Accessibility SWING: • usa AWT per interfacciarsi col sistema di finestre sottostanti nativo • offre nuove componenti (rispetto all'AWT) • ri-implementa componenti AWT in codice Java 100% puro peggiore performance per la stessa componente aspetto migliore (look and feel) maggiore flessibilità SWING incluse in AWT incluso in JFC • JDK 1.2 include compiler JIT Package AWT • La classe principale del package è component, che rappresenta un oggetto grafico che ha una posizione ed una dimensione sullo schermo, su cui è possibile disegnare e che può ricevere 'azioni' dall'utente Component è una classe astratta (circa 120 metodi). Rappresenta gli elementi di base dell'interfaccia grafica. Una istanza di component può essere visualizzata solo se contenuta in un oggetto visibile. Esempi di componenti: List, Scrollbar, TextArea, TextField, Choice, Button, Label... Tutti i Container • Container è un'altra classe astratta che deriva da Component. Può contenere componenti. Notiamo che un container è anche un componente. In genere un container prevede le funzionalità per la gestione degli eventi e dello scambio di informazioni tra i componenti. Esempi di Container: Applet, Frame, Window, Dialog, Panel Il modello astratto (Java 1.1) • Definiamo come sorgente di un evento (Event source) una qualunque componente grafica su cui l'utente possa svolgere delle azioni (ad esempio un bottone o una lista di scelte in un menu) Il programmatore decide come gli eventi generati da azioni utente sulle componenti grafiche dell'interfaccia vengono gestiti da opportuni oggetti delegati a gestire (o ascoltare) il verificarsi di un determinato evento. Qualsiasi oggetto (anche più di uno) può essere delegato ad ascoltare (e quindi gestire) il verificarsi di un determinato evento. Per questo motivo si parla di event delegation model. Notiamo che è più flessibile di Visual Basic, in cui l'ascoltatore è fissato a priori, però richiede di scrivere più codice. I vantaggi rispetto a C sono ovvi, ad esempio il fatto che l'utente non vede la coda di sistema degli eventi. Ovviamente un programma C potrebbe gestire più eventi di quelli trattabili in Java. Modello con delega della gestione eventi • Un oggetto ascoltatore è una istanza di una classe che implementa la interfaccia detta listener interface Un oggetto ascoltatore deve comunque essere delegato per poter gestire un evento generato da una componente grafica. Un oggetto delegato riceverà un messaggio (notifica) quando si verifica un evento. Il messaggio attiva il corrispondente metodo della listener interface. • L'oggetto ascoltatore viene delegato mediante una linea di codice del tipo seguente: eventSourceObject.addEventListener(eventListenerObject) • Esempio: Button b = new Button(“clear”); b.addActionListener(OggDeleg); In questo modo viene inviato un messaggio a 'OggDeleg' ogni volta che si verifica un evento (cioè un'azione) su Button. Un evento su un bottone può essere un 'click' sul bottone. L'oggetto delegato ('OggDeleg') come ascoltatore deve implementare l'interfaccia opportuna, quindi in questo caso ActionListener Il metodo actionPerformed di tale interfaccia riceve come parametro un oggetto di tipo ActionEvent, che permette all'ascoltatore di avere informazioni ulteriori sull'evento verificatosi. Alcuni esempi di programmazione • esempi di applicazioni standalone (console grafica) esempi di applets Gestione Finestre import java.awt.*; public class FinestraVuota extends Frame { public static void main (String argv[]) { FinestraVuota ist = new FinestraVuota(); System.out.println ("Esco da main"); } FinestraVuota () { setBounds(30, 10, 300, 200); setTitle (getClass().getName()); setVisible(true); } } • Sostanzialmente disegna un rettangolo di 300x200 pixel sulla console grafica con un vertice di coordinate 30 e 10 Infine scrive sulla console grafica la scritta “Esco da main”. • Questo è un esempio di creazione di una finestra, senza gestione degli eventi, e quindi senza interazione con l'utente. import java.awt.*; import java.awt.event.*; public class Eventi extends Frame { Ascoltatore asc = new Ascoltatore(); public static void main (String argv[]) { Eventi ist = new Eventi(); } Eventi () { setBounds(30, 10, 300, 200); setTitle (getClass().getName()); setVisible(true); addWindowListener(asc); } } class Ascoltatore implements WindowListener { public void windowClosed (WindowEvent evt) { System.out.println (evt); System.exit(0); } public void windowClosing (WindowEvent evt) { evt.getWindow().dispose(); System.out.println (evt); } public void windowDeiconified (WindowEvent evt) { System.out.println (evt); } public void windowIconified (WindowEvent evt) { System.out.println (evt); } public void windowOpened (WindowEvent evt) { System.out.println (evt); } public void windowActivated (WindowEvent evt) { System.out.println (evt); } public void windowDeactivated (WindowEvent evt) { System.out.println (evt); } } java.awt.event.WindowEvent[WINDOW_DEACTIVATED] on frame0 java.awt.event.WindowEvent[WINDOW_ICONIFIED] on frame0 java.awt.event.WindowEvent[WINDOW_DEICONIFIED] on frame0 java.awt.event.WindowEvent[WINDOW_CLOSING] on frame0 java.awt.event.WindowEvent[WINDOW_CLOSED] on frame0 Listener interfaces e gerarchia degli eventi • ActionListener AdjustementListener ComponentListener FocusListener ItemListener KeyListener MouseListener MouseMotionListener TextListener WindowListener • Non tutti gli eventi nella gerarchia di sistema sono utili per l'utente. Quelli che sono inviati agli ascoltatori sono: ActionEvent AdjustmentEvent ComponentEvent FocusEvent ItemEvent KeyEvent MouseEvent TextEvent WindowEvent Eventi ed Oggetti Eventi • Gli eventi possono essere di diverso tipo, ma comunque condividono alcune caratteristiche. Pertanto le classe degli eventi sono rappresentate in modo gerarchico e seguono le usuali regole dell'inheritance. • La classe radice si chiama java.util.EventObject. La sola caratteristica comune condivisa da tutti gli eventi è un oggetto sorgente. Pertanto nella classe radice EventObject troviamo il seguente metodo: public Object getSource(); Controlli grafici • un contenitore permette di organizzare (contenere in modo ordinato) un insieme di componenti grafiche e di presentarle visualmente in modo controllato. • È possibile aggiungere componenti ad un contenitore mediante il metodo Component add(Component) e specificare un gestore della organizzazione all'interno del contenitore mediante il metodo public void setLayout(LayoutManager mgr) APPLET • Un applet si crea come sottoclasse di java.applet.Applet • È necessario definire un tag APPLET per poter inserire una “chiamata” al codice compilato in un documento html • si può inoltre eseguire un applet mediante l' “appletviewer” del JDK (Java Development Kit) della Oracle Esempio di tag: <APPLET CODE = ProvaApplet.class WIDTH = 50 HEIGHT = 100> </APPLET> • WIDTH ed HEIGHT definiscono le dimensioni della regione assegnata sulla finestra gestita dal browser per visualizzare l'applet. • CODE indica il bytecode dell'applet da caricare Ciclo di vita di un Applet • public void init() eseguito la prima volta che viene caricato l'applet • public void start() eseguito ogni volta che il browser ri-visualizza la parte di pagina dove è contenuta l'applet (ad esempio caricando o ricaricando la pagina html dov'è definita) • public void stop() eseguito quando la pagina dove viene caricata l'applet viene disattivata (ad esempio visualizzandone un'altra) • public void destroy() eseguito quando si esce dal browser o si chiude la pagina html dove sta il tag per l'applet. -------------------------------------------------------------------------• Si può utilizzare uno o più di questi metodi nel codice che definisce un applet. Non c'è nessun obbligo di usarne anche solo uno, anche se pare ragionevole utilizzare almeno init e/o start. Un applet può contenere la definizione di un costruttore (anche se normalmente è sostituito dal metodo init() ) import java.util.Date; import java.awt.*; import java.awt.event.*; public class Bottoni extends Frame implements ActionListener { Date data = new Date(); public static void main (String argv[]) { Bottoni ist = new Bottoni(); } Bottoni () { setLayout(null); setBounds (30, 10, 300, 200); setTitle (getClass().getName()); Button bData = new Button("Data"); bData.setBounds (50, 150, 50, 30); bData.addActionListener (this); Button bEsci = new Button("Esci"); bEsci.setBounds (200, 150, 50, 30); bEsci.addActionListener (this); add (bData); add (bEsci); setVisible(true); } public void paint (Graphics g) { g.drawString (data.toString(), 5, 40); } public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("Esci")) System.exit(0); if (e.getActionCommand().equals("Data")){ data = new Date(); repaint(); } } } • Disegna sulla console grafica una finestra di 300x200 pixel, con un vertice di coordinate 30 e 10. Due bottoni con scritta “Data” e “Esci” vengono collocati nella finestra. • Ogni volta che l'utente clicca su “Data” viene visualizzata sulla console la data e ora del giorno. • Quando l'utente clicca su “Esci” termina l'esecuzione. import java.util.Date; import java.awt.*; import java.awt.event.*; import java.applet.*; public class ABottoni extends Applet implements ActionListener { Date data = new Date(); public void init(){ setLayout(null); setBounds (30, 10, 300, 200); // setTitle (getClass().getName()); Button bData = new Button("Data"); bData.setBounds (50, 150, 50, 30); bData.addActionListener (this); Button bEsci = new Button("Esci"); bEsci.setBounds (200, 150, 50, 30); bEsci.addActionListener (this); add (bData); add (bEsci); } public void paint (Graphics g) { g.drawString (data.toString(), 5, 40); } public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("Esci")) System.exit(0); if (e.getActionCommand().equals("Data")){ data = new Date(); repaint(); } } } Suoni ed Immagini import java.applet.*; import java.awt.*; import java.awt.image.*; public class Immagine extends Applet { Image img; AudioClip auc; public void init () { img = getImage (getDocumentBase(), "mar.gif"); auc = getAudioClip (getDocumentBase(), "97381.au"); } public void start () { auc.loop(); } public void stop () { auc.stop(); } public void paint (Graphics g){ g.drawImage (img, 0, 0, this); } } Sviluppo di Applicazioni per Android (o per iOS) • Per lo sviluppo di applicazioni iOS è necessario far riferimento a Objective-C, un linguaggio orientato ad oggetti che estende propriamente C (il compilatore può compilare qualsiasi programma C), con caratteristiche simili a Java, ma anche con importanti differenze (non è fortemente tipato, si possono invocare metodi inesistenti, ecc...), oltre ad un ambiente di sviluppo adeguato (e.g.: Xcode di Apple). • Per lo sviluppo di applicazioni per Android si può utilizzare Java, studiando le caratteristiche di un ambiente di sviluppo adeguato, come ad esempio Eclipse o Android Studio (fornito da Google). Eclipse (dal 2001, scritto in Java) è un ambiente di sviluppo integrato multi-linguaggio (per Java, C++, ecc) e multipiattaforma. Ideato da un consorzio di grandi società quali Ericsson, Borland, HP, IBM, Intel, MontaVista Software, QNX, SAP e Serena Software, chiamato Eclipse Foundation. Android Studio - permette di vedere tutte le modifiche visive apportate ad una app in tempo reale e di vedere come apparirà su diversi dispositivi Android, ognuno con configurazioni e risoluzioni diverse, contemporaneamente. Inoltre fornisce: -un modo semplice per testare le prestazioni su vari tipi di dispositivi. -procedure guidate e modelli per gli elementi comuni a tutte le programmazioni Android (ad esempio per le interfacce grafiche). -un editor completo con strumenti per velocizzare lo sviluppo delle applicazioni.