Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Minimiser vos appel de fonction - Event.RENDER & Invalidation

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible Flash CS3. Cliquer pour en savoir plus sur les compatibilités.Par billyben (Billyben), le 12 juin 2010

Je me propose aujourd’hui de vous montrer comment mettre en place un procédé tout simple, facilement réutilisable, qui va permettre au final de minimiser des appels de fonctions dans vos objets graphiques personnels.
Il s'agit d'un procédé mis en oeuvre dans les composants fournis avec votre logiciel préféré, tels que List, ComboBox et autre DataGrid, que j'ai tout de même un peu simplifié. Il se retrouve tout en bas de la cascade d'héritage, dans la classe UIComponent.

Pour ce faire, je vous propose de mettre en place, pour l'exemple, un objet qui va répartir des items qui lui sont fournis selon une grille, modifiable, et leur ajouter une ombre dont la couleur est elle aussi modifiable.
(le but inavoué étant de mimer le comportement d'un composant)

Puisqu’un exemple vaut mille explications voici deux exemples :

le point de départ :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

et d'arrivée :

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Quoi, vous ne remarquez rien ??? Vous vous dites, bôa, juste pour quelques lignes ajoutées dans un textfield… C’est tout de même pas glorieux… Détrompez vous, ces quelques lignes traduisent chacune un appel à une fonction de mise à jour de la grille, et la mise en œuvre de plusieurs boucles sur chacun des objets affichés.


Prérequis : Niveau débutant +

dans tout les cas : Pratique d'actionScript 3, Thibault IMBERT



Le principe

Le principe de ce procédé repose sur deux choses :

  • un évènement qui est diffusé juste avant le rendu par le lecteur, qui va nous permettre de retarder jusqu'au dernier moment les fonctions à exécuter
  • l’invalidation des propriétés mises en jeu, qui va nous permettre d’identifier ce qui doit être modifié.

Un p’tit peu de définition :

  • Invalider : ” Rendre ou déclarer non valable, nul. ” (définition Larrousse)

Dans notre cas, ceci permet de signifier une propriété comme n’étant plus valide, soit plus à jour.

L’évènement Event.RENDER

C’est cet événement qui va être le point central du procédé que je vais tenter d’expliquer. Et quoi de mieux que la documentation officielle de DisplayObjectContainer (et oui, ce sont ces objets qui diffusent cet événement) pour nous renseigner sur ce dernier :

render Evénement

Type d'objet événement: flash.events.Event

propriété Event.type = flash.events.Event.RENDER

Version du langage: ActionScript 3.0

Versions du moteur d'exécution: AIR 1.0 Flash Player 9

[événement de diffusion] Distribué lorsque la liste d'affichage est sur le point d'être mise à jour et restituée. Cet événement offre une dernière possibilité de modification des objets qui écoutent cet événement avant le rendu de la liste d'affichage. Vous devez appeler la méthode invalidate() de l'objet Stage chaque fois que vous souhaitez distribuer un événement render. Les événements Render ne sont distribués à un objet que s'il existe un lien d'approbation entre ce dernier et l'objet qui a appelé Stage.invalidate(). Cet événement est un événement de diffusion, ce qui signifie qu'il est distribué par tous les objets d'affichage avec un écouteur enregistré pour cet événement.


Cet évènement est donc parfaitement adapté à notre cas, puisqu'il intervient pile poil au dernier moment où nous pouvons modifier nos objets graphiques avant qu'ils soient rendus. Toutefois, afin que cet évènement soit distribué vers notre objet, il faut que ce dernier appelle Stage.invalidate().

Ceci nous donne un script du type :

stage.addEventListener(Event.RENDER, maFonction)
Stage.invalidate();



Les fichiers de base

le fla

les élements

Le but de ce fichier est de pouvoir jouer avec notre futur classe d'intérêt, et de mettre en avant les problèmes qui y sont liés. La scène se compose de :

  • 3 boutons, qui vont permettre de jouer sur les propriétés/méthodes de notre classe :
  1. ajouter des items à notre classe (méthode addItem(item:DisplayObject))
  2. modifier l'espacement de ces items sur la grille (set espacement(value:Number))
  3. modifier la couleur de l'ombre ajoutée aux items (set color(value:int))
  • un TextField, qui va seulement permettre d'afficher les appels aux différentes fonctions de mise à jour (on pourrait utiliser des trace, mais elles ne s'afficheront pas sur les versions en ligne…)

