Licence CC BY-NC-SA

Les événements

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Nous voici dans une nouvelle partie, portant sur l'interactivité et l'animation en Actionscript. Ces deux concepts sont étroitement liés à la notion d'évènements, qui est l'objet de ce chapitre. Il sera malheureusement le plus théorique et le plus complexe de cette partie. Une fois que vous aurez acquis les bases de la programmation événementielle, les autres chapitres seront beaucoup plus pratiques et faciles à aborder.

Qu'est ce qu'un évènement ?

Introduction

Dans ce chapitre nous allons aborder la programmation événementielle. A l'instar la POO, cette manière de programmer s'inspire d'éléments du quotidien, à savoir : les évènements.

Attention, j'en vois déjà certains penser aux festivals de cet été : il ne s'agit pas de ça ! :p Les évènements, tels que nous allons les voir, sont définis comme des éléments non prévisibles, généralement déclenché par un changement d'état quelconque. Par exemple dans la vie courante, nous pouvons citer :

  • un réveil qui sonne,
  • un accident de voiture,
  • une panne de courant,
  • la réception d'un fax.

Pour compléter la définition précédente, nous pourrions dire qu'il s'agit d'évènements soudains qui ne surgisse pas vraiment lorsqu'on s'y attend. Dans l'exemple du réveil, il est vrai qu'on s'attend forcément à ce qu'il sonne, cependant vous devez avouer que vous êtes toujours surpris lorsque ce dernier se met à sonner !

En programmation, le principe est le même. Dans le cas de l'ActionScript 3, nous pourrions citer comme événements :

  • un clic à l'aide de la souris,
  • le déplacement du curseur de la souris,
  • l'enfoncement d'une touche du clavier,
  • la fin de chargement d'une image depuis un serveur,
  • une erreur lors du chargement de cette image.

À l'apparition de l'un des évènements, il est alors possible d'exécuter des instructions spécifiques, pour mettre à jour l'affichage par exemple. Étant donné qu'on ne sait pas à l'avance quand ceux-ci se produiront, il serait possible mais très fastidieux d'inclure des instructions au milieu de votre programme qui bouclent indéfiniment pour détecter ces événements : c'était la méthode avant de l'entrée en scène du concept de gestion des évènements, qui facilite grandement ce processus.

Pour introduire ce concept, nous allons maintenant faire une analogie en prenant l'idée de la réception d'un fax.

Un jour au bureau…

Aujourd'hui, vous travaillez pour une grande entreprise de renommée internationale. Votre compagnie dispose ainsi de différentes agences réparties sur plusieurs continents. En tant que responsable produits, vous allez devoir prochainement présenter un nouveau concept à l'ensemble du groupe. Il est donc prévu que vous assistiez à une réunion avec des collaborateurs de diverses agences. En revanche, les détails pratiques tels que l'heure et l'endroit de cette réunion ne sont pas encore fixés.

Fonction d'écouteur

Monsieur Gumble, directeur d'une agence nord-américaine, a promis qu'il vous enverrait les informations concernant la réunion aujourd'hui par fax. Comme à votre habitude, ce matin-là vous arrivez au bureau légèrement en avance. Vous en profitez alors pour sortir une feuille, et commencez à noter différentes choses dessus.

Machinalement, vous avez commencé à anticiper et vous avez noté les différentes tâches à faire à la réception de ce fameux fax : cela ressemble à une fonction contenant une série d'instructions, non ? Cette « fonction » qui devra être appelée dès que vous recevrez le fax est désignée comme étant une fonction d'écouteur.

En ActionScript 3, elle sera donc une fonction quelconque ou une méthode de classe regroupant des instructions à exécuter lors de l'apparition d'un évènement. Généralement, les actions qui en découleront, dépendent de paramètres liés à l'évènement. Par exemple, savoir quelle touche a été enfoncée permettrait d'exécuter des instructions différentes suivant s'il s'agit d'un A ou d'un S.

Écouter un événement

Ça y est ! Vous êtes prêts à recevoir votre fax. Vous jetez régulièrement un coup d'œil sur le télécopieur qui se trouve dans le coin.

