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
// 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
// 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
//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.