Tutoriel 6 : Les événements de base en Flash

Les événements de base en ActionScript 3 sont assez simples à comprendre et à utiliser, je vais dans un premier temps vous parler des principaux événements (ceux liés au stage par exemple), puis ensuite nous verrons les événements utilisateurs (souris, clavier). Si vous regardez le diagramme des classes du tutoriel 3, vous pouvez voir que toutes les classes de la liste d’affichage ont au moins comme parent la classe DisplayObject, qui dérive elle même de la classe EventDispatcher. Il faut savoir que ActionScript 3 est un langage orienté objet, mais c’est aussi un langage très événementiel car comme vous allez le voir, presque tout est événement !

La classe  EventDispatcher fourni à tous ses héritiers la possibilité d’émettre et de recevoir des événements. Examinons rapidement les méthodes de cette classe :

  1. addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void ;
  2. dispatchEvent(event:Event):Boolean ;
  3. hasEventListener(type:String):Boolean ;
  4. removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void ;
  5. willTrigger(type:String):Boolean.

Nous utiliserons principalement les méthodes addEventListener() et removeEventListener() qui permettent respectivement de s’abonner et se désabonner à un type d’événement en particulier. L’API Flash propose plusieurs types d’événements que vous pouvez retrouver dans le package flash.events.* La méthode dispatchEvent() permet de déclencher un événement (on parle de notification), hasEventListener() permet de savoir si l’objet en cours est abonné à un gestionnaire d’événement, et enfin la méthode willTrigger() permettra de savoir si un événement est attaché à l’objet en cours ou à un de ces parents.

En Action Script 3 les événements sont diffusés à des gestionnaires d’événements qui sont chargés de notifier les objets abonnés, c’est le modèle de conception (ou design pattern) Observateur.

S’abonner et se désabonner à un événement

Pour s’abonner à un événement rien de plus simple, il faut dans un premier temps que votre objet puisse émettre et recevoir des événements, c’est le cas avec les objets de la liste d’affichage mais si ce n’est pas le cas (une classe métier par exemple), celle ci devra dériver de la classe EventDispatcher ou alors implémenter l’interface IEventDispatcher (voir le pattern composite pour plus d’information).

Comme toute application est une application graphique et que l’objet de base est le Stage, on peut déjà citer quelques événements important et commencer de travailler avec. Comme vous le savez le Stage est le conteneur de base et c’est à partir de lui que tout démarre, on y ajoute des objets qui seront ensuite affichés. Le stage diffuse énormément d’événements et en voici quelqu’un :

  • Event.ENTER_FRAME : C’est événement est très important car c’est lui qui est diffusé à chaque mise à jour de l’affichage. Votre programme une fois en route est dans une boucle et cette boucle ne s’arrête que quand vous quittez l’application, l’événement ENTER_FRAME est diffusé à chaque tour de boucle, c’est donc un événement très important pour réaliser des animations, des mises à jours de positions, etc… Nous aurons l’occasion d’y revenir 😉
  • Event.FULLSCREEN : Cet événement est diffusé lorsque vous passez en mode plein écran.
  • Event.RESIZE : Celui là est diffuser au redimensionnement de la fenêtre.
  • Event.ADDED_TO_STAGE : Alors celui là est vraiment très important car il est diffusé par le stage à chaque fois qu’un objet est attaché au stage avec la méthode addChild(). Il est souvent pratique d’attendre qu’un objet soit initialisé, puis qu’il soit ajouté au stage avant de travailler avec.
  • Event.REMOVED_FROM_STAGE : L’inverse de ADDED_TO_STAGE, car il est déclenché quand on objet est retiré du stage. Attention, un objet retiré de la liste d’affichage n’est pas supprimé pour autant, il existe donc toujours en mémoire.

Maintenant que nous avons vue tout ça, nous allons commencer de travailler avec ces événements de base puis nous passerons à la souris 😉 Voici un exemple qui implémente un peut tout ce que j’ai dis en haut, vous y trouverez les événements de base ADDED_TO_STAGE et REMOVED_FROM_STAGE ainsi qu’un événement souris. Le plus important est l’exploitation du résultat des méthodes de rappel, mais j’y reviens tout de suite après l’exemple.