Attendre qu'un événement survienne est appelé écouter un événement. On écoute toujours sur un objet désigné comme étant la cible de l'événement. On dit également que cet objet déclenchera l'événement lorsque celui-ci surviendra. Ici, le télécopieur est la cible de l'événement qui se produira à la réception d'un fax.

En programmation, on écoute un événement particulier en renseignant une sorte de « signature », appelée type d'événement, décrite par une chaîne de caractères : il est alors possible de distinguer un clic de souris du déplacement du curseur au-dessus d'un objet identique. Écouter un événement qu'un objet sera susceptible de déclencher consiste donc à associer une fonction d'écouteur à un type d'évènements tout en ciblant cet objet. Ainsi, à chaque fois que ledit objet déclenchera l'évènement correspondant au type en question, la ou les fonctions d'écouteur impliquées seront appelées.

Seul un objet peut faire apparaître un événement. Nous écouterons donc toujours des événements relatifs à des objets (ici, notre télécopieur).

Cependant, le fax n'arrive toujours pas et l'ennui pointe le bout de son nez. Vous levez alors la tête et parcourez votre bureau du regard : vous apercevez un collègue et vous remarquez que ce dernier semble également attendre un fax devant la machine. Toutefois il est peu probable qu'il provienne de Monsieur Gumble. De la même façon, votre voisin n'effectuera pas les mêmes tâches que vous après réception de son fax, sa fonction d'écouteur est donc probablement différente.

Il est possible qu'un événement soit écouté par plusieurs fonctions d'écouteur, mêmes si elles sont différentes. Elles seront toutes appelées lorsque l'événement sera déclenché.

L'évènement

Après la pause déjeuner, vous revenez à votre poste pour poursuivre votre travail de la matinée. Vous n'avez alors pas le temps de vous asseoir, qu'un fax arrive ! Vous regardez celui-ci de plus près, il s'agit bien du fax de monsieur Gumble. Vous le parcourez donc des yeux et voyez que celui-ci regorge d'informations. Vous y trouvez le nom de toutes les personnes qui seront présentes, l'objet de la réunion, mais également la date, l'heure et l'endroit de la réunion. Ça tombe bien, c'est exactement les données qu'il vous manquait.

Vous ressortez alors votre feuille puis commencez les réservations. Parmi les différentes informations fournies dans le fax, vous en avez extrait celles qui vous étaient utiles pour pouvoir exécuter les tâches que vous vous étiez fixés. On dit que le fax représente l'événement sous la forme d'un objet qui contient différentes données sur ce qui s'est produit.

En programmation événementielle et comme tout en Actionscript, les évènements sont donc représentés par des objets, qui renferment également des informations concernant l'évènement en question. De la même façon, il est possible « d'interroger » cette classe, pour pouvoir gérer et traiter correctement ce dernier en fonction de son type et de ses attributs. Ces objets sont tous issus de la classe Event, dont nous reparlerons plus loin.

Les écouteurs en POO

Nous allons à présent revenir et nous pencher plus sérieusement sur les écouteurs et leur gestion. Comme nous l'avons dit plus haut, les écouteurs sont associés aux objets. Ainsi tout objet peut théoriquement utiliser les écouteurs pour répondre aux évènements, à une condition néanmoins : l'objet doit implémenter l'interface IEventDispatcher.

L'interface IEventDispatcher

L'interface IEventDispatcher définit donc le jeu de méthodes servant à la gestion des écouteurs. Sans plus attendre, voici à la figure suivante l'ensemble des définitions de méthodes que propose cette interface.

L'interface IEventDispatcher

Comme vous pouvez le voir, cette interface propose cinq définitions de méthodes pour la gestion des écouteurs. La méthode addEventListener() est celle que nous utiliserons le plus, et sa signature complète est la suivante :

1
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

Néanmoins dans un premier temps, nous allons laisser de côté les paramètres facultatifs. Ce qui nous donne donc :

1
addEventListener(type:String, listener:Function):void

