PROGRAMMAZIONE GRAFICA Creazione di interfaccie utente “grafiche”, ovvero basate su concetti quali finestra, casella di testo, bottone, barra degli strumenti, menu. Elementi fondamentali: • Componenti e composizione: l’interfaccia grafica viene costruita assemblando componenti pronti, quali finestre, bottoni, caselle di testo; • Layout: posizionamento degli elementi nell’interfaccia grafica. E’ un problema non banale quando si vuole che l’applicazione sia multipiattaforma e indipendente dalla risoluzione e dalla dimensione dei caratteri. • Event-oriented programming: il programma non è più in comando dell’interazione con l’utente. Deve reagire appropriatamente alle azioni svolte dall’utente. • Programmazione grafica: è possibile creare componenti che si disegnano autonomamente. Librerie per la grafica in Java: • AWT: presente nel JDK, risale a Java 1.0. E’ poco flessibile, possiede meno componenti. Ogni componente è nativo, ovvero ha lo stesso aspetto degli altri componenti del sistema operativa; • Swing: presente nel JDK, risale a Java 2. Flessibile, potente, anche se più complessa da usare. Indipendente dalla piattaforma e skinnable (vedi Look And Feel). Ing. Aime – Programmazione grafica in Java con la libreria Swing 1 COMPOSIZIONE Finestra Componenti Pannello (contenitore) Secondo pannello Osservazioni: • Componenti: la finestra mostra vari componenti, quali etichette (nome, descrizione), caselle di testo, bottoni, liste a discesa, radio e check buttons, liste, e pannelli, ovvero generici contenitori di altri componenti (decorati con un bordo e un titolo) • Composizione: la finestra contiene un pannello, che contiene dei componenti, ma anche un secondo pannello, il quale contiene a sua volta dei bottoni; Ing. Aime – Programmazione grafica in Java con la libreria Swing 2 POSIZIONAMENTO E GESTIONE LAYOUT X Y Ogni componente è dotato di un sistema di riferimento cartesiano, la cui unità di misura è il pixel (la cui dimensione dipende dal numero di pixel presenti sullo schermo e dalla dimensione dello schermo, per una definizione più accurata si veda http://it.wikipedia.org/wiki/Pixel). Ad esempio, possiamo dire che l’etichetta nome ha una posizione (3, 6) rispetto all’origine, e una dimensione di 20 per 8 pixel. Ciascun contenitore ha il proprio riferimento cartesiano. La posizione di jButton2, ad esempio, è riferita al sistema cartesiano di Pannello2, non all’intera finestra. Ing. Aime – Programmazione grafica in Java con la libreria Swing 3 X X Y Y Il dimensionamento e il posizionamento fisso in pixel non sono però affidabili: se si cambia la dimensione o il tipo dei font o la risoluzione del video, o si cerca di ridimensionare la finestra, si possono ottenere componeti sovrapposti, mal distribuiti, o posizionati fuori dall’area visibile. Java introduce i layout manager, gestori del posizionamento, che risolvono questi problemi ricalcolando la posizione assoluta dei componenti in funzione delle suddette variabili. Ing. Aime – Programmazione grafica in Java con la libreria Swing 4 EVENT ORIENTED PROGRAMMING Paradigma di interazione in un programma testuale: il software propone delle domande all’utente, e si aspetta una risposta (testuale). Il programma di norma interrompe la sua esecuzione nell’attesa della risposta, la riposta è una sola, è sufficiente verificare che sia appropriata. Il programma domina l’interazione con l’utente. Il programma termina quanto termina la main. Paradigma di interazione in un programma grafico: il programma predispone una interfaccia utente grafica, l’utente decidere cosa quale azioni vuole eseguire. Ad esempio, può compilare prima il campo nome e poi la descrizione, e premere ok senza aver fornito la sua età. Il programma di norma non termina quanto termina la main, ma quando viene chiusa l’ultima finestra. L’utente domina l’interazione, il programma può soltanto “reagire” agli stimoli forniti dall’utente. Quindi, il programma resta in attesa di “eventi” generati dall’azione dell’utente, ed esegue determinate routine quando accadono eventi ritenuti interessanti. Vi sono molti eventi, ma il programma ne ascolterà soltanto una parte. Ad esempio, relativamente ad un bottone siamo interessati al click sul bottone, ma probabilmente non al fatto che qualcuno ha premuto il tasto “A” mentre il bottone era attivo (anche se potremmo ascolatare anche questo evento). Alla stessa maniera, non saremo direttamente interessati ad eventi quali il ridimensionamento di una finestra (ma il layout manager, al contrario, sarà molto interessato in questi ultimi). Ing. Aime – Programmazione grafica in Java con la libreria Swing 5 Mouse Mouse Mouse Tastiera Tastiera Tastiera Semantici Virtual Machine + Swing Componente Ascoltatori L’utente opera sulla finestra del programma con mouse e tastiera. La virtual machine e la libreria Swing ricevono questi eventi “grezzi”, o di basso livello, e li inviano al componente a cui sono dedicati. Per decidere quale componente è il destinatario, vengono usati due algoritmi: • se l’evento origina dal mouse, si determina quale è il componente che sta sotto il puntatore del mouse; • se l’evento origina dalla tastiera, si invia al componente che ha il “focus” (di norma tale componente è evidenziato con un bordo particolare). Il “focus” può essere spostato ciccando sui componenti o premendo i tasti TAB e MAISC-TAB. Il componente notifica questi eventi così come li ha ricevuti, ma crea anche eventi di più alto livello. Ad esempio, un click può comportare la selezione/deselezione: verrà notificato sia l’evento di click, che un evento di selezione/deselezione. Un programma grafico aggancia degli “ascoltatori” ai componenti, per poter ascoltare i particolari eventi a cui è interessato e reagire di conseguenza. Ing. Aime – Programmazione grafica in Java con la libreria Swing 6 Come sempre in Java, questi concetti sono mappati su classi: • un evento è rappresentato da un oggetto, istanza di una particolare classe di evento, riporta un riferimento al componente che lo ha generato e ulteriori informazioni specifiche in funzione del tipo di evento (ad esempio, un evento di click riporta quale bottone del mouse è stato premuto, se erano premuti alcuni tasti speciali sulla tastiera insieme al click, eccetera); • un ascoltatore (listener) è un oggetto che implementa una determinata interfaccia. Quando un evento viene scatenato, verrà invocato uno dei metodi di questa interfaccia. Ad esempio, MouseListener è una interfaccia, fra i vari metodi di questa interfaccia abbiamo public void mouseClicked(MouseEvent e); Quando l’evento di click viene scatenato, verrà invocato questo metodo passando come parametro il MouseEvent. • l’ascoltatore di deve registrare presso il componente per poter ascoltare (e può rimoeve la registrazione per smettere di ascoltare). public void addMouseListener(MouseListener l) public void removeMouseListener(MouseListener l) • Un componente può avere più ascoltatori, il medesimo ascoltatore può ascoltare eventi da più componenti. Bottone 1 E1 Ascoltatore1 E1 Bottone 2 E2 Ascoltatore2 Ing. Aime – Programmazione grafica in Java con la libreria Swing 7 RIASSUMENDO Una interfaccia grafica viene costruita mediante i seguenti passi: 1. si creano e si configurano i componenti; 2. si assembla l’interfaccia inserendo componenti base dentro i contenitori (pannelli) e questi dentro una finestra; 3. si specifica la gestione del layout (algoritmo generale, dettagli per i singoli componenti) 4. si agganciano dei listener ai componenti per poter reagire alle azioni dell’utente. A run time, il programma aspetta gli input dell’utente e reagisce. Alcune interazioni sono già predisposte nei componenti (ad esempio, un campo di testo reagire alla tastiera inserendo i caratteri premuti nella casella). Altre interazioni sono programmate nei listener, e vengono attivate quando l’utente esegue determinate azioni (ad esempio, premere un bottone). ESEMPIO Ing. Aime – Programmazione grafica in Java con la libreria Swing 8 import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Date; class FirstExample extends JPanel implements ActionListener { JButton bottone; JTextField testo; public FirstExample() { // costruzione e configurazione componenti bottone = new JButton("Premi qui"); testo = new JTextField(); testo.setColumns(20); // aggiunta dei componenti al pannello this.add(bottone); this.add(testo); // aggancio i listener (me stesso) bottone.addActionListener(this); } // dichiarato in ActionListener // verrà invocato quando il bottone sarà premuto public void actionPerformed(ActionEvent event) { Date ora = new Date(); testo.setText(ora.toString()); System.out.println("Sono stato invocato da:\n" + event.getSource()); System.out.println(); } public static void main(String[] args) { JFrame frame = new JFrame("Primo esempio"); frame.setContentPane(new FirstExample()); frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); // la main termina, ma il programma no!!! } } Ing. Aime – Programmazione grafica in Java con la libreria Swing 9 GERARCHIA DEI COMPONENTI IN JAVA Object Component AWT Swing Container Window JComponent Frame Dialog JFrame JDialog JPanel JList JTextComponent JTextField AbstractButton JTextArea JToggleButton JCheckBox JButton JRadioButton Osservazioni: • ogni componente grafico è rappresentato da una classe. • i componenti sono disposti lungo una gerarchia di ereditarietà i metodi presenti nelle classi base sono disponibili in tutte le sottoclassi! • Nell’ovale sono riportate le classi che rappresentano le finestre. Di due tipi, normale (JFrame), di dialogo (usata di norma per chiedere all’utente informazioni). Ing. Aime – Programmazione grafica in Java con la libreria Swing 10 • In corsivo le classi di AWT, le classi di Swing iniziano tutte per J. GERARCHIA DEGLI EVENTI Object EventObject AWTEvent ActionEvent ContainerEvent KeyEvent ComponentEvent InOutEvent MouseEvent ItemEvent WindowEvent Package java.awt.event Ogni evento viene accompagnato da un oggetto che lo rappresenta. Per ogni evento, è possibile risalire al componente che lo ha generato invocando il metodo public Object getSource(); Ing. Aime – Programmazione grafica in Java con la libreria Swing 11 COMPONENTI AWT Component Classe base (astratta) di tutti i componenti grafici. Di seguito sono riportati alcune proprietà e eventi del componente. Alcune proprietà (manipolate con metodi set/get) Background Color di sfondo Bounds Area occupata dal componente nel suo contenitore (Rectangle) Enabled Un componente riceve gli eventi solo se abilitato Font Tipo di carattere usato nel componente Focusable Se il componente può ricevere il focus Foreground Colore di primo piano Location Posizione dell’angolo in alto a sinistra (Point) MinimumSize Dimensione minima per il componente (Dimension) MaximumSize Dimensione massima per il componente Parent Il contenitore in cui è collocato il componente. Può essere null Size Dimensione del componente (classe Dimension) Visibile Se vero, il componente è visibile. Usato anche per mostrare le finestre a video dopo averle costruite Alcuni fra i listener accettati KeyListener Per ascoltare eventi come la pressione di tasti (solo se il componente ha il focus) MouseListener Eventi base del mouse (click, ingresso e uscita del puntatore) MouseMotionListener Eventi avanzati, movimento, trascinamento MouseWheelListener Eventi della rotellina del mouse, se presente Ing. Aime – Programmazione grafica in Java con la libreria Swing 12 Esiste poi un metodo che viene invocato ogni volta che il componente deve essere disegnato: public void paint(Graphics g); dove Graphics è un oggetto dotato di metodi per disegnare linee, rettangoli, ellissi, poligoni, eccetera. Ogni componente ridefinisce questo metodo per poter disegnare correttamente il suo aspetto. Container E’ un contenitore di altri componenti. Pertanto: • esistatono metodi per aggiungere/rimuovere i componenti contenuti, e per ispezionare quali componenti sono presenti; • è possibile associare un layout manager public public public public public public Component add(Component comp); add(Component comp, Object constraints); void remove(Component comp); int getComponentCount(); Component getComponent(int n); void setLayout(LayoutManager mgr); Ing. Aime – Programmazione grafica in Java con la libreria Swing 13 COMPONENTI SWING JFrame La finestra. Alcune proprietà (manipolate con metodi set/get) Title Titolo della finestra Resizable Se vero, la finestra è ridimensionabile DefaultClose Operazione svolta alla chiusura. Valori possibili: Operation WindowConstants.DO_NOTHING_ON_CLOSE WindowConstants.HIDE_ON_CLOSE WindowConstants.DISPOSE_ON_CLOSE WindowConstants.EXIT_ON_CLOSE import javax.swing.*; import java.awt.*; public class Finestra extends JFrame { public Finestra(String title) { super(title); setSize(200, 200); setLocation(100, 200); setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE); } public static void main(String[] args) { System.out.println( "Prima di creare la finestra"); Finestra f = new Finestra("Prima finestra"); f.setVisible(true); System.out.println( "Sto per uscire dalla main"); } } Ing. Aime – Programmazione grafica in Java con la libreria Swing 14 JComponent Classe base dei componenti Swing. Aggiunge molti metodi e proprietà, ma la maggior parte sono “avanzati”, e non verranno presi in considerazione. Alcune proprietà (manipolate con metodi set/get) Border Bordo del componente (sottoclassi di Border) Opaque Boolean, indica se lo sfondo è trasparente o pieno (default, trasparente) Metodi per il disegno (invocato dalla paint di JComponent, si occupa di richiamare paintComponent dopo aver disegnato il bordo): protected void paintComponent(Graphics g); In aggiunta, tutti i discendenti di JComponent sono in grado di modificare il proprio aspetto quando viene cambiato il “Look and Feel”. JPanel Pensato per essere un semplice contenitore di componenti. Non ha nessuna differenza particolare, a meno che il look and feel non imponga qualche differenza estetica (ad esempio, uno sfondo grafico). JLabel Semplice etichetta di testo. Attenzione: JLabel non è passivo, ha tutta la gamma di eventi ereditati dai suoi padri, come ad esempio la gestine degli eventi del mouse, presente sin dalla radice della gerarchia, Container. Alcune proprietà (manipolate con metodi set/get) Text Il testo mostrato nell’etichetta Ing. Aime – Programmazione grafica in Java con la libreria Swing 15 AbstractButton e i bottoni Classe base astratta di tutti i bottoni. Qualunque bottone sarà dotato di un testo, la proprietà text. Inoltre, qualunque bottone emette un ActionEvent ogni volta che il bottone viene premuto (con il mouse, o con la barra spaziatrice quando il bottone ha il focus). Un ascolatore di ActionEvent deve implementare ActionListener: public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent e); } L’ascoltatore dovrà poi registrarsi presso il bottone usando il metodo: public void addActionListener(ActionListener l); JButton è una sottoclasse di AbstractButton semplice, non vi sono funzionalità aggiuntive degne di nota. JToggleButton è una sottoclasse di AbstractButton. Ha lo stesso aspetto di un JButton, ma può restare “premuto”. Aggiunge una proprietà selected (setSelected, isSelected) per indicare il suo stato (premuto o non premuto). JCheckBox è una sottoclasse di JToggleButton, con un aspetto grafico differente. Ing. Aime – Programmazione grafica in Java con la libreria Swing 16 JRadioButton è una ulteriore sottoclasse di JToggleButton. Ha un aspetto grafico differente, e può essere incluso in un ButtonGroup per creare un insieme di selezioni esclusive fra loro. Ad esempio: import javax.swing.*; import java.awt.*; import java.awt.event.*; public class RadioSample extends JPanel implements ActionListener { JRadioButton rbtUno, rbtDue, rbtTre; ButtonGroup group; JLabel label; public RadioSample() { // crea e configura i componenti rbtUno = new JRadioButton("Uno"); rbtUno.setSelected(true); rbtDue = new JRadioButton("Due"); rbtTre = new JRadioButton("Tre"); label = new JLabel("Seleziona un bottone"); // crea e configura il gruppo group = new ButtonGroup(); group.add(rbtUno); group.add(rbtDue); group.add(rbtTre); // aggiungi i componenti add(rbtUno); add(rbtDue); add(rbtTre); add(label); // gestione eventi rbtUno.addActionListener(this); rbtDue.addActionListener(this); rbtTre.addActionListener(this); } Ing. Aime – Programmazione grafica in Java con la libreria Swing Gestione di più sorgenti di evento con lo stesso listener 17 public void actionPerformed(ActionEvent evt) { JRadioButton src = (JRadioButton) evt.getSource(); System.out.println("Evento da " + src.getText()); if(src.isSelected()) label.setText("Selezionato: " + src.getText()); } public static void main(String[] args) { JFrame frame = new JFrame("Radio sample"); frame.setContentPane(new RadioSample()); frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } } JTextField e JTextArea Entrambi i componenti espongono la proprietà text che consente di leggere/impostare il testo contenuto. JTextField è pensato per testi brevi, disposti su una sola riga. JTextArea al contrario permette di scrivere testi ampi. Inoltre, è capace di effettuare il ritorno a capo automatico quando siano state attivate le proprietà wrapStyleWord e lineWrap. Poiché JTextArea può raggiungere dimensioni ragguardevoli, è opportuno aggiungere delle barre di scorrimento. Per tale scopo esiste JScrollPane, un contenitore dotato di barre di scorrimento pensato per contenere componenti troppi “ampi” per essere visualizzati per intero. Ing. Aime – Programmazione grafica in Java con la libreria Swing 18 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class TextSample extends JPanel implements ActionListener { JTextField testo; JTextArea area; JScrollPane scroll; JButton btnAggiungi; public TextSample() { // crea e configura i componenti testo = new JTextField("Campo di testo"); testo.setColumns(20); btnAggiungi = new JButton("Aggiungi testo"); area = new JTextArea("TextArea"); area.setLineWrap(true); area.setWrapStyleWord(true); scroll = new JScrollPane(area); scroll.setPreferredSize( new Dimension(300, 200)); // aggiungi i componenti add(testo); add(btnAggiungi); add(scroll); // gestione eventi btnAggiungi.addActionListener(this); } public void actionPerformed(ActionEvent evt) { area.setText(area.getText() + testo.getText()); } public static void main(String[] args) { ... } } Ing. Aime – Programmazione grafica in Java con la libreria Swing 19 GESTIONE DEL LAYOUT Ogni Container è dotato di un layout manager, che calcola la posizione dei componenti in base alle loro dimensioni minime/preferite/massime e in base ad eventuali vincoli aggiuntivi specificati quando si inseriscono i componenti nel container. Vedremo tre tipi di layout: • FlowLayout: è il layout di default dei JPanel, ovvero il layout manager visto sino ad ora; • BorderLayout: un semplice layout manager, utile in varie occasioni • GridBagLayout: il layout manager più flessibile, ma anche il più difficile da utilizzare, tanto complesso che faremo uso di una classe di supporto per semplificarne l’utilizzo. I constraint di norma sono specificati quando si aggiunge il componente al Container. Vi sono infatti più metodi per l’aggiunta, in overload, fra i quali troviamo: void add(Component comp); void add(Component comp, Object constraint); Ing. Aime – Programmazione grafica in Java con la libreria Swing 20 FlowLayout Un layout manager che dispone i componenti per riga, ciascuno dimensionato secondo la sua preferred size. Se il container non è sufficientemente largo, il layout manager va a capo. Ing. Aime – Programmazione grafica in Java con la libreria Swing 21 BorderLayout Il border layout prevede la presenza di un massimo di 5 componenti, disposti come in figura: Gli elementi sono disposti come i 4 segni cardinali, più il centro. L’elemento di centro si allarga sia in altezza che in larghezza, gli altri solo in larghezza (west e east) o solo in altezza (north e south). Ing. Aime – Programmazione grafica in Java con la libreria Swing 22 // aggiungi i componenti add(button1, BorderLayout.NORTH); add(button2, BorderLayout.CENTER); add(button3, BorderLayout.EAST); add(button4, BorderLayout.WEST); add(button5, BorderLayout.SOUTH); Nota: non è obbligatorio riempire tutte le posizioni. GridBagLayout con GridBuilder In questa sede verrà presentato un sottoinsieme delle possibilità del GridBagLayout. L’idea di base è quella di collocare i componenti nelle celle di una griglia “elastica”, ovvero capace di ridimensionarsi quando il contenitore viene ridimensionato. Ogni riga e colonna ha un peso. Un peso nullo significa che la riga o la colonna faranno 0 1 0 uso della dimensione 0 minima dei componenti in esse, e non cercheranno di allargarsi, un peso non nullo 0 al contrario consentirà alla colonna di allargarsi. Maggiore il peso, maggiore lo spazio occupato dalla riga 1 o dalla colonna. Ing. Aime – Programmazione grafica in Java con la libreria Swing 23 0 1 0 0 0 1 Ogni componente è posizionato in una cella, ma può eventualmente occupare più celle (grid size maggiore di uno). Nella figura sono mostrati 4 componenti, tre dei quali occupano più di una cella. 0 1 0 0 0 1 Ing. Aime – Programmazione grafica in Java con la libreria Swing 24 Ogni componente può sfruttare in modo diverso l’area della cella: può occupare soltanto la sua area preferenziale, estendersi in larghezza, in altezza, o in entrambe le direzioni: 0 1 0 1 1 Se il componente non sfrutta tutta l’area della cella, può essere posizionato in modo diverso al suo intero: NorthWest West SouthWest North Center South NorthEast East SouthEast Infine, un componente può avere una spaziatura rispetto al bordo della cella, detto inset (usato per evitare che i componenti siano troppo addossati l’uno all’altro): 0 1 0 1 Ing. Aime – Programmazione grafica in Java con la libreria Swing 25 Per creare un layout con il GridBagLayout occorre decidere: • quante righe e quante colonne sono necessarie; • quali pesi dare a righe e colonne; • quali posizioni e numero di celle saranno occupate dai componenti; • come i componenti si allargano e, se necessario, in che posizione devono essere collocati. GridBuilder facilita la codifica di queste operazioni. Nel costruttore vengono specificati il container, i pesi e l’inset: // 5 righe, con i rispettivi pesi, 2 colonne, // un inset pari a 3 pixel GridBuilder gb = new GridBuilder(container, new double[] { 0, 0, 0, 1, 0 }, new double[] { 0, 1}, 3); Il grid builder tiene traccia della riga e della colonna correnti, partendo da 0,0. Ogni volta che si aggiunge un componente la colonna viene incrementata, se si raggiunge la fine riga, si passa automaticamente alla riga successiva: non è quindi necessario specificare riga e colonna per i componenti, si continua semplicemente a chiamare la add per aggiungere nuovi componeti. gb.add(new JLabel(“Etichetta”)); gb.add(new JTextFiled(“Prova”)); gb.add(new Jlabel(“Etichetta2”)); Ing. Aime – Programmazione grafica in Java con la libreria Swing 26 gb.add(new JButton(“Bottone”)); Se è necessario saltare una cella si può chiamare skip(), se è necessario andare a capo prima della fine della riga, newLine(). E’ inoltre possibile leggere e impostare row e column con gli opportuni getter e setter. Il metodo add restituisce un oggetto GridConstraints, che consente di specificare i restanti parametri, ovvero dimensione, spazio utlizzato e posizionamento. public interface GridConstraints { public GridConstraints gridSize(int gridWidth, int gridHeight); public public public public public public public public public GridConstraints GridConstraints GridConstraints GridConstraints GridConstraints GridConstraints GridConstraints GridConstraints GridConstraints north(); northEast(); northWest(); east(); center(); west(); southEast(); south(); southWest(); public GridConstraints fillHorizontal(); public GridConstraints fillVertical(); public GridConstraints fillBoth(); } Ogni metodo restituisce un riferimento all’oggetto GridConstraints stesso, questo consente di effettuare più chiamate in cascata: gb.add(new JLabel(“Ciao”)). Ing. Aime – Programmazione grafica in Java con la libreria Swing 27 gridSize(2, 1).fillHorizontal().north(); Attenzione: specificare una gridSize > 0 non fa spostare il cursore del grid builder. Se una cella è stata coperta da un componente con larghezza > 0 bisogna saltarla chiamando skip() Ing. Aime – Programmazione grafica in Java con la libreria Swing 28 Esempio Si costruisca una finestra come la seguente: Per prima cosa, individuiamo righe, colonne e pesi: 0 1 0 0 0 1 0 Ing. Aime – Programmazione grafica in Java con la libreria Swing 29 Questo individua anche la posizione degli elementi. Notiamo però che l’etichetta Modulo di registrazione e il bottone Ok sono centrati rispetto alla finestra devono occupare una intera riga per essere posizionati al centro. Pensiamo all’occupazione di spazio: l’etichetta del nome sembra occupare tutto lo spazio orizzontale, la text area del commento, anche quello verticale. Infine, il posizionamento. Le etichette Nome e Età e il testo per l’età sono a West, Commento è a NorthWest i restanti sono al centro. Il codice di costruzione dentro un JPanel allora è: GridBuilder gb = new GridBuilder(this, new double[] { 0, 0, 0, 1, 0 }, new double[] { 0, 1}, 3); gb.add(new JLabel("Modulo di registrazione")).gridSize(2, 1); gb.newLine(); // attenzione! gb.add(new JLabel("Nome")).west(); gb.add(new JTextField()).fillHorizontal(); gb.add(new JLabel("Età")).west(); gb.add(new JTextField(3)).west(); gb.add(new JLabel("Commento")).northWest(); JscrollPane scroll = new JScrollPane( new JTextArea()); gb.add(scroll).fillBoth(); gb.add(new JButton("Ok")).gridSize(2, 1); Ing. Aime – Programmazione grafica in Java con la libreria Swing 30 Reazione del pannello ai ridimensionamenti: Ing. Aime – Programmazione grafica in Java con la libreria Swing 31 LAYOUT E COMPOSIZIONE DI PANNELLI Ogni pannello è dotato di un proprio layout manager. Se non si riesce ad ottenere il risultato voluto con un solo layout manager, si possono comporre fra loro i pannelli e usare un layout manager separato per ciascuno. Ad esempio: In questa finestra sono presenti tre pannelli: • il panello principale, con un BorderLayout, completamente ricoperto dagli altri; • il pannello dati, posto al centro del pannello principale, dotato di un GridBagLayout • il pannello con i bottoni, a sud del panello principale, con un flow layout allineato a destra. dataPanel (GridBagLayout) buttonPanel (FlowLayout, destra) Principale (BorderLayout) Ing. Aime – Programmazione grafica in Java con la libreria Swing 32 class MultiLayout extends JPanel { JTextField txfName; JTextField txfSurname; JTextField txfAge; JButton btnOk; JButton btnCancel; public MultiLayout() { JPanel dataPanel = new JPanel(); txfName = new JTextField(); txfName.setColumns(20); txfSurname = new JTextField(); txfSurname.setColumns(20); txfAge = new JTextField(); txfAge.setColumns(3); GridBuilder builder = new GridBuilder(dataPanel, new double[] {0,0,0, 1}, new double[] {0,1}, 3); builder.add(new JLabel("Nome")).west(); builder.add(txfName).fillHorizontal(); builder.add(new JLabel("Cognome")).west(); builder.add(txfSurname).fillHorizontal(); builder.add(new JLabel("Età")).west(); builder.add(txfAge).west(); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout( new FlowLayout(FlowLayout.RIGHT)); btnOk = new JButton("Ok"); btnCancel = new JButton("Cancel"); buttonPanel.add(btnOk); buttonPanel.add(btnCancel); setLayout(new BorderLayout()); add(dataPanel, BorderLayout.CENTER); add(buttonPanel, BorderLayout.SOUTH); } } Ing. Aime – Programmazione grafica in Java con la libreria Swing 33 DISEGNO LIBERO CON SWING Ogni classe di Swing si disegna appropriatamente usando il metodo paintComponent. E’ possibile creare una propria classe che ridefinisce (override) il metodo paintComponent per disegnare liberamente con l’oggetto Graphics. I metodi per disegnare figure aperte e testo sono: drawLine(), drawRect(), drawRoundRect(), draw3DRect(), drawOval(), drawArc(), drawString(), drawPolygon(), drawPolyLine() I metodi per disegnare figure piene sono: fillRect(), fillRoundRect(), fill3DRect(), fillOval(), fillArc(), fillPolygon(), fillPolyLine() Infine, i seguenti metodi impostano il colore e il font (ovvero, il tipo di carattere usato): getColor(), getFont(), setColor(), setFont(). I colori vengono specificati con gli oggetti Color. Si può istanziare un oggetto Color specificando le componenti RGB o usare le costanti simboliche definite in Color, come Color.BLACK, Color.RED, Color.GREEN, … I font possono essere creati specificando il nome di un carattere (potete usare “Arial”, “Times New Roman”, “Courier”), lo stile (Font.BOLD, Font.ITALIC o metterli insieme con Font.BOLD | Font.ITALIC), e la dimensione. Ing. Aime – Programmazione grafica in Java con la libreria Swing 34 Tutti i metodi di disegno accettano delle coordinate: sono coordinate in pixel rispetto all’origine. import javax.swing.*; import java.awt.*; import java.awt.event.*; class Grafica1 extends JPanel { public void paintComponent(Graphics g) { g.setColor(Color.RED); g.fillOval(80, 10, 40, 20); g.setColor(Color.GREEN); g.fillRoundRect(80, 160, 40, 40, 10, 5); g.setColor(Color.BLUE); g.drawArc(20, 20, 160, 160, 90, 270); g.drawLine(20, 100, 100, 100); g.drawLine(100, 20, 100, 180); g.drawRect(10, 90, 20, 20); g.setColor(Color.BLACK); g.setFont(new Font("Arial", Font.BOLD, 12)); g.drawString("Testo libero!", 140, 80); } public static void main(String[] args) { ... } } Ing. Aime – Programmazione grafica in Java con la libreria Swing 35 FINESTRE DI DIALOGO Una finestra di dialogo rappresenta una finestra secondaria, usata di norma per confermare inserimenti, aggiungere dettagli, eccetera (si pensi alle finestre di dialogo formato/carattere di Word, o alla finestra Configure/Options di JCreator). Di norma queste finestre hanno un padre e sono modali. Una finestra è modale rispetto al padre quando impedisce qualunque interazione con il padre fino a che non viene chiusa. Di fatto, una volta chiamato setVisible(true) il codice del padre resta congelato. I costruttori possono avere le seguenti forme: JDialog(Frame parent, String title, boolean modal); JDialog(Dialog parent, String title, boolean modal); Dato un componente comp si può ottenere il Frame che lo contiene con: Frame padre = OptionPane.getFrameForComponent(comp); Al di la di questa differenza, una JDialog si comporta esattamente come un JFrame: è una finestra, ha un content pane, un metodo pack, e tutti gli altri metodi ereditati da Component. Ing. Aime – Programmazione grafica in Java con la libreria Swing 36 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class DialogSample extends JPanel implements ActionListener { JCheckBox check; public DialogSample() { check = new JCheckBox("Prova la selezione"); JButton btnSeleziona = new jButton("Seleziona"); add(btnSeleziona); add(check); btnSeleziona.addActionListener(this); } public void actionPerformed(ActionEvent event) { SelectionDialog dialog = new SelectionDialog(this); System.out.println("Il codice si blocca e “ + “ aspetta che la dialog si chiuda"); dialog.setVisible(true); System.out.println("Il codice prosegue"); if(dialog.risposta) { check.setSelected(true); check.setText("Hai risposto si"); } else { check.setSelected(false); check.setText("Hai risposto no"); } } public static void main(String[] args) { JFrame frame = new JFrame("Due finestre"); frame.setContentPane(new DialogSample()); frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } } Ing. Aime – Programmazione grafica in Java con la libreria Swing 37 class SelectionDialog extends JDialog implements ActionListener { boolean risposta = false; JButton btnSi; JButton btnNo; public SelectionDialog(Component parent) { super(JOptionPane.getFrameForComponent(parent), "Selezione", true); JPanel buttons = new JPanel(); JLabel label = new JLabel("Scegli una risposta"); btnSi = new JButton("Si"); btnNo = new JButton("No"); buttons.add(label); buttons.add(btnSi); buttons.add(btnNo); setContentPane(buttons); pack(); btnSi.addActionListener(this); btnNo.addActionListener(this); } public void actionPerformed(ActionEvent event) { risposta = event.getSource() == btnSi; // chiudi la finestra e rilascia le risorse setVisible(false); dispose(); } Ing. Aime – Programmazione grafica in Java con la libreria Swing 38