la bibliothèque contient un symbole (j'ai choisi un carré vert 25×25 pixels), exporté pour actionScript, qui va nous permettre de fournir des items à ajouter à notre occurrence de la dite classe.

le script

Peu de chose ici, instanciation de la classe, ajout de celle-ci à la liste d'affichage.
3 écouteurs sur le MouseEvent.CLICK des boutons, pour engager les 3 différentes actions, avec au préalable la suppression du texte contenu dans le TextField pour une question de lisibilité.

la source : Le fichier Fla

La classe de base

le fichier se nomme ClipGrid.as, à enregistrer à coté de votre fla

Alors, cette classe doit :

  • Ajouter des clips à sa liste d'affichage
  • Les répartir sur la grille (un concept minimaliste ici)
  • Leur ajouter une ombre, de couleur choisie (via un filtre DropShadowFilter)
Le code
package {
 
	// les import, tout d’abord la classe étend Sprite
	import flash.display.Sprite;
	// les objets qu’elle va utiliser vont être typés DisplayObject
	import flash.display.DisplayObject;
	//nous allons créer les ombres grâce à DropShadowFilter (pourquoi se compliquer la vie?)
	import flash.filters.DropShadowFilter;
	//Pour le textField qui va nous permettre d'afficher des informations
	import flash.text.TextField;
 
	public class ClipGrid extends Sprite {
 
		private var clipArray:Array=new Array  ;//qui va nous permettre de référencer
                                             // les objets ajoutés à ClipGrid
		private var _couleurFiltre:int=0x000000;// la couleur par défaut de l’ombre
		private var _nbreClipLargeur:int=3;// nombre maximal de clips sur une ligne
		private var _espacement:Number=25;// espacement entre les clips
		private var textField:TextField;// le fameux champ de texte
 
                // :::: le constructeur :::: //
		public function ClipGrid(txt:TextField) {
			textField=txt;
			// voila, on lui a fournit une référence au textField,
                        // on fait pas plus ici
		}
 
		// :::: La méthode qui permet d'ajouter un objet :::: //
		public function addItem(item:DisplayObject):void {
			// on va ajouter dans le tableau et dans la liste d’affichage 
                        ///    l’objet transmis en paramètre
			clipArray.push(item);
			addChild(item);
			// et comme il faut bien qu’il se mette dans la grille, 
                        //et qu’on lui fournisse son ombre, on lance la mise à jour de ClipGrid
			updateClipGrid();
		}
 
		// :::: Définir l'espacement entre les items :::: //
		public function set espacement(value:Number):void {
			_espacement=value;
			// comme on change la valeur de l'espacement, on lance là encore 
                        //       la mise à jour de ClipGrid
			updateClipGrid();
		}
 
		// :::: Définir la couleur de l'ombre :::: //
		public function set couleurFiltre(value:int):void {
			_couleurFiltre=value;
			// Idem, pour la couleur cette fois
			updateClipGrid();
		}
 
 
		//  :::: les méthodes qui permettent la mise à jour de l'objet :::: //
		// A partir d’ici, les explications se déroulent en dessous
 
		private function updateClipGrid():void {
			textField.text+=" update appelé "+String.fromCharCode(13);
			repartirObjet();
			ajouterFiltre();
		}
 
		private function repartirObjet():void {
			textField.text+="Repartition appelé” "+String.fromCharCode(13);
			var i:int;
			var nbreDeClip:int=clipArray.length;
			var currentX:int=0;
			var currentY:Number=0;
			for (i=0; i<nbreDeClip; i++) {
				var clip:DisplayObject=clipArray[i];
				if (i%_nbreClipLargeur==0&&i!=0) {
					currentX=0;
					currentY+=clip.height+_espacement;
				}
				clip.x=currentX;
				clip.y=currentY;
				currentX+=clip.width+_espacement;
			}
 
		}
 
		private function ajouterFiltre():void {
			textField.text+="Couleur Filtre appelé” "+String.fromCharCode(13);
			var clip:DisplayObject;
			for each (clip in clipArray) {
				clip.filters=[new DropShadowFilter(4,45,_couleurFiltre)];
			}
		}
 
		// :::: Fin de la classe :::: //
	}
 
}
  • La fonction updateClipGrid() :

Cette fonction va permettre de lancer d’autres fonctions qui se chargent de répartir les cellules et de leur coller une ombre.
Pourquoi avoir une telle fonction me direz-vous, autant avoir les actions directement dans celle-ci ? Et vous auriez raison. Cette fonction va tenir un rôle de régulateur par la suite, et permettre de lancer sélectivement l’action selon les besoins (outre le fait que ça rende le code un peu plus lisible à mon avis).

  • La fonction ajouterFiltre() :

Cette fonction a pour unique but de rajouter un filtre DropShadowFilter à chaque item référencé dans l’array clipArray (qui contient les références aux clips ajoutés par addItem).
Là encore, vous allez remarquer que le filtre est retracé pour chaque item, à chaque fois, et que l’on pourrait se contenter d'ajouter l’ombre lorsque cet item est ajouté (dans addItem), mais elle permettra de modifier à la volée la couleur de cette ombre, donc pour tous les clips.

  • La fonction repartirObjet() :

Celle-ci permet de répartir les items ajoutés sur plusieurs lignes, chaque ligne ne pouvant contenir que ‘_nbreClipLargeur’ clips, et espacés de ‘_espacement’.

Le principe reste assez simple, on incrémente deux variables (currentX et currentY) selon la position de l’item dans le tableau clipArray.

if (i % _nbreClipLargeur == 0 && i != 0) {

permet de ramener la valeur de currentX à 0 et incrémente currentY de la hauteur du clip+espacement, à chaque changement de ligne.
En Effet, i % _nbreClipLargeur retournera 0 uniquement quand ‘i’ sera un multiple de ‘_nbreClipLargeur’. Pratique non ?

la source : clipgrid_ini.as (pensez à renommer le fichier en 'ClipGrid.as' pour utiliser la source)


Ce qu’on observe

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Maintenant, vous pouvez lancer la compilation de votre fla ! Et vous constatez que nous avons le 1er exemple sous les yeux. Nous voyions bien toutes les lignes “update appelé” dans notre champ texte, suivies de 'Repartition appelé' et 'Couleur Filtre appelé'. En effet, à chaque fois que la méthode ‘addItem’, ‘set espacement’ ou encore ‘set color’ est appelé sur votre occurrence de ClipGrid, cette dernière lance sa mise à jour via l’appel à ’updateClipGrid()’, qui à son tour lance ajouterFiltre() et repartirObjet()…. Voilà tout est là ! Alors évidemment, pour placer 10 carrés, on peut faire plus simple, et ça ne mange pas de pain. Maintenant imaginez un composant type list que vous remplissez à l’aide d’entrée d’une base de données, et que vous ayez 10 000 appels à addItem !!!! Nous allons donc remédier à cela !



Optimisation : 1ère étape

Ici, nous allons mettre en place la 1ère partie du principe : la gestion de l'événement Event.RENDER, qui va nous permettre de minimiser le nombre d'appel(s) de la fonction de mise à jour (updateClipGrid), celle qui par la suite deviendra la plaque tournante de la régulation….

Des objets en plus

Pour ce faire, nous allons avoir besoin de 2 autres petits objets, 2 petits booléens : (à déclarer avec les autres variables dans la classe)

private var invalidation:Boolean=false;
private static var enPhaseAppel:Boolean=false;

A quoi vont-elles servir….. ?

  • invalidation : va nous permettre de dire que nous avons invalidé/modifié une des propriétés.
  • enPhaseAppel : va nous permettre de savoir si nous sommes dans le processus d’attente de l’évènement RENDER.

Pourquoi en static? tout simplement parce qu'une fois l'invalidation du stage appelé, il n'est pas nécessaire de le relancer si une autre occurrence de notre classe venait à être modifiée.

Montons donc notre script

Maintenant, nous allons assembler l’ensemble des fonctions nous permettant de temporiser l’appel aux fonctions.

lancer l’invalidation

		protected function invalidate():void {
			if (invalidation) {
				return;
			}
			invalidation=true;
			this.appelRetarder();
		}

Ici, rien de bien méchant, on appelle invalidate() lorsque l’on modifie une propriété ou que l’on ajoute un item. La condition permet que lorsqu’un appel de invalidate() a déjà été lancé, il n'est plus nécessaire de relancer le processus.

Mais que se cache-t-il derrière cette méthode appelRetarder() ?

appelRetarder() ou la mise en place des écouteurs

		protected function appelRetarder():void {
 
			if (stage!=null) {
				stage.addEventListener(Event.RENDER,appelRetarderLancement,false,0,true);
				if (enPhaseAppel) {
					return;
				}
				enPhaseAppel=true;
				stage.invalidate();
			} else {
				addEventListener(Event.ADDED_TO_STAGE,appelRetarderLancement,false,0,true);
			}
		}

Ha Ha ! Vous vous dites c’est tout ? Oui, le meilleur pour la fin !
Cette fonction permet ‘simplement’ de vérifier que l’on a bien accès au stage (et oui, sinon nous ne pourrions pas souscrire un écouteur à ce dernier), le cas échéant, on va suivre l’évènement RENDER, sinon on va patiemment attendre que l’utilisateur daigne bien vouloir ajouter notre occurrence de ClipGrid dans la liste d'affichage !
Une fois ceci fait, nous allons donc à :

appelRetarderLancement ou là où ça commence (et finit) par être intéressant !

		private function appelRetarderLancement(event:Event):void {
			if (event.type==Event.ADDED_TO_STAGE) {
				removeEventListener(Event.ADDED_TO_STAGE,appelRetarderLancement);
				stage.addEventListener(Event.RENDER,appelRetarderLancement,false,0,true);
				enPhaseAppel=true;
				stage.invalidate();
				return;
			} else {
				event.target.removeEventListener(Event.RENDER,appelRetarderLancement);
			}
			updateClipGrid();
			enPhaseAppel=false;
		}

Tadammmmmm! La première partie de cette fonction permet elle aussi de gérer les écouteurs : Si on vient d’être ajouté au scénario, alors on va aller écouter l’évènement RENDER, sinon, on retire l'écoute sur l'évènement RENDER, et on va aller gérer les mises à jour, et lancer la désormais fameuse fonction updateClipGrid(), qui va nous permettre de mettre à jour notre occurrence.

La fonction de validation

En effet, une fois que la propriété signifiée comme invalide à été mise à jour, il faut la rendre à nouveau valide, en réinitialisant le premier Booléen :

		protected function validate():void {
			invalidation=false;
		}

Il suffit de placer un appel à cette fonction de validation à la fin de l'update.

C'est quelque chose à ne pas oublier, sinon les prochains appel à invalidate() n'aboutirons pas (me suis déjà fait avoir)

Modification des fonctions publiques, ça sent la fin :

Alors tout ceci est bien beau, mais pour le moment on ne l’utilise pas. Toutefois, je suis sûr que vous voyez ce qu’il faut faire pour remédier à cela, modifier ‘addItem’ et les 2 setter, en leur ajoutant l’appel à invalidate() :

		public function addItem(item:DisplayObject):void {
			clipArray.push(item);
			addChild(item);
			invalidate();// ICI
 
		}
		public function set espacement(value:Number):void {
			_espacement=value;
			invalidate(); //et là
		}
		public function set couleurFiltre(value:int):void {
			_couleurFiltre=value;
			invalidate();// et enfin ici
	}

Evidemment, il faut supprimer les appels à updateClipGrid !

et la fonction updateClipGrid devient :

private function updateClipGrid():void {
			textField.text+=" update appelé "+String.fromCharCode(13);
			repartirObjet();
			ajouterFiltre();
			validate();
		}
la source : clipgrid_1.as(pensez à renommer le fichier en 'ClipGrid.as' pour utiliser la source)

Maintenant compilons………

Ce que ça donne

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Formidable, nous n’avons plus qu’un seul appel à updateClipGrid, qui lance ajouterFiltre() et repartirObjet() .

  • Cliquons sur ajouter Clip : Idem
  • Cliquons sur modifier espacement : Idem
  • Cliquons sur modifier couleur : Idem

Alors comme moi, vous constatez que lorsque l’on veut uniquement modifier l’espacement, et bien on relance également l’ajout de l’ombre, et comme moi, vous vous dites, c’est un mieux, mais ce n’est pas encore ça, peut mieux faire !
Et vous avez raison ! Embarquons-nous pour la dernière étape, la mise en place de la sélectivité des fonctions de mise à jour !



L’ultime optimisation : introduire la sélectivité

Le principe réside en l'introduction de différents types d'invalidation, qui vont permettre de selectionner quelles actions il conviendra d'engager. Pour ce faire, nous allons avoir besoin d'introduire des constantes, ainsi que l'objet qui va répertorier quels types d'invalidation ont été appelés.

Objets supplémentaires

les constantes :

Celles-ci vont constituer les types d'invalidation.

private static const DISPOSITION:String="disposition";
private static const COULEUR:String="couleur";
private static const TOUT:String="tout";
Il existe une classe AS toute faite, dédiée à ceci : fl.core.InvalidationType, et qui regroupe plusieurs constantes de ce type. Tout ceci pour vous dire que vous pouvez l'utiliser si vous le souhaitez.

les objets : (remplacent ceux que nous avions rajoutés au point précédent)

private var invalidProp:Object=new Object();
private static var enPhaseAppel:Boolean=false;
  • invalidProp : va nous permettre de référencer les types d'invalidation

Le script :

Peu de choses vont changer par rapport au script précédent.

Tout d'abord l'invalidation :

		protected function invalidate(property:String):void {
                        if(invalidProp[property]){
                             return;
                        }
			invalidProp[property]=true;
			this.appelRetarder();
		}

Maintenant cette fonction prend un argument : le type d'invalidation appelé, qui va être référencé dans l'objet 'invalidProp'.

Puis la fonction de validation :

		protected function validate():void {
			invalidProp={};
		}

(eh oui, c'est un objet, plus un booléen !)

Nous allons également créer une petite fonction pour tester l'invalidité d'une propriété :

		protected function isInvalid(property:String):Boolean {
			if (invalidProp[property] || invalidProp[TOUT]) {
				return true;
			}
			return false;
		}

Vous remarquerez que si nous avons invalidé 'TOUT', alors chaque propriété l'est (c'est Lapalissadien!!!).

Alors, me direz-vous, où se situe la sélectivité ? Elle est mise en œuvre dans la fonction 'updateClipGrid'! (cette fameuse fonction de régulation)

		private function updateClipGrid():void {
			textField.text+=" update appelé "+String.fromCharCode(13);
			if (isInvalid(TOUT)) {
				repartirObjet();
				ajouterFiltre();
				validate();
				return;
			}
			if (isInvalid(COULEUR)) {
				ajouterFiltre();
				validate();
				return;
			}
			if (isInvalid(DISPOSITION)) {
				repartirObjet();
				validate();
				return;
			}
			validate();
		}

Voilà, il suffit de tester le type d'invalidation et d'appeler les mise à jour nécessaires.

Nous pouvons noter que la première condition n'est pas nécessaire, puisque le test d'invalidité renvoie 'vrai' pour toutes les propriétés si TOUT est 'vrai'. Toutefois, si vous deviez appeler les fonctions de mise à jour dans un ordre précis, il est plus aisé de les regrouper ici, que de se casser la tête à organiser les conditions suivantes. L'organisation de cette fonction dépend donc de votre projet, à vous de l'optimiser!
Vous noterez également que le dernier appel à 'validate()' n'est pas nécessaire ici, c'est juste une mesure de prudence, pour être sur qu'il soit appelé à la fin des fonction de validation.
(il n'y a pas de raison, mais bon, on ne sait jamais…)

Il ne nous reste plus qu'à mettre en place les appels dans les fonctions publiques.

Modification des fonctions publiques

		public function addItem(item:DisplayObject):void {
			clipArray.push(item);
			addChild(item);
			invalidate(TOUT);
 
		}
		public function set espacement(value:Number):void {
			_espacement=value;
			invalidate(DISPOSITION);
		}
		public function set couleurFiltre(value:int):void {
			_couleurFiltre=value;
			invalidate(COULEUR);
		}

Comme vous pouvez le constater, chaque changement d'une propriété s'accompagne de l'invalidation adéquate.

Testons donc notre script.

la source : clipgrid_final.as(pensez à renommer le fichier en 'ClipGrid.as' pour utiliser la source)

Ce que ça donne

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Nous constatons dans un premier temps que les fonctions de mise à jour n'ont été appelées qu'une seule fois. Ensuite, pour chaque modification de la couleur ou de l'espacement, seule la fonction adéquate a été appelée !

Victory !!!



La facilité - étendez UIComponent

Pour la petite histoire, j'ai trouvé ce procédé caché au fin fond des composants de flash : Dans la classe fl.core.UIComponent (qui tout est de même un poil plus complexe, mais plus adaptable!). Et donc, il est très facile d'étendre cette classe pour obtenir ce comportement ! (et bien plus, je vous laisse jeter un oeil sur la doc). Cette classe utilise comme base fl.core.InvalidationType, mais nous pouvons garder les types d'invalidation que nous avons définis. En outre, UIComponent hérite de Sprite, c'est toujours bon à savoir !

Ce que nous devons changer :

  • Plus besoin de toutes les fonctions que nous avons créées au dessus.
  • Plus besoin des objets que nous avons créés spécifiquement (on va garder les constantes de type d'invalidation)
  • A la place de updateClipGrid, nous allons devoir surcharger la méthode draw.
import fl.core.UIComponent;
public class ClipGrid extends UIComponent {//...
		override protected function draw():void {
			textField.text+=" update appelé "+String.fromCharCode(13);
			if (isInvalid(TOUT)) {
				repartirObjet();
				ajouterFiltre();
				validate();
				return;
			}
			if (isInvalid(COULEUR)) {
				ajouterFiltre();
				validate();
				return;
			}
			if (isInvalid(DISPOSITION)) {
				repartirObjet();
				validate();
				return;
			}
			validate();
		}

Le reste ne change pas ! (parce que isInvalid(property) est également une fonction de UIComponent, de même que invalidate(property) et validate(), différentes par le script, mais avec les mêmes fonctions).
Il faut bien sûr conserver les appels à invalidate(“property”) dans 'addItem' et les deux setter!

la source : clipgrid_ui.as (pensez à renommer le fichier en 'ClipGrid.as' pour utiliser la source)

Ce que ça donne

Compilons…….

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Hurra, en 3 lignes, nous avons le même comportement que précédemment !! Finalement, pourquoi se fouler ? A vous de voir, la classe UIComponent est bien plus complète et permet de gérer beaucoup d'autre choses.



Un petit plus : votre propre classe à étendre

Si vous ne voulez pas étendre UIComponent, et que ce procédé vous intéresse, mais que vous ne voulez pas copier ce (petit) bout de script systématiquement, vous pouvez créer votre propre classe à étendre, à l'instar de UIComponent.

En voici un exemple : DelayedCall.as

package {
 
	import flash.display.Sprite;
	import flash.events.Event;
 
	public class DelayedCall extends Sprite {
 
		private var invalidProp:Object=new Object();
		private static var enPhaseAppel:Boolean=false;
 
		private static const DISPOSITION:String="disposition";
		private static const COULEUR:String="couleur";
		private static const TOUT:String="tout";
 
		public function DelayedCall() {
		}
		protected function draw():void {
		}
 
		protected function invalidate(property:String):void {
			if (invalidProp[property]) {
				return;
			}
			invalidProp[property]=true;
			appelRetarder();
		}
 
		//// fonction de verif de présence des propde InvalidationTyp ds invalidProp
		protected function isInvalid(property:String):Boolean {
			if (invalidProp[property]||invalidProp[TOUT]) {
				return true;
			}
			return false;
		}
 
		protected function validate():void {
			invalidProp={};
		}
 
		protected function appelRetarder():void {
 
			if (stage!=null) {
				stage.addEventListener(Event.RENDER,appelRetarderLancement,false,0,true);
				if (enPhaseAppel) {
					return;
				}
				enPhaseAppel=true;
				stage.invalidate();
			} else {
				addEventListener(Event.ADDED_TO_STAGE,appelRetarderLancement,false,0,true);
			}
		}
		private function appelRetarderLancement(event:Event):void {
			if (event.type==Event.ADDED_TO_STAGE) {
				removeEventListener(Event.ADDED_TO_STAGE,appelRetarderLancement);
				stage.addEventListener(Event.RENDER,appelRetarderLancement,false,0,true);
				enPhaseAppel=true;
				stage.invalidate();
				return;
			} else {
				event.target.removeEventListener(Event.RENDER,appelRetarderLancement);
			}
 
			draw();
			enPhaseAppel=false;
		}
		// :::: END :::: //
 
	}
}

Vous retrouvez tous les éléments qui permettent de retarder l'appel à la fonction de mise à jour (que j'ai nommée ici draw(), ça vous rappelle quelque chose??!?!), et les constantes qui caractérisent les types d'invalidation. Vous n'avez plus qu'à étendre cette classe et à surcharger la méthode draw(), à l'instar de ce que nous avons fait pour UIComponent, et de mettre en place les appels à 'invalidate(“property”)' dans vos fonctions publiques.

La source : delayedcall.as



Conclusion

En voila un chouette procédé, facilement réutilisable (pour peu que l'on utilise un nom un peu plus générique pour la fonction de mise à jour, ou en mettant en œuvre le dernier point), qui, en fin de compte, ne tient que sur quelques lignes!



Articles connexes

On parle ici de la création de composants personnalisés Flex, et donc de l'invalidation: Création avancée de composants en Actionscript