Avant de vous montrer un exemple d'utilisation, nous allons revenir sur la notion d'écouteurs. Si vous êtes pointilleux, vous aurez alors remarqué la présence du mot anglais « add » dans le nom cette méthode, qui signifie ajout. Ainsi lorsque nous utilisons cette méthode nous ajoutons un nouvel écouteur à notre objet. En effet, il est possible de définir plusieurs écouteurs pour un même objet, et pouvoir ainsi répondre à divers évènements. Grâce à cela, il est donc possible d'être à l'écoute à la fois d'évènements provenant de la souris et d'autres provenant du clavier.

Tout comme il est possible d'ajouter des écouteurs à un objet, il est possible d'en retrancher. C'est donc la méthode removeEventListener() qui s'en charge, et voici sa signature complète :

1
removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void

Également, nous retiendrons pour l'instant cette méthode sous sa forme simplifié :

1
removeEventListener(type:String, listener:Function):void

Dans un premier temps, nous allons oublier les méthodes dispatchEvent() et willTrigger(). En revanche, vous pouvez noter la présence de la méthode hasEventListener(), qui permet de vérifier si un écouteur est enregistré auprès d'un objet pour un type d’événement spécifique. Cette méthode renvoie donc un booléen.

La classe EventDispatcher

Comme nous l'avons dit, c'est l'interface IEventDispatcher qui définit le jeu de méthodes servant à la gestion des écouteurs. Néanmoins, c'est à l'intérieur de la classe EventDispatcher, que le contenu de l'ensemble de ces méthodes est défini. Bien entendu cette classe implémente l'interface IEventDispatcher, et c'est elle qui va réellement servir de classe de base pour toute classe voulant « distribuer » des évènements. Par héritage, cette classe permet donc à tout objet d’utiliser les méthodes de l’interface IEventDispatcher, et d'être à l'écoute d'évènements ; on dit alors que l'objet devient une cible d'évènements.

Ainsi toutes les classe filles de EventDispatcher héritent donc de ces méthodes liées à la gestion des écouteurs. À l'instar de nombreux objets, nous retrouvons parmi elles la classe DisplayObject, que nous avons déjà vue. Donc si vous vous souvenez bien, l'ensemble des classes d'affichage sont, par héritage, des sous-classes de la classe EventDispatcher (voir figure suivante).

Extrait de l'arbre d'héritage des classes d'affichage

Cela tombe bien, tous les éléments de notre liste d'affichage vont donc pouvoir répondre à divers évènements. Nous verrons que les objets appartenant à la liste d'affichage répondent aux évènements d'une manière assez spécifique : on appelle cela le flux d'évènements. Nous aborderons donc cette notion au cours de ce chapitre.

Mise en place d'un écouteur

Introduction aux fonctions de rappel

La théorie

En regardant la méthode addEventListener() de plus près, vous vous apercevrez que celle-ci dispose d'un paramètre de type Function :

1
addEventListener(type:String, listener:Function):void

Dans ce cas, la fonction listener est ce qu'on appelle une fonction de rappel (ou callback en anglais). Il s’agit donc d’une fonction ou d'une méthode qui est passée en paramètre à une autre fonction. Ainsi à l'intérieur de cette dernière, il est possible de faire appel à la fonction de rappel à sa guide.

Quel est l'intérêt de cette fonction de rappel ? N'est-il pas plus simple de définir les instructions directement dans la seconde fonction ?

L'intérêt ici est justement de pouvoir se servir d'une fonction, sachant qu'on ne la connaît pas à l'avance. De cette manière, il est alors possible d'exécuter des instructions dynamiquement sans avoir besoin de les définir directement. Le plus souvent, les fonctions de rappel sont appelés suite à une condition, comme l'apparition d'un évènement par exemple. En utilisant ce concept, vous pouvez ainsi créer une classe et laisser à l'utilisateur la liberté d'exécuter les instructions qu'il souhaites, sans qu'il est accès à la classe elle-même. Cela est extrêmement pratique lorsqu'on conçoit son programme par héritage ou lorsqu'on crée une bibliothèque de classes.

