PROGRAMMATION SWING ET XML

I. CONCEPTS ET CARACTERISTIQUES DE SWING

3. La gestion des événements (Event handling).

A chaque fois que l'utilisateur presse un bouton, appuie sur une touche ou déplace la souris, un événement est crée par la JVM. Pour qu'un objet puisse accéder à ces événements, il faut qu'il implémente une interface lui permettant d'accueillir ce type d'événement. En effet, il existe une multitude d'événements possible (cf le tableau ci-contre), et les objets intéressés doivent implémenter l'interface particulière les intéressant. 

Le mécanisme général, est celui de l'abonneur/abonné, c'est-à-dire qu'il existe un abonneur (l'objet qui génére les évenements, event source) et un ou des abonnés (objets qui sont intéressés par ce type d'événement, event listener) qui s'enregistre auprès de cet abonneur.

Action provoquant l'événement
Type de listener
L'utilisateur clique sur un bouton de souris, tape sur une touche ...
ActionListener
L'utilisateur ferme ou iconifie une fenetre
WindowListener
L'utilisateur presse un bouton au dessus d'un composant
MouseListener
Détection du mouvement de la souris
MouseMotionListener
Un composant devient visible
ComponentListener
Un composant obtient le focus
FocusListener
Changement de la sélection dans une liste
ListSelectionListener

Comme le montre le tableau, chaque type d'événement est représenté par un objet qui détient les informations concernant l'événement (par exemple, le contenu de la touche ayant été actionné) ainsi que le producteur de l'événement (par exemple, une zone de saisie). Les producteurs d'événements sont généralements des composants atomiques - comme les zones de saisies JTextField, les listes de choix JComboBox, ou les menus JMenu - mais peuvent etre aussi n'importe quel objet non Swing implémentant les mécanismes d'abonnements (i.e. addXXXListener(XXXListener) et removeXXXListener(XXXListener)).

Dans ce mécanisme, chaque abonneur peut avoir plusieurs abonnés, et réciproquement chaque abonné peut s'abonner auprès de plusieurs abonneurs (ne produisant pas forcément le meme type d'événement). Voici un petit schéma permettant de synthétiser tout cela :

Mécanisme abonneur/abonné

Comment implémenter un traitement d'événement (Event handler) :

Pour mettre en place un listener pour un type d'événement donné, trois étapes sont nécessaires :

  1. Déclarer la classe comme implémentant ou étendant l'interface du listener concerné, par exemple :
public class MyClass implements ActionListener { ... }
  1. Abonner une instance de notre classe aux composants producteurs d'événements :
someComponent.addActionListener(instanceOfMyClass);
  1. Effectuer le traitement associé à cet événement en écrivant le code de la méthode de l'interface que l'on implémente :
public void actionPerformed(ActionEvent evt){
// code de traitement de l'événement evt
}

Parfois on utilise des classes internes (voire anonymes) pour effectuer le traitement d'événement dans la classe meme ou à lieu l'abonnement. Cela permet de ne pas éclater et alourdir le code (les classes internes pouvant accéder directement aux attributs de la classe englobante).

Le traitement d'événement et le flux d'exécution :

Le code associé aux événements est exécuté dans un unique thread : le thread de distribution des événements (event-dispatching thread). Cela permet d'assurer que chaque traitement est fini avant de passer au suivant. Cela signifie que tant que l'on est pas sorti de la méthode actionPerformed (dans l'exemple précédant), l'affichage est gelé - et ne se rafraichira pas et ne répondra pas à un autre événement !

Important: Cela entraine que le code de traitement d'événement doit s'exécuter très rapidement ! Sinon, l'utilisateur décelera des temps de latence, un manque de réactivité et trouvera l'interface peu ergonomique ... Si on est dans le cas ou le traitement d'événement est long, il faut alors créer un nouveau thread qui se chargera d'effectuer ce traitement en dehors de l'event-dispatching thread.