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 menuEffacer
; - que les formes et les couleurs soient accessibles via le menu !
La figure suivante vous montre ce que j'ai obtenu.
La figure suivante vous montre les possibilités de l'application.
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 commemouseMoved
, 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.