Avant de nous lancer dans l'implémentation de cette dernière, nous allons voir ce que nous allons obtenir à la fin :
Vous avez vu ce que nous allons obtenir, mais vous allez tout de même passer par un peu de théorie avant d'arriver à ce résultat.
Je ne me suis pas encore attardé sur le sujet (et je ne le ferai toujours pas), mais, pour arriver à détecter les événements qui surviennent à votre composant, Java utilise ce qu'on appelle le
design pattern Observer.
Je ne vous l'expliquerai pas dans le détail tout de suite, une partie concernant les
design patterns sera rédigée...
En fait, un
dp est un modèle de conception, une idée directrice afin d'avoir des programmes stables, réutilisables à souhait et paramétrables au possible !
Le design pattern
Observer consiste en un modèle qui permet à des objets de se tenir au courant automatiquement. Ceci se fait par le biais d'interfaces que les objets devant se tenir au courant doivent implémenter.
Pour le moment, retenez qu'un objet observable implémente une interface communément appelée
Observable et qu'un observateur va, lui, implémenter une interface communément appelée
Observer (ceci dans le cas où nous utilisons le pattern Observer fourni avec Java ; oui, ces interfaces existent...). Le principe est simple :
- l'objet observable va avoir un ou plusieurs objets observateurs
- lorsqu'une donnée change dans un objet observable, il met tous ses objets observateurs abonnés à sa "news letter" au courant !
Ce principe, adapté aux composants
swing, permet aux objets composants d'être écoutés et donc de pouvoir faire réagir votre application en conséquence !
Ne nous attardons pas sur le sujet, nous y reviendrons.
Maintenant que vous avez une très vague idée de la façon dont Java gère les événements, nous allons commencer à gérer les passages de notre souris sur notre objet !
Comme vous l'avez sûrement deviné, vous allez devoir implémenter l'interface
MouseListener dans votre classe
Bouton. Utilisez l'astuce d'Eclipse vue lors des implémentations d'interfaces, afin de générer automatiquement les méthodes à implémenter.
Nous aurons aussi à dire à notre classe
Bouton qu'elle va devoir tenir quelqu'un au courant de ses changements d'état par rapport à la souris. Ce quelqu'un n'est autre... qu'elle-même !

