VIII. Chapitre 6. Evénements de la fenêtre▲
Au cours de son exécution, un programme peut être confronté à divers événements : un utilisateur clique sur un bouton dans une fenêtre, le navigateur web décide de repeindre la fenêtre, etc. Je suis sûr que tu as essayé de cliquer sur les boutons de notre calculatrice du Chapitre 5, mais ces boutons n'étaient pas encore prêts à réagir à tes actions.
Chaque composant d'une fenêtre peut traiter un certain nombre d'événements ou, comme on dit, être à l'écoute (listen) de ces événements, et, à son tour, émettre des événements, possiblement différents de ceux qu'il a reçu. Pour réagir aux actions de l'utilisateur, ton programme doit s'enregistrer auprès des composants de la fenêtre au moyen de classes Java appelées récepteurs (listeners). Il vaut mieux que ton programme ne reçoive que les événements qui l'intéressent. Par exemple, quand une personne déplace le pointeur de la souris au-dessus d'un bouton de la calculatrice : peu importe l'endroit exact où se trouve la souris quand la personne appuie sur le bouton ; ce qui compte, c'est que c'est à l'intérieur de la surface du bouton, et que ton programme sache que le bouton a été cliqué. C'est pourquoi tu n'as pas besoin d'enregistrer ton programme auprès du bouton en tant que MouseMotionListener, qui est le récepteur traitant les déplacements de la souris. Par contre, ce récepteur est bien utile pour tout es sortes de programmes de dessin.
Ton programme devrait s'enregistrer auprès des boutons de la calculatrice en tant que récepteur ActionListener, qui peut traiter les clics sur un bouton. Tous les récepteurs sont des classes Java spéciales appelées interfaces.
VIII-A. Interfaces▲
La plupart des classes définissent des méthodes qui effectuent diverses actions, par exemple réagir aux clics sur les boutons, réagir aux mouvements de la souris, etc. La combinaison de telles actions est appelée le comportement de la classe (class behavior).
Les interfaces sont des classes spéciales qui donnent juste des noms à un ensemble d'actions particulières sans écrire le code réel qui implanterait ces actions. Par exemple :
interface
MouseMotionListener {
void
mouseDragged
(
MouseEvent événement);
void
mouseMoved
(
MouseEvent événement);
}
Comme tu vois, les méthodes mouseDragged() et mouseMoved() ne contiennent pas de code - elles sont juste déclarées dans l'interface MouseMotionListener. Mais si ta classe a besoin de réagir quand la souris est déplacée ou qu'elle fait glisser un objet, elle doit implanter(6) (to implement) cette interface. Le terme implanter signifie que cette classe va inclure pour de bon les méthodes déclarées dans l'interface, par exemple :
import
java.awt.event.MouseEvent;
import
java.awt.event.MouseMotionListener;
class
monBlocADessin implements
MouseMotionListener {
// Insère ici ton code capable de dessiner
public
void
mouseDragged
(
MouseEvent événement) {
// Insère ici le code à exécuter quand un "glisser"
// est effectué avec la souris
}
public
void
mouseMoved
(
MouseEvent événement) {
// Insère ici le code à exécuter quand la souris est
// déplacée
}
}
Tu te demandes peut-être à quoi bon prendre la peine de créer des interfaces sans même y écrire le code ? C'est pour la bonne raison que, une fois que cette interface est créée, elle peut être réutilisée par de nombreuses classes. Par exemple, quand d'autres classes (ou Java lui-même) voient que la classe monBlocADessin implante l'interface MouseMotionListener, elles sont certaines que cette classe définit les méthodes mouseDragged() et mouseMoved(). Chaque fois qu'un utilisateur déplace la souris, Java appelle la méthode mouseMoved()et exécute le code que tu y a écrit. Imagine ce qui se passerait si un programmeur John décidait de nommer une telle méthode mouseMoved(), mais que Marie la nommait mouvementSouris() et Pierre préférait sourisDéplacée() ? Java serait embrouillé et ne saurait pas quelle méthode de ta classe appeler pour lui signaler le déplacement de la souris.
Une classe Java peut implanter plusieurs interfaces. Par exemple, elle peut avoir besoin de traiter les déplacements de la souris et le clic sur un bouton :
class
MonProgrammeDeDessin implements
MouseMotionListener, ActionListener {
// Tu dois écrire ici le code de toutes les méthodes
// définies par les deux interfaces
}
Une fois à l'aise avec les interfaces fournies avec Java, tu seras à même de créer tes propres interfaces, mais il s'agit d'un sujet avancé. N'en parlons même pas pour l'instant.
VIII-B. Récepteur d'événements▲
Revenons à notre calculatrice. Si tu as fait tes devoirs du précédent chapitre, la partie visuelle est terminée. Nous allons maintenant créer une autre classe, un récepteur qui effectuera certaines actions quand l'utilisateur cliquera sur l'un des boutons. En réalité, nous aurions pu ajouter le code traitant les événements clic à la classe Calculatrice.java, mais les bons programmeurs mettent toujours les parties affichage et traitement dans des classes distinctes.
Nous allons appeler la deuxième classe MoteurCalcul. Elle doit implanter l'interface java.awt.ActionListener qui ne déclare qu'une méthode - actionPerformed(ActionEvent). Java appelle cette méthode de la classe qui implémente l'interface à chaque fois qu'une personne clique sur un bouton.
Crée s'il te plaît cette classe simple :
import
java.awt.event.ActionListener;
public
class
MoteurCalcul implements
ActionListener {
}
Si tu essaies de compiler cette classe (ou simplement de l'enregistrer dans Eclipse), tu obtiens un message d'erreur disant que cette classe doit implanter la méthode actionPerformed(ActionEvent événement). Corrigeons cette erreur :
import
java.awt.event.ActionListener;
import
java.awt.event.ActionEvent;
public
class
MoteurCalcul implements
ActionListener {
public
void
actionPerformed
(
ActionEvent événement) {
// On peut laisser ici une méthode vide, même s'il
// ne se produira rien quand Java l'appellera
}
}
La version suivante de cette classe affiche une boîte de message (message box) depuis la méthode actionPerformed(). Tu peux afficher n'importe quel message en utilisant la classe JOptionPane et sa méthode showConfirmDialog(). Par exemple, la classe MoteurCalcul affiche cette boîte de message :
Il y a différentes version de la méthode showConfirmDialog(). Nous allons utiliser celle qui prend quatre arguments. Dans le code ci-dessous, le mot-clé null signifie que cette boîte de message n'a pas de fenêtre mère, le deuxième argument contient le message, le troisième le titre de la boîte de message et le quatrième te permet de choisir le(s) bouton(s) à inclure dans la boîte (PLAIN_MESSAGE signifie que seul un bouton OK est affiché).
import
java.awt.event.ActionListener;
import
java.awt.event.ActionEvent;
import
javax.swing.JOptionPane;
public
class
MoteurCalcul implements
ActionListener {
public
void
actionPerformed
(
ActionEvent événement) {
JOptionPane.showConfirmDialog
(
null
,
"Quelque chose s'est produit..."
,
"Juste un test"
,
JOptionPane.PLAIN_MESSAGE);
}
}
Dans la prochaine section, je t'expliquerai comment compiler et exécuter une nouvelle version de notre calculatrice, qui affichera la boîte de message "Quelque chose s'est produit".
VIII-C. Enregistrement d'un ActionListener auprès d'un composant▲
Par qui et quand sera appelé le code que nous avons écrit dans la méthode actionPerformed() ? C'est Java qui appellera cette méthode si tu enregistres (ou relies) la classe MoteurCalcul auprès des boutons de la calculatrice ! Ajoute simplement les deux lignes suivantes à la fin du constructeur de la classe Calculatrice.java pour enregistrer notre récepteur d'actions auprès du bouton zéro :
MoteurCalcul moteurCalcul =
new
MoteurCalcul
(
);
bouton0.addActionListener
(
moteurCalcul);
Désormais, chaque fois qu'un utilisateur cliquera sur bouton0, Java appellera la méthode actionPerformed() de l'objet MoteurCalcul. Maintenant, compile et exécute la classe Calculatrice. Clique sur le bouton zéro - ton programme affiche la boîte de message "Quelque chose s'est produit" ! Les autres boutons restent inactifs car ils ne connaissent pas encore notre récepteur d'actions. Continue à ajouter des lignes semblables pour donner vie à tous les boutons :
bouton1.addActionListener
(
moteurCalcul);
bouton2.addActionListener
(
moteurCalcul);
bouton3.addActionListener
(
moteurCalcul);
bouton4.addActionListener
(
moteurCalcul);
&
#8230
;
VIII-D. Quelle est la source d'un événement ?▲
La prochaine étape consiste à rendre notre récepteur un peu plus subtil - il affichera différentes boîtes de message, en fonction du bouton sélectionné. Quand un événement d'action (action event) se produit, Java appelle la méthode actionPerformed (ActionEvent événement) de ta classe récepteur et lui fournit de précieuses informations sur l'événement dans l'argument événement. Tu peux obtenir cette information en appelant les méthodes appropriées de cet objet.
VIII-D-1. Conversion de type explicite▲
Dans l'exemple suivant, nous appelons la méthode getSource() de la classe ActionEvent pour savoir sur quel bouton a appuyé l'utilisateur - la variable événement est une référence à cet objet qui vit quelque part dans la mémoire de l'ordinateur. Mais d'après la documentation Java, cette méthode retourne la source de l'événement sous la forme d'une instance du type Object, qui est la superclasse de toutes les classes Java, y compris les composants de fenêtre. C'est fait ainsi de façon à avoir une méthode universelle qui fonctionne avec tous les composants. Mais nous savons bien que, dans notre fenêtre, seuls les boutons peuvent être à l'origine de l'événement d'action ! C'est pourquoi nous effectuons une conversion de type explicite (casting) de l'Object retourné en un JButton, en indiquant le type (JButton) entre parenthèses devant l'appel de la méthode :
JButton boutonCliqué =
(
JButton) événement.getSource
(
);
Nous déclarons une variable de type JButton à la gauche du signe égale et, bien que la méthode getSource() retourne des données du type Object, nous disons à Java : Ne t'inquiète pas, je suis certain d'obtenir une instance de JButton.
C'est seulement après avoir effectué la conversion d'Object en JButton que nous avons le droit d'appeler la méthode getText() définie par la classe JButton.
import
java.awt.event.ActionListener;
import
java.awt.event.ActionEvent;
import
javax.swing.JOptionPane;
import
javax.swing.JButton;
public
class
MoteurCalcul implements
ActionListener {
public
void
actionPerformed
(
ActionEvent événement) {
// Retrouve la source de l'action
JButton boutonCliqué =
(
JButton) événement.getSource
(
);
// Retrouve le libellé du bouton
String libelléBoutonCliqué =
boutonCliqué.getText
(
);
// Concatène le libellé du bouton au texte
// de la boîte de message
JOptionPane.showConfirmDialog
(
null
,
"Tu as appuyé sur "
+
libelléBoutonCliqué,
"Juste un test"
,
JOptionPane.PLAIN_MESSAGE);
}
}
Maintenant, si tu appuies par exemple sur le bouton cinq, tu obtiendras la boîte de message suivante :
Mais que faire si les événements d'une fenêtre proviennent non seulement des boutons, mais aussi d'autres composants ? Nous ne voulons pas convertir n'importe quel objet en JButton ! Pour traiter ces cas, tu dois utiliser l'opérateur Java spécial instanceof pour effectuer la conversion de type appropriée. L'exemple suivant vérifie d'abord quel type d'objet est à l'origine de l'événement, puis effectue une conversion de type, soit en JButton soit en JTextField :
public
void
actionPerformed
(
ActionEvent événement) {
JTextField monChampAffichage =
null
;
JButton boutonCliqué =
null
;
Object sourceEvénement =
événement.getSource
(
);
if
(
sourceEvénement instanceof
JButton) {
boutonCliqué =
(
JButton) sourceEvénement;
}
else
if
(
sourceEvénement instanceof
JTextField) {
monChampAffichage =
(
JTextField) sourceEvénement;
}
}
Notre calculatrice doit exécuter différentes portions de code pour les différents boutons. Le fragment de code suivant montre comment faire.
public
void
actionPerformed
(
ActionEvent événement) {
Object source =
événement.getSource
(
);
if
(
source ==
boutonPlus) {
// Insère ici le code qui ajoute deux nombres
}
else
if
(
source ==
boutonMoins) {
// Insère ici le code qui soustrait deux nombres
}
else
if
(
source ==
boutonDiviser) {
// Insère ici le code qui divise deux nombres
}
else
if
(
source ==
boutonMultiplier) {
// Insère ici le code qui multiplie deux nombres
}
}
VIII-E. Comment passer des données entre classes ▲
En fait, quand tu appuies sur une touche numérique d'une calculatrice réelle, elle n'affiche pas une boîte de message, mais plutôt le chiffre dans le champ textuel en haut. Voici un nouveau challenge - il nous faut parvenir à atteindre l'attribut displayField de la classe Calculatrice depuis la méthode actionPerformed() de la classe MoteurCalcul. C'est faisable si nous définissons, dans la classe MoteurCalcul, une variable qui stockera une référence à l'instance de l'objet Calculatrice.
Nous allons déclarer un constructeur dans la prochaine version de la classe MoteurCalcul. Ce constructeur a un argument de type Calculatrice. Ne sois pas étonné, les types des arguments de méthodes peuvent être des classes que tu as toi-même créées !
Java exécute le constructeur de l'instance de MoteurCalcul au cours de sa création en mémoire. La classe Calculatrice instancie le MoteurCalcul et passe au constructeur du moteur une référence à elle-même :
MoteurCalcul moteurCalcul =
new
MoteurCalcul
(
this
);
Cette référence contient l'adresse de la calculatrice en mémoire. Le constructeur du moteur stocke cette valeur dans la variable membre parent et enfin l'utilise dans la méthode actionPerformed() pour accéder au champ textuel de la calculatrice.
parent.champAffichage.getText
(
);
&
#8230
;
parent.champAffichage.setText
(
textChampAffichage +
libelléBoutonCliqué);
Ces deux lignes sont extraites de l'exemple de code suivant.
import
java.awt.event.ActionListener;
import
java.awt.event.ActionEvent;
import
javax.swing.JButton;
public
class
MoteurCalcul implements
ActionListener {
Calculatrice parent; // une référence à la Calculatrice
// Le constructeur stocke la référence à la fenêtre
// Calculatrice dans la variable membre parent
MoteurCalcul
(
Calculatrice parent) {
this
.parent =
parent;
}
public
void
actionPerformed
(
ActionEvent événement) {
// Retrouve la source de cette action
JButton boutonCliqué =
(
JButton) événement.getSource
(
);
// Retrouve le texte existant dans le champ Affichage
// de la Calculatrice
String texteChampAffichage =
parent.champAffichage.getText
(
);
// Retrouve le libellé du bouton
String libelléBoutonCliqué =
boutonCliqué.getText
(
);
// Affecte le nouveau texte au champ Affichage
parent.champAffichage.setText
(
texteChampAffichage +
libelléBoutonCliqué);
}
}
Quand tu déclares une variable pour stocker une référence à l'instance d'une classe particulière, cette variable doit être du type de cette classe ou de l'une de ses superclasses. En Java, chaque classe hérite de la classe Object. Si la classe Poisson est une sous-classe de AnimalFamilier, toutes ces lignes sont correctes : Poisson monPoisson = new Poisson(); AnimalFamilier monPoisson = new Poisson(); Object monPoisson = new Poisson();
VIII-F. Fin de la calculatrice▲
Intéressons-nous à quelques règles (un algorithme) concernant la façon dont notre calculatrice doit fonctionner :
- L'utilisateur entre tous les chiffres du premier nombre.
- Si l'utilisateur tape l'un des boutons d'action +, -, / ou *, alors stocker le premier nombre et l'action sélectionnée dans des variables membres, puis effacer le nombre du champ textuel.
- L'utilisateur entre un deuxième nombre et appuie sur le bouton égale.
- Convertir la valeur de type String du champ textuel dans le type numérique double pour pouvoir stocker de grands nombres décimaux. Exécuter l'action sélectionnée en utilisant cette valeur et le nombre stocké dans la variable à l'étape 2.
- Afficher le résultat de l'étape 4 dans le champ textuel et stocker cette valeur dans la variable utilisée à l'étape 2.
Nous allons programmer toutes ces actions dans la classe MoteurCalcul. En lisant le code ci-dessous, rappelle-toi que la méthode actionPerformed() est appelée après chaque clic sur un bouton et qu'entre ces appels de méthode les données sont stockées dans les variables actionSélectionnée et résultatCourant.
import
java.awt.event.ActionListener;
import
java.awt.event.ActionEvent;
import
java.text.NumberFormat;
import
java.text.ParsePosition;
import
javax.swing.JButton;
public
class
MoteurCalcul implements
ActionListener {
Calculatrice parent; // une référence à la Calculatrice
char
actionSélectionnée =
' '
; // +, -, /, ou *
double
résultatCourant =
0
;
NumberFormat formatNombres =
NumberFormat.getInstance
(
);
// un objet capable de lire et présenter les nombres
// Le constructeur stocke la référence à la fenêtre
// Calculatrice dans la variable membre parent
MoteurCalcul
(
Calculatrice parent) {
this
.parent =
parent;
}
public
void
actionPerformed
(
ActionEvent événement) {
// Retrouve la source de l'action
JButton boutonCliqué =
(
JButton) événement.getSource
(
);
String texteChampAffichage =
parent.champAffichage.getText
(
);
double
valeurAffichée =
0
;
// Retrouve le nombre présenté dans le champ texte
// s'il n'est pas vide
if
(!
""
.equals
(
texteChampAffichage)) {
valeurAffichée =
// analyse la chaîne de caractères
formatNombres.parse
(
texteChampAffichage,
new
ParsePosition
(
0
) /* ne sert pas */
).
// puis donne sa valeur en tant que double
doubleValue
(
);
}
Object sourceEvénement =
événement.getSource
(
);
// Pour chaque bouton d'action, mémorise l'action
// sélectionnée, +, -, /, ou *, stocke la valeur courante
// dans la variable résultatCourant et vide le champ
// Affichage avant l'entrée du nombre suivant
Classe MoteurCalcul (partie 1 de 2)
if
(
sourceEvénement ==
parent.boutonPlus) {
actionSélectionnée =
'+'
;
résultatCourant =
valeurAffichée;
parent.champAffichage.setText
(
""
);
}
else
if
(
sourceEvénement ==
parent.boutonMoins) {
actionSélectionnée =
'-'
;
résultatCourant =
valeurAffichée;
parent.champAffichage.setText
(
""
);
}
else
if
(
sourceEvénement ==
parent.boutonDiviser) {
actionSélectionnée =
'/'
;
résultatCourant =
valeurAffichée;
parent.champAffichage.setText
(
""
);
}
else
if
(
sourceEvénement ==
parent.boutonMultiplier) {
actionSélectionnée =
'*'
;
résultatCourant =
valeurAffichée;
parent.champAffichage.setText
(
""
);
}
else
if
(
sourceEvénement ==
parent.boutonEgale) {
// Effectue les calculs en fonction de actionSélectionnée
// Modifie la valeur de la variable résultatCourant
// et affiche le résultat
if
(
actionSélectionnée ==
'+'
) {
résultatCourant +=
valeurAffichée;
// Convertit le résultat en le transformant en String
// à l'aide de formatNombres
parent.champAffichage.setText
(
formatNombres.format
(
résultatCourant));
}
else
if
(
actionSélectionnée ==
'-'
) {
résultatCourant -=
valeurAffichée;
parent.champAffichage.setText
(
formatNombres.format
(
résultatCourant));
}
else
if
(
actionSélectionnée ==
'/'
) {
résultatCourant /=
valeurAffichée;
parent.champAffichage.setText
(
formatNombres.format
(
résultatCourant));
}
else
if
(
actionSélectionnée ==
'*'
) {
résultatCourant *=
valeurAffichée;
parent.champAffichage.setText
(
formatNombres.format
(
résultatCourant));
}
}
else
{
// Pour tous les boutons numériques, ajoute le libellé // du bouton au champ texte
String libelléBoutonCliqué =
boutonCliqué.getText
(
);
parent.champAffichage.setText
(
texteChampAffichage +
libelléBoutonCliqué);
}
}
}
Classe MoteurCalcul (partie 2 de 2)
La version finale de la fenêtre de la calculatrice ressemblera à ceci :
La classe Calculatrice effectue les étapes suivantes :
- Créer et afficher tous les composants de la fenêtre.
- Créer une instance du récepteur d'événements MoteurCalcul.
- Passer au moteur une référence à elle-même.
- Enregistrer le moteur en tant que récepteur auprès de tous les composants qui peuvent générer des événements.
Voici la version finale de la classe Calculatrice :
import
javax.swing.*;
import
java.awt.GridLayout;
import
java.awt.BorderLayout;
public
class
Calculatrice {
// Déclare et instancie les composants de la fenêtre
JButton bouton0 =
new
JButton
(
"0"
);
JButton bouton1 =
new
JButton
(
"1"
);
JButton bouton2 =
new
JButton
(
"2"
);
JButton bouton3 =
new
JButton
(
"3"
);
JButton bouton4 =
new
JButton
(
"4"
);
JButton bouton5 =
new
JButton
(
"5"
);
JButton bouton6 =
new
JButton
(
"6"
);
JButton bouton7 =
new
JButton
(
"7"
);
JButton bouton8 =
new
JButton
(
"8"
);
JButton bouton9 =
new
JButton
(
"9"
);
JButton boutonVirgule =
new
JButton
(
","
);
JButton boutonEgale =
new
JButton
(
"="
);
JButton boutonPlus =
new
JButton
(
"+"
);
JButton boutonMoins =
new
JButton
(
"-"
);
Classe Calculatrice (partie 1 de 3)
JButton boutonDiviser =
new
JButton
(
"/"
);
JButton boutonMultiplier =
new
JButton
(
"*"
);
JPanel contenuFenêtre =
new
JPanel
(
);
JTextField champAffichage =
new
JTextField
(
30
);
// Constructeur
Calculatrice
(
) {
// Affecte le gestionnaire de disposition pour ce panneau
BorderLayout disposition =
new
BorderLayout
(
);
contenuFenêtre.setLayout
(
disposition);
// Ajoute le champ d'affichage en haut de la fenêtre
contenuFenêtre.add
(
"North"
, champAffichage);
// Crée le panneau avec le quadrillage qui contient
// 12 boutons - les 10 boutons numériques et ceux
// représentant la virgule et le signe égale
JPanel panneauChiffres =
new
JPanel
(
);
GridLayout dispositionChiffres =
new
GridLayout
(
4
, 3
);
panneauChiffres.setLayout
(
dispositionChiffres);
panneauChiffres.add
(
bouton1);
panneauChiffres.add
(
bouton2);
panneauChiffres.add
(
bouton3);
panneauChiffres.add
(
bouton4);
panneauChiffres.add
(
bouton5);
panneauChiffres.add
(
bouton6);
panneauChiffres.add
(
bouton7);
panneauChiffres.add
(
bouton8);
panneauChiffres.add
(
bouton9);
panneauChiffres.add
(
bouton0);
panneauChiffres.add
(
boutonVirgule);
panneauChiffres.add
(
boutonEgale);
// Ajoute le panneau des chiffres à la zone centrale
// de la fenêtre
contenuFenêtre.add
(
"Center"
, panneauChiffres);
// Crée le panneau avec le quadrillage qui contient 4
// boutons d'opération - Plus, Moins, Diviser, Multiplier
JPanel panneauOpérations =
new
JPanel
(
);
GridLayout dispositionOpérations =
new
GridLayout
(
4
, 1
);
panneauOpérations.setLayout
(
dispositionOpérations);
panneauOpérations.add
(
boutonPlus);
panneauOpérations.add
(
boutonMoins);
panneauOpérations.add
(
boutonMultiplier);
panneauOpérations.add
(
boutonDiviser);
Classe Calculatrice (partie 2 de 3)
// Ajoute le panneau des opérations à la zone est
// de la fenêtre
contenuFenêtre.add
(
"East"
, panneauOpérations);
// Crée le cadre et lui affecte son contenu
JFrame frame =
new
JFrame
(
"Calculatrice"
);
frame.setContentPane
(
contenuFenêtre);
// Affecte à la fenêtre des dimensions suffisantes pour
// prendre en compte tous les contrôles
frame.pack
(
);
// Affiche la fenêtre
frame.setVisible
(
true
);
// Instancie le récepteur d'événements et l'enregistre
// auprès de chaque bouton
MoteurCalcul moteurCalcul =
new
MoteurCalcul
(
this
);
bouton0.addActionListener
(
moteurCalcul);
bouton1.addActionListener
(
moteurCalcul);
bouton2.addActionListener
(
moteurCalcul);
bouton3.addActionListener
(
moteurCalcul);
bouton4.addActionListener
(
moteurCalcul);
bouton5.addActionListener
(
moteurCalcul);
bouton6.addActionListener
(
moteurCalcul);
bouton7.addActionListener
(
moteurCalcul);
bouton8.addActionListener
(
moteurCalcul);
bouton9.addActionListener
(
moteurCalcul);
boutonVirgule.addActionListener
(
moteurCalcul);
boutonPlus.addActionListener
(
moteurCalcul);
boutonMoins.addActionListener
(
moteurCalcul);
boutonDiviser.addActionListener
(
moteurCalcul);
boutonMultiplier.addActionListener
(
moteurCalcul);
boutonEgale.addActionListener
(
moteurCalcul);
}
public
static
void
main
(
String[] args) {
// Instancie la classe Calculatrice
Calculatrice calc =
new
Calculatrice
(
);
}
}
Classe Calculatrice (partie 3 de 3)
Maintenant, compile le projet et exécute la classe Calculatrice. Elle marche presque comme les calculatrices du monde réel.
Félicitations ! C'est ton premier programme utilisable par plein d'autres personnes - fais-en cadeau à tes amis.
Pour mieux comprendre comment fonctionne ce programme, je te recommande de te familiariser avec le déboguage (debugging) de programmes. Je te prie de lire l'Annexe B sur le débogueur et de revenir ici ensuite.
VIII-G. Autres récepteurs d'événements▲
Il y a dans le paquetage java.awt d'autres récepteurs Java qu'il est bon de connaître :
- Le récepteur d'activation (FocusListener) envoie un signal à ta classe quand un composant devient actif ou inactif. Par exemple, on dit qu'un champ textuel est actif si il a un curseur clignotant.
- Le récepteur de changement d'élément (ItemListener) réagit à la sélection d'éléments dans une liste ou une liste déroulante (combobox).
- Le récepteur de touche (KeyListener) répond à la frappe des touches du clavier.
- Le récepteur de souris (MouseListener) réagit si on clique sur la souris ou si le pointeur entre ou sort de la surface d'un composant de la fenêtre.
- Le récepteur de mouvement de souris (MouseMotionListener) indique les déplacements ou les glissements de la souris. Glisser (to drag) signifie déplacer la souris tout en maintenant son bouton enfoncé.
- Le récepteur de fenêtre (WindowListener) te prévient lorsque l'utilisateur ouvre, ferme, iconise ou active une fenêtre.
La table suivante indique pour chaque récepteur le nom de son interface et les méthodes qu'elle déclare.
Interface | Méthodes à implanter |
---|---|
FocusListener ItemListener KeyListener MouseListener MouseMotionListener WindowListener | focusGained(FocusEvent) focusLost(FocusEvent) itemStateChanged(ItemEvent) keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent) mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent) mouseDragged(MouseEvent) mouseMoved(MouseEvent) windowActivated(WindowEvent) windowClosed(WindowEvent) windowClosing(WindowEvent) windowDeactivated (WindowEvent) windowDeiconified(WindowEvent) windowIconified(WindowEvent) windowOpened(WindowEvent) |
Par exemple, l'interface FocusListener déclare deux méthodes : focusGained() et focusLost(). Cela implique que, même si ta classe n'est intéressée que par le traitement des événements correspondant à l'activation d'un champ particulier - et n'a donc besoin d'implanter que focusGained(), tu dois aussi inclure la méthode vide focusLost(). Cela peut être pénible, mais Java fournit des classes adapteurs (adapter classes) spéciales pour chaque récepteur afin de faciliter le traitement des événements.
VIII-H. Utilisation des adapteurs▲
Disons que tu as besoin d'enregistrer des données sur le disque quand l'utilisateur ferme la fenêtre. Selon la table ci-dessus, la classe qui implémente l'interface WindowsListener doit inclure sept méthodes. C'est-à-dire que tu dois écrire le code de la méthode windowClosing() et en outre inclure six méthodes vides.
Le paquetage java.awt fournit des adapteurs, qui sont des classes qui implantent déjà toutes les méthodes requises (les corps de ces méthodes sont vides). L'une de ces classes est nommée WindowAdapter. Tu peux créer la classe qui doit traiter les événements en héritant de WindowAdapter et en redéfinissant juste les méthodes qui t'intéressent, comme la méthode windowClosing().
import
java.awt.event.WindowAdapter;
import
java.awt.event.WindowEvent;
class
MonProcesseurEvénements extends
WindowAdapter {
public
void
windowClosing
(
WindowEvent événement) {
// Insère ici le code qui enregistre
// les données sur disque
}
}
Le reste est facile - enregistre simplement cette classe comme récepteur d'événements dans la classe de la fenêtre :
MonProcesseurEvénements monRécepteur =
new
MonProcesseurEvénements
(
);
addWindowListener
(
monRécepteur);
On peut obtenir le même résultat en utilisant ce qu'on appelle des classes internes anonymes (anonymous inner classes), mais c'est un sujet un peu trop compliqué pour ce livre.
VIII-I. Autres lectures ▲
Ecriture de récepteurs d'événements :http://java.sun.com/ (...) /events/ |
VIII-J. Exercices ▲
VIII-K. Exercices pour les petits malins▲
Modifie la classe MoteurCalcul pour qu'on ne puisse plus saisir plus d'une virgule dans le nombre. Indice : jette un œil à la méthode indexOf() de la classe String ; elle te permettra de vérifier si le champ contient déjà une virgule. |