package
{
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.engine.JustificationStyle;
	import flash.text.TextField;

	[SWF(width="320", height="240", background="#000000")]

	public class SimpleEvent extends Sprite
	{
		private var monTexte:TextField;
		private var etatSprite:Boolean;

		public function SimpleEvent()
		{
			etatSprite = false;

			// Création d'un champ texte perso
			monTexte = new TextField();
			monTexte.text = "Texte de debug";
			monTexte.x = 10;
			monTexte.y = 10;
			monTexte.width = 250;
			monTexte.height = 150;
			monTexte.addEventListener(Event.ADDED_TO_STAGE, onObjectAddedToStage);
			addChild(monTexte);

			// Création d'un sprite perso dans le constructeur, il n'est référencé nul par ailleurs
			// Seul le stage connais cet objet, sortie du constructeur, je ne peux plus y accéder
			var monSprite:Sprite = new Sprite();
			monSprite.graphics.beginFill(0x781111);
			monSprite.graphics.drawRect(100, 100, 65, 65);
			monSprite.graphics.endFill();
			monSprite.addEventListener(Event.ADDED_TO_STAGE, onObjectAddedToStage);
			addChild(monSprite);
		}

		private function onObjectAddedToStage(evt:Event):void
		{
			monTexte.text = "Un objet a été ajouté au stage !";
			monTexte.appendText("nnC'est l'objet : " + evt.target.toString() + " qui a déclenché l'événement");

			// Si c'est un sprite qui déclanche l'événement
			// Le mot clé is permet de tester l'appartenance
			if (evt.target is Sprite)
			{
				// On récupère une instance de monSprite via un cast sur evt.target
				var s:Sprite = Sprite(evt.target);
				// On lui attache un événement de type Click
				s.addEventListener(MouseEvent.CLICK, onMonSpriteClick);
			}
		}

		// Lors d'un double clique sur le sprite et suivant l'état de la variable
		// etatSprite on affiche un motif différent
		private function onMonSpriteClick(mvt:MouseEvent):void
		{
			// Suppression de l'ancien motif
			var s:Sprite = Sprite(mvt.target);
			s.graphics.clear();

			if (etatSprite) // Si vrai on dessine un carré
			{

				s.graphics.beginFill(0x781111);
				s.graphics.drawRect(100, 100, 65, 65);
				etatSprite = false;
			}
			else			// Si faux on dessine un cercle
			{
				s.graphics.beginFill(0x00F00F);
				s.graphics.drawCircle(150, 150, 50);
				etatSprite = true;
			}

			s.graphics.endFill();
		}
	}
}

Examinons la structure de base d’un abonnement à un événement. Vous pouvez constater que lorsque l’on s’abonne à un événement avec la méthode addEventListener() il faut spécifier plusieurs choses, au minimum :

  • Un type d’événement : Sous forme de chaîne de caractère. Ici on utilise les constantes définies dans le package flash.events.
  • Une fonction de rappel aussi appelée fonction Callback qui sera appelée à chaque fois que l’événement sera déclenché.

Ensuite il faut définir une fonction de rappel qui sera donc appelée à chaque fois que l’événement sera déclenché. Elle prend en paramètre un objet de type Event ou dérivé et ne retourne rien (void). L’objet Event disponible en paramètre contient plusieurs propriétés et fonctions, deux propriétés sont à examiner de très prêt :

Dans mon code précédent je récupère une instance de Sprite (après un cast) grâce à la propriété target, car elle cible l’objet déclencheur (monSprite). La propriété currentTarget a un fonctionnement similaire mais il y a une subtilité. Imaginons que vous ayez un objet Sprite que l’on va appeler monConteneur, contenant lui même plusieurs autres objets Sprite, Shape, Bitmap, etc… vous abonnez votre objet conteneur monConteneur au gestionnaire d’événement responsable de Event.CLICK (lorsque l’on clique sur cet objet), que va t’il se passer quand l’utilisateur cliquera sur votre conteneur et sur ses enfants ? Au clic sur un enfant la propriété currentTarget ciblera le conteneur et la propriété target l’enfant (donc Sprite, Shape, Bitmap, etc…).

 

Les propriétés target et currentTarget

Optimisation des événements

On pourrait être tenté de s’abonner à un gestionnaire d’événement dés le début de l’application (dans le constructeur), mais ce n’est pas la meilleurs façon de faire car l’idéal est d’attendre que vos objets soient totalement initialisés, voir ajoutés au stage, avant qu’ils puissent réagir aux événements. Par exemple il serait inutile de mettre à jour la position d’une image à chaque frame de l’application si celle ci n’est pas affichée. C’est pour cela que dans le code du dessus j’ai attendu que l’objet monSprite soit ajouté au stage pour l’abonner à un gestionnaire d’événement (Event.CLICK).

Une autre bonne pratique consiste à désabonner un objet d’un gestionnaire d’événement quand il n’a plus besoin de réagir aux événements (par exemple quand on le retire de la scène). Effectivement, la gestion des événement est coûteuse et c’est pour ça que quand vous n’avez plus besoin d’écouter un type d’événement vous devrez vous désabonner. Vous pouvez par exemple abonner votre objet à l’événement Event.REMOVED_FROM_STAGE et dans la fonction callback utiliser la méthode removeEventListener sur celui ci.

private function onObjectAddedToStage(evt:Event):void
{
	//
	// Code du haut ...
	//
	if (evt.target is Sprite)
	{
		// On récupère une instance de monSprite via un cast sur evt.target
		var s:Sprite = Sprite(evt.target);
		// On lui attache un événement de type Click
		s.addEventListener(MouseEvent.CLICK, onMonSpriteClick);
		// Abonnement à l'événement suppression du stage
		s.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage)
	}
}

private function onRemovedFromStage(evt:Event):void
{
	// Récupération de l'instance de l'objet
	if (evt.target is Sprite)
	{
		var s:Sprite = Sprite(evt.target);
		s.removeEventListener(MouseEvent.CLICK, onMonSpriteClick);
		s = null;
	}
}

De cette manière vous êtes sûr qu’au retrait de l’objet du stage, ce dernier ne consomme plus de ressources.

Voilà qui clôture cet article qui je l’espère aura été enrichissant pour vous. Les événements sont très important en ActionScript 3 (comme dans beaucoup d’autres langages me direz vous) car tous les composants de votre application vont réagir aux événements et il faudra prendre en compte certaines contraintes pour être performant et efficace, par exemple lorsque vous chargez une ressources depuis un serveur il vous faudra vous abonner à plusieurs types d’événements avant de pouvoir utiliser la ressource (état de téléchargement, téléchargement terminé, erreur, etc…).