Une fonction de rappel est une fonction appelée à l'intérieur d'une autre. Son utilisation et sa signature sont donc définies et figées lors de son appel. Ainsi à la définition de la fonction de rappel, il est nécessaire de respecter les points suivants :

  • le nombre et le type des paramètres
  • le type de la valeur renvoyée.

Vous pouvez donc tout à fait utiliser ce concept à l'intérieur de vos fonctions et ainsi améliorer la ré-utilisabilité de votre code.

Un exemple

Pour mieux comprendre, voici un exemple basique de fonction utilisant une fonction de rappel suivant une condition :

1
2
3
4
5
6
function uneFonction(condition:Boolean, fonction:Function):void 
{ 
    if(condition){
        fonction.call();
    }
}

Et voici la définition de la fonction de rappel :

1
2
3
4
function fonctionDeRappel():void 
{ 
    trace("Ceci est une fonction de rappel.");
}

Pour utiliser au mieux ce concept, je vous conseille de vous rendre sur la documentation officielle de la classe Function. Vous verrez notamment comment vous servir de la méthode call() suivant les paramètres définies pour votre fonction.

Si vous souhaitez tester cet exemple, vous pouvez alors exécuter l'instruction suivante, après avoir défini les deux fonctions précédentes :

1
uneFonction(true, fonctionDeRappel);

Ce qui donne naturellement à l'exécution du code :

1
Ceci est une fonction de rappel.

Créer une fonction d'écouteur

Lorsqu'on parle de gestion d'évènements, les fonctions de rappel sont alors plus communément appelées fonction d'écouteur, comme décrit plus haut. La gestion de cette fonction est alors du ressort de la superclasse EventDispatcher. Lors de la mise en place d'un écouteur, nous avons donc uniquement à nous préoccuper de la définition de la fonction d'écouteur.

Les fonctions d'écouteur doivent donc répondre aux exigences fixées par la classe EventDispatcher. Voici donc la signature type d'une fonction d'écouteur :

1
2
3
4
function uneFonction(event:Event):void 
{ 
    // Instructions à exécutées lors de l'apparition d'un évènement de type donné
}

Le paramètre event à renseigner ici est de type Event, cependant dans la pratique il s'agira plus généralement d'une sous-classe de cette dernière. La classe utilisée pour ce paramètre dépendra directement du type d'évènement spécifié lors de la mise en place de l'écouteur.

Comme exemple, je vous propose cette fonction d'écouteur qui pourrait servir à la gestion d'évènements liés à la souris :

1
2
3
4
function monEcouteur(event:MouseEvent):void 
{ 
    trace("Vous avez cliqué sur la souris.");
}

Comme vous pouvez le voir ici, le paramètre de cette fonction est de type MouseEvent, dont nous reparlerons dès le prochain chapitre. Il s'agit donc d'une sous-classe de la classe Event, spécifique aux évènements générés par votre souris. Bien évidemment, cette classe aurait été différente s'il s'agissait d'évènements provenant du clavier par exemple.

Gérer les écouteurs d'un objet

Ajouter un écouteur

Comme vous l'aurez compris, pour ajouter un écouteur à un objet, nous aurons besoin de la méthode addEventListener(). Parce que mieux vaut deux fois plutôt qu'une, je vous rappelle l'allure de la signature de cette dernière :

1
addEventListener(type:String, listener:Function):void

À présent, vous savez donc comment définir la fonction à passer en paramètre de cette méthode. Quant au type d'évènement ici sous forme de String, celui-ci peut être renseigné via les constantes définies à l'intérieur de la classe Event, ou de l'une de ses sous-classes.

Encore une fois, l'utilisation de ces constantes facilitent grandement la lisibilité et le débogage du code. Par exemple, la chaîne de caractères "click" peut être remplacée par l'expression MouseEvent.CLICK. De cette manière, le code est plus clair. Par ailleurs, le compilateur peut alors détecter une éventuelle faute de frappe, ce qui n'est pas le cas pour la chaîne en « brut ».

