Aller au contenu

Programmation Java Swing/Gestion des évènements

Un livre de Wikilivres.

Un composant peut recevoir divers évènements issus de l'interaction avec l'utilisateur :

  • Avec la souris : enfoncement et relâchement d'un bouton, déplacement de la souris, défilement de la molette.
  • Avec le clavier : enfoncement et relâchement d'une touche, génération d'un caractère.
  • Avec d'autres périphériques d'entrée.

Il peut aussi en générer :

  • Un bouton peut notifier le déclenchement d'une action.
  • La sélection d'un item dans une liste peut déclencher un évènement.
  • ...

Principes de gestion des évènements

[modifier | modifier le wikicode]

De manière générale, la gestion des évènements en Java se fait de la manière suivante :

  • Les classes voulant recevoir une notification d'un évènement enregistrent un écouteur auprès de la source d'évènements. L'écouteur est un objet dont la classe implémente une interface particulière pour traiter un évènement comportant en général une seule méthode appelée pour notifier l'évènement qui s'est produit.
  • La source d'évènement (un composant, un périphérique d'entrée : souris ou clavier, ...) notifie les écouteurs enregistrés en appelant la méthode de l'interface particulière. Cette méthode ne comporte en général qu'un seul argument dont la classe dérive de la classe java.util.EventObject.

L'exemple ci-dessous illustre comment recevoir un évènement d'action sur un bouton, qui se déclenche au clic avec la souris, ou au clavier quand le bouton a le focus.

JButton b_ok = new JButton("OK");
b_ok.addActionListener(new ActionListener()
{
	@Override
	public void actionPerformed(ActionEvent e)
	{
		// ... Action du bouton OK ici
		System.out.println("Clic du bouton OK");
	}
});

La convention de gestion des évènements est basée sur celle des JavaBeans. Pour un évènement de type type (MouseWheel, Key, Mouse, ...) :

  • Une classe d'évènement typeEvent dérivant de la classe java.util.EventObject contient toutes les informations sur l'évènement notifié.
  • Un écouteur doit implémenter l'interface typeListener dont la ou les méthode(s) ont comme seul argument un objet de classe typeEvent.
  • La source d'évènement possède deux méthodes publiques :
    • addtypeListener pour ajouter un écouteur de l'évènement.
    • removetypeListener pour retirer un écouteur.
  • En général, en interne, la source d'évènement notifie les écouteurs enregistrés avec une méthode privée ou protégée nommée firetypeEvent.

Les différents types d'évènements

[modifier | modifier le wikicode]

Il existe un grand nombre de types d'évènements représentés par des classes dérivant de la classe java.util.EventObject. Cette section ne citera que les principaux types parmi ceux concernant les interfaces graphiques avec Swing.

La hiérarchie des types d'évènements contient un certain nombre de classes, chacune pouvant ajouter une ou plusieurs informations sur l'évènement.

Classe Information(s) sur l'évènement définie(s)/ajoutée(s) par la classe
java.util.EventObject Source de l'évènement : public Object getSource();
└─ java.awt.AWTEvent Identification de l'évènement (ID), consommation de l'évènement
├─ java.awt.event.ActionEvent Heure de l'évènement et modificateurs (boutons de souris, touches enfoncées : Shift, Alt, Ctrl...)
(ID: ACTION_PERFORMED)
└─ java.awt.event.ComponentEvent Composant source de l'évènement (conversion de type)
└─ java.awt.event.InputEvent Heure de l'évènement et modificateurs (boutons de souris, touches enfoncées : Shift, Alt, Ctrl...)
├─ java.awt.event.KeyEvent Code de la touche, caractère généré
(ID: KEY_TYPED, KEY_PRESSED, KEY_RELEASED)
└─ java.awt.event.MouseEvent Position de la souris, nombre de clics, indicateur de menu popup
(ID: MOUSE_CLICKED, MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_MOVED, MOUSE_ENTERED, MOUSE_EXITED, MOUSE_DRAGGED, MOUSE_WHEEL)
└─ java.awt.event.MouseWheelEvent Rotation de la molette de la souris (nombre, pixels de défilement)

Les sous-classes de la classe java.awt.AWTEvent ont un attribut (ID) qui identifie une notification particulière d'un évènement, chacune correspondant à une méthode de l'interface d'écoute de l'évènement. Par exemple, pour les évènements provenant de la souris, les méthodes de l'interface java.awt.event.MouseListener sont appelées avec un objet évènement de classe java.awt.event.MouseEvent dont la valeur de l'identificateur (ID) correspond à celles-ci :

public void mouseReleased(MouseEvent e); // e.getID() == MouseEvent.MOUSE_RELEASED
public void mousePressed(MouseEvent e);  // e.getID() == MouseEvent.MOUSE_PRESSED
public void mouseExited(MouseEvent e);   // e.getID() == MouseEvent.MOUSE_EXITED
public void mouseEntered(MouseEvent e);  // e.getID() == MouseEvent.MOUSE_ENTERED
public void mouseClicked(MouseEvent e);  // e.getID() == MouseEvent.MOUSE_CLICKED

Même principe pour les méthodes de l'interface java.awt.event.MouseMotionListener pour intercepter les évènements de mouvement de la souris :

