Ch. 2-4 — Programmation événementielle
Événements (source, cible, type), abonnement et lambdas, route capture/bubbling, Canvas et CSS JavaFX — puis MVC avec ObservableList, ListView et ListChangeListener.
Événementiel vs procédural
En programmation procédurale, le déroulement est contrôlé par une séquence d'instructions écrites : le programmeur écrit lui-même la boucle principale — initialisations, puis répéter « lire une commande / traiter une commande » jusqu'à la commande finir. En programmation événementielle, le déroulement est contrôlé par la survenue d'événements : pas de boucle principale visible, le programme se réduit aux initialisations et aux fonctions de réaction.
Ces fonctions de rappel (callbacks) sont enregistrées dans le widget à sa création
(abonnement) et appelées quand un événement le concernant survient (notification). La
boucle existe toujours, mais enfouie dans la toolkit : initialisation ; while () { attendre l'événement suivant E ; traiter E ; }. Les événements — liés aux périphériques
(entrée/sortie du curseur, boutons, frappes clavier, gestes multitouch, sélection d'un item)
ou aux applications (création/destruction de fenêtres, ré-affichage) — sont placés dans une
queue FIFO que la boucle de gestion vide un par un.
L'objet événement : source, cible, type
Un événement JavaFX est un objet qui hérite de javafx.event.Event. Tout événement a
trois propriétés :
- une source (event source) — le nœud d'où l'événement semble provenir ; elle change au fil de la route ;
- une cible (event target, interface
EventTarget) — le nœud visé, identique du début à la fin de la traversée ; - un type (event type, classe
EventType) — par exemple mouse pressed, mouse released, mouse moved.
La classe de l'événement dépend de la signification logique de l'interaction, pas
seulement du geste physique : un clic sur un bouton émet un ActionEvent, un clic sur un
rectangle un MouseEvent, un clic sur le bouton de fermeture de fenêtre un WindowEvent.
Un même ActionEvent couvre d'ailleurs plusieurs déclenchements physiques : clic souris,
appui sur la touche espace, touch sur une surface tactile.
Abonnement et notification
Deux catégories d'entités : l'abonneur — la source d'information, le lieu où sont gérés
les événements — et un ou plusieurs abonnés. L'abonnement passe par
addEventHandler(EventType, EventHandler), où le type précise l'événement géré :
bplus.addEventHandler(ActionEvent.ACTION, new MyClicHandler());
// ...
public class MyClicHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent event) {
System.out.println("Clic sur bouton");
}
}EventHandler<T extends Event> est une interface à une seule méthode, handle(T event) —
plusieurs handlers peuvent s'abonner au même nœud, ils seront tous notifiés dans l'ordre.
Cinq façons d'écrire un handler
Le cours les déroule toutes sur le même exemple (incrémenter/décrémenter un label) :
classe externe par bouton, classe interne (elle accède aux membres, même privés, de
sa classe englobante), classe interne anonyme (déclarée et instanciée en même temps),
l'Application elle-même qui implémente EventHandler et s'abonne avec this, et
enfin l'expression lambda :
bplus.setOnAction(e -> {
int currentValue = Integer.parseInt(label.getText());
label.setText("" + (currentValue + 1));
});Une lambda n'est possible que pour une interface à une seule méthode, et ne peut capturer
que les variables du contexte englobant dont la valeur ne change pas (effectively
final) : une variable de boucle for est refusée à la compilation.
Recommandations du cours
Privilégier les méthodes setOnXXX (setOnAction, setOnMouseClicked,
setOnMouseDragged, setOnKeyPressed, setOnTouchPressed…) avec des lambdas quand le
code est court ; ne pas utiliser de classes internes anonymes ; une classe interne ou
l'implémentation de EventHandler si le code grandit, une classe externe encore au-delà.
Et toujours privilégier la lisibilité.
La route d'un événement
Quand un événement est généré : sélection de la cible, construction de la route (la chaîne Stage → Scene → conteneurs → cible), puis traversée de la route en deux phases. À la capture, l'événement descend de la tête (Stage) à la queue (cible) et appelle les event filters ; au bubbling, il remonte de la cible vers la tête en appelant les event handlers. Un parent intercepte ainsi les événements destinés à ses enfants : un filtre posé sur le Stage voit tous les clics de la fenêtre.
event.consume() consomme l'événement et stoppe son traitement : les autres filtres ou
handlers du nœud courant s'exécutent encore, mais l'événement n'atteint plus aucun autre
nœud — c'est ainsi qu'une cible fournit une réponse spécifique avant la réponse par défaut
du parent.
Dessiner et styliser
Canvas + GraphicsContext : tout est dessiné dans le contexte graphique — l'ensemble
des paramètres (couleur des traits, de remplissage, police courante, changements de repère)
appliqués au dessin du nouvel élément — obtenu par canvas.getGraphicsContext2D().
Primitives : fillRect, fillOval, fillArc, strokeRect, strokeOval, strokeArc,
strokeLine, drawImage, fillText… avec l'origine du repère au coin supérieur gauche.
Le style reprend le principe des CSS du Web : des règles « sélecteur + couples
propriété-valeur », aux propriétés préfixées -fx- (-fx-background-color,
-fx-text-fill…). En ligne : bouton.setStyle("-fx-text-fill: red;") ; ou feuille externe :
scene.getStylesheets().add("cours3/bouton.css").
MVC et ListView
MVC (Modèle-Vue-Contrôleur) : l'idée est de séparer les données d'une application de leur présentation. Le modèle informe la ou les vues de se mettre à jour, sans connaître aucune vue spécifique ; c'est un mécanisme d'abonnement (observateur) qui relie modèle et vues.
En JavaFX, le modèle d'une ListView est une ObservableList : list.getItems().add("Rome")
met la vue à jour, sans toucher au widget. Pour réagir aux changements, on s'abonne avec
addListener et un ListChangeListener, notifié dans onChanged(Change report). La
sélection a son propre modèle : list.getSelectionModel() — setSelectionMode(SelectionMode.MULTIPLE) pour la sélection multiple, et un listener sur getSelectedItems(), une seconde
ObservableList. Au total, une ListView possède deux modèles (ObservableList) et une
vue.
A retenir
- la boucle événementielle est enfouie dans la toolkit ; on écrit des callbacks, enregistrées par abonnement et appelées par notification ;
- tout événement a une source (qui change), une cible (fixe) et un type ; la
classe dépend de la signification logique (
ActionEvent≠MouseEvent) ; setOnXXX+ lambda : la forme par défaut ; lambdas réservées aux interfaces à une seule méthode, captures effectively final ;- capture (filters) → cible → bubbling (handlers) ;
consume()stoppe la route ; - MVC : le modèle (
ObservableList) prévient, la vue (ListView) obéit — jamais l'inverse.
R2.02 - Cours 2 - Programmation événementielle.pdf
R2.02 · Cours 2 — Programmation événementielle · 33 diapositives
Chargement du document…
R2.02 - Cours 3 - Composants et événements (12).pdf
R2.02 · Cours 3 — Composants et événements (1/2) · 31 diapositives
Chargement du document…
R2.02 - Cours 4 - Composants et événements (22).pdf
R2.02 · Cours 4 — Composants et événements (2/2) : MVC et ListView · 16 diapositives
Chargement du document…
Dernière mise à jour le
Ch. 1 — JavaFX : widgets et placement
Ce qu'est un système interactif, la plateforme JavaFX (Stage, Scene, graphe de scène), les familles de widgets et les gestionnaires de placement — avec les quatre tailles d'un nœud et le redimensionnement.
Ch. 5-6 — Conception et prototypage
Conception centrée utilisateurs et ISO 9241, personas et analyse des tâches, brainstorming — puis séquences d'interaction, schémas d'interface et prototypage papier avec ses rôles et son protocole.