Programmation Java Swing/Gestion des évènements
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 classejava.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 classetypeEvent
. - 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)
.
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 :