RELAZIONE 5 : JAVA E LA GRAFICA Lo scopo di questa esercitazione è quello di realizzare applicazioni grafiche in Java. Java permette di rendere la grafica “interattiva”, cioè in grado di compiere diverse azioni a seconda degli ordini impartiti di volta in volta dall’utente. Java dispone di una libreria ( Swing ), che definisce una gerarchia di classi che forniscono ogni tipo di componente grafico. Di conseguenza, per creare una grafica personalizzata, spesso basta estendere classi già esistenti. Descrizione del problema Si chiede di: - implementare un frame ( una finestra ), partendo dalla classe JFrame di Swing, centrato nello schermo e di grandezza 400x400 pixel; - gestire la chiusura della finestra mediante un pulsante di Quit; - porre al centro della finestra un puntatore a crocetta; - il puntatore si deve poter muovere all’interno di una “griglia”; - il puntatore deve essere sensibile al click del mouse e controllabile tramite quattro pulsanti di movimento ( Up, Down, Left, Right ); - la grandezza dello spostamento dato dai pulsanti di movimento deve essere controllata mediante un campo di testo; - evitare di far uscire la crocetta dai margini della griglia. Analisi Questa esercitazione ci consentirà di familiarizzare con una programmazione “event-driven”, in cui il comportamento dei componenti creati è condizionato dalle azioni dell’utente sui componenti stessi. Inoltre ci permetterà di prendere confidenza con una classe predefinita, e di imparare a sfruttare a pieno le potenzialità del linguaggio, senza dover partire da zero nella creazione dei componenti grafici. Java ci offre infatti una sorta di “percorso guidato” che è conveniente sfruttare. Progetto: come risolvere il problema Per creare un’interfaccia grafica completa ed efficace non basta un singolo gestore di Layout, conviene suddividere l’area da trattare in zone, corrispondenti ad altrettanti pannelli, e applicare a ogni zona il Layout manager più opportuno. Zone: - Center : che conterrà la griglia con al centro il cursore. Questa griglia rappresenta lo spazio limite di movimento del cursore. - South : che è a sua volta diviso in due parti: o la prima contiene il pulsante di Quit, che fa chiudere la finestra ; o la seconda contiene i quattro pulsanti che comandano il movimento del cursore. - Noth: che conterrà una casella di testo per contenere le istruzioni per la grandezza degli spostamenti del cursore. Il risultato sarà più o meno questo: GRIGLIA QUIT Tutti i pannelli usati si appoggiano su un pannello principale che li contiene tutti e che fa da ascoltatore (nel caso di pannelli con ascoltatore esterno). In modo semplicistico si può pensare ad un pannello “padre”, che tiene d’occhio e accoglie tutti i pannelli figli. Descrizione delle classi e dei metodi usati MyFrame: classe che estende il componente predefinito JFrame (che crea una finestra ): il metodo Toolkit.getDefautToolkit().getScreenSize mi permette di ottenere le dimensioni dello schermo, il metodo setBound mi permette di fissare posizione e dimensioni del Frame. Contiene un costruttore che accetta come parametro in entrata un Layout Manager. MyPanel1: classe che crea un pannello personalizzato che estende quello di deauft (JPanel). Dentro al pannello viene posto il bottone Quit ( mediante il metodo add ) e il pannello stesso è designato ad ascoltatore del bottone. Per poter fungere da ascoltatore, MyPanel1 deve implementare l’interfaccia ActionListener e usare il metodo addActionListener (this). Contiene un costruttore che accetta come parametro in entrata un Layout Manager. MyPanel2: classe che crea un pannello personalizzato che estende quello di deauft (JPanel). Crea quattro pulsanti (JButton u=new JButton (“Up”)) per il controllo dei movimenti del cursore ( Up, Down, Left, Right ). L’ascoltatore è esterno al pannello e definito con exList=externalListener. Contiene un costruttore che accetta come parametro in entrata un Layout Manager. FielPanel: crea il pannello che contiene una piccola “finestra di scrittura”, che conterrà il valore del passo degli spostamenti del cursore. Con un ciclo try-catch si controlla siano scritti solo numeri. Contiene un costruttore che accetta come parametro in entrata un Layout Manager. MyCenterPanel: è il pannello centrale, deve gestire una griglia che rappresenta lo spazio di movimento del cursore. Inizialmente il cursore a crocetta deve essere disegnato nel centro. MyCenterPanel necessita di due ascoltatori esterni ( il costruttore public MyCenterPanel accetta in entrata due parametri), uno per reagire agli spostamenti del mouse, l’altro per controllare che il cursore non superi i limiti imposti. Vengono implementati i metodi per settare e leggere la posizione del cursore ( setPosition, GetPosition ) e per disegnare il cursore (paintComponent). DadPanel: è il pannello padre, su cui si appoggiano gli altri pannelli. Al suo interno i pannelli vengono richiamati con il costruttore che accetta come parametro di entrata il Layout opportuno. Il pannello padre fa da ascoltatore quando: o vengono usati i bottoni di movimento ( public void actionPerformed ); o viene usato il mouse ( public void mouseClick ). Terminator : classe che implementa l’interfaccia WindowListener, in modo che WindowClosing invochi System.exit(). PersonalFrame: è il componente Software che mi permette di testare i componenti creati. Implementazione MyFrame import java.awt.*; import javax.swing.*; public class MyFrame extends JFrame { public MyFrame (int x, int y) { super(); Dimension d =Toolkit.getDefaultToolkit().getScreenSize(); int xc=d.height/2; int yc=d.width/2; /* Setto le dimensioni del frame*/ setBounds(xc-x/2,yc-y/2,x,y); } } /* *Il frame che devo usare non ha titolo */ MyPanel1 import java.awt.*; import javax.swing.*; import java.awt.event.*; public class MyPanel1 extends JPanel implements ActionListener { /*costruttore */ public MyPanel1() { super(); JButton b=new JButton("Quit"); b.addActionListener(this); add(b); } public void actionPerformed (ActionEvent e) { System.exit(0); } /* aggiungo un costruttore che accetti in entrata un parametro * per la gestione del LayoutManager */ public MyPanel1(LayoutManager lm) { super(lm); JButton b=new JButton("Quit"); b.addActionListener(this); add(b); } } MyPanel2 import java.awt.*; import javax.swing.*; import java.awt.event.*; public class MyPanel2 extends JPanel { ActionListener exList; /*costruttore*/ public MyPanel2(ActionListener externalActionListener) { super(); /*ascoltatore esterno*/ exList= externalActionListener; /* creazione bottoni aggiunta dei bottoni al pannelo*/ JButton u=new JButton("Up"); add(u); JButton d=new JButton("Down"); add(d); JButton l=new JButton("Left"); add(l); JButton r=new JButton("Right"); add(r); /* associo ai bottoni creati l'ascoltatore esterno*/ u.addActionListener(exList); d.addActionListener(exList); l.addActionListener(exList); r.addActionListener(exList); } /* definisco un altro costruttore che accetta come parametro in entrata * un LayoutManager * per il resto è uguale al costruttore sopra */ public MyPanel2(ActionListener externalActionListener,LayoutManager lm) { super(lm); exList= externalActionListener; JButton u=new JButton("Up"); add(u); JButton d=new JButton("Down"); add(d); JButton l=new JButton("Left"); add(l); JButton r=new JButton("Right"); add(r); u.addActionListener(exList); d.addActionListener(exList); l.addActionListener(exList); r.addActionListener(exList); } } FieldPanel import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.event.*; public class FieldPanel extends JPanel implements ActionListener { JTextField tf; int max; int num; public FieldPanel () { super(); tf=new JTextField(5); tf.addActionListener(this); add(tf); max=100; num=0; } /* aggiungo un costruttore con un parametro per la gestione del Layout */ public FieldPanel (LayoutManager lm) { super(lm); tf=new JTextField(5); tf.addActionListener(this); add(tf); max=100; num=0; } /*Controllo errori*/ public void actionPerformed(ActionEvent ae) { int n; try { n=Integer.parseInt(tf.getText()); if (n>max) { tf.setText(" "); tf.setText(" "+num); repaint(); } else { num=n; } } catch (NumberFormatException nfe) { tf.setText(" "); tf.setText(" "+num); repaint(); } } /*Lettura del valore del field*/ public int getValue() { return num; } } MyCenterPanel import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.event.*; public class MyCenterPanel extends JPanel { MouseListener mouse; int px, py, ox, oy, rx, ry, sx, sy; public MyCenterPanel (MouseListener externalMouseListener,Rectangle rectpad) { super(); setBackground(Color.white); /* ascoltatore per il muose*/ mouse=externalMouseListener; addMouseListener(mouse); ox=px; oy=py; rx=rectpad.x; ry=rectpad.y; sx=rectpad.width; sy=rectpad.height; px=rx+sx/2; py=ry+sy/2; } public void paintComponent(Graphics g) { super.paintComponent(g); /* disegno griglia*/ g.setColor(Color.lightGray); g.drawRect(rx,ry,sx,sy); /*disegno cursore a crocetta*/ g.setColor(Color.black); g.drawLine(px-5,py,px+5,py); g.drawLine(px,py-5,px,py+5); } /*Setto la posizione del cursore*/ public void setPosition(int xPosition, int yPosition) { if ((xPosition>rx)&&(yPosition>ry)&&(xPosition<rx+sx)&&(yPosition<ry+sy)) { px=xPosition; py=yPosition; repaint(); } } /*Leggo la posizione del cursore*/ public int getPositionX(){ return px; } public int getPositionY(){ return py; } } DadPanel import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.event.*; public class DadPanel extends JPanel implements ActionListener, MouseListener { /*variabili private */ MyPanel1 mPanel; MyPanel2 mdPanel; MyCenterPanel mcPanel; FieldPanel fPanel; int step; public DadPanel() { super(); setLayout(new BorderLayout()); JPanel southPanel=new JPanel(); JPanel northPanel=new JPanel(); southPanel.setLayout(new BorderLayout()); northPanel.setLayout(new BorderLayout()); FlowLayout fLayout=new FlowLayout(); fLayout.setAlignment(FlowLayout.LEFT); /*South*/ mdPanel=new MyPanel2(this,fLayout); southPanel.add(mdPanel,BorderLayout.WEST); fLayout.setAlignment(FlowLayout.RIGHT); mPanel=new MyPanel1(fLayout); southPanel.add(mPanel,BorderLayout.EAST); add(southPanel,BorderLayout.SOUTH); /*North*/ fLayout.setAlignment(FlowLayout.RIGHT); fPanel=new FieldPanel(fLayout); northPanel.add(fPanel,BorderLayout.EAST); add(northPanel,BorderLayout.NORTH); /*Center*/ Rectangle CenterDimension=new Rectangle(10,10,370,280); mcPanel=new MyCenterPanel(this,CenterDimension); add(mcPanel,BorderLayout.CENTER); /*Setto il passo degli spostamenti*/ step=fPanel.getValue(); } /*Ascoltare per i bottoni di movimento*/ public void actionPerformed(ActionEvent ae) { step=fPanel.getValue(); String argButton=ae.getActionCommand(); if ("Up".equals(argButton)){ mcPanel.setPosition(mcPanel.getPositionX(),mcPanel.getPositionY()-step); } if ("Down".equals(argButton)){ mcPanel.setPosition(mcPanel.getPositionX(),mcPanel.getPositionY()+step); } if ("Left".equals(argButton)){ mcPanel.setPosition(mcPanel.getPositionX()-step,mcPanel.getPositionY()); } if ("Right".equals(argButton)){ mcPanel.setPosition(mcPanel.getPositionX()+step,mcPanel.getPositionY()); } } /*Ascoltatore per il muose*/ public void mouseClicked(MouseEvent me) { mcPanel.setPosition(me.getX(),me.getY()); } public void mouseEntered(MouseEvent me) {} public void mouseExited(MouseEvent me) {} public void mousePressed(MouseEvent me) {} public void mouseReleased(MouseEvent me) {} } Terminator import java.awt.*; import javax.swing.*; import java.awt.event.*; public class Terminator implements WindowListener { /* devo implementare formalmente tutti i metodi dell'intefaccia * anche se non tutti mi servono realmente */ public void windowClosed(WindowEvent e) {} public void windowClosing(WindowEvent e) { System.exit(0); } public void windowOpened(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowActivated (WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} } PersonalFrame /* Componente SW*/ import java.awt.*; import javax.swing.*; import java.awt.event.*; import javax.swing.event.*; public class PersonalFrame { public static void main (String[] args) { DadPanel dPanel = new DadPanel(); MyFrame mFrame = new MyFrame(400,400); mFrame.addWindowListener(new Terminator()); Container mFrameContainer=mFrame.getContentPane(); mFrameContainer.add(dPanel); mFrame.show(); } } Concetti appresi Con questa esercitazione ho imparato a creare e gestire applicazioni grafiche, sfruttando un insieme di classi che Java mette a disposizione. Ancora una volta va sottolineata l’importanza delle astrazioni e delle gerarchie che permettono di sfruttare al meglio le potenzialità del linguaggio. Per quanto riguarda gli elementi di grafica, ho lavorato con gli elementi base (finestre, pannelli, bottoni …) e ho imparato a gestirne gli ascoltatori. Già con questi pochi elementi si può creare una grafica piacevole e interattiva.