La gestion des évènements


L'Abstract Window Toolkit, l'AWT, est la librairie du JDK 1.0 qui a été la plus touchée par les modifications introduites par la version 1.1. La gestion des évènements a subi une véritable révolution.

L'ancien système avait de nombreux inconvénients : création de nombreuses classes inutiles, séparation entre l'interface utilisateur et les traitements fonctionnels difficile, mauvaises performances,...

Le nouveau modèle se fonde sur la délégation. Les composants "délèguent" la gestion des évènements utilisateur à une classe extérieure.

Nous avons 3 types d'objets qui effectuent cette gestion :

Il faudra relier chaque source d'évènements à son délégué (qui traîte l'évènement). Concrètement, chaque délégué doit s'enregistrer auprès de sa source. Pour cela, les sources d'évènements définissent des méthodes d'enregistrement du type setTypeEvtListener et addTypeEvtListener.

Mais un exemple va vite clarifier cela :

import java.awt.*; // pour pouvoir accéder à Frame et à Button
import java.awt.event.*; // pour pourvoir accéder à ActionListener

public class IHMSimpliste extends Frame{
	public IHMSimpliste() {
		Button monBouton = new Button("Tshaw !"); // ajout du bouton à l'interface
		// La méthode suivante enregistre le délégué auprès du bouton après l'avoir créé.
		// tous les composants standard de l'AWT permettent la diffusion multiple (multi-cast)
		// c'est pour cela qu'il n'existe que des méthodes de type addListener.
		monBouton.addActionListener(new MonDelegue()); 
		add(monBouton);
	}

	public static void main(String args[]) { // le point d'entrée
		Frame f = new IHMSimpliste(); // on crée la fenêtre
		f.pack(); // on l'affiche
		f.setVisible(true); //remplace l'ancien show()
	}
        
}

// ceci est la classe déléguée, gérant les événements sur le bouton
class MonDelegue implements ActionListener {
	public void actionPerformed(ActionEvent e) {
		System.exit(0);
	}
}

Ceci peut être grandement simplifié grâce aux classes internes :

import java.awt.*; // pour pouvoir accéder à Frame et à Button
import java.awt.event.*; // pour pourvoir accéder à ActionListener

public class IHMSimpliste1 extends Frame{
	public IHMSimpliste1() {
		Button monBouton = new Button("Tshaw !"); // ajout du bouton à l'interface
		monBouton.addActionListener(
			// on crée une classe anonyme implémentant ActionListener
			new ActionListener() { 
				public void actionPerformed(ActionEvent e) {
					System.exit(0);
				}
			}
		); 
		add(monBouton);
	}

	public static void main(String args[]) { // le point d'entrée
		Frame f = new IHMSimpliste1(); // on crée la fenêtre
		f.pack(); // on l'affiche
		f.setVisible(true); //remplace l'ancien show()
	}
}

Dans la pratique, on essaie de séparer encore mieux l'interface utilisateur du code de traitement en utilisant des adaptateurs. Etudiez l'exemple suivant :

import java.awt.*;
import java.awt.event.*;

public class ApplicationSimpliste { // la classe principale est l'application, et sert de délégué. 
	// elle contient les deux méthodes fonctionnelles
	public void quitterAppli() {
		System.exit(0);
	}
	public void maximiser(Frame f){
		f.setSize(f.getToolkit().getScreenSize());
	}
	
	public static void main(String args[]) {
		// on instancie l'application
		ApplicationSimpliste monAppli = new ApplicationSimpliste() ;
		// on crée l'IHM et on la relie à l'application
		IHMSimpliste monIHM = new IHMSimpliste(monAppli) ;
	}
}

// cette classe constitue l'IHM de l'application
class IHMSimpliste {
	// son constructeur possède comme paramètre l'application à laquelle elle est reliée 
	public IHMSimpliste(ApplicationSimpliste uneAppli) {
		Frame maFrame = new Frame() ;
		maFrame.setLayout(new FlowLayout());
	
		// on crée le bouton Quitter
		Button monBoutonQuitter = new Button("Tshaw!");
		// on lui affecte un adaptateur
		monBoutonQuitter.addActionListener(new MonAdaptateur(MonAdaptateur.QUITTER,uneAppli));
		// on fait pareil avec le bouton maximiser
		Button monBoutonMaximise = new Button("Maximiser");
		monBoutonMaximise.addActionListener(new MonAdaptateur(MonAdaptateur.MAXIMISE,uneAppli));
		// on ajoute les boutons dans l'interface et on rend la fenêtre visible
		maFrame.add(monBoutonQuitter);
		maFrame.add(monBoutonMaximise);
		maFrame.pack();
		maFrame.setVisible(true); //remplace l'ancien show()
	}
}

// l'adaptateur effectue l'aiguillage 
class MonAdaptateur implements ActionListener {
	static final int QUITTER = 1;
	static final int MAXIMISE = 2;
	protected int typeAction; // l'action affectée à l'adaptateur
	protected ApplicationSimpliste lAppli; // le délégué qui va traiter l'action

	public MonAdaptateur(int unTypeAction,ApplicationSimpliste uneAppli) {
		typeAction = unTypeAction;
		lAppli = uneAppli;
	}
	public void actionPerformed(ActionEvent e) {
		// on récupère la fenêtre source de l'événement :  on remonte la chaîne
		// des composants, jusqu'à trouver une instance de la classe Window
		Object unComposant = e.getSource();
		do {
			unComposant = ((Component) unComposant).getParent();
		}
		while (!(unComposant instanceof Window));
		Window fenetreParente = (Window) unComposant;		

		switch (typeAction) {
			case QUITTER :
				// on appelle la méthode quitterAppli du délégué
				lAppli.quitterAppli();
			break;
	 		case MAXIMISE :
				// on appelle la méthode maximiser en passant la Frame 
				// qui contient le composant sur lequel s'est produit l'événement.
				lAppli.maximiser((Frame) fenetreParente);
			break;
		}
	}
}

Consultez les paquetages java.util et java.awt.event pour connaître les différents évènements disponibles.

Enfin, notez qu'il existe des adaptateurs par défauts tels que MouseAdapter qui, en les dérivant, permettent de créer rapidement des adaptateurs sans avoir à implémenter toutes les méthodes de certaines interfaces Listener.