La constante MouseEvent.CLICK permet de faire référence aux évènements générés par le clic de votre souris. En outre, nous sommes capables de terminer la mise en place de notre écouteur. Voici donc comment enregistrer l'évènement monEcouteur() définie plus haut à l'objet stage :

1
stage.addEventListener(MouseEvent.CLICK, monEcouteur);

Si vous le désirez, vous pouvez à présent tester le code complet suivant :

1
2
3
4
5
function monEcouteur(event:MouseEvent):void 
{ 
    trace("Vous avez cliqué sur la scène principale.");
}
stage.addEventListener(MouseEvent.CLICK, monEcouteur);

Une fois l'application lancée et si vous cliquez à plusieurs reprise à l'intérieur de votre scène, vous verrez apparaître ceci :

1
2
3
Vous avez cliqué sur la scène principale.
Vous avez cliqué sur la scène principale.
Vous avez cliqué sur la scène principale.

Dans cet exemple, nous avons enregistré l'écouteur auprès de l'objet stage, c'est-à-dire la scène principale. Nous aurions très bien pu le faire pour notre classe Main de type Sprite. Cependant si vous n'avez rien ajouté à celle-ci, ses dimensions sont nulles et il n'est pas possible d'y cliquer à l'intérieur.

Supprimer un écouteur

À tout moment, il est possible de retirer un écouteur enregistré auprès d'un objet. Pour cela, nous disposons de la méthode removeEventListener() définie par l'interface IEventDispatcher :

1
removeEventListener(type:String, listener:Function):void

Une fois cette méthode appelée, l'objet ne répond alors plus aux évènements en question. Nous n'allons pas épiloguer plus longtemps sur cette méthode qui s'utilise de la même façon que addEventListener().

Voici donc comment retirer l'écouteur ajouté plus haut :

1
stage.removeEventListener(MouseEvent.CLICK, monEcouteur);

Le flux d'événements

Présentation du concept de flux d'évènements

Introduction

Lorsque survient un évènement, Flash Player distribue celui-ci directement à l'objet cible. Ce dernier peut alors gérer l'évènement comme il le souhaite, grâce à un éventuel écouteur. En revanche, ceci est plus complexe s'il s'agit d'un objet d'affichage. Pour vous en convaincre, nous allons reprendre nos histoires de voitures !

Souvenez-vous de notre voiture organisée comme à la figure suivante dans la liste d'affichage.

L'arbre d'affichage de la voiture

Il s'agit donc de la hiérarchie à l'intérieur de notre programme. Ceci dit, une fois que vous aurez lancé l'exécution du code, vous obtiendrez la fenêtre visible à la figure suivante.

Les différents objets de la voiture à l'intérieur du Flash Player

Dans l'image précédente, vous voyez que le curseur est positionné sur l'une des deux roues. Au hasard, nous dirons qu'il s'agit de l'objet roue2. Si vous cliquez à l'aide de votre souris, un objet MouseEvent va donc être généré pour que l'évènement puisse être traité. Cependant une petite question se pose alors !

À qui doit être distribué l'évènement généré par la souris ?

Certains diront qu'il semble plus logique de distribuer l'évènement auprès de l'objet roue2. En effet, c'est bien l'objet qui réellement la cible de l'évènement, puisque c'est sur lui que le clic est effectué. À première vue, il est normal de faire de l'objet roue2 la cible de l'évènement.

Toutefois en y réfléchissant bien, ceci n'est finalement pas ce qu'il y a de plus pratique. Imaginons que dans notre application nous voulons non pas détecter un clic sur l'une des roues ou même sur la carrosserie, mais sur la voiture elle-même. Comment allons-nous pouvoir gérer cette situation au niveaux des évènements ? Allons nous devoir définir un écouteur pour chacun des objets enfants de notre objet voiture ? Heureusement, non ! Le Flash Player intègre ce qu'on appelle le flux d'évènements, qui élargit grandement les possibilités de la gestion des évènements.

Qu'est-ce que le flux d'évènements ?

