Mòdul 6

Pràctica 7: el ratolí
Tornar presentació tema
Pràctica 2 Pràctica 2 Pràctica 2 Pràctica 2 Pràctica 2 Pràctica 2 Pràctica 1  
     
 

 

 
  El ratolí  
     
 
 
     
 

Molts anys abans que els ordinadors personals fossin, no només comuns, sinó tan sols pensables, Douglas Engelbart ja havia inventat uns quants dispositius d'accés als sistemes informàtics, entre els quals l'ara ben comú ratolí (mouse). Engelbart va presentar-ne un primer prototipus a la Conferència de Tardor de 1968 a San Francisco amb el títol de "X-Y Position Indicator for a Display System". Després, la incorporació de les interfícies gràfiques d'usuari (GUI) als sistemes Apple Macintosh va popularitzar el dispositiu i, ben aviat, aparegueren noves GUI als sistemes MS-DOS (Windows 3.x), UNIX (X-Windows) i OS/2, per les quals, l'ús del ratolí era ja imprescindible.

 
     
  Java tracta les accions que fa l'usuari amb el ratolí sobre components gràfics com a esdeveniments. Alguns controls, els botons per exemple, ja incorporen un mecanisme d'emisió d'esdeveniments adequat a la seva funció, tal com ja hauràs vist a les pràctiques anteriors d'aquest mòdul. Ara, però, es tracta d'aprendre a controlar els esdeveniments de ratolí que no estan lligats a cap funcionalitat concreta del control sobre el qual es produeixen:  
     
  Esdeveniments de ratolí  
     
  Java contempla les següents accions:  
     
 
  • Clic sobre un punt ("mouse clicked"): l'usuari prem i deixa anar un botó del ratolí quan el cursor és sobre un punt d'un cert component.

  • Entrada en un component ("mouse entered"): el cursor del ratolí entra en un component.

  • Sortida d'un component ("mouse exited"): el cursor surt d'un component.

  • Pressionar sobre un punt ("mouse pressed"): l'usuari prem un botó del ratolí quan el cursor és sobre un punt d'un cert component.

  • Deixar anar sobre un punt ("mouse released"): l'usuari deixa anar un botó del ratolí (que mantenia apretat) quan el cursor és sobre un punt d'un cert component.

  • Arrossegar ("mouse dragged"): l'usuari, amb un botó del mouse apretat, mou el cursor sobre un component.

  • Moure ("mouse moved"): l'usuari, sense cap botó del mouse apretat, mou el cursor sobre un component.
 
     
  Per a cadascuna d'aquestes accions, el component sobre les quals s'han produït emet un esdeveniment (objecte!) java.awt.event.MouseEvent que ha de ser escoltat pels listeners corresponents. Cada objecte MouseEvent conté informació important, que es pot recuperar mitjançant els mètodes corresponents:  
     
 
  • public Object getSource() (de la classe java.util.EventObject, de la qual deriva la classe java.awt.event.MouseEvent) que retorna l'objecte (Atenció: Object. Segur que caldrà fer càsting per identificar-lo!) sobre el qual s'ha produït l'esdeveniment.

  • public int getX() (de la pròpia classe MouseEvent) que retorna la coordenada x del lloc del component on s'ha produït l'esdeveniment.

  • public int getY() que fa el mateix que l'anterior, però per a la coordenada y.

  • public long getWhen() (de la classe java.awt.event.InputEvent, de la qual deriva la classe MouseEvent) que retorna el temps del sistema (milisegons) en el qual s'ha produït l'esdeveniment.
 
     
  Aquestes informacions i, en general, tot el que es refereix a esdeveniments de ratolí (MouseEvents) són completament independents de l'aspecte concret que tingui el cursor del ratolí a cada moment. Com canviar l'aspecte del cursor ja ha estat estudiat a la pràctica 1 d'aquest mateix mòdul.  
     
  Hi ha dues interfícies (interfaces) per escoltar esdeveniments MouseEvent:  
     
 
  • La interfície java.awt.event.MouseListener, amb els mètodes

    • public void mouseClicked(MouseEvent e), que s'executa quan l'usuari fa l'acció "Click sobre un punt",

    • public void mousePressed(MouseEvent e), que s'executa quan hi ha l'acció "Pressionar sobre un punt",

    • public void mouseReleased(MouseEvent e), que s'executa quan hi ha l'acció "Deixar anar sobre un punt",

    • public void mouseEntered(MouseEvent e), que s'executa quan hi ha l'acció
      "Entrada en un component",

    • i public void mouseExited(MouseEvent e), que s'executa quan hi ha l'acció "Sortida d'un component",

  • i la interfície java.awt.event.MouseMotionListener, amb els mètodes

    • public void mouseDragged(MouseEvent e), que s'executa quan hi ha l'acció d'"Arrossegar",

    • public void mouseMoved(MouseEvent e), que s'executa quan hi ha l'acció de "Moure"
 
     
  Per tal que un component tingui listeners que escoltin els esdeveniments MouseEvent que pugui emetre, els mètodes corresponents (de la classe java.awt.Component) són  
     
 
  • public void addMouseListener(MouseListener mL), i

  • public void addMouseMotionListener(MouseMotionListener mML)
 
     