Eh oui... Notre classe va s'écouter elle-même, donc ; dès que notre objet observable, notre bouton, va avoir des informations concernant les actions de la souris, il va dire à l'objet qui l'observe, lui-même, ce qu'il doit faire !
Ceci se fait grâce à la méthode
addMouseListener(MouseListener obj) qui prend un objet
MouseListener en paramètre ; ici, elle prendra
this.
Rappelez-vous que vous pouvez utiliser le type d'une interface comme super-type : ici, notre classe implémente l'interface MouseListener , nous pouvons donc utiliser cet objet comme référence de cette interface !
Voici notre classe
Bouton à présent :
Code : 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 | import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton implements MouseListener{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Avec cette instruction, notre objet va s'écouter lui-même
//Dès qu'un événement de la souris sera intercepté, il sera au courant !
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
/**
* Méthode appelée lors du clic de souris
*/
public void mouseClicked(MouseEvent event) {
}
/**
* Méthode appelée lors du survol de la souris
*/
public void mouseEntered(MouseEvent event) {
}
/**
* Méthode appelée lorsque la souris sort de la zone du bouton
*/
public void mouseExited(MouseEvent event) {
}
/**
* Méthode appelée lorsque l'on presse le clic gauche de la souris
*/
public void mousePressed(MouseEvent event) {
}
/**
* Méthode appelée lorsque l'on relâche le clic de souris
*/
public void mouseReleased(MouseEvent event) {
}
}
|
C'est par le biais de ces différentes méthodes que nous allons gérer les différentes images à dessiner dans notre objet ! Mais rappelez-vous que :
Même si vous n'utilisez pas toutes les méthodes d'une interface, vous DEVEZ tout de même mettre le squelette des méthodes non utilisées (avec les accolades), et ceci est aussi valable pour les classes abstraites.
Dans notre cas, la méthode repaint() est appelée de façon tacite. Lorsqu'un événement est déclenché, notre objet se redessine automatiquement ! Comme lorsque vous redimensionnez votre fenêtre dans les premiers chapitres...
Avec cette information, nous n'avons plus qu'à changer notre image selon la méthode invoquée :
- notre objet aura une teinte jaune lors du survol de la souris
- une teinte orangée lorsque l'on pressera le clic gauche
- reviendra à la normale si on relâche le clic.
Pour ce faire, voici les fichiers
.png dont je me suis servi (mais rien ne vous empêche de les faire vous-mêmes

).
Je vous rappelle que dans le code qui suit, les fichiers images sont mis à la racine du projet ! !
Maintenant, voici le code de notre classe
Bouton personnalisée :
Code : 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 | import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton implements MouseListener{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
@Override
public void mouseClicked(MouseEvent event) {
//Pas utile d'utiliser cette méthode ici
}
@Override
public void mouseEntered(MouseEvent event) {
//Nous changeons le fond en jaune pour notre image lors du survol
//avec le fichier fondBoutonHover.png
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseExited(MouseEvent event) {
//Nous changeons le fond en vert pour notre image lorsqu'on quitte le bouton
//avec le fichier fondBouton.png
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mousePressed(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image lors du clic gauche
//avec le fichier fondBoutonClic.png
try {
img = ImageIO.read(new File("fondBoutonClic.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseReleased(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image
//lorsqu'on relâche le clic
//avec le fichier fondBoutonHover.png
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
Et voilà le travail ! Il va de soi que si vous avez fait un
clic-droit / Enregistrer-sous sur mes images, elles ne doivent pas avoir le même nom que sur mon morceau de code... Vous
DEVEZ mettre le nom que vous leur avez donné... Je sais ça va de soi... Mais on ne sait jamais !
Et maintenant, vous avez un bouton personnalisé qui réagit aux passages de souris !

Mais un bémol se profile à l'horizon...
Lequel ? L'objet marche très bien !
Je sais qu'il va y avoir des p'tits malins qui vont cliquer sur le bouton et relâcher le clic en dehors du bouton !

Dans ce cas, vous devez voir que le fond du bouton est jaune, vu que c'est ce que nous avons demandé de faire à notre méthode
mouseReleased ! Pour palier à ce problème, nous allons vérifier que, lorsque le clic est relâché, la souris est toujours sur le bouton.
Comment tu vas réussir à faire ça ? J'ai eu beau regarder dans les méthodes proposées par notre objet, mais il n'y a rien qui nous permette de faire ça !
Je sais... Mais vous n'avez pas regardé au bon endroit !
Maintenant que nous avons implémenté l'interface
MouseListener, il y a un autre objet que nous n'avons pas encore utilisé... Vous ne le voyez pas ? C'est le paramètre présent dans toutes les méthodes de cette interface ! Oui, c'est
MouseEvent.
Grâce à cet objet, nous pouvons avoir beaucoup de renseignements sur les événements. Nous ne détaillerons pas tout ici mais vous verrez quelques utilités de ces types d'objets tout au long de cette partie !
Dans notre cas, nous pouvons récupérer la position X et Y de notre souris par rapport à notre
Bouton, tout ceci avec les méthodes
getX() et
getY(). Donc, si nous relâchons notre clic de souris en dehors de la zone où se trouve notre objet, la valeur retournée par l'une des méthodes
getX() ou
getY() sera négative ou supérieure aux dimensions du bouton !
Voici enfin le code final de notre classe
Bouton :
Code : 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 | import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
public class Bouton extends JButton implements MouseListener{
private String name;
private Image img;
public Bouton(String str){
super(str);
this.name = str;
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.addMouseListener(this);
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
GradientPaint gp = new GradientPaint(0, 0, Color.blue, 0, 20, Color.cyan, true);
g2d.setPaint(gp);
// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
g2d.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
g2d.setColor(Color.black);
g2d.drawString(this.name, this.getWidth() / 2 - (this.getWidth() / 2 /4), (this.getHeight() / 2) + 5);
}
@Override
public void mouseClicked(MouseEvent event) {
//Pas utile d'utiliser cette méthode ici
}
@Override
public void mouseEntered(MouseEvent event) {
//Nous changeons le fond en jaune pour notre image lors du survol
//avec le fichier fondBoutonHover.png
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseExited(MouseEvent event) {
//Nous changeons le fond en vert pour notre image lorsqu'on quitte le bouton
//avec le fichier fondBouton.png
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mousePressed(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image lors du clic gauche
//avec le fichier fondBoutonClic.png
try {
img = ImageIO.read(new File("fondBoutonClic.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void mouseReleased(MouseEvent event) {
//Nous changeons le fond en orangé pour notre image
//lorsqu'on relâche le clic
//avec le fichier fondBoutonHover.png
//Si on est à l'extérieur de l'objet, on dessine le fond par défaut
if(event.getY() < 0 || event.getX() < 0 || event.getY() > this.getHeight() || event.getX() > this.getWidth())
{
try {
img = ImageIO.read(new File("fondBouton.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//Sinon on met le fond jaune, la souris est encore dessus...
else
{
try {
img = ImageIO.read(new File("fondBoutonHover.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
|
Vous allez voir tout au long des chapitres qui suivent qu'il y a différentes interfaces pour les différentes actions possibles sur une IHM ! Sachez seulement qu'il y a une convention aussi pour ces interfaces.
Leur nom commence par le type d'action ensuite suivi du mot Listener. Nous avons vu ici les actions de la souris, et voyez le nom de l'interface : MouseListener
Voilà ! Ce chapitre est clos !
Eh ! Attends... Notre bouton ne sais toujours rien faire !
C'est justement le sujet du prochain chapitre. Passez faire un tour sur le topo de cette partie et si vous avez un score raisonnable au QCM, vous pourrez y aller...