Pour faciliter la gestion des évènements pour les objets d'affichage, le Flash Player utilise une technique qui consiste à acheminer l'objet d'évènement dans toute la hiérarchie de la liste d'affichage, depuis la scène principale jusqu'à l'objet cible. Pour être plus précis, l'objet évènement effectue en réalité un aller-retour sur le chemin décrit précédemment. Nous pouvons alors distinguer trois phases :

  • la phase de capture : cette phase comprend la distribution de l'objet évènement depuis la scène principale jusqu'à l'objet parent de la cible,
  • la phase cible : il s'agit de la gestion de l'évènement par l'objet cible,
  • la phase de propagation : cette dernière phase est la « remontée » jusqu'à la scène principale, il s'agit donc du chemin inverse à la phase de capture.

Appliqué au cas d'un clic sur l'objet roue2 de notre voiture, le flux d'évènements peut alors être décomposé comme à la figure suivante.

La diffusion dans le flux d'évènements

Avec cette manière de procéder, il devient possible de gérer l'évènement à tous les étages de la hiérarchie de notre liste d'affichage. Ainsi, nous pouvons alors enregistrer un écouteur pour cet évènement auprès du conteneur voiture. Pour mieux comprendre tous les mécanismes du flux d'évènements, nous allons maintenant réaliser quelques manipulations.

Bien comprendre le fonctionnement

Dans le flux d'évènements, l'objet d'évènements est distribué à chaque « nœud » ou objet de la liste d'affichage. Pour bien cerner comment cela fonctionne, nous allons tester quelques petits bouts de code. Nous allons donc reprendre notre voiture.

Commençons par définir nos éléments :

1
2
3
4
5
6
7
// Définition des objets d'affichage
var voiture:Sprite = new Sprite();
voiture.name = "voiture";
var roue2:Sprite = new Sprite();
roue2.name = "roue2";
voiture.addChild(roue2);
addChild(voiture);

Pour simplifier l'exemple, nous limiterons le tracé de notre voiture à une unique roue. C'est un peu ridicule, je sais ! Mais cela ne nous empêchera pas de comprendre les mécanismes liés au flux d'évènements.

Voici donc le code proposé pour dessiner cette roue :

1
2
3
4
5
6
7
// Traçage de la roue
roue2.graphics.lineStyle(2, 0x000000);
roue2.graphics.beginFill(0xCCE5FF); 
roue2.graphics.drawCircle(0,0,25); 
roue2.graphics.endFill();
roue2.x = stage.stageWidth / 2;
roue2.y = stage.stageHeight / 2;

En guise de fonction d'écouteur, nous nous contenterons de citer le nom de l'objet d'affichage qui reçoit l'objet évènement. C'est pour cela que nous avons défini la propriété name pour chacun de nos objets.

Pour rappel, le nom des variables est arbitraire et n'a de sens qu'à l'intérieur du code. Une fois le code compilé, Flash Player gère directement les variables en mémoire sans utiliser le nom que vous avez défini dans votre code. C'est pourquoi la propriété name définie pour toutes les sous-classes de DisplayObject, permet facilement de distinguer nos objets d'affichage lors de l'exécution du code.

Voici donc notre fonction d'écouteur :

1
2
3
4
5
// Définition de la fonction d'écouteur
function monEcouteur(event:MouseEvent):void 
{ 
    trace("Événement en cours de traitement par l'objet " + event.currentTarget.name);
}

Appliquons alors cette fonction d'écouteur à nos deux objets voiture et roue2, pour les évènements de type MouseEvent.CLICK :

1
2
3
// Mise en place des écouteurs
voiture.addEventListener(MouseEvent.CLICK, monEcouteur);
roue2.addEventListener(MouseEvent.CLICK, monEcouteur);

Je vous propose à présent de lancer l'exécution du code, puis de cliquer sur votre roue à l'aide de votre souris. Vous verrez alors apparaître ceci dans la console :

1
2
Événement en cours de traitement par l'objet roue2
Événement en cours de traitement par l'objet voiture

