4 Cours ENPC, printemps 1999
Java, objets, interfaces et Internet

Le modèle événementiel

La programmation d'interfaces graphiques recourt à la notion d'événement pour représenter les interactions entre l'interface et l'utilisateur. Le modèle événementiel de Java (version 1.1) fait intervenir trois catégories d'objets :

  • l'objet source de l'événement
  • l'objet événement ; la méthode getSource détermine sa source
  • les objets récepteurs, qui doivent avoir été enregistrés comme tels par la source pour que les événements leur soient notifiés.

Les objets événements

Les événements sont des objets d'une classe dérivée de java.util.EventObject. La sous-classe java.awt.AWTEvent est celle des événements créés par des interactions avec les composantes d'interface : bouton pressé, clic souris, changement de taille des fenêtres, touches du clavier pressées, etc. Quelques unes de ses sous-classes sont :

Dans une classe d'événements donnée, les événements particuliers sont identifiés par des constantes de classe. Par exemple, la classe WindowEvent définit les constantes WINDOW_CLOSING, WINDOW_ICONIFIED, WINDOW_DEICONIFIED, etc.

D'autres classes d'événements peuvent être définies selon le même modèle pour représenter des situations importantes qui doivent être notifiées pour que certaines actions soient déclenchées, par exemple, quand la valeur d'une certaine variable est modifiée.

Les objets récepteurs

Un récepteur d'événement est un objet d'une classe quelconque qui implémente une interface ...Listener, dérivée de l'interface java.util.EventListener et dépendante de la catégorie des événements notifiés :

Une telle interface déclare un certain nombre de méthodes qui doivent être implémentées par la classe du récepteur. Par exemple, l'interface ActionListener déclare la méthode

void actionPerformed(ActionEvent e);

L'interface MouseListener déclare les cinq méthodes suivantes :

void mouseClicked(MouseEvent e);
void mouseEntered(MouseEvent e);
void mouseExited(MouseEvent e);
void mousePressed(MouseEvent e);
void mouseReleased(MouseEvent e);

C'est en implémentant ces méthodes que l'on programme les actions qui doivent être effectuées quand un événement est notifié. Comme un récepteur peut recevoir la notification d'événements d'une même catégorie, mais provenant de plusieurs sources, il peut être nécessaire de tester la source de l'événement notifié :

  public void actionPerformed(ActionEvent e) {
    if (e.getSource() == ouvrir) {
      ouvrir();
    } else if (e.getSource() == ecrire) {
      ecrire();
    } else if (e.getSource() == sauvegarder) {
      sauvegarder();
    } else {
      quitter();
    }
  }

Un objet peut être la source de plusieurs types d'événements ; pour chacun d'entre eux, il peut enregistrer plusieurs récepteurs, chacun étant responsable d'actions spécifiques.

Enregistrement des récepteurs

Les récepteurs doivent être enregistrés par l'objet source pour être notifiés des événements dont il est à l'origine. L'enregistrement d'un récepteur se fait grâce aux méthodes add..Listener de l'objet source ; l'annulation de l'enregistrement se fait par la méthode remove...Listener correspondante :

source.addActionListener(recepteur);
...
source.removeActionListener(recepteur);
Il est fréquent d'utiliser comme récepteur l'instance d'une classe conteneur des composantes d'interface. Dans ce cas, l'enregistrement se fait à l'aide de la référence this :
source.addActionListener(this);

Un exemple : les événements souris

L'applette suivante affiche la position et l'instant des événements souris.

Voici le squelette du programme [source] :

public class MouseEventDemo extends Applet
  implements MouseListener {
  BlankArea zoneSource;
  TextArea zoneTexte;

  public void init() {
    ...
    // enregistrement comme recepteur des événements souris
    // de source zoneSource
    zoneSource.addMouseListener(this);
  }

  public void mousePressed(MouseEvent e) {
    zoneTexte.append("Mouse pressed"
		     + ", clics = " + e.getClickCount() 
		     + ", (x, y) = (" + e.getX() + ", " + e.getY() + ")"
		     + ", t = " + e.getWhen() + "\r\n");
  }
  
  public void mouseReleased(MouseEvent e) { ... }
  
  public void mouseEntered(MouseEvent e) { ... }

  public void mouseExited(MouseEvent e) { ... }
  
  public void mouseClicked(MouseEvent e) { ... }
}

Adaptateurs et classes internes

Si l'on ne s'intéresse pas à toutes les méthodes déclarées dans l'interface MouseListener, on n'est pas obligé de les implémenter toutes, mais dans ce cas, on ne doit plus spécifier implements MouseListener. On utilise alors la classe MouseAdapter qui implémente l'interface MouseListener par des méthodes vides (c'est-à-dire { ... }). Il suffit alors d'étendre cette classe en redéfinissant uniquement les méthodes auxquelles on s'intéresse. La classe dérivée est une classe interne à la classe en cours de définition, qui est capable d'accéder aux membres de la classe englobante :

public class MouseEventAdapterDemo extends Applet {
  BlankArea zoneSource;
  TextArea zoneTexte;
  
  public void init() {

    // enregistrement d'un récepteur de classe InnerMouseAdapter
    // pour les événements de source zoneSource
    class InnerMouseAdapter extends MouseAdapter {

      public void mousePressed(MouseEvent e) {
        zoneTexte.append("Mouse pressed:"
		         + ", t = " + e.getWhen() + newline);
    }
  }
    zoneSource.addMouseListener(new InnerMouseAdapter());
  }

}

Il est possible de combiner la définition de la classe interne et son instantiation en une seule expression, dite de classe anonyme :

  public void init() {

    zoneSource.addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent e) {
	zoneTexte.append("Mouse pressed"
		         + ", t = " + e.getWhen() + newline);
      }
    });
  }

Un exemple : les événements clavier

L'applette suivante affiche les caractéristiques des événements provenant du clavier.

Voici le squelette du programme [source] :

public class KeyEventDemo extends Applet 
  implements KeyListener {

  TextField zoneSource;
  TextArea zoneTexte;
  
  public void init() {
    ...
    // enregistrement comme récepteur des événements clavier
    // de source zoneSource
    zoneSource.addKeyListener(this);
  }

  public void keyTyped(KeyEvent e) {
    zoneTexte.append("KEY TYPED: ");
    keyDisplay(e);
  }
  
  public void keyPressed(KeyEvent e) { ... }

  public void keyReleased(KeyEvent e) { ... }

  protected void keyDisplay(KeyEvent e){
    String
      charString = "caractère = '" + e.getKeyChar(),
      keyCodeString = "code = " + e.getKeyCode(),
      modString = "modif. = " + e.getModifiers();

    zoneTexte.append("\n"
                     + charString + ", "
		     + keyCodeString +  ", "
		     + modString + "\n");
  }
}


[Cours Java] [Notes 3] [Notes 5]

Mise à jour :