Programmation Java pour les enfants, les parents et les grands-parents


précédentsommairesuivant

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.

Image non disponible

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 :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

Image non disponible

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é).

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
bouton1.addActionListener(moteurCalcul);
bouton2.addActionListener(moteurCalcul);
bouton3.addActionListener(moteurCalcul);
bouton4.addActionListener(moteurCalcul);
…

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 :

 
Sélectionnez
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.

Image non disponible

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.

 
Sélectionnez
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 :

Image non disponible

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 :

 
Sélectionnez
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.

 
Sélectionnez
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 :

 
Sélectionnez
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.

 
Sélectionnez
parent.champAffichage.getText();
… 
parent.champAffichage.setText(textChampAffichage + 
                                      libelléBoutonCliqué);

Ces deux lignes sont extraites de l'exemple de code suivant.

 
Sélectionnez
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 :

  1. L'utilisateur entre tous les chiffres du premier nombre.
  2. 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.
  3. L'utilisateur entre un deuxième nombre et appuie sur le bouton égale.
  4. 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.
  5. 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.

 
Sélectionnez
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)

 
Sélectionnez
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 :

Image non disponible

La classe Calculatrice effectue les étapes suivantes :

  1. Créer et afficher tous les composants de la fenêtre.
  2. Créer une instance du récepteur d'événements MoteurCalcul.
  3. Passer au moteur une référence à elle-même.
  4. 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 :

 
Sélectionnez
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)

 
Sélectionnez
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)

 
Sélectionnez
// 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().

 
Sélectionnez
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 :

 
Sélectionnez
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

Image non disponible

Ecriture de récepteurs d'événements :http://java.sun.com/ (...) /events/

VIII-J. Exercices

Image non disponible

Essaie de diviser un nombre par zéro à l'aide de notre calculatrice - le champ textuel affiche un signe ? qui veut dire "infini". Modifie la classe MoteurCalcul pour afficher le message "Impossible de diviser par zéro" si l'utilisateur tente une division par zéro.

VIII-K. Exercices pour les petits malins

Image non disponible

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.


précédentsommairesuivant
NDT on rencontre fréquemment aussi le terme implémenter, mais implanter est la recommandation officielle.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2015 Yakov Fain. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.