Attendez ! Pourquoi l'évènement est-il distribué à l'objet voiture après avoir été traité par l'objet roue2 ? Et sachant que le flux d'évènements représente un aller-retour dans la liste d'affichage, ne devrait-il pas y avoir deux traitement de la fonction d'écouteur par l'objet stage ?

En toute rigueur, si ! Toutefois, lors de la mise en place d'un écouteur avec la méthode addEventListener(), nous définissons en réalité la phase durant laquelle l'écouteur est actif. Pour cela, il faut revenir sur la définition de cette fonction :

1
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

Dans cette méthode, le troisième paramètre nommé useCapture, de type Boolean, permet de définir les phases durant lesquelles l'écouteur est actif. Voilà donc comment ce paramètre influe sur le flux d'évènements :

  • true : l'écouteur est actif uniquement pendant la phase de capture,
  • false : l'écouteur est actif pendant les phases cible et de propagation.

Par défaut, ce paramètre vaut false. Dans le cas de notre objet voiture, l'écouteur est donc actif seulement pendant la phase de propagation.

Essayons maintenant de définir l'écouteur de notre objet voiture pendant la phase de capture :

1
voiture.addEventListener(MouseEvent.CLICK, monEcouteur, true);

Cette fois-ci, notre voiture voit bien l'évènement avant l'objet roue2 :

1
2
Événement en cours de traitement par l'objet voiture
Événement en cours de traitement par l'objet roue2

Si vous avez bien compris la logique du flux d'évènements, vous aurez alors deviné que l'objet voiture peut intervenir à la fois durant la phase de capture et la phase de propagation. Cependant, cela nécessite d'enregistrer deux écouteurs différents auprès de l'objet en question.

Même si cela semble superflu, voilà les instructions de définition des écouteurs :

1
2
3
voiture.addEventListener(MouseEvent.CLICK, monEcouteur, true);
roue2.addEventListener(MouseEvent.CLICK, monEcouteur);
voiture.addEventListener(MouseEvent.CLICK, monEcouteur);

À présent, nous pouvons donc agir à tous les niveaux, ou disons durant toutes les phases du flux d'évènements, comme le montre la console de sortie :

1
2
3
Événement en cours de traitement par l'objet voiture
Événement en cours de traitement par l'objet roue2
Événement en cours de traitement par l'objet voiture

Enfin avant de passer à la suite, notez que le passage en paramètre de la valeur true à la méthode addEventListener() de l'objet cible a pour effet de désactiver l'écouteur pendant la phase cible. Voici un exemple illustrant ces propos :

1
2
3
voiture.addEventListener(MouseEvent.CLICK, monEcouteur, true);
roue2.addEventListener(MouseEvent.CLICK, monEcouteur, true);
voiture.addEventListener(MouseEvent.CLICK, monEcouteur);

Ce qui donne évidemment dans la console de sortie :

1
2
Événement en cours de traitement par l'objet voiture
Événement en cours de traitement par l'objet voiture

L'objet Event

Présentation de la classe Event

Avant de clôturer ce chapitre, nous allons brièvement parler de la classe Event. Nous avons déjà vu que cette dernière est la superclasse à tout objet évènement. Ainsi les sous-classes hériteront de toutes ses propriétés. Ces accesseurs et méthodes serviront principalement à la gestion de l'évènement, nous allons donc rapidement les étudier.

Voici à la figure suivante l'heureuse élue que nous allons étudier.

La classe Event

Bien entendu, nous ne verrons et ne détaillerons pas toutes ces propriétés. Nous nous focaliserons uniquement sur celles qui ont le plus d'intérêt pour nous pour l'instant. Parmi les attributs, certains sont plutôt d'ordre général, et nous retiendrons donc les deux suivants :

  • type : cet accesseur définit le type de l'évènement sous la forme d'une chaîne de caractères. Il s'agit de cette même chaîne qui est transmise en paramètre à la méthode addEventListener().
  • target : cet attribut fait référence à l'objet cible de l'évènement.

Voici un petit code d'exemple d'utilisation de ces propriétés :

