TP : l'ardoise magique

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Nous voilà partis pour un nouveau TP. Les objectifs de celui-ci sont :

  • d'utiliser les menus, les accélérateurs et les mnémoniques ;
  • d'ajouter une barre d'outils ;
  • de créer des implémentations et de savoir les utiliser sur plusieurs composants ;
  • d'utiliser des classes anonymes ;
  • etc.

Cahier des charges

Vous allez devoir faire une sorte d'ardoise magique. Celle-ci devra être composée d'un JPanel amélioré (ça sent l'héritage…) sur lequel vous pourrez tracer des choses en cliquant et en déplaçant la souris.

Vos tracés devront être effectués point par point, je vous laisse apprécier leur taille. Par contre, vous devrez pouvoir utiliser deux sortes de « pinceaux » :

  • un carré ;
  • un rond.

Vous aurez aussi la possibilité de changer la couleur de vos traits. Les couleurs que j'ai choisies sont :

  • le bleu ;
  • le rouge ;
  • le vert.

Il faut obligatoirement :

  • un menu avec accélérateurs et mnémoniques ;
  • une barre d'outils avec les formes et les couleurs ;
  • un menu Quitter et un menu Effacer ;
  • que les formes et les couleurs soient accessibles via le menu !

La figure suivante vous montre ce que j'ai obtenu.

Points de menu

La figure suivante vous montre les possibilités de l'application.

L'auteur s'exprime

Vous allez utiliser la méthode repaint() de votre composant ; cependant, souvenez-vous que celle-ci est appelée automatiquement lors du redimensionnement de votre fenêtre, de la réduction et de l'agrandissement… Vous allez devoir gérer ce cas de figure, sans quoi votre zone de dessin s'effacera à chaque redimensionnement !

Je vous conseille de créer une classe Point qui va contenir les informations relatives à un point tracé (couleur, taille, position…). Il va falloir que vous gériez une collection de points (générique) dans votre classe dérivée de JPanel ! J'en ai presque trop dit…

Concernant les images utilisées, je vous laisse le soin d'en trouver.

Avant de vous lancer dans votre code, vous devez savoir quelques petites choses…

Prérequis

Afin de faire les tracés, il va falloir détecter le mouvement de la souris. Je ne vous en ai pas encore parlé auparavant, mais vous avez l'habitude d'utiliser des interfaces de gestion d'événements, maintenant…

Afin de détecter les mouvements de la souris, vous allez devoir utiliser l'interface MouseMotionListener ; celle-ci contient deux méthodes :

  • mouseMoved(MouseEvent e), qui détecte le mouvement de la souris sur le composant ;
  • mouseDragged(MouseEvent e), qui fonctionne comme mouseMoved, sauf que vous devrez avoir cliqué sur le composant et maintenir ce clic enfoncé pendant le mouvement (exactement ce dont vous avez besoin).

Voilà : vous allez devoir créer une implémentation de cette interface pour réussir à dessiner sur votre conteneur !

Ne vous précipitez pas, réfléchissez bien à ce dont vous avez besoin, comment utiliser vos implémentations, etc. Un code bien réfléchi est un code rapidement opérationnel !

C'est à vous, maintenant… À vos claviers.

Correction

Je vous propose une des corrections possibles.

Vous constaterez que c'est un code assez simple. Cet exercice n'a rien de difficile et a surtout le mérite de vous faire travailler un peu tout ce que vous avez vu jusqu'ici…

Point.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// CTRL + SHIFT + O pour générer les imports 
public class Point {

  //Couleur du point
  private Color color = Color.red;
  //Taille
  private int size = 10;
  //Position sur l'axe X : initialisé au dehors du conteneur
  private int x = -10;
  //Position sur l'axe Y : initialisé au dehors du conteneur
  private int y = -10;
  //Type de point
  private String type = "ROND";

  // Constructeur par défaut
  public Point(){}

  public Point(int x, int y, int size, Color color, String type){
    this.size = size;
    this.x = x;
    this.y = y;
    this.color = color;
    this.type = type;
  }

  //ACCESSEURS
  public Color getColor() {
    return color;
  }
  public void setColor(Color color) {
    this.color = color;
  }
  public int getSize() {
    return size;
  }
  public void setSize(int size) {
    this.size = size;
  }
  public int getX() {
    return x;
  }
  public void setX(int x) {
    this.x = x;
  }
  public int getY() {
    return y;
  }
  public void setY(int y) {
    this.y = y;
  }
  public String getType() {
    return type;
  }
  public void setType(String type) {
    this.type = type;
  }
}

DrawPanel.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// CTRL + SHIFT + O pour générer les imports 
public class DrawPanel extends JPanel{

  //Couleur du pointeur
  private Color pointerColor = Color.red;
  //Forme du pointeur
  private String pointerType = "CIRCLE";
  //Position X du pointeur
  private int posX = -10, oldX = -10;
  //Position Y du pointeur
  private int posY = -10, oldY = -10;
  //Pour savoir si on doit dessiner ou non
  private boolean erasing = true;
  //Taille du pointeur
  private int pointerSize = 15;
  //Collection de points ! 
  private ArrayList<Point> points = new ArrayList<Point>();  

  public DrawPanel(){

    this.addMouseListener(new MouseAdapter(){
      public void mousePressed(MouseEvent e){
        points.add(new Point(e.getX() - (pointerSize / 2), e.getY() - (pointerSize / 2), pointerSize, pointerColor, pointerType));
        repaint();
      }
    });

    this.addMouseMotionListener(new MouseMotionListener(){
      public void mouseDragged(MouseEvent e) {
        //On récupère les coordonnées de la souris et on enlève la moitié de la taille du pointeur pour centrer le tracé
        points.add(new Point(e.getX() - (pointerSize / 2), e.getY() - (pointerSize / 2), pointerSize, pointerColor, pointerType));
        repaint();
      }

      public void mouseMoved(MouseEvent e) {}
    });

  }

  // Vous la connaissez maintenant, celle-là
  public void paintComponent(Graphics g) {

    g.setColor(Color.white);
    g.fillRect(0, 0, this.getWidth(), this.getHeight());

    //Si on doit effacer, on ne passe pas dans le else => pas de dessin
    if(this.erasing){
      this.erasing = false;
    }
    else{
      //On parcourt notre collection de points
      for(Point p : this.points)
      {
        //On récupère la couleur
        g.setColor(p.getColor());

        //Selon le type de point
        if(p.getType().equals("SQUARE")){
          g.fillRect(p.getX(), p.getY(), p.getSize(), p.getSize());
        }
        else{
          g.fillOval(p.getX(), p.getY(), p.getSize(), p.getSize());
        }
      }
    }        
  }

  //Efface le contenu
  public void erase(){
    this.erasing = true;
    this.points = new ArrayList<Point>();
    repaint();
  }

  //Définit la couleur du pointeur
  public void setPointerColor(Color c){
    this.pointerColor = c;
  }

  //Définit la forme du pointeur
  public void setPointerType(String str){
    this.pointerType = str;
  }      
}

Fenetre.java

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//CTRL + SHIFT + O pour générer les imports 
public class Fenetre extends JFrame {

  //LE MENU 
  private JMenuBar menuBar = new JMenuBar();
  JMenu   fichier = new JMenu("Fichier"),
    edition = new JMenu("Edition"),
    forme = new JMenu("Forme du pointeur"),
    couleur = new JMenu("Couleur du pointeur");

  JMenuItem   nouveau = new JMenuItem("Effacer"),
    quitter = new JMenuItem("Quitter"),
    rond = new JMenuItem("Rond"),
    carre = new JMenuItem("Carré"),
    bleu = new JMenuItem("Bleu"),
    rouge = new JMenuItem("Rouge"),
    vert = new JMenuItem("Vert");

  //LA BARRE D'OUTILS
  JToolBar toolBar = new JToolBar();

  JButton square = new JButton(new ImageIcon("images/carré.jpg")),
    circle = new JButton(new ImageIcon("images/rond.jpg")),
    red = new JButton(new ImageIcon("images/rouge.jpg")),
    green = new JButton(new ImageIcon("images/vert.jpg")),
    blue = new JButton(new ImageIcon("images/bleu.jpg"));

  //LES ÉCOUTEURS
  private FormeListener fListener = new FormeListener();
  private CouleurListener cListener = new CouleurListener();

  //Notre zone de dessin
  private DrawPanel drawPanel = new DrawPanel();

  public Fenetre(){
    this.setSize(700, 500);
    this.setLocationRelativeTo(null);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //On initialise le menu
    this.initMenu();
    //Idem pour la barre d'outils
    this.initToolBar();
    //On positionne notre zone de dessin
    this.getContentPane().add(drawPanel, BorderLayout.CENTER);
    this.setVisible(true);    
  }

  //Initialise le menu
  private void initMenu(){
    nouveau.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent arg0) {
        drawPanel.erase();
      }      
    });

    nouveau.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK));

    quitter.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent arg0) {
        System.exit(0);
      }      
    });
    quitter.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK));

    fichier.add(nouveau);
    fichier.addSeparator();
    fichier.add(quitter);
    fichier.setMnemonic('F');

    carre.addActionListener(fListener);
    rond.addActionListener(fListener);
    forme.add(rond);
    forme.add(carre);

    rouge.addActionListener(cListener);
    vert.addActionListener(cListener);
    bleu.addActionListener(cListener);
    couleur.add(rouge);
    couleur.add(vert);
    couleur.add(bleu);

    edition.setMnemonic('E');
    edition.add(forme);
    edition.addSeparator();
    edition.add(couleur);

    menuBar.add(fichier);
    menuBar.add(edition);

    this.setJMenuBar(menuBar);
  }

  //Initialise la barre d'outils
  private void initToolBar(){

    JPanel panneau = new JPanel();
    square.addActionListener(fListener);
    circle.addActionListener(fListener);
    red.addActionListener(cListener);
    green.addActionListener(cListener);
    blue.addActionListener(cListener);

    toolBar.add(square);
    toolBar.add(circle);

    toolBar.addSeparator();
    toolBar.add(red);
    toolBar.add(blue);
    toolBar.add(green);

    this.getContentPane().add(toolBar, BorderLayout.NORTH);
  }

  //ÉCOUTEUR POUR LE CHANGEMENT DE FORME
  class FormeListener implements ActionListener{
    public void actionPerformed(ActionEvent e) {
      if(e.getSource().getClass().getName().equals("javax.swing.JMenuItem")){
        if(e.getSource()==carre) drawPanel.setPointerType("SQUARE");
        else drawPanel.setPointerType("CIRCLE");
      }
      else{
        if(e.getSource()==square)drawPanel.setPointerType("SQUARE");
        else drawPanel.setPointerType("CIRCLE");        
      }
    }    
  }

  //ÉCOUTEUR POUR LE CHANGEMENT DE COULEUR
  class CouleurListener implements ActionListener{
    public void actionPerformed(ActionEvent e) {
      System.out.println(e.getSource().getClass().getName());
      if(e.getSource().getClass().getName().equals("javax.swing.JMenuItem")){
        System.out.println("OK !");
        if(e.getSource()==vert)drawPanel.setPointerColor(Color.green);
        else if(e.getSource()==bleu)drawPanel.setPointerColor(Color.blue);
        else drawPanel.setPointerColor(Color.red);
      }
      else{
        if(e.getSource()==green)drawPanel.setPointerColor(Color.green);
        else if(e.getSource()==blue)drawPanel.setPointerColor(Color.blue);
        else drawPanel.setPointerColor(Color.red);        
      }
    }    
  }

  public static void main(String[] args){
    Fenetre fen = new Fenetre();
  }    
}

Améliorations possibles

Voici ce que vous pouvez faire afin de rendre cette application plus attractive :

  • permettre de changer la taille du pinceau ;
  • proposer une plus grande palette de couleurs ;
  • proposer des pinceaux supplémentaires ;
  • créer une gomme ;
  • utiliser les énumérations (ou encore le pattern strategy) pour gérer les différentes fonctionnalités ;
  • etc.