public void mouseMoved(MouseEvent e);   // e.getID() == MouseEvent.MOUSE_MOVED    (Souris déplacée sans bouton enfoncé)
public void mouseDragged(MouseEvent e); // e.getID() == MouseEvent.MOUSE_DRAGGED  (Souris déplacée avec bouton enfoncé)

Cela s'applique aussi aux interfaces qui n'ont qu'une seule méthode, comme l'interface java.awt.event.MouseWheelListener pour la notification de rotation de molette de la souris :

public void mouseWheelMoved(MouseWheelEvent e); // e.getID() == MouseEvent.MOUSE_WHEEL

Cette redondance apparente d'identification du type d'évènement, à la fois par la méthode appelée et par la valeur de l'attribut ID, facilite la réutilisation d'une même méthode pour traiter plusieurs évènements de façon similaire tout en distinguant les évènements.

Implémentation partielle

[modifier | modifier le wikicode]

Les interfaces d'écoute définissant au moins deux méthodes possèdent une classe dont le nom est celui de l'interface où l'on remplace le suffixe Listener par Adapter. Cette classe définit des méthodes vides et permet aux applications de la surcharger pour ne définir que certaines méthodes de l'interface.

Exemple :

addWindowListener(new WindowAdapter()
{
	@Override
	public void windowClosing(WindowEvent e)
	{
		fermerFenetre();
	}
});

Déclenchement d'une action sur appui d'une touche

[modifier | modifier le wikicode]

Il est courant d'utiliser des raccourcis clavier pour effectuer des actions, comme par exemple utiliser la touche Suppr pour supprimer l'élément sélectionné.

La méthode registerKeyboardAction permettant d'associer une action à un raccourci clavier est devenue obsolète. Elle s'utilisait comme illustré dans le code d'exemple suivant :

// Dans le constructeur d'un composant, par exemple un éditeur de diagrammes
// permettant la suppression d'élément avec la touche Suppr :

// Créer l'action de suppression associée à la touche Suppr.
a_delete = new AbstractAction("Delete")
{
	public void actionPerformed(ActionEvent e)
	{
		// Supprimer le ou les éléments sélectionnés
		deleteSelection();
	}
};

// Déclencher l'action sur appui de la touche [Suppr] (Delete) quand le composant est dans la fenêtre ayant le focus :
// Code obsolète :
registerKeyboardAction(a_delete, "delete", KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE,0), WHEN_IN_FOCUSED_WINDOW);

Cette méthode est remplacée par l'utilisation des méthodes getInputMap() et getActionMap() récupérant les tables associatives permettant, respectivement, d'associer une touche à une commande et une commande à une action.

// Déclencher l'action sur appui de la touche [Suppr] (Delete) quand le composant est dans la fenêtre ayant le focus :
getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE,0), "delete"); // Association touche [Suppr] --> commande "delete"
getActionMap().put("delete", a_delete);  // Association commande "delete" --> action de suppression

La méthode getInputMap(int condition) retourne la table associative pour la condition spécifiée :

WHEN_IN_FOCUSED_WINDOW
Touches prises en compte quand le composant est affiché dans la fenêtre courante. Le composant n'a pas forcément le focus.
WHEN_FOCUSED
Touches prises en compte quand le composant a le focus.
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
Touches prises en compte quand le composant contient le composant qui a le focus.

La méthode getInputMap() appelée sans argument équivaut à getInputMap(WHEN_FOCUSED).

Logo

Certaines touches sont déjà utilisées par les composants Java Swing. Il faut éviter d'utiliser les mêmes car l'action voulue peut ne pas se déclencher ou seulement dans certaines conditions de focus.

Pour éviter un tel conflit, il suffit souvent de combiner la touche avec un modificateur comme Shift (InputEvent.SHIFT_DOWN_MASK) ou Ctrl (InputEvent.CTRL_DOWN_MASK) ou les deux à la fois.

La section suivante liste la plupart des raccourcis de touches pouvant potentiellement entrer en conflit.

Touches utilisées par les composants Swing

[modifier | modifier le wikicode]

Certaines touches sont déjà utilisées par les composants Java Swing pour interagir avec l'interface graphique[source 1] sans utiliser la souris.

Composants Touche Action
ceux permettant d'associer une lettre :

JButton

Alt A..Z Déclencher l'action
JMenuBar Alt Passer le focus à la barre de menus / Rendre le focus au composant précédent.
tous ceux gérant le focus :

JButton, JCheckbox, JRadioButton,

JToggleButton, JComboBox, JList

Passer le focus au composant suivant dans la chaîne
Passer le focus au composant précédent dans la chaîne
Navigation dans le composant
JCheckbox, JRadioButton, JToggleButton   Cocher / décocher
JButton   Déclencher l'action du bouton
JButton Déclencher l'action du bouton par défaut (par exemple : le bouton "OK" d'une boîte de dialogue).
JComboBox Échap Cacher la liste déroulante
JComboBox Alt Afficher la liste déroulante
JComboBox Alt Cacher la liste déroulante
JComboBox, JList Sélectionner l'item suivant dans la liste
JComboBox, JList Sélectionner l'item précédent dans la liste
JSplitPane F8 Passer le focus à la barre de séparation

Source :

  1. https://www.ibm.com/docs/en/sdk-java-technology/8?topic=applications-default-swing-key-bindings