1
2
3
4
5
6
stage.addEventListener(MouseEvent.CLICK, monEcouteur);
function monEcouteur(event:MouseEvent):void 
{ 
    trace("Événement de type " + event.type);
    trace("Cible de l'événement" + event.target);
}

Ce qui donne :

1
2
Événement de type click
Cible de l'événement[object Stage]

Les propriétés liés au flux d'évènements

Certaines propriétés de la classe Event sont en lien direct avec le flux d'évènements. Celles-ci permettent alors de situer et de contrôler l'objet Event tout au long du flux d'évènements. Nous pouvons donc citer les deux attributs suivant :

  • eventPhase : cette propriété indique la phase en cours dans le flux d’évènements,
  • currentTarget : cet attribut est une référence à l'objet cible « provisoire », qui peut se situer n'importe où dans la hiérarchie de la liste d'affichage.

Afin de mieux comprendre, nous allons reprendre nos objets voiture et roue2. Puis nous allons redéfinir la fonction d'écouteur comme ceci, pour faire apparaître les valeurs de ces attributs :

1
2
3
4
5
6
// Définition de la fonction d'écouteur
function monEcouteur(event:MouseEvent):void 
{ 
    trace("Événement en cours de traitement par l'objet " + event.currentTarget.name);
    trace("Phase du flux d'évènements en cours : " + event.eventPhase);
}

Maintenant, enregistrons un écouteur pour chacune des phases du flux d'évènements :

1
2
3
4
// Mise en place des écouteurs
voiture.addEventListener(MouseEvent.CLICK, monEcouteur, true);
voiture.addEventListener(MouseEvent.CLICK, monEcouteur);
roue2.addEventListener(MouseEvent.CLICK, monEcouteur);

À présent, il n'y a plus aucun doute sur la manière dont est géré l'objet évènement à travers les différentes phases :

1
2
3
4
5
6
Événement en cours de traitement par l'objet voiture
Phase du flux d'évènements en cours : 1
Événement en cours de traitement par l'objet roue2
Phase du flux d'évènements en cours : 2
Événement en cours de traitement par l'objet voiture
Phase du flux d'évènements en cours : 3

Comme vous le voyez, les phases sont représentés par un nombre entier allant de 1 à 3. Comme souvent en Actionscript, l'utilisation de ces valeurs est simplifiée par la classe EventPhase et ses constantes respectives CAPTURING_PHASE, AT_TARGET et BUBBLING_PHASE.

Également, la classe Event dispose de deux méthodes stopImmediatePropagation() et stopPropagation() qui servent à stopper la diffusion de l'évènement dans le flux d'évènements, respectivement à partir du nœud actuel ou du nœud suivant. Ces méthodes permettent donc de gérer directement la propagation de l'objet évènement.

Pour visualiser ceci, je vous invite à remplacer la fonction d'écouteur monEcouteur() par la suivante :

1
2
3
4
5
6
7
function monEcouteur(event:MouseEvent):void 
{ 
    trace("Événement en cours de traitement par l'objet " + event.currentTarget.name);
    if(event.eventPhase == EventPhase.AT_TARGET){
        event.stopPropagation();
    }
}

La propagation est alors stoppée à la fin de l'exécution de la fonction d'écouteur associée à l'objet roue2 :

1
2
Événement en cours de traitement par l'objet voiture
Événement en cours de traitement par l'objet roue2

En résumé

  • Un évènement est généré de manière imprévisible, comme le clic d'une souris par exemple.
  • Les méthodes de gestion des écouteurs sont définies par l'interface IEventDispatcher et la classe EventDispatcher.
  • La méthode addEventListener() sert à associer une fonction de rappel ou fonction d'écouteur à un type d'évènements pour un objet donné.
  • Le flux d'évènements définit le parcours effectué par un évènement dans la hiérarchie de la liste d'affichage, entre la scène principale et l'objet cible.
  • Le flux d'évènement est composé de la phase de capture, la phase cible et la phase de propagation.
  • Les objets évènement sont représentés par la classe Event, ou l'une de ses classes filles.