Ara ja pots experimentar. Crea el projecte ratoli i la classe Ratoli, de moment, amb aquest codi:  
     
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Color;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

/**
 * Escriviu aquí una descripcìó de la classe Ratoli
 *
 * @author (el vostre nom)
 * @version (un número de versió o la data)
 */

public class Ratoli extends JFrame implements MouseListener,
                                              MouseMotionListener {

    /**
     * Mètode constructor per objectes de la classe Ratoli.
     */
    public Ratoli () { // constructor
        setTitle("Proves amb el ratolí");
        Container cnt=getContentPane();

        JPanelAmbNom panelNord=new JPanelAmbNom("panel Nord");
        panelNord.setPreferredSize(new Dimension(300,50));
        panelNord.setBackground(Color.BLUE);
        panelNord.addMouseListener(this);
        panelNord.addMouseMotionListener(this);
        cnt.add(panelNord,BorderLayout.NORTH);

        JPanelAmbNom panelSud=new JPanelAmbNom("panel Sud");
        panelSud.setPreferredSize(new Dimension(300,50));
        panelSud.setBackground(Color.RED);
        panelSud.addMouseListener(this);
        panelSud.addMouseMotionListener(this);
        cnt.add(panelSud,BorderLayout.SOUTH);

        JPanelAmbNom panelEst=new JPanelAmbNom("panel Est");
        panelEst.setPreferredSize(new Dimension(50,300));
        panelEst.setBackground(Color.YELLOW);
        panelEst.addMouseListener(this);
        panelEst.addMouseMotionListener(this);
        cnt.add(panelEst,BorderLayout.EAST);

        JPanelAmbNom panelOest=new JPanelAmbNom("panel Oest");
        panelOest.setPreferredSize(new Dimension(50,300));
        panelOest.setBackground(Color.GREEN);
        panelOest.addMouseListener(this);
        panelOest.addMouseMotionListener(this);
        cnt.add(panelOest,BorderLayout.WEST);

        JPanelAmbNom panelCentre=new JPanelAmbNom("panel Centre");
        panelCentre.setBackground(Color.WHITE);
        panelCentre.addMouseListener(this);
        panelCentre.addMouseMotionListener(this);
        cnt.add(panelCentre,BorderLayout.CENTER);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan l'acció és "Click sobre un punt"
     * @param e un MouseEvent
     */
    public void mouseClicked (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Entrada en un
     * component"
     * @param e un MouseEvent
     */
    public void mouseEntered (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Sortida d'un
     * component"
     * @param e un MouseEvent
     */
    public void mouseExited (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Pressionar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mousePressed (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Deixar anar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mouseReleased (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Arrossegar"
     * @param e un MouseEvent
     */
    public void mouseDragged (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Moure"
     * @param e un MouseEvent
     */
    public void mouseMoved (MouseEvent e) {
    }

}

/**
 * La classe JPanelAmbNom no és més que la classe JPanel a la
 * qual se li ha afegit la variable nom, per tal de poder iden-
 * tificar cadascun dels panels.
 */
class JPanelAmbNom extends JPanel {

    /**
     * El nom que identifica cada objecte JPanelAmbNom
     */

    String nom;

    /**
     * Mètode constructor per a objectes de la classe JPanelAmbNom.
     * @param nom el nom identificador d'aquest JPanelAmbNom
     */

    JPanelAmbNom (String nom) { // constructor
        super();
        this.nom=nom;
    }

}
 
     
  Com pots veure, es tracta d'una finestra amb cinc objectes JPanel, cadascun a una de les posicions que permet el layout BorderLayout. En realitat, aquest objectes són de la classe JPanelAmbNom, filla de la classe JPanel, a la qual se li ha incorporat el camp String nom, per tal que els poguem identificar després. El codi per a aquesta classe és al mateix fitxer Ratoli.java, al final, després del codi per a la classe Ratoli.  
     
 
     
  Algunes observacions sobre el codi:  
     
 
  • Com que aquests objectes JPanelAmbNom no contenen cap component, cal establir-ne les dimensions d'avançada amb el mètode de la classe JComponent (mare de JPanel)

    public void setPreferredSize(Dimension preferredSize)

    Si no, swing els faria de dimensió 0 x 0!

  • Per tal que cadascun d'aquests objectes JPanelAmbNom presenti un color diferent, cal cridar el mètode, també de la classe JComponent,

    public void setBackground(Color colorFons)

    que determina el color del fons d'aquests components.

  • Cal establir qui ha d'escoltar els esdeveniments de ratolí que es produeïxin als objectes JPanelAmbNom. Els mètodes (de la classe Component) són:

    public void addMouseListener(MouseListener mL)

    i

    public void addMouseMotionListener(MouseMotionListener mML)

    com ja ha estat comentat abans.

  • El listener d'esdeveniments, en els dos casos, és l'objecte Ratoli, al qual se li han implementat les dues interfícies: MouseListener i MouseMotionListener.

  • I, com sempre que s'implementen interfícies a una classe, cal implementar tots els mètodes d'aquestes interfícies, encara que els deixem buits de codi, com ara mateix estan.
 
     
  Naturalment, com que els mètodes de les interfícies estan buits de codi, no fan res i sembla que cap dels objectes JPanelAmbNom sigui sensible a les accions del ratolí. És ara el moment d'omplir aquests mètodes:  
     
<codi anterior>

    /**
     * Mètode que s'executa quan l'acció és "Click sobre un punt"
     * @param e un MouseEvent
     */
    public void mouseClicked (MouseEvent e) {

        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanelAmbNom?
            if (font instanceof JPanelAmbNom) {
                // Càsting per tal de poder-lo tractar com a
                // objecte JPanelAmbNom
                JPanelAmbNom panel=(JPanelAmbNom)font;
                // Quin és el seu nom identificador?
                String nom=panel.nom;
                // En quines coordenades s'ha produït l'esdeveni-
                // ment?
                int x=e.getX();
                int y=e.getY();
                // Imprimeix a la sortida standard del sistema
                // un missatge que expliqui tot això...
                System.out.println("Esdeveniment: "+
                                   "Clic sobre el "+
                                   nom+" a la posició "+
                                   x+", "+y);
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Entrada en un
     * component"
     * @param e un MouseEvent
     */
    public void mouseEntered (MouseEvent e) {

        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanelAmbNom?
            if (font instanceof JPanelAmbNom) {
                // Càsting per tal de poder-lo tractar com a
                // objecte JPanelAmbNom
                JPanelAmbNom panel=(JPanelAmbNom)font;
                // Quin és el seu nom identificador?
                String nom=panel.nom;
                // En quines coordenades s'ha produït l'esdeveni-
                // ment?
                int x=e.getX();
                int y=e.getY();
                // Imprimeix a la sortida standard del sistema
                // un missatge que expliqui tot això...
                System.out.println("Esdeveniment: "+
                                   "El ratolí ha entrat al "+
                                   nom+" a la posició "+
                                   x+", "+y);
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Sortida d'un
     * component"
     * @param e un MouseEvent
     */
    public void mouseExited (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanelAmbNom?
            if (font instanceof JPanelAmbNom) {
                // Càsting per tal de poder-lo tractar com a
                // objecte JPanelAmbNom
                JPanelAmbNom panel=(JPanelAmbNom)font;
                // Quin és el seu nom identificador?
                String nom=panel.nom;
                // En quines coordenades s'ha produït l'esdeveni-
                // ment?
                int x=e.getX();
                int y=e.getY();
                // Imprimeix a la sortida standard del sistema
                // un missatge que expliqui tot això...
                System.out.println("Esdeveniment: "+
                                   "El ratolí ha sortit del "+
                                   nom+" a la posició "+
                                   x+", "+y);
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Pressionar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mousePressed (MouseEvent e) {

        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanelAmbNom?
            if (font instanceof JPanelAmbNom) {
                // Càsting per tal de poder-lo tractar com a
                // objecte JPanelAmbNom
                JPanelAmbNom panel=(JPanelAmbNom)font;
                // Quin és el seu nom identificador?
                String nom=panel.nom;
                // En quines coordenades s'ha produït l'esdeveni-
                // ment?
                int x=e.getX();
                int y=e.getY();
                // Imprimeix a la sortida standard del sistema
                // un missatge que expliqui tot això...
                System.out.println("Esdeveniment: "+
                                   "Botó apretat sobre el "+
                                   nom+" a la posició "+
                                   x+", "+y);
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Deixar anar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mouseReleased (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanelAmbNom?
            if (font instanceof JPanelAmbNom) {
                // Càsting per tal de poder-lo tractar com a
                // objecte JPanelAmbNom
                JPanelAmbNom panel=(JPanelAmbNom)font;
                // Quin és el seu nom identificador?
                String nom=panel.nom;
                // En quines coordenades s'ha produït l'esdeveni-
                // ment?
                int x=e.getX();
                int y=e.getY();
                // Imprimeix a la sortida standard del sistema
                // un missatge que expliqui tot això...
                System.out.println("Esdeveniment: "+
                                   "Botó deixat anar sobre el "+
                                   nom+" a la posició "+
                                   x+", "+y);
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Arrossegar"
     * @param e un MouseEvent
     */
    public void mouseDragged (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanelAmbNom?
            if (font instanceof JPanelAmbNom) {
                // Càsting per tal de poder-lo tractar com a
                // objecte JPanelAmbNom
                JPanelAmbNom panel=(JPanelAmbNom)font;
                // Quin és el seu nom identificador?
                String nom=panel.nom;
                // En quines coordenades s'ha produït l'esdeveni-
                // ment?
                int x=e.getX();
                int y=e.getY();
                // Imprimeix a la sortida standard del sistema
                // un missatge que expliqui tot això...
                System.out.println("Esdeveniment: "+
                                   "Arrossegament sobre el "+
                                   nom+" a la posició "+
                                   x+", "+y);
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Moure"
     * @param e un MouseEvent
     */
    public void mouseMoved (MouseEvent e) {

        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanelAmbNom?
            if (font instanceof JPanelAmbNom) {
                // Càsting per tal de poder-lo tractar com a
                // objecte JPanelAmbNom
                JPanelAmbNom panel=(JPanelAmbNom)font;
                // Quin és el seu nom identificador?
                String nom=panel.nom;
                // En quines coordenades s'ha produït l'esdeveni-
                // ment?
                int x=e.getX();
                int y=e.getY();
                // Imprimeix a la sortida standard del sistema
                // un missatge que expliqui tot això...
                System.out.println("Esdeveniment: "+
                                   "Moviment sobre el "+
                                   nom+" a la posició "+
                                   x+", "+y);
            }
    }

<codi següent>

 
     
Ara ja pots jugar amb el ratolí: fes clics sobre els panels, arrossega, mou, entra, surt... T'apareixerà, a la finestra de terminal, un rapport detallat a temps real de tot allò que facis:  
     
 
 
     
  Dibuixar amb el ratolí: rectangles    
     
  Dominar els esdeveniments del ratolí obre un munt de possibilitats, de les quals et convidem a explorar-ne una: la de dibuixar sobre un objecte JPanel:  
     
Muntaràs la classe Rectangles, la qual consisteix amb un objecte JFrame, el contenidor per defecte del qual se substitueix per un objecte JPanel, que és on podràs dibuixar-hi rectangles. Així, doncs, al projecte ratoli, afegeix-li la classe Rectangles, amb aquest codi:  
     
import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.Color;
import javax.swing.JPanel;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

/**
 * Escriviu aquí una descripcìó de la classe Rectangles
 *
 * @author (el vostre nom)
 * @version (un número de versió o la data)
 */

public class Rectangles extends JFrame implements MouseListener,
                                              MouseMotionListener {

    /**
     * Mètode constructor per objectes de la classe Rectangles.
     */
    public Rectangles () { // constructor
        setTitle("Dibuix de rectangles");

        // L'objecte JPanel per dibuixar-hi
        JPanel panel=new JPanel();
        // Determinació dels listeners d'esdeveniments de ratolí
        panel.addMouseListener(this);
        panel.addMouseMotionListener(this);
        // Cal determinar-ne les dimensions perquè, com que no
        // conté res, swing el faria de dimensions 0 x 0
        panel.setPreferredSize(new Dimension(300,200));
        // Substitució del contenidor per defecte del JFrame per
        // aquest JPanel
        setContentPane(panel);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan l'acció és "Click sobre un punt"
     * @param e un MouseEvent
     */
    public void mouseClicked (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Entrada en un
     * component"
     * @param e un MouseEvent
     */
    public void mouseEntered (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Sortida d'un
     * component"
     * @param e un MouseEvent
     */
    public void mouseExited (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Pressionar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mousePressed (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Deixar anar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mouseReleased (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Arrossegar"
     * @param e un MouseEvent
     */
    public void mouseDragged (MouseEvent e) {
    }

    /**
     * Mètode que s'executa quan l'acció és "Moure"
     * @param e un MouseEvent
     */
    public void mouseMoved (MouseEvent e) {
    }

}

 
     
  Per dibuixar rectangles, faràs servir el mètode

public void drawRect(int x,int y,int ample,int alt)

de la classe java.awt.Graphics (mira la pràctica 6 d'aquest mateix mòdul) . Les dades que es necessiten són
 
     
 
  1. Les coordenades del primer punt on es prem el botó, que seran les coordenades del vèrtex superior esquerre del rectangle. Com que aquest punt és constant per a cada rectangle, cal desar-ne les coordenades en sengles camps de la classe: caldrà afegir-li, doncs, els camps int puntIniciX i int puntIniciY. La tasca del mètode mousePressed() és, doncs, desar les coordenades de l'esdeveniment en aquests camps.

    puntIniciX=e.getX();
    puntIniciY=e.getY();

  2. Les amplades i altures successives dels rectangles que es dibuixen en arrossegar el ratolí. Una de les feines que ha de fer el mètode mouseDragged() és, precisament, fer aquests càlculs:

    int ample=e.getX()-puntIniciX;
    int alt=e.getY()-puntIniciY;
 
     
  El mateix mètode mouseDragged()pot encarregar-se d'esborrar i dibuixar els rectangles:

Graphics g=panel.getGraphics();
panel.update(g);
g.setColor(Color.BLACK);
g.drawRect(puntIniciX,puntIniciY,ample,alt);


 
  El codi de la classe Rectangles queda:  
     
import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.Color;
import javax.swing.JPanel;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Graphics;

/**
 * Escriviu aquí una descripcìó de la classe Rectangles
 *
 * @author (el vostre nom)
 * @version (un número de versió o la data)
 */
public class Rectangles extends JFrame implements MouseListener,
                                              MouseMotionListener {

    /**
     * Les coordenades del primer punt on es prem el botó
     */
    int puntIniciX,puntIniciY;

    /**
     * Mètode constructor per objectes de la classe Rectangles.
     */
    public Rectangles () { // constructor
        setTitle("Dibuix de rectangles");

        // L'objecte JPanel per dibuixar-hi
        JPanel panel=new JPanel();
        // Determinació dels listeners d'esdeveniments de ratolí
        panel.addMouseListener(this);
        panel.addMouseMotionListener(this);
        // Cal determinar-ne les dimensions perquè, com que no
        // conté res, swing el faria de dimensions 0 x 0
        panel.setPreferredSize(new Dimension(300,200));
        // Substitució del contenidor per defecte del JFrame per
        // aquest JPanel
        setContentPane(panel);

        pack();
        show();
    }

    /**
     * Mètode que s'executa quan l'acció és "Click sobre un punt"
     * @param e un MouseEvent
     */
    public void mouseClicked (MouseEvent e) {
        // Aquest mètode no es fa servir
    }

    /**
     * Mètode que s'executa quan l'acció és "Entrada en un
     * component"
     * @param e un MouseEvent
     */
    public void mouseEntered (MouseEvent e) {
        // Aquest mètode no es fa servir
    }

    /**
     * Mètode que s'executa quan l'acció és "Sortida d'un
     * component"
     * @param e un MouseEvent
     */
    public void mouseExited (MouseEvent e) {
        // Aquest mètode no es fa servir
    }

    /**
     * Mètode que s'executa quan l'acció és "Pressionar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mousePressed (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanel?
            if (font instanceof JPanel) {
                // Coordenades de l'esdeveniment
                int x=e.getX();
                int y=e.getY();
                // Desa-les als camps corresponents
                puntIniciX=x;
                puntIniciY=y;
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Deixar anar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mouseReleased (MouseEvent e) {
        // Aquest mètode no es fa servir
    }

    /**
     * Mètode que s'executa quan l'acció és "Arrossegar"
     * @param e un MouseEvent
     */
    public void mouseDragged (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanel?
            if (font instanceof JPanel) {
                // Coordenades de l'esdeveniment
                int x=e.getX();
                int y=e.getY();
                // Calcula les dimensions del rectangle
                int ample=x-puntIniciX;
                int alt=y-puntIniciY;
                // Identifica la font com a objecte JPanel
                JPanel panel=(JPanel)font;
                // L'objecte Graphics del panel
                Graphics g=panel.getGraphics();
                // Esborra els dibuixos del panel
                panel.update(g);
                // Determina el color i pinta el rectangle
                g.setColor(Color.BLACK);
                g.drawRect(puntIniciX,puntIniciY,ample,alt);
            }
    }

    /**
     * Mètode que s'executa quan l'acció és "Moure"
     * @param e un MouseEvent
     */
    public void mouseMoved (MouseEvent e) {
        // Aquest mètode no es fa servir
    }

}

 
     
  i ja pots dibuixar tants rectangles com et vingui de gust!  
     
 
     
  MouseAdapter i MouseMotionAdapter    
     
  Quan, com en el cas anterior, hi ha mètodes d'una interfície que no es fan servir, pot resultar feixuc haver d'escriurel's, tot i que no serveixin per a res. El SDK de Java preveu això i posa a la teva disposició dues classes, java.awt.event.MouseAdapter i java.awt.event.MouseMotionAdapter, que no són més que objectes amb les interfícies MouseListener i MouseMotionListener respectivament implementades i amb els mètodes buits. Només caldrà, doncs construir-los i sobreescriure-hi (override) els mètodes que t'interessin i oblidar-te dels altres. Aleshores, la classe principal ja no cal que implementi cap de les interfícies MouseListener i MouseMotionListener i, per tant, tampoc cal implementar-hi cap dels seus mètodes. Els mètodes que sí interessen van a parar als corresponents objectes MouseAdapter i MouseMotionAdapter.  
     
Per veure com funciona això, crea la classe RectanglesAlt, que no és més que la mateixa classe Rectangles, amb aquestes modificacions:  
     
import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.Color;
import javax.swing.JPanel;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;

import java.awt.Graphics;

/**
 * Escriviu aquí una descripcìó de la classe RectanglesAlt
 *
 * @author (el vostre nom)
 * @version (un número de versió o la data)
 */

public class RectanglesAlt extends JFrame {

    /**
     * Les coordenades del primer punt on es prem el botó
     */
    int puntIniciX,puntIniciY;


    /**
     * Mètode constructor per objectes de la classe RectanglesAlt.
     */
    public RectanglesAlt () { // constructor
        setTitle("Dibuix de rectangles");

        // L'objecte JPanel per dibuixar-hi
        JPanel panel=new JPanel();
        // Determinació dels listeners d'esdeveniments de ratolí
        // Aquí es crea l'objecte MouseAdapter
        panel.addMouseListener(new MouseAdapter() {
                         // Mètode mousePressed per a aquest
                         // MouseAdapter
                         public void mousePressed (MouseEvent e) {
                             // Quina és la font de l'esdeveni-
                             // ment?
                             Object font=e.getSource();
                             // És un objecte JPanel?
                                 if (font instanceof JPanel) {
                                     // Coordenades de l'esdeve-
                                     // niment

                                     int x=e.getX();
                                     int y=e.getY();
                                     // Desa-les als camps corres-
                                     // ponents
                                     puntIniciX=x;
                                     puntIniciY=y;
                                 }
                         }
                               }
                              );
        // Aquí es crea l'objecte MouseMotionAdapter
        panel.addMouseMotionListener(new MouseMotionAdapter() {
                         // Mètode mouseDragged per a aquest
                         // MouseAdapter
                         public void mouseDragged (MouseEvent e) {
                             // Quina és la font de l'esdeveni-
                             // ment?
                             Object font=e.getSource();
                             // És un objecte JPanel?
                                 if (font instanceof JPanel) {
                                     // Coordenades de l'esdeve-
                                     // niment

                                     int x=e.getX();
                                     int y=e.getY();
                                     // Calcula les dimensions
                                     // del rectangle
                                     int ample=x-puntIniciX;
                                     int alt=y-puntIniciY;
                                     // Identifica la font com
                                     // a objecte JPanel
                                     JPanel panel=(JPanel)font;
                                     // L'objecte Graphics del
                                     // panel
                                     Graphics g=
                                              panel.getGraphics();
                                     // Esborra els dibuixos
                                     // del panel
                                     panel.update(g);
                                     // Determina el color i
                                     // pinta el rectangle

                                     g.setColor(Color.BLACK);
                                     g.drawRect(puntIniciX,
                  
                              puntIniciY,
                                                ample,
                                                alt);
                                 }
                         }
                               }
                              );
        // Cal determinar-ne les dimensions perquè, com que no
        // conté res, swing el faria de dimensions 0 x 0
        panel.setPreferredSize(new Dimension(300,200));
        // Substitució del contenidor per defecte del JFrame per
        // aquest JPanel
        setContentPane(panel);

        pack();
        show();
    }

}

 
     
  i, despres de compilar i crear un objecte RectanglesAlt, comprovaràs que el comportament de l'objecte és el mateix d'abans...  
     
  Quin botó?    
     
  Potser no te n'has adonat, però, per ara, tot funciona indistintament amb qualsevol dels botons del ratolí. Però hi ha algunes maneres de distingir quin dels botons ha estat el responsable de l'acció. La més popular és apelar al mètode static de la classe javax.swing.SwingUtilities,  
     
 
  • public static boolean isLeftMouseButton(MouseEvent e), que retorna true si el responsable de l'esdeveniment és el botó esquerre del ratolí,

  • public static boolean isMiddleMouseButton(MouseEvent e), que retorna true si el responsable és el botó central del ratolí,

  • i public static boolean isRightMouseButton(MouseEvent e), que retorna true si el responsable és el botó de la dreta del ratolí.
 
     
Una petita modificació al mètode mouseDragged() de la classe Rectangles et permetrà dibuixar rectangles plens amb el botó de l'esquerra i rectangles sense interior amb el botó de la dreta:  
     

<codi anterior>

    /**
     * Mètode que s'executa quan l'acció és "Arrossegar"
     * @param e un MouseEvent
     */
    public void mouseDragged (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanel?
            if (font instanceof JPanel) {
                // Coordenades de l'esdeveniment
                int x=e.getX();
                int y=e.getY();
                // Calcula les dimensions del rectangle
                int ample=x-puntIniciX;
                int alt=y-puntIniciY;
                // Identifica la font com a objecte JPanel
                JPanel panel=(JPanel)font;
                // L'objecte Graphics del panel
                Graphics g=panel.getGraphics();
                // Esborra els dibuixos del panel
                panel.update(g);
                // Determina el color i pinta el rectangle
                g.setColor(Color.BLACK);
                    // És el botó esquerre?
                    if (SwingUtilities.isLeftMouseButton(e)) {
                       g.drawRect(puntIniciX,puntIniciY,ample,alt);
                    // És el botó de la dreta?
                    } else
                    if (SwingUtilities
.isRightMouseButton(e)){
                       g.fillRect(puntIniciX,puntIniciY,ample,alt);
                  }
            }
    }

<codi següent>

 
     
  No t'oblidis d'importar el paquet javax.swing.SwingUtilities!  
     
 
  Amb el botó de l'esquerra  
     
 
 
  Amb el botó de la dreta  
     
  Línies    
     
  La classe Rectangles podem fer-la més interessant: en lloc de dibuixar rectangles, que es dibuixin línies, tot seguint l'itinerari del cursor del ratolí. Només cal, al mètode mouseDragged(), substituir la crida al mètode drawRect() per la crida al mètode  
     
 
public void drawLine(int x1,int y1,int x2,y2)
 
     
  Els paràmetres x1 i y1 han de ser, respectivament, puntIniciX i puntIniciY, els paràmetres x2 i y2 han de ser les coordenades del punt de l'esdeveniment "arrossegar", és a dir, e.getX() i e.getY(). A més, just després del dibuix, els valors de x2 i y2 han de substituir els anteriors a i, en a puntIniciX i puntIniciY, per preparar el dibuix del següent fragment de línia. Cal, també, no esborrar els fragments anteriors i, per tant, cal suprimir la crida al mètode update(). Finalment, el botó esquerre dibuixa en negre i el de la dreta en vermell. El nou mètode mouseDragged() és:  
     

<codi anterior>

    /**
     * Mètode que s'executa quan l'acció és "Arrossegar"
     * @param e un MouseEvent
     */
    public void mouseDragged (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanel?
            if (font instanceof JPanel) {
                // Coordenades de l'esdeveniment
                int x=e.getX();
                int y=e.getY();
                // Identifica la font com a objecte JPanel
                JPanel panel=(JPanel)font;
                // L'objecte Graphics del panel
                Graphics g=panel.getGraphics();

                // Determina el botó i determina el color
                    // És el botó esquerre?
                    if (SwingUtilities.isLeftMouseButton(e)) {
                        g.setColor(Color.BLACK);
                    // És el botó de la dreta?
                    } else
                    if (SwingUtilities.isRightMouseButton(e)){
                        g.setColor(Color.RED);
                    }
                // Dibuixa la línia
                g.drawLine(puntIniciX,puntIniciY,x,y);
                // Determina el nou punt inicial
                
puntIniciX=x;
                
puntIniciY=y;
            }
    }

<codi següent>

 
     
  Quan tornis a compilar i creis un nou objecte Rectangles, podràs fer això:  
     
 
     
  i, si vols millorar l'aspecte de les línies, només cal que facis servir la classe java.awt.Graphics2D en lloc de la classe java.awt.Graphics, (mira la pràctica anterior) i els corresponents RenderingHints:  
     

<codi anterior>

    /**
     * Mètode que s'executa quan l'acció és "Arrossegar"
     * @param e un MouseEvent
     */
    public void mouseDragged (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanel?
            if (font instanceof JPanel) {
                // Coordenades de l'esdeveniment
                int x=e.getX();
                int y=e.getY();
                // Identifica la font com a objecte JPanel
                JPanel panel=(JPanel)font;
                // L'objecte Graphics del panel
                Graphics g=panel.getGraphics();

                // Objecte Graphics g convertit a objecte
                // Graphics2D
                Graphics2D g2D=(Graphics2D)g;
                // A partir d'ara, qui dibuixa és l'objecte g2D:
                // El RenderingHint per activar antialiasing
                g2D.setRenderingHint(
                               RenderingHints.KEY_ANTIALIASING,
                               RenderingHints.VALUE_ANTIALIAS_ON);

                // Determina el botó i determina el color
                    // És el botó esquerre?
                    if (SwingUtilities.isLeftMouseButton(e)) {

                        g2D.setColor(Color.BLACK);
                    // És el botó de la dreta?
                    } else
                    if (SwingUtilities.isRightMouseButton(e)){

                        g2D.setColor(Color.RED);
                    }
                // Dibuixa la línia

                g2D.drawLine(puntIniciX,puntIniciY,x,y);
                // Determina el nou punt inicial
                puntIniciX=x;
                puntIniciY=y;
            }
    }

<codi següent>

 
     
  No t'hauràs pas oblidat d'importar les classes java.awt.Graphics2D i java.awt.RenderingHints, oi?  
     
 
     
  Doble clic    
     
  Hi ha una acció, que és molt corrent en el maneig del ratolí, que és el doble clic sobre un punt. Ara et mostrarem com implementar-lo: es tracta, simplement de mesurar el temps entre dos execucions del mètode mousePressed() i, si aquest temps és menor que un temps prefixat, es considerarà un doble clic. Naturalment, necessites un camp de la classe, long tempsAnteriorClic, que contingui el temps en el qual es va esdevenir l'anterior clic. Ara cal actuar sobre el mètode mousePressed():  
     

<codi anterior>
public class RectanglesAlt extends JFrame {

    /**
     * Les Les coordenades del primer punt on es prem el botó
     */
    int puntIniciX,puntIniciY;

    /**
     * El temps (instant) en el qual es va esdevenir l'anterior
     * clic
     */
    long tempsAnteriorClic;

    /**
     * Mètode constructor per objectes de la classe RectanglesAlt.
     */
    public RectanglesAlt () { // constructor

<codi següent>
    ...
    ...
    ...
<codi anterior>

    /**
     * Mètode que s'executa quan l'acció és "Pressionar sobre
     * un punt"
     * @param e un MouseEvent
     */
    public void mousePressed (MouseEvent e) {
        // Quina és la font de l'esdeveniment?
        Object font=e.getSource();
            // És un objecte JPanel?
            if (font instanceof JPanel) {
                // Coordenades de l'esdeveniment
                int x=e.getX();
                int y=e.getY();

                // Temps de l'esdeveniment
                long temps=e.getWhen();
                // Desa-les als camps corresponents
                puntIniciX=x;
                puntIniciY=y;
                // Interval entre dos clicks
                long interval=temps-tempsAnteriorClic;
                // Cal fer això per garantir que interval > 0
                    if (interval<0) {
                        interval=-interval;
                    }
                // És un doble click? és a dir, entre aquest click
                // i l'anterior hi ha hagut menys de 300 mili-
                // segons
?
                    if (interval<300L) {
                        // Sí: és un doble clic. Ara pots fer
                        // qualsevol cosa. Per exemple, esborrar
                        // el panel...
                        JPanel panel=(JPanel)font;
                        panel.repaint();
                    }
                // El temps d'aquest clic cal desar-lo per tal que
                // el següent pugui comparar...
                tempsAnteriorClic=temps;
            }
    }

<codi següent>

 
     
  El resultat és que, ara, a la classe Rectangles, pots esborrar les línies que hi hagi dibuixades amb un doble clic de qualsevol dels botons del ratolí...  
     
  Un exercici, per acabar...  
     
La última versió de la classe Rectangles, la que et permet dibuixar línies de dos colors segons el botó del ratolí que facis servir, i que et permet esborrar els dibuixos amb un doble clic, està construida implementant les interfícies MouseListener i MouseMotionListener. Es tracta que n'escriguis una nova versió, la classe RectanglesAlt, que no implementi aquestes interfícies i que, en canvi, faci servir objectes MouseAdapter i MouseMotionAdapter, de la manera que ho hauràs fet aquí.  
     
  No miréssis pas la solució, eh? la qual està ben amagadeta aquí...  
     
    Tornar al principi