Vous êtes sur la page 1sur 1096

trace("Pratique d'ActionScript 3");

// Thibault IMBERT

Remerciements

//

"Je voudrais remercier tout particulirement Mathieu Anthoine (Mama) pour sa relecture soigne et ses conseils aviss durant toute la rdaction de louvrage. Son exprience et sa vision du Flash est une source dinspiration. Jrme Decoster pour sa relecture attentive, son soutien et ses conseils toujours pertinents. Pour nos soires pizza discuter ActionScript 3 et te faire rentrer chez toi en vlib. Chris Georgenes (www.mudbubble.com) et Aurelien Marcheguay de Carton Blanc (www.cartonblanc.com) et Stphane Mortka pour leur soutien et leurs illustrations qui mont permis denrichir graphiquement louvrage. Je remercie tous mes compagnons flasheurs qui mont forcment soutenu et aid un moment donn : Joa Ebert, Didier Brun, Jrme Cordiez, Vincent Maitray, David Deraedt, Frderic Saunier, Benjamin Jung. Le centre Regart.net pour mavoir soutenu. A Nicolas pour ses opinions, Eric pour ses bonnes blagues, et Guylaine pour ces annes chez Regart. Justin Everett-Church dAdobe et Henri Torgemane de Yahoo pour leur aide et leur soutien. Sgolne (www.chevaldetroie.net) pour avoir relu tous ces chapitres la recherche de fautes dorthographes. Mes amis que je n'ai pas vus pendant quasiment 8 mois : Sbastien, Bessam, Laurent, Paul, Juan, Pascal, Bob Groove et bien sr Stevie Wonder. Sonia pour ce dimanche 20 aot 2006 o ma vie a change. Mes parents et ma sur qui mont soutenu pendant lcriture. Bien entendu, je ne peux pas terminer les remerciements sans remercier tous mes stagiaires que jai pu rencontrer depuis plus de trois ans de formations. Jai appris normment chaque jour vos cts, vous avez particip cet ouvrage dune faon ou dune autre."

Prface

//

"Je pratique Flash depuis maintenant 10 ans et jai pu suivre de prs toutes ses volutions. Flash 3 a accompagn mes premiers pas dans l'univers du web. J'ai dcouvert un logiciel incroyablement accessible et dont l'ergonomie astucieuse, les outils d'animation et d'interactivit rpondaient parfaitement aux besoins de l'poque. Quelques annes plus tard, un bond de gant est franchi avec Flash MX et l'apparition de la premire version robuste du langage ActionScript. Flash est alors un outil mr et quilibr. Au mme moment, sortent les premires versions de Flash Media Server et Flash Remoting qui deviennent des complments indispensables tout dveloppement ambitieux. Suivant l'volution des crations web de plus en plus dynamiques et complexes, Flash bascule, non sans mal, vers un outil de dveloppement plus sophistiqu (Flash MX 2004), ncessitant la mise en place d'quipes spcialises. L'poque du flasheur maitrisant tout de A Z est rvolue. Flash s'adresse dsormais deux publics distincts : graphistes d'un cot, dveloppeurs de l'autre. L'quilibre devient alors prcaire et la tendance favoriser le code au dtriment de l'accessibilit de loutil se dveloppe. Les ralisations du graphiste ou de l'animateur sont maintenant souvent subordonnes au travail du dveloppeur, ce qui ne favorise pas toujours la cration. Si Flash 8, grce aux filtres et mlanges, ouvre de nouvelles voies la cration et redonne un peu de place au graphiste, c'est nanmoins, le plus souvent, le dveloppeur qui contrle le projet. Flash 9 et l'ActionScript 3 reprsentent une nouvelle tape vers la technicisation de loutil. Connatre l'environnement auteur ne suffit plus, il est indispensable de comprendre et de maitriser les rouages du lecteur pour viter la ralisation dapplications totalement instables. Etant au cur de la production, j'ai toujours dplor que les ouvrages Actionscript prennent si peu en compte les ralits que peuvent rencontrer une agence, un studio, une quipe ou tout professionnel dans son travail quotidien. Japprcie particulirement louvrage de Thibault pour les exemples concrets, directement utilisables en production, quil nous livre. Il nous dispense justement dexplications trop virtuelles ou dun esthtisme superflu du code. Fort de son exprience de formateur, il reste simple et clair au moyen d'un vocabulaire prcis, sans jamais tomber, ni dans le perfectionnisme lexical, ni dans la vulgarisation imprcise. Cet ouvrage va bien au del de l'apprentissage du langage ActionScript 3 en posant les bases de sa bonne utilisation dans Flash et en fournissant un clairage neuf et pertinent sur cet outil fabuleux."

Mathieu Anthoine Game Designer et co-fondateur du studio Yamago www.yamago.net

trace("Pratique d'ActionScript 3");

//

Pratique dActionScript 3 sadresse tous les flasheurs. Cet ouvrage dresse un panorama de lutilisation dActionScript 3 et de ses nouveauts, ainsi que du nouveau lecteur Flash 9. Lauteur explique au travers de nombreux exemples et cas concrets comment traiter dsormais les objets, le texte, le son et la vido, le chargement et lenvoi de donnes externes (variables, XML, etc.), les sockets, etc. Certains sujets avancs et peu abords comme les classes bas niveau ou encore Flash Remoting sont galement traits. Enfin, lauteur livre une application complte qui reprend les diffrents points voqus.

//

Thibault IMBERT est aujourd'hui ingnieur systme chez Adobe France. Il a commenc en tant que dveloppeur Flash pour diffrentes agences, avant de rejoindre Regart.net comme formateur et responsable pdagogique pour toutes les formations conernant la plate-forme Flash. Pendant son temps libre, Thibault exprimente ActionScript 3, comme en tmoigne son site www.bytearray.org. Il travaille galement sur diffrents projets tels que WiiFlash www.wiiflash.org ou AlivePDF www.alivepdf.org

Avril 2008 Environ 1200 pages. Pour un livre tlcharg, un platane plant. Graphisme couverture : Guillaume DURAND dgezeo

Chapitre 1 Quest ce que lActionScript 3 ? - version 0.1.1

1
Quest ce que lActionScript 3

HISTORIQUE .......................................................................................................... 1 10 RAISONS DE CODER EN ACTIONSCRIPT 3 .............................................. 3 OUTILS..................................................................................................................... 4 LA PLATEFORME FLASH ................................................................................... 4

Historique
Expliquer comment crer un projet AS dans Flex Builder Cest en 1996 que laventure Flash commence lorsque la firme Macromedia rachte la petite socit FutureWave auteur dun logiciel danimation vectoriel nomm Future Splash Animator.

1/5
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 1 Quest ce que lActionScript 3 ? - version 0.1.1

Figure 1-1. Future Splash Animator sorti en avril 1996. Dvelopp lorigine pour pouvoir animer du contenu vectoriel sur Internet, Future Splash Animator intgrait dj les bases de Flash en matire danimation mais ne possdait cette poque aucun langage de programmation associ. Macromedia renomma alors le logiciel sous le nom de Flash 1.0 en 1996, mais il faut attendre 1999 afin que la notion de programmation fasse officiellement son apparition avec Flash 4. Les documentations de lpoque ne parlent pas encore de langage ActionScript mais plus simplement dactions. Grce celles-ci, il devient possible dajouter des comportements avancs aux boutons et autres objets graphiques. De nombreux graphistes laise avec la programmation commencrent dvelopper des comportements avancs et se mirent changer des scripts par le biais de forums et autres plateformes communautaires. Flash connu alors un engouement fulgurant et devint rapidement un outil danimation avanc, capable de produire diffrents types de contenus interactifs comme des sites internet, des jeux, ou des applications multimdia. Cest en 2001 que le langage ActionScript fait officiellement apparition au sein de Flash 5. La notion de syntaxe pointe est intgre au langage qui suit pour la premire fois les spcifications ECMAScript. Nous reviendrons sur cette notion au cours du prochain chapitre intitul Langage et API.

2/5
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 1 Quest ce que lActionScript 3 ? - version 0.1.1

Afin de rpondre une demande de la communaut pour un langage ActionScript plus structur, Macromedia dveloppe alors une nouvelle version dActionScript et lintgre en 2003 au sein de Flash MX 2004 sous le nom dActionScript 2.0. Cette nouvelle version rpond aux besoins des dveloppeurs en offrant un langage orient objet et non procdural comme ctait le cas en ActionScript 1.0. A lpoque, Macromedia fait le choix dune migration en douceur, et propose un langage ActionScript 2.0 souple permettant aux dveloppeurs et graphistes de coder au sein dun mme projet en ActionScript 1.0 et 2.0. Si cette nouvelle version du langage ne satisfait pas les dveloppeurs puristes, elle permet nanmoins aux dveloppeurs dbutant de migrer doucement vers un dveloppement orient objet. Macromedia dveloppe alors une nouvelle version 3.0 du langage ActionScript afin doffrir des performances optimales leur nouvelle cration nomme Flex. Deux ans plus tard, la socit Macromedia se voit rachete par le gant Adobe et lActionScript 3 voit le jour au sein de Flash en 2007, lors de la sortie de Flash CS3.

10 raisons de coder en ActionScript 3


Si vous ntes pas encore convaincu de migrer vers ActionScript 3, voici 10 raisons pour ne plus hsiter :
En dcembre 2007, le lecteur Flash 9 possde un taux de pntration de plus de 95%. Il demeure le lecteur multimdia le plus prsent sur Internet. La vitesse dexcution du code est environ 10 fois suprieure aux prcdentes versions dActionScript. ActionScript 3 offre des possibilits incomparables aux prcdentes versions dActionScript. Le code ActionScript 3 savre plus logique que les prcdentes versions du langage. Le langage ActionScript 3 permet de dvelopper du contenu en Flash, Flex, ou AIR. Nous reviendrons trs vite sur ces diffrents frameworks. Il nest pas ncessaire de connatre un autre langage orient objet au pralable. ActionScript 3 est un langage orient objet. Sa connaissance vous permettra daborder plus facilement dautres langages objets tels Java, C#, ou C++. Le langage ActionScript 3 et lAPI du lecteur Flash ont t entirement repenss. Les dveloppeurs ActionScript 1 et 2 seront ravis de dcouvrir les nouveauts du langage et la nouvelle organisation de lAPI du lecteur.

3/5
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 1 Quest ce que lActionScript 3 ? - version 0.1.1

La technologie Flash ne cesse dvoluer, vous ne risquez pas de vous ennuyer. ActionScript 3 est un langage souple, qui demeure ludique et accessible.

Bien entendu, vous pouvez mmoriser ces diffrents points afin de convaincre vos amis au cours dune soire geek. ActionScript 3 ne vous sera pas dune grande utilit sans un environnement de dveloppement ddi. Nous allons nous attarder quelques instants sur les diffrents outils disponibles permettant de produire du contenu ActionScript 3.

Outils
Afin quil ny ait pas de confusions, voici les diffrents outils vous permettant de coder en ActionScript 3 :
Flash CS3 : permet le dveloppement danimations et dapplications en ActionScript 3. Pour plus dinformations :

http://www.adobe.com/fr/products/flash/

Flex Builder : il sagit dun environnement auteur permettant le dveloppement dapplications riches (RIA). Flex repose sur deux langages, le MXML afin de dcrire les interfaces, et ActionScript pour la logique. Pour plus dinformations :

http://www.adobe.com/fr/products/flex/

Eclipse (SDK Flex et AIR) : les SDK de Flex et AIR permettent de produire du contenu Flex et AIR gratuitement. Il sagit de plugins pour lenvironnement de dveloppement Eclipse.

Vous avez dit plateforme ?

La plateforme Flash
Par le terme de plateforme Flash nous entendons lensemble des technologies travers lesquelles nous retrouvons le lecteur Flash. Nous pouvons diviser la plateforme Flash en trois catgories :
Les lecteurs Flash : parmi les diffrents lecteurs, nous comptons le lecteur Flash, le lecteur Flash intgr AIR, ainsi que Flash Lite (notons que Flash lite nest pas ce jour compatible avec ActionScript 3). Les outils de dveloppement : Flex Builder et Flash CS3 sont les deux environnements auteurs permettant de produire du contenu Flash. Notons quil est aussi possible dutiliser lenvironnement Eclipse et les SDK de Flex et AIR.

4/5
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 1 Quest ce que lActionScript 3 ? - version 0.1.1

Les frameworks : Flex est un framework conu pour faciliter le dveloppement dapplications riches. Grce AIR, ces applications peuvent sortir du navigateur et tre dployes sur le bureau.

Chaque outil de dveloppement ou framework rpond une attente spcifique et correspond un profil type. Un graphiste-dveloppeur sera intress par dinterfaces animes et de sites Internet au sein connaissance dun environnement comme Flash aussi de dvelopper des composants rutilisables AIR. le dveloppement de Flash CS3. La CS3 lui permettra au sein de Flex et

Un dveloppeur prfrera peut tre un environnement de dveloppement comme celui offert par Flex Builder. Les applications produites grce au framework Flex seront de nature diffrente et pourront facilement tre dployes sur le bureau grce AIR. Notons que les exemples prsents dans cet ouvrage peuvent tre utiliss au sein de Flash CS3, Flex et AIR. Afin dentamer cette aventure au cur dActionScript 3, nous allons nous intresser tout dabord aux diffrentes nouveauts du langage. En route pour le prochain chapitre intitul Langage et API !

5/5
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

2
Langage et API

LE LANGAGE ACTIONSCRIPT 3 .........................................................................1 MACHINES VIRTUELLES ............................................................................................4 TRADUCTION DYNAMIQUE ........................................................................................5 GESTION DES TYPES A LEXECUTION .........................................................................6 ERREURS A LEXECUTION........................................................................................ 11 NOUVEAUX TYPES PRIMITIFS .................................................................................. 13 VALEURS PAR DEFAUT ............................................................................................ 18 NOUVEAUX TYPES COMPOSITES .............................................................................. 20 NOUVEAUX MOTS-CLES .......................................................................................... 20 FONCTIONS ............................................................................................................. 21 CONTEXTE DEXECUTION ........................................................................................ 24 BOUCLES ................................................................................................................. 25 ENRICHISSEMENT DE LA CLASSE ARRAY ................................................................. 26 RAMASSE-MIETTES ................................................................................................. 29 BONNES PRATIQUES ................................................................................................ 32 AUTRES SUBTILITES ................................................................................................ 33

Le langage ActionScript 3
Le langage ActionScript 3 intgre de nombreuses nouveauts que nous allons traiter tout au long de cet ouvrage. Ce chapitre va nous permettre de dcouvrir les nouvelles fonctionnalits et comportements essentiels tout dveloppeur ActionScript 3. Avant de dtailler les nouveauts lies au langage ActionScript 3, il convient de dfinir tout dabord ce que nous entendons par le terme ActionScript. De manire gnrale, le terme ActionScript englobe deux composantes importantes :

1 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Le cur du langage : il sagit du langage ActionScript bas sur la spcification ECMAScript (ECMA-262) et intgre partiellement certaines fonctionnalits issues de la spcification ECMAScript 4. LAPI du lecteur Flash : il sagit des fonctionnalits du lecteur Flash. Toutes les classes ncessitant dtre importes font partie de lAPI du lecteur et non du cur du langage ActionScript.

Ainsi, linterface de programmation du lecteur ou le langage peuvent tre mise jour indpendamment. Le lecteur Flash 10 devrait normalement intgrer une gestion de la 3D native ainsi quune implmentation plus complte de la spcification ECMAScript 4. La gestion de la 3D concerne ici uniquement linterface de programmation du lecteur Flash, linverse les nouveaux objets dfinis par la spcification ECMAScript 4 sont directement lis au cur du langage ActionScript. Dun ct rside le langage ActionScript 3, de lautre lAPI du lecteur appele gnralement interface de programmation. La figure 2-1 illustre les deux entits :

Figure 2-1. Langage ActionScript 3. Contrairement aux prcdentes versions dActionScript, nous remarquons quen ActionScript 3, les diffrentes fonctionnalits du lecteur Flash sont dsormais stockes dans des paquetages spcifiques. 2 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Afin dafficher une vido nous utiliserons les objets issus du paquetage flash.media. A linverse, pour nous connecter un serveur, nous utiliserons les objets issus du paquetage flash.net. Flash CS3 est configur afin dimporter automatiquement toutes les classes issues de lAPI du lecteur Flash. Il nest donc pas ncessaire dimporter manuellement les classes lorsque nous codons au sein de lenvironnement auteur. Un fichier implicitImports.xml situ au sein du rpertoire dinstallation de Flash CS3 (C:\Program Files\Adobe\Adobe Flash CS3\fr\Configuration\ActionScript 3.0) contient toutes les dfinitions de classe importer :
<implicitImportsList> <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name <implicitImport name </implicitImportsList> = = = = = = = = = = = = = = = "flash.accessibility.*"/> "flash.display.*"/> "flash.errors.*"/> "flash.events.*"/> "flash.external.*"/> "flash.filters.*"/> "flash.geom.*"/> "flash.media.*"/> "flash.net.*"/> "flash.printing.*"/> "flash.system.*"/> "flash.text.*"/> "flash.ui.*"/> "flash.utils.*"/> "flash.xml.*"/>

Afin de crer un clip dynamiquement nous pouvons crire directement sur une image du scnario :
var monClip:MovieClip = new MovieClip();

Si nous plaons notre code lextrieur de Flash au sein de classes, nous devons explicitement importer les classes ncessaires :
import flash.display.MovieClip; var monClip:MovieClip = new MovieClip();

Dans cet ouvrage nous nimporterons pas les classes du lecteur lorsque nous programmerons dans lenvironnement auteur de Flash. A linverse ds lintroduction des classes au sein du chapitre 8 intitul Programmation oriente objet, nous importerons explicitement les classes utilises.

A retenir

3 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Le langage ActionScript 3 englobe deux composantes : le cur du langage ActionScript et linterface de programmation du lecteur Flash. Le cur du langage est dfini par la spcification ECMAScript.

Machines virtuelles
Le code ActionScript est interprt par une partie du lecteur Flash appele machine virtuelle. Cest cette dernire qui se charge de retranscrire en langage machine le code binaire (ActionScript byte code) gnr par le compilateur. Les prcdentes versions du lecteur Flash intgraient une seule machine virtuelle appele AVM1 afin dinterprter le code ActionScript 1 et 2. En ralit, le code binaire gnr par le compilateur en ActionScript 1 et 2 tait le mme, cest la raison pour laquelle nous pouvions faire cohabiter au sein dun mme projet ces deux versions du langage ActionScript. La figure 2-2 illustre la machine virtuelle 1 (AVM1) prsente dans le lecteur Flash 8 :

Figure 2-2. AVM1 au sein du lecteur Flash 8. Le langage ActionScript 3 nest pas compatible avec cette premire machine virtuelle, pour des raisons videntes de rtrocompatibilit, le lecteur Flash 9 embarque donc deux machines virtuelles. Lors de la lecture dun SWF, le lecteur slectionne automatiquement la machine virtuelle approprie afin dinterprter le code ActionScript prsent au sein du SWF. Ainsi, une application ActionScript 1 et 2 sera interprte au sein du lecteur Flash 9 par la machine virtuelle 1 (AVM1) et ne bnficiera daucune optimisation des performances.

4 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

La figure 2-3 prsente les deux machines virtuelles au sein du lecteur Flash 9 :

Figure 2-3. Le lecteur Flash 9 et les deux machines virtuelles AVM1 et AVM2. Seules les animations compiles en ActionScript 3 pourront bnficier des optimisations ralises par la nouvelle machine virtuelle (AVM2).

A retenir
Les prcdentes versions du lecteur Flash intgraient une seule machine virtuelle afin dinterprter le code ActionScript 1 et 2. La machine virtuelle 1 (AVM1) interprte le code ActionScript 1 et 2. La machine virtuelle 2 (AVM2) interprte seulement le code ActionScript 3. Le lecteur Flash intgre les deux machines virtuelles (AVM1 et AVM2). Le langage ActionScript 3 ne peut pas cohabiter avec les prcdentes versions dActionScript.

Traduction dynamique
Afin doptimiser les performances, la machine virtuelle 2 (AVM2) du lecteur Flash 9 intgre un mcanisme innovant de compilation du code la vole. Bien que le terme puisse paratre tonnant, ce principe appel gnralement traduction dynamique permet dobtenir de meilleures performances dexcution du code en compilant ce dernier lexcution. Dans les prcdentes versions du lecteur Flash, le code prsent au sein du SWF tait directement retranscrit par la machine virtuelle en 5 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

langage machine sans aucune optimisation lie la plateforme en cours. En ActionScript 3 la machine virtuelle retranscrit le code binaire (ActionScript byte code) en langage machine laide dun compilateur la vole appel couramment compilateur la vole. (Just-in-time compiler). Ce dernier permet de compiler uniquement le code utilis et de manire optimise selon la plateforme en cours. La machine virtuelle peut donc optimiser les instructions pour un processeur spcifique tout en prenant en considration les diffrentes contraintes de la plateforme. Pour plus dinformations lies la compilation lexcution, rendez vous ladresse suivante : http://en.wikipedia.org/wiki/Just-in-time_compilation http://fr.wikipedia.org/wiki/Compilation_%C3%A0_la_vol%C3%A9e

Gestion des types lexcution


ActionScript 2 introduit au sein de Flash MX 2004 la notion de typage fort. Cela consistait associer un type de donnes une variable laide de la syntaxe suivante :
variable:Type

Dans le code suivant, nous tentions daffecter une chane une variable de type Number :
var distance:Number = "150";

Lerreur suivante tait gnre la compilation :


Incompatibilit de types dans l'instruction d'affectation : String dtect au lieu de Number.

En ActionScript 3, nous bnficions du mme mcanisme de vrification des types la compilation. En compilant le mme code en ActionScript 3, lerreur suivante est gnre :
1067: Contrainte implicite d'une valeur du type String vers un type sans rapport Number.

Ce comportement est appel Mode prcis dans Flash CS3 et peut tre dsactiv par lintermdiaire du panneau Paramtres dActionScript 3.0. A travers le panneau Paramtres de publication, puis de longlet Flash, nous cliquons sur le bouton Paramtres. Nous obtenons un panneau Paramtres dActionScript 3 contenant deux options lies aux erreurs comme lillustre la figure 2-4 :

6 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Figure 2-4. Options du compilateur ActionScript 3. Nous remarquons que par dfaut, le Mode prcis est activ, nous reviendrons trs vite sur le Mode avertissements. En dcochant la case Mode prcis, nous dsactivons la vrification des types la compilation afin de dcouvrir un comportement extrmement important apport par ActionScript 3. En testant le code suivant en mode non prcis, nous remarquons quaucune erreur de compilation nest gnre :
var distance:Number = "150";

A lexcution, la machine virtuelle 2 (AVM2) convertit automatiquement la chane de caractres 150 en un nombre entier de type int. Afin de vrifier cette conversion automatique, nous pouvons utiliser la fonction describeType du paquetage flash.utils :
var distance:Number = "150"; /* affiche : <type name="int" base="Object" isDynamic="false" isFinal="true" isStatic="false"> <extendsClass type="Object"/> <constructor> <parameter index="1" type="*" optional="true"/> </constructor> </type> */ trace( describeType ( distance ) );

La fonction describeType renvoie un objet XML dcrivant le type de la variable. Nous pouvons remarquer que lattribut name du nud type renvoie int. En modifiant la chane de caractres nous obtenons une conversion automatique vers le type Number :
var distance:Number = "150.5"; /* affiche : <type name="Number" base="Object" isDynamic="false" isFinal="true" isStatic="false"> <extendsClass type="Object"/> <constructor> <parameter index="1" type="*" optional="true"/> </constructor> </type> */

7 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

trace( describeType ( distance ) );

Si nous tentons daffecter un autre type de donnes celle-ci, la machine virtuelle 2 (AVM2) conserve le type Number et convertit implicitement les donnes lexcution. Contrairement au mode prcis, ce comportement de vrification des types lexcution ne peut pas tre dsactiv. Nous pourrions ainsi en conclure de toujours conserver le mode prcis afin de ne pas tre surpris par ce comportement, mais certaines erreurs de types ne peuvent tre dtectes par le compilateur car celles-ci ninterviennent qu lexcution. Dans le code suivant, le compilateur ne dtecte aucune erreur :
var tableauDonnees:Array = [ "150", "250" ]; // l'entre du tableau est automatiquement convertie en int var distance:Number = tableauDonnees[0];

A lexcution, la chane de caractres prsente lindex 0 est automatiquement convertie en int. Cette conversion reste silencieuse tant que celle-ci russit, le cas chant une erreur lexcution est leve. Dans le code suivant, nous tentons de stocker une chane de caractres au sein dune variable de type MovieClip :
var tableauDonnees:Array = [ "clip", "250" ]; // lve une erreur l'excution var clip:MovieClip = tableauDonnees[0];

A lexcution, la machine virtuelle 2 (AVM2) tente de convertir la chane en MovieClip et choue, lerreur lexcution suivante est leve :
TypeError: Error #1034: Echec de la contrainte de type : conversion de "clip" en flash.display.MovieClip impossible.

Nous pouvons alors nous interroger sur lintrt dun tel comportement, pourquoi la machine virtuelle svertue-t-elle conserver les types lexcution et convertit automatiquement les donnes ? Afin de garantir des performances optimales, la machine virtuelle 2 (AVM2) sappuie sur les types dfinis par le dveloppeur. Ainsi, lorsque nous typons une variable, loccupation mmoire est optimise spcifiquement pour ce type, ainsi que les instructions processeur.

8 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Il ne faut donc pas considrer ce comportement comme un dsagrment mais comme un avantage contribuant de meilleures performances. Ce comportement diffre dActionScript 2, o la machine virtuelle 1 (AVM1) valuait dynamiquement tous les types lexcution, aucune optimisation ntait ralise. Le typage des variables ntait quune aide la compilation. La grande nouveaut lie ActionScript 3 rside donc dans lintrt du typage la compilation comme lexcution. En associant un type une variable en ActionScript 3 nous bnficions dune vrification des types la compilation et dune optimisation des calculs raliss par le processeur et dune meilleure optimisation mmoire. Il est donc primordial de toujours typer nos variables en ActionScript 3, les performances en dpendent trs nettement. Nous typerons systmatiquement nos variables durant lensemble de louvrage. Voici un exemple permettant de justifier cette dcision : Une simple boucle utilise une variable dincrmentation i de type int :
var debut:Number = getTimer(); for ( var i:int = 0; i< 500000; i++ ) { } // affiche : 5 trace( getTimer() - debut );

La boucle ncessite 5 millisecondes pour effectuer 500 000 itrations. Sans typage de la variable i, la boucle suivante ncessite 14 fois plus de temps sexcuter :
var debut:Number = getTimer(); for ( var i = 0; i< 500000; i++ ) { } // affiche : 72 trace( getTimer() - debut );

9 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Dans le code suivant, la variable prenom ne possde pas de type spcifique, la machine virtuelle doit valuer elle-mme le type ce qui ralentit le temps dexcution :
var debut:Number = getTimer(); var prenom = "Bobby"; var prenomRaccourci:String; for ( var i:int = 0; i< 500000; i++ ) { prenomRaccourci = prenom.substr ( 0, 3 ); } // affiche : 430 trace( getTimer() - debut ); // affiche : Bob trace ( prenomRaccourci );

En typant simplement la variable prenom nous divisons le temps dexcution de presque deux fois :
var debut:Number = getTimer(); var prenom:String = "Bobby"; var prenomRaccourci:String; for ( var i:int = 0; i< 500000; i++ ) { prenomRaccourci = prenom.substr ( 0, 3 ); } // affiche : 232 trace( getTimer() - debut ); // affiche : Bob trace ( prenomRaccourci );

Au sein du panneau Paramtres ActionScript 3, nous pouvons apercevoir un deuxime mode de compilation appel Mode avertissements. Ce dernier permet dindiquer plusieurs types derreurs comme par exemple les erreurs lies la migration de code. Supposons que nous tentions dutiliser la mthode attachMovie dans un projet ActionScript 3 :
var ref:MovieClip = this.attachMovie ("clip", "monClip", 0);

Au lieu dindiquer un simple message derreur, le compilateur nous renseigne que notre code nest pas compatible avec ActionScript 3 et nous propose son quivalent. 10 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Le code prcdent gnre donc le message derreur suivant la compilation :


Warning: 1060: Problme de migration : la mthode 'attachMovie' n'est plus prise en charge. Si le nom de la sous-classe de MovieClip est A, utilisez var mc= new A(); addChild(mc). Pour plus d'informations, consultez la classe DisplayObjectContainer.

Le Mode avertissements permet davertir le dveloppeur de certains comportements lexcution risquant de le prendre au dpourvu. Attention, les avertissements nempchent ni la compilation du code ni son excution, mais avertissent simplement que le rsultat de lexcution risque de ne pas tre celui attendu. Dans le code suivant, un dveloppeur tente de stocker une chane de caractre au sein dune variable de type Boolean :
var prenom:Boolean = "Bobby";

A la compilation, lavertissement suivant est affich :


Warning: 3590: String utilise alors qu'une valeur boolenne est attendue. L'expression va tre transtype comme boolenne.

Il est donc fortement conseill de conserver le Mode prcis ainsi que le mode avertissements afin dintercepter un maximum derreurs la compilation. Comme nous lavons vu prcdemment, le lecteur Flash 9 nchoue plus en silence et lve des erreurs lexcution. Nous allons nous intresser ce nouveau comportement dans la partie suivante.

A retenir
Il est possible de dsactiver la vrification de type la compilation. Il nest pas possible de dsactiver la vrification de type lexcution. Le typage en ActionScript 2 se limitait une aide la compilation. Le typage en ActionScript 3 aide la compilation et lexcution. Dans un souci doptimisation des performances, il est fortement recommand de typer les variables en ActionScript 3. Il est fortement conseill de conserver le mode prcis ainsi que le mode avertissements afin dintercepter un maximum derreurs la compilation.

Erreurs lexcution
Nous avons trait prcdemment des erreurs de compilation laide du mode prcis et abord la notion derreurs lexcution. 11 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Une des grandes nouveauts du lecteur Flash 9 rside dans la gestion des erreurs. Souvenez-vous, les prcdentes versions du lecteur Flash ne levaient aucune erreur lexcution et chouaient en silence. En ActionScript 3 lorsque lexcution du programme est interrompue de manire anormale, on dit quune erreur dexcution est leve. Afin de gnrer une erreur lexcution nous pouvons tester le code suivant :
// dfinition d'une variable de type MovieClip // sa valeur par dfaut est null var monClip:MovieClip; // nous tentons de rcuprer le nombre d'images du scnario du clip // il vaut null, une erreur d' excution est leve var nbImages:int = monClip.totalFrames;

La fentre de sortie affiche lerreur suivante :


TypeError: Error #1009: Il est impossible d'accder la proprit ou la mthode d'une rfrence d'objet nul.

Afin de grer cette erreur, nous pouvons utiliser un bloc try catch :
// dfinition d'une variable de type MovieClip // sa valeur par dfaut est null var monClip:MovieClip; var nbImages:int; // grce au bloc try catch nous pouvons grer l'erreur try { nbImages = monClip.totalFrames; } { trace ( "une erreur d'excution a t leve !"); } catch ( pErreur:Error )

Bien entendu, nous nutiliserons pas systmatiquement les blocs try catch afin dviter dafficher les erreurs lexcution. Certains tests simples, que nous dcouvrirons au cours de louvrage, nous permettrons quelque fois dviter davoir recours ces blocs. Dans un contexte derreurs lexcution, il convient de dfinir les deux dclinaisons du lecteur Flash existantes :
Version de dbogage (Player Debug) : cette version du lecteur est destine au dveloppement et affiche les erreurs lexcution en ouvrant une fentre spcifique indiquant lerreur en cours. Ce lecteur est install

12 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

automatiquement lors de linstallation de dveloppement Flash CS3 ou Flex Builder 2 et 3.

lenvironnement

de

Version production (Player Release) : cette version du lecteur est disponible depuis le site dAdobe. Les personnes nayant pas denvironnement de dveloppement install utilisent cette version du lecteur. Ce lecteur naffiche pas les erreurs lexcution afin de ne pas interrompre lexprience de lutilisateur.

Avec le lecteur de dbogage, les erreurs non gres par un bloc try catch ouvrent un panneau derreur au sein du navigateur comme lillustre la figure 2-5 :

Figure 2-5. Exemple derreur lexcution. Lorsquune erreur est leve, lexcution du code est alors mise en pause. Nous pouvons alors dcider de continuer lexcution du code bien quune erreur vienne dtre leve ou bien de stopper totalement lexcution de lapplication.

A retenir
Le lecteur Flash 9 lve des erreurs lexcution. Ces erreurs ouvrent avec le lecteur de dbogage une fentre indiquant lerreur au sein du navigateur. Toutes les mthodes de lAPI du lecteur en ActionScript 3 peuvent lever des erreurs lexcution. Le lecteur Flash 9 nchoue plus en silence, le dbogage est donc facilit.

Nouveaux types primitifs


En ActionScript 2, seul le type Number existait afin de dfinir un nombre, ainsi pour stocker un nombre entier ou dcimal le type Number tait utilis :
var age:Number = 20; var vitesse:Number = 12.8;

13 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Aucune distinction ntait faite entre les nombres entiers, dcimaux et non ngatifs. ActionScript 3 intgre dsormais trois types afin de reprsenter les nombres :
int : reprsente un nombre entier 32 bit (32 bit signed integer) uint : reprsente un nombre entier non sign 32 bit. (32 bit unsigned integer) Number : reprsente un nombre dcimal 64 bit (64-bit IEEE 754 double-precision floating-point number)

Notons que les deux nouveaux types int et uint ne prennent pas de majuscule, contrairement au type Number dj prsent au sein dActionScript 2. Une variable de type int peut contenir un nombre oscillant entre -2147483648 et 2147483648 :
// affiche : -2147483648 trace( int.MIN_VALUE ); // affiche : 2147483648 trace( int.MAX_VALUE );

Une variable de type uint peut contenir un nombre entier oscillant entre 0 et 4294967295 :
// affiche : 0 trace( uint.MIN_VALUE ); // affiche : 4294967295 trace( uint.MAX_VALUE );

Attention, la machine virtuelle ActionScript 3 conserve les types lexcution, si nous tentons de stocker un nombre virgule flottante au sein dune variable de type int ou uint, le nombre est automatiquement converti en entier par la machine virtuelle :
var age:int = 22.2; // affiche : 22 trace ( age );

Notons, que la machine virtuelle arrondi lentier infrieur :


var age:int = 22.8; // affiche : 22 trace ( age );

Cette conversion automatique assure par la machine virtuelle savre beaucoup plus rapide que la mthode floor de la classe Math.

14 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Dans le code suivant, nous arrondissons lentier au sein dune boucle laide de la mthode floor, la boucle ncessite 111 millisecondes :
var distance:Number = 45.2; var arrondi:Number; var debut:Number = getTimer(); for ( var i:int = 0; i< 500000; i++ ) { arrondi = Math.floor ( distance ); } // affiche : 111 trace( getTimer() - debut );

A prsent, nous laissons la machine virtuelle grer pour nous larrondi du nombre :
var distance:Number = 45.2; var arrondi:int; var debut:Number = getTimer(); for ( var i:int = 0; i< 500000; i++ ) { arrondi = distance; } // affiche : 8 trace( getTimer() - debut ); // affiche : 45 trace( arrondi );

Nous obtenons le mme rsultat en 8 millisecondes, soit un temps dexcution presque 14 fois plus rapide. Attention, cette astuce nest valable uniquement dans le cas de nombres positifs. Dans le code suivant, nous remarquons que la mthode floor de la classe Math ne renvoie pas la mme valeur que la conversion en int par la machine virtuelle :
var distance:int = -3.2; // affiche : -3 trace(distance); var profondeur:Number = Math.floor (-3.2); // affiche : -4

15 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

trace( profondeur );

Partant du principe quune distance est toujours positive, nous pouvons utiliser le type uint qui offre dans ce cas prcis des performances similaires au type int :
var arrondi:uint;

Malheureusement, le type uint savre gnralement beaucoup plus lent, ds lors quune opration mathmatique est effectue. En revanche, le type Number savre plus rapide que le type int lors de division. Lors de la dfinition dune boucle, il convient de toujours prfrer lutilisation dune variable dincrmentation de type int :
var debut:Number = getTimer(); for ( var i:int = 0; i< 5000000; i++ ) { } // affiche : 61 trace( getTimer() - debut );

A linverse, si nous utilisons un type uint, les performances chutent de presque 400% :
var debut:Number = getTimer(); for ( var i:uint = 0; i< 5000000; i++ ) { } // affiche : 238 trace( getTimer() - debut );

Gardez lesprit, quen cas dhsitation, il est prfrable dutiliser le type Number :
var debut:Number = getTimer(); for ( var i:Number = 0; i< 5000000; i++ ) { } // affiche : 102 trace( getTimer() - debut );

16 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Nous obtenons ainsi un compromis en termes de performances entre le type int et uint. De manire gnrale, il est prfrable dviter le type uint. La mme optimisation peut tre obtenue pour calculer larrondi suprieur. Nous prfrons laisser la machine virtuelle convertir lentier infrieur puis nous ajoutons 1 :
var distance:Number = 45.2; var arrondi:int; var debut:Number = getTimer(); for ( var i:int = 0; i< 500000; i++ ) { arrondi = distance + 1; } // affiche : 12 trace( getTimer() - debut ); // affiche : 46 trace( arrondi );

En utilisant la mthode ceil de la classe Math, nous ralentissons les performances denviron 300% :
var distance:Number = 45.2; var arrondi:Number; var debut:Number = getTimer(); for ( var i:int = 0; i< 500000; i++ ) { arrondi = Math.ceil ( distance ); } // affiche : 264 trace( getTimer() - debut ); // affiche : 46 trace( arrondi );

Pour plus dastuces lies loptimisation, rendez-vous ladresse suivante : http://lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math/

A retenir
17 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Le type uint permet de reprsenter un nombre entier 32 bit non ngatif. Il est conseill dutiliser le type int pour les nombres entiers, son utilisation permet doptimiser les performances. Il est dconseill dutiliser le type uint. En cas dhsitation, il convient dutiliser le type Number. Le type Number permet de reprsenter un nombre dcimal 64 bit.

Le type int permet de reprsenter un nombre entier 32 bit.

Valeurs par dfaut


Il est important de noter que les valeurs undefined et null ont un comportement diffrent en ActionScript 3. Dsormais, une variable renvoie undefined uniquement lorsque celle-ci nexiste pas o lorsque nous ne la typons pas :
var prenom; // affiche : undefined trace( prenom );

Lorsquune variable est type mais ne possde aucune valeur, une valeur par dfaut lui est attribue :
var var var var var var var condition:Boolean; total:int; couleur:uint; resultat:Number; personnage:Object; prenom:String; donnees:*;

// affiche : false trace( condition ); // affiche : 0 trace( total ); // affiche : 0 trace( couleur ); // affiche : NaN trace( resultat ); // affiche : null trace( personnage ); // affiche : null trace( prenom ); // affiche : undefined trace( donnees );

Le tableau suivant illustre les diffrentes valeurs attribues par dfaut aux types de donnes : 18 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Type de donnes Boolean int Number Object String uint Non type (quivalent au type *) Autres types

Valeur par dfaut false 0 NaN null null 0 undefined null

Tableau 1. Valeurs par dfaut associes aux types de donnes. De la mme manire, si nous tentons daccder une proprit inexistante au sein dune instance de classe non dynamique telle String, nous obtenons une erreur la compilation :
var prenom:String = "Bob"; // gnre une erreur la compilation trace( prenom.proprieteInexistante );

Si nous tentons daccder une proprit inexistante, au sein dune instance de classe dynamique, le compilateur ne procde aucune vrification et nous obtenons la valeur undefined pour la proprit cible :
var objet:Object = new Object(); // affiche : undefined trace( objet.proprieteInexistante );

Attention, une exception demeure pour les nombres, qui ne peuvent tre null ou undefined. Si nous typons une variable avec le type Number, la valeur par dfaut est NaN :
var distance:Number; // affiche : NaN trace( distance );

19 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

En utilisant le type int ou uint, les variables sont automatiquement initialises 0 :


var distance:int; var autreDistance:uint; // affiche : 0 trace( distance ); // affiche : 0 trace( autreDistance );

A retenir

Une variable renvoie undefined uniquement lorsque celle-ci nexiste pas ou nest pas type. Lorsquune variable est type mais ne possde aucune valeur, la machine virtuelle attribue automatiquement une valeur par dfaut.

Nouveaux types composites


Deux nouveaux types composites sont intgrs en ActionScript 3 :
Les expressions rgulires (RegExp) : elles permettent deffectuer des recherches complexes sur des chanes de caractres. Nous reviendrons sur les expressions rgulires au cours de certains exercices. E4X (ECMAScript 4 XML) : la spcification ECMAScript 4 intgre un objet XML en tant quobjet natif. Nous reviendrons sur le format XML et E4X au cours de certains exercices.

Nouveaux mots-cls
Le mot cl is introduit par ActionScript 3 remplace lancien mot-cl instanceof des prcdentes versions dActionScript. Ainsi pour tester si une variable est dun type spcifique nous utilisons le mot-cl is :
var tableauDonnees:Array = [5654, 95, 54, 687968, 97851]; // affiche : true trace( tableauDonnees is Array ); // affiche : true trace( tableauDonnees is Object ); // affiche : false trace( tableauDonnees is MovieClip );

Un autre mot-cl nomm as fait aussi son apparition. Ce dernier permet de transtyper un objet vers un type spcifique.

20 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Dans le code suivant, nous dfinissons une variable de type DisplayObject, mais celle-ci contient en ralit une instance de MovieClip :
var monClip:DisplayObject = new MovieClip();

Si nous tentons dappeler une mthode de la classe MovieClip sur la variable monClip, une erreur la compilation est gnre. Afin de pouvoir appeler la mthode sans que le compilateur ne nous bloque, nous pouvons transtyper vers le type MovieClip :
// transtypage en MovieClip (monClip as MovieClip).gotoAndStop(2);

En cas dchec du transtypage, le rsultat du transtypage renvoie null, nous pouvons donc tester si le transtypage russit de la manire suivante :
var monClip:DisplayObject = new MovieClip(); // affiche : true trace( MovieClip(monClip) != null );

Nous aurions pu transtyper avec lcriture traditionnelle suivante :


var monClip:DisplayObject = new MovieClip(); // transtypage en MovieClip MovieClip(monClip).gotoAndStop(2);

En termes de performances, le mot cl as savre presque deux fois plus rapide. En cas dchec lors du transtypage lcriture prcdente ne renvoie pas null mais lve une erreur lexcution.

Fonctions
ActionScript 3 intgre de nouvelles fonctionnalits lies la dfinition de fonctions. Nous pouvons dsormais dfinir des paramtres par dfaut pour les fonctions. Prenons le cas dune fonction affichant un message personnalis :
function alerte ( pMessage:String ):void { trace( pMessage ); }

Cette fonction alerte accepte un paramtre accueillant le message afficher. Si nous souhaitons lexcuter nous devons obligatoirement passer un message :
alerte ("voici un message d'alerte !");

21 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Si nous omettons le paramtre :


// gnre une erreur la compilation alerte ();

Lerreur la compilation suivante est gnre :


1136: Nombre d'arguments incorrect. 1 attendus.

ActionScript 3 permet de dfinir une valeur par dfaut pour le paramtre :


function alerte ( pMessage:String="message par dfaut" ):void { trace( pMessage ); }

Une fois dfinie, nous pouvons appeler la fonction alerte sans passer de paramtres :
function alerte ( pMessage:String="message par dfaut" ):void { trace( pMessage ); } // affiche : message par dfaut alerte ();

Lorsque nous passons un paramtre spcifique, celui-ci crase la valeur par dfaut :
function alerte ( pMessage:String="message par dfaut" ):void { trace( pMessage ); } // affiche : un message personnalis ! alerte ( "un message personnalis !" );

En plus de cela, ActionScript 3 intgre un nouveau mcanisme li aux paramtres alatoires. Imaginons que nous devions crer une fonction pouvant accueillir un nombre alatoire de paramtres. En ActionScript 1 et 2, nous ne pouvions lindiquer au sein de la signature de la fonction. Nous dfinissions donc une fonction sans paramtre, puis nous utilisions le tableau arguments regroupant lensemble des paramtres : 22 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

function calculMoyenne ():Number { var lng:Number = arguments.length; var total:Number = 0; for (var i:Number = 0; i< lng; i++) { total += arguments[i]; } return total / lng; } var moyenne:Number = calculMoyenne ( 50, 48, 78, 20, 90 ); // affiche : 57.2 trace( moyenne );

Bien que cette criture puisse paratre trs souple, elle posait nanmoins un problme de relecture du code. En relisant la signature de la fonction, un dveloppeur pouvait penser que la fonction calculMoyenne nacceptait aucun paramtre, alors que ce ntait pas le cas. Afin de rsoudre cette ambigut, ActionScript 3 introduit un mot-cl permettant de spcifier dans les paramtres que la fonction en cours reoit un nombre variable de paramtres. Pour cela nous ajoutons trois points de suspensions en tant que paramtre de la fonction, suivi dun nom de variable de notre choix. Le mme code scrit donc de la manire suivante en ActionScript 3 :
function calculMoyenne ( ...parametres ):Number { var lng:int = parametres.length; var total:Number = 0; for (var i:Number = 0; i< lng; i++) { total += parametres[i]; } return total / lng; } var moyenne:Number = calculMoyenne ( 50, 48, 78, 20, 90 );

23 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

// affiche : 57.2 trace( moyenne );

En relisant le code prcdent, le dveloppeur ActionScript 3 peut facilement dtecter les fonctions accueillant un nombre de paramtres alatoires.

Contexte dexcution
Afin que vous ne soyez pas surpris, il convient de sattarder quelques instants sur le nouveau comportement des fonctions passes en rfrence. Souvenez-vous, en ActionScript 1 et 2, nous pouvions passer en rfrence une fonction, celle-ci perdait alors son contexte dorigine et pousait comme contexte le nouvel objet :
var personnage:Object = { age : 25, nom : "Bobby" }; // la fonction parler est passe en rfrence personnage.parler = parler; function parler ( ) { trace("bonjour, je m'appelle " + this.nom + ", j'ai " + this.age + " ans"); } // appel de la mthode // affiche : bonjour, je m'appelle Bobby, j'ai 25 ans personnage.parler();

En ActionScript 3, la fonction parler conserve son contexte dorigine et ne sexcute donc plus dans le contexte de lobjet personnage :
var personnage:Object = { age : 25, nom : "Bobby" }; // la fonction parler est passe en rfrence personnage.parler = parler; function parler ( ) { trace("bonjour, je m'appelle " + this.nom + ", j'ai " + this.age + " ans"); } // appel de la mthode // affiche : bonjour, je m'appelle undefined, j'ai undefined ans personnage.parler();

De nombreux dveloppeurs ActionScript se basaient sur ce changement de contexte afin de rutiliser des fonctions.

24 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Nous devrons donc garder lesprit ce nouveau comportement apport par ActionScript 3 durant lensemble de louvrage.

Boucles
ActionScript 3 introduit une nouvelle boucle permettant ditrer au sein des proprits dun objet et daccder directement au contenu de chacune dentre elles. Dans le code suivant, nous affichons les proprits de lobjet personnage laide dune boucle for in :
var personnage:Object = { prenom : "Bobby", age : 50 }; for (var p:String in personnage) { /* affiche : age prenom */ trace( p ); }

Attention, lordre dnumration des proprits peut changer selon les machines. Il est donc essentiel de ne pas se baser sur lordre dnumration des proprits. Notons que la boucle for in en ActionScript 3 ne boucle plus de la dernire entre la premire comme ctait le cas en ActionScript 1 et 2, mais de la premire la dernire. Nous pouvons donc dsormais utiliser la boucle for in afin ditrer au sein dun tableau sans se soucier du fait que la boucle parte de la fin du tableau :
var tableauDonnees:Array = [ 5654, 95, 54, 687968, 97851]; for ( var p:String in tableauDonnees ) { /* affiche : 5654 95 54 687968 97851 */ trace( tableauDonnees[p] ); }

25 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

La boucle for each accde elle, directement au contenu de chaque proprit :


var personnage:Object = { prenom : "Bobby", age : 50 }; for each ( var p:* in personnage ) { /* affiche : 50 Bobby */ trace( p ); }

Nous pouvons donc plus simplement itrer au sein du tableau laide de la nouvelle boucle for each :
var tableauDonnees:Array = [ 5654, 95, 54, 687968, 97851 ]; for each ( var p:* in tableauDonnees ) { /* affiche : 5654 95 54 687968 97851 */ trace( p ); }

Nous allons nous intresser dans la prochaine partie au concept de ramasse-miettes qui savre trs important en ActionScript 3.

Enrichissement de la classe Array


La classe Array bnficie de nouvelles mthodes en ActionScript 3 facilitant la manipulation de donnes. La mthode forEach permet ditrer simplement au sein du tableau :
// Array.forEach procde une navigation simple // sur chaque lement l'aide d'une fonction spcifique var prenoms:Array = [ "bobby", "willy", "ritchie" ]; function navigue ( element:*, index:int, tableau:Array ):void { trace ( element + " : " + index + " : " + tableau); }

26 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

/* affiche : bobby : 0 : bobby,willy,ritchie willy : 1 : bobby,willy,ritchie ritchie : 2 : bobby,willy,ritchie */ prenoms.forEach( navigue );

Toutes ces nouvelles mthodes fonctionnent sur le mme principe. Une fonction de navigation est passe en paramtre afin ditrer et de traiter les donnes au sein du tableau. La mthode every excute la fonction de navigation jusqu' ce que celle ci ou llment parcouru renvoient false. Il est donc trs simple de dterminer si un tableau contient des valeurs attendues. Dans le code suivant, nous testons si le tableau donnees contient uniquement des nombres :
var donnees:Array = [ 12, "bobby", "willy", 58, "ritchie" ]; function navigue ( element:*, index:int, tableau:Array ):Boolean { return ( element is Number ); } var tableauNombres:Boolean = donnees.every ( navigue ); // affiche : false trace( tableauNombres );

La mthode map permet la cration dun tableau relatif au retour de la fonction de navigation. Dans le code suivant, nous appliquons un formatage aux donnes du tableau prenoms. Un nouveau tableau de prnoms formats est cr :
var prenoms:Array = ["bobby", "willy", "ritchie"]; function navigue ( element:*, index:int, tableau:Array ):String { return element.charAt(0).toUpperCase()+element.substr(1).toLowerCase(); } // on cre un tableau partir du retour de la fonction navigue var prenomsFormates:Array = prenoms.map ( navigue ); // affiche : Bobby,Willy,Ritchie trace( prenomsFormates );

La mthode map ne permet pas de filtrer les donnes. Toutes les donnes du tableau source sont ainsi places au sein du tableau gnr. 27 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Si nous souhaitons filtrer les donnes, nous pouvons appeler la mthode filter. Dans le code suivant nous filtrons les utilisateurs mineurs et obtenons un tableau dutilisateurs majeurs :
var utilisateurs:Array = [ { prenom : "Bobby", age : 18 }, { prenom : "Willy", age : 20 }, { prenom : "Ritchie", age : 16 }, { prenom : "Stevie", age : 15 } ]; function navigue ( element:*, index:int, tableau:Array ):Boolean { return ( element.age >= 18 ); } var utilisateursMajeurs:Array = utilisateurs.filter ( navigue ); function parcourir ( element:*, index:int, tableau:Array ):void { trace ( element.prenom, element.age ); } /* affiche : Bobby 18 Willy 20 */ utilisateursMajeurs.forEach( parcourir );

La mthode some permet de savoir si un lment existe au moins une fois au sein du tableau. La fonction de navigation est excute jusqu ce que celle ci ou un lment du tableau renvoient true :
var utilisateurs:Array = [ { { { { prenom prenom prenom prenom : : : : "Bobby", age : 18, sexe : "H" }, "Linda", age : 18, sexe : "F" }, "Ritchie", age : 16, sexe : "H"}, "Stevie", age : 15, sexe : "H" } ]

function navigue ( element:*, index:int, tableau:Array ):Boolean { return ( element.sexe == "F" ); } // y'a t'il une femme au sein du tableau d'utilisateurs ? var resultat:Boolean = utilisateurs.some ( navigue ); // affiche : true trace( resultat );

28 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Les mthodes indexOf et lastIndexOf font elles aussi leur apparition au sein de la classe Array, celles-ci permettent de rechercher si un lment existe et dobtenir sa position :
var utilisateurs:Array = [ "Bobby", "Linda", "Ritchie", "Stevie", "Linda" ]; var position:int = utilisateurs.indexOf ("Linda"); var positionFin:int = utilisateurs.lastIndexOf ("Linda"); // affiche : 1 trace( position ); // affiche : 4 trace( positionFin );

Les mthodes indexOf et lastIndexOf permettent de rechercher un lment de type primitif mais aussi de type composite. Nous pouvons ainsi rechercher la prsence de rfrences au sein dun tableau :
var monClip:DisplayObject = new MovieClip(); var monAutreClip:DisplayObject = new MovieClip(); // une rfrence au clip monClip est place au sein du tableau var tableauReferences:Array = [ monClip ]; var position:int = tableauReferences.indexOf (monClip); var autrePosition:int = tableauReferences.lastIndexOf (monAutreClip); // affiche : 0 trace( position ); // affiche : -1 trace( autrePosition );

Nous reviendrons sur certaines de ces mthodes au sein de louvrage.

A retenir
Pensez utiliser les nouvelles mthodes de la classe Array afin de traiter plus facilement les donnes au sein dun tableau.

Ramasse-miettes
Tout au long de louvrage nous reviendrons sur le concept de ramassemiettes. Bien que le terme puisse paraitre fantaisiste, ce mcanisme va savrer extrmement important durant nos dveloppements ActionScript 3. Pendant la dure de vie dun programme, certains objets peuvent devenir inaccessibles. Afin, de ne pas saturer la mmoire, un mcanisme de suppression des objets inutiliss est intgr au sein du 29 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

lecteur Flash. Ce mcanisme est appel ramasse-miettes ou plus couramment Garbage collector en Anglais. Afin de bien comprendre ce mcanisme, nous dfinissons un simple objet rfrenc par une variable personnage :
var personnage:Object = { prenom : "Bobby", age : 50 };

Pour rendre cet objet inaccessible et donc ligible la suppression par le ramasse-miettes, nous pourrions tre tents dutiliser le mot cl delete :
var personnage:Object = { prenom : "Bobby", age : 50 }; // gnre une erreur la compilation delete personnage;

Le code prcdent, gnre lerreur de compilation suivante :


1189: Tentative de suppression de la proprit fixe personnage. proprits dfinies dynamiquement peuvent tre supprimes. Seules les

Cette erreur traduit une modification du comportement du mot cl delete en ActionScript 3, qui ne peut tre utilis que sur des proprits dynamiques dobjets dynamiques. Ainsi, le mot cl delete pourrait tre utilis pour supprimer la proprit prenom au sein de lobjet personnage :
var personnage:Object = { prenom : "Bobby", age : 50 }; // affiche : Bobby trace( personnage.prenom ); // supprime la proprit prenom delete ( personnage.prenom ); // affiche : undefined trace( personnage.prenom );

Afin de supprimer correctement une rfrence, nous devons affecter la valeur null celle-ci :
var personnage:Object = { prenom : "Bobby", age : 50 }; // supprime la rfrence vers lobjet personnage personnage = null;

Lorsque lobjet ne possde plus aucune rfrence, nous pouvons estimer que lobjet est ligible la suppression. Nous devrons donc toujours veiller ce quaucune rfrence ne subsiste vers notre objet, au risque de le voir conserv en mmoire. Attention, laffectation de la valeur null une rfrence ne dclenche en aucun cas le passage du 30 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

ramasse-miettes. Nous rendons simplement lobjet ligible la suppression. Il est important de garder lesprit que le passage du ramasse-miettes reste potentiel. Lalgorithme de nettoyage effectu par ce dernier tant relativement gourmand en termes de ressources, son dclenchement reste limit au cas o le lecteur utiliserait trop de mmoire. Dans le code suivant, nous supprimons une des deux rfrences seulement :
var personnage:Object = { prenom : "Bobby", age : 50 }; // copie d'une rfrence au sein du tableau var tableauPersonnages:Array = [ personnage ]; // suppression dune seule rfrence personnage = null;

Une autre rfrence vers lobjet personnage subsiste au sein du tableau, lobjet ne sera donc jamais supprim par le ramasse-miettes :
var personnage:Object = { prenom : "Bobby", age : 50 }; // copie d'une rfrence au sein du tableau var tableauPersonnages:Array = [ personnage ]; // supprime une des deux rfrences seulement personnage = null; var personnageOrigine:Object = tableauPersonnages[0]; // affiche : Bobby trace( personnageOrigine.prenom ); // affiche : 50 trace( personnageOrigine.age );

Nous devons donc aussi supprimer la rfrence prsente au sein du tableau afin de rendre lobjet personnage ligible la suppression :
var personnage:Object = { prenom : "Bobby", age : 50 }; // copie d'une rfrence au sein du tableau var tableauPersonnages:Array = [ personnage ]; // supprime la premire rfrence personnage = null; // supprime la seconde rfrence tableauPersonnages[0] = null;

Si la situation nous le permet, un moyen plus radical consiste craser le tableau contenant la rfrence :
var personnage:Object = { prenom : "Bobby", age : 50 };

31 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

// copie d'une rfrence au sein du tableau var tableauPersonnages:Array = [ personnage ]; // supprime la premire rfrence personnage = null; // crase le tableau stockant la rfrence tableauPersonnages = new Array();

Depuis la version 9.0.115 du lecteur Flash 9, il est possible de dclencher le ramasse-miettes manuellement laide de la mthode gc de la classe System. Notons que cette fonctionnalit nest accessible quau sein du lecteur de dbogage. Nous reviendrons sur cette mthode au cours du prochain chapitre intitul Le modle vnementiel.

A retenir
Il est possible de dclencher manuellement le ramasse-miettes au sein du lecteur de dbogage 9.0.115. Afin quun objet soit ligible la suppression par le ramasse-miettes nous devons passer toutes ses rfrences null. Le ramasse-miettes intervient lorsque la machine virtuelle juge cela ncessaire.

Bonnes pratiques
Durant lensemble de louvrage nous ferons usage de certaines bonnes pratiques, dont voici le dtail : Nous typerons les variables systmatiquement afin doptimiser les performances de nos applications et garantir une meilleure gestion des erreurs la compilation. Lors de la dfinition de boucles, nous utiliserons toujours une variable de rfrence afin dviter que la machine virtuelle ne rvalue la longueur du tableau chaque itration. Nous prfrerons donc le code suivant :
var tableauDonnees:Array = [ 5654, 95, 54, 687968, 97851]; // stockage de la longueur du tableau var lng:int = tableauDonnees.length; for ( var i:int = 0; i< lng; i++ ) { }

A cette criture non optimise : 32 / 35


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

var tableauDonnees:Array = [ 5654, 95, 54, 687968, 97851]; for ( var i:int = 0; i< tableauDonnees.length; i++ ) { }

De la mme manire, nous viterons de redfinir nos variables au sein des boucles. Nous prfrerons lcriture suivante :
var tableauDonnees:Array = [5654, 95, 54, 687968, 97851]; var lng:int = tableauDonnees.length; // dclaration de la variable elementEnCours une seule et unique fois var elementEnCours:int; for ( var i:int = 0; i< lng; i++ ) { elementEnCours = tableauDonnees[i]; }

A lcriture suivante non optimise :


var tableauDonnees:Array = [5654, 95, 54, 687968, 97851]; for ( var i:int = 0; i< tableauDonnees.length; i++ ) { // dclaration de la variable elementEnCours chaque itration var elementEnCours:int = tableauDonnees[i]; }

Dcouvrons prsent quelques dernires subtilits du langage ActionScript 3.

Autres subtilits
Attention, le type Void existant en ActionScript 2, utilis au sein des signatures de fonctions prend dsormais un v minuscule en ActionScript 3. Une fonction ActionScript 2 ne renvoyant aucune valeur scrivait de la manire suivante :
function initilisation ( ):Void { }

33 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

En ActionScript 3, le type void ne prend plus de majuscule :


function initilisation ( ):void { }

ActionScript 3 intgre un nouveau type de donnes permettant dindiquer quune variable peut accueillir nimporte quel type de donnes. En ActionScript 2 nous nous contentions de ne pas dfinir de type, en ActionScript 3 nous utilisons le type * :
var var var var var var condition:* = true; total:* = 5; couleur:* = 0x990000; resultat:* = 45.4; personnage:* = new Object(); prenom:* = "Bobby";

Nous utiliserons donc systmatiquement le type * au sein dune boucle for each, car la variable p doit pouvoir accueillir nimporte quel type de donnes :
var personnage:Object = { prenom : "Bobby", age : 50 }; for each ( var p:* in personnage ) { /* affiche : 50 Bobby */ trace( p ); }

En utilisant un type spcifique pour la variable ditration p, nous risquons de convertir implicitement les donnes itres. Dans le code suivant, nous utilisons le type Boolean pour la variable p:
var personnage:Object = { prenom : "Bobby", age : 50 }; for each ( var p:Boolean in personnage ) { /* affiche : true true */ trace( p );

34 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 2 Langage et API - version 0.1.2

Le contenu des proprits est automatiquement converti en boolen. Au cas o une donne ne pourrait tre convertie en boolen, le code prcdent pourrait lever une erreur lexcution. Passons prsent aux nouveauts lies linterface de programmation du lecteur Flash. Les deux grandes nouveauts du lecteur Flash 9 concernent la gestion de laffichage ainsi que le modle vnementiel. Au cours du prochain chapitre intitul Le modle vnementiel nous allons nous intresser ce nouveau modle travers diffrents exercices dapplications. Nous apprendrons matriser ce dernier et dcouvrirons comment en tirer profit tout au long de louvrage. Puis nous nous intresserons au nouveau mcanisme de gestion de laffichage appele Liste daffichage au cours du chapitre 4, nous verrons que ce dernier offre beaucoup plus de souplesse en termes de manipulation des objets graphiques et doptimisation de laffichage. Vous tes prt pour la grande aventure ActionScript 3 ?

35 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

3
Le modle vnementiel

LHISTOIRE ............................................................................................................ 1 UN NOUVEAU MODELE EVENEMENTIEL ..................................................... 6 TOUT EST VNEMENTIEL ....................................................................................... 8 ECOUTER UN VNEMENT ..................................................................................... 10 LOBJET VNEMENTIEL ....................................................................................... 13 LA CLASSE EVENT................................................................................................. 16 LES SOUS-CLASSES DEVENT ......................................................................... 17 ARRTER LCOUTE DUN VNEMENT ................................................................. 19 MISE EN APPLICATION ........................................................................................... 20 LA PUISSANCE DU COUPLAGE FAIBLE....................................................... 25 SOUPLESSE DE CODE ............................................................................................. 28 ORDRE DE NOTIFICATION .............................................................................. 30 REFERENCES FAIBLES ..................................................................................... 32 SUBTILITES .......................................................................................................... 34

Lhistoire
ActionScript 3 intgre un nouveau modle vnementiel que nous allons tudier ensemble tout au long de ce chapitre. Avant de laborder, rappelons-nous de lancien modle apparu pour la premire fois avec le lecteur Flash 6. Jusqu maintenant nous dfinissions des fonctions que nous passions en rfrence lvnement cout. Prenons un exemple simple, pour couter lvnement onRelease dun bouton nous devions crire le code suivant :
monBouton.onRelease = function ()

1 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

{ // affiche : _level0.monBouton trace ( this ); this._alpha = 50; }

Une fonction anonyme tait dfinie sur la proprit onRelease du bouton. Lorsquun clic souris intervenait sur le bouton, le lecteur Flash excutait la fonction que nous avions dfinie et rduisait lopacit du bouton de 50%. Cette criture posait plusieurs problmes. Dans un premier temps, la fonction dfinie sur le bouton ne pouvait pas tre rutilise pour un autre vnement, sur un objet diffrent. Pour pallier ce problme de rutilisation, certains dveloppeurs faisaient appels des rfrences de fonctions afin de grer lvnement :
function clicBouton () { // affiche : _level0.monBouton trace (this); this._alpha = 50; } monBouton.onRelease = clicBouton;

Cette criture permettait de rutiliser la fonction clicBouton pour dautres vnements lis diffrents objets, rendant le code plus ar en particulier lors de lcoute dvnements au sein de boucles. Point important, le mot-cl this faisait ici rfrence au bouton et non au scnario sur lequel est dfinie la fonction clicBouton. Le contexte dexcution dorigine de la fonction clicBouton tait donc perdu lorsque celle-ci tait passe en rfrence. La fonction pousait le contexte de lobjet auteur de lvnement. Ensuite lauto rfrence traduite par lutilisation du mot-cl this tait le seul moyen de faire rfrence ce dernier. Ce comportement tait quelque peu droutant pour une personne dcouvrant ActionScript. Voici un exemple mettant en vidence lambigut possible :
function clicBouton () { // affiche : _level0.monBouton

2 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

trace ( this ); this._alpha = 50; } monBouton.onRelease = clicBouton; clicBouton();

En excutant la fonction clicBouton de manire traditionnelle le mot-cl this faisait alors rfrence au scnario sur lequel elle tait dfinie, cest dire son contexte dorigine. Le code suivant tait peu digeste et rigide :
var ref:MovieClip; for ( var i:Number = 0; i< 50; i++ ) { ref = this.attachMovie ( "monClip", "monClip"+i, i ); ref.onRelease = function () { trace("cliqu"); } }

Les dveloppeurs prfraient donc lutilisation dune fonction spare rutilisable :


var ref:MovieClip; for ( var i:Number = 0; i< 50; i++ ) { ref = this.attachMovie ( "monClip", "monClip"+i, i ); ref.onRelease = clicBouton; } function clicBouton ( ) { trace("cliqu"); }

Le mcanisme tait le mme pour la plupart des vnements. Macromedia, lpoque de Flash MX (Flash 6) stait rendu compte du manque de souplesse de ce systme de fonctions dfinies sur les 3 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

vnements, et intgra la notion dcouteurs et de diffuseurs au sein du lecteur Flash 6. Les classes Key, Selection, TextField furent les premires utiliser cette notion de diffuseurs couteurs. Pour couter la saisie dans un champ texte, nous pouvions alors utiliser la syntaxe suivante :
var monEcouteur:Object = new Object(); monEcouteur.onChanged = function(pChamp:TextField) { trace ( pChamp._name + " a diffus lvnement onChanged" ); }; monChampTexte.addListener ( monEcouteur );

En appelant la mthode addListener sur notre champ texte nous souscrivions un objet appel ici monEcouteur en tant qucouteur de lvnement onChanged. Une mthode du mme nom devait tre dfinie sur lobjet couteur afin que celle-ci soit excute lorsque lvnement tait diffus. Pour dterminer quelle touche du clavier tait enfonce, nous pouvions crire le code suivant :
var monEcouteur:Object = new Object(); monEcouteur.onKeyDown = function() { trace ( "La touche " + String.fromCharCode( Key.getCode() ) + " est enfonce" ); }; Key.addListener ( monEcouteur );

Cette notion de mthode portant le nom de lvnement diffus ntait pas vidente comprendre et souffrait de la faiblesse suivante. Aucun contrle du nom de lvnement ntait effectu la compilation ou lexcution. Le code suivant ne gnrait donc aucune erreur bien quune faute de frappe sy soit glisse :
var monEcouteur:Object = new Object(); monEcouteur.onKeydown = function() { trace ( "La touche " + String.fromCharCode( Key.getCode() ) + " est enfonce" );

4 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

}; Key.addListener ( monEcouteur );

Bien que critique, cette approche permettait nanmoins de souscrire un nombre illimit dcouteurs auprs dun vnement, mais aussi de renvoyer des paramtres la mthode couteur. Ces paramtres taient gnralement des informations lies lvnement, par exemple lauteur de lvnement diffus. En 2004, lors de la sortie du lecteur Flash 7 (Flash MX 2004), ActionScript 2 fit son apparition accompagn dun lot de nouveaux composants appels composants V2. Ces derniers tendaient le concept de diffuseurs couteurs en intgrant une mthode addEventListener pour souscrire un couteur auprs dun vnement. Contrairement la mthode addListener, cette mthode stockait chaque couteur dans des tableaux internes diffrents pour chaque vnement. En regardant lvolution dActionScript au cours des dernires annes, nous pouvons nous rendre compte du travail des ingnieurs visant intgrer la notion dcouteurs auprs de tous nouveaux objets crs. ActionScript 3 a t dvelopp dans le but doffrir un langage puissant et standard et prolonge ce concept en intgrant un nouveau modle vnementiel appel Document Object Model (DOM3 Event Model) valable pour tous les objets lis lAPI du lecteur Flash 9. Le W3C dfinit la spcification de ce modle vnementiel. Vous pouvez lire les dernires rvisions ladresse suivante : http://www.w3.org/TR/DOM-Level-3-Events/ Les dveloppeurs ActionScript 2 ayant dj utilis la classe EventDispatcher seront ravis de savoir que toutes les classes de lAPI du lecteur Flash hritent directement de celle-ci. Nous allons donc dcouvrir en profondeur le concept de diffuseurs couteurs et voir comment cela amliore la clart et la souplesse de notre code.

A retenir

5 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Lancien modle vnementiel a t entirement revu en ActionScript 3. La grande puissance dActionScript 3 rside dans limplmentation native dEventDispatcher.

Un nouveau modle vnementiel


En ActionScript 3, le modle vnementiel a clairement volu. La manire dont les objets interagissent entre eux a t entirement revue. Le principe mme de ce modle vnementiel est tir dun modle de conception ou Design Pattern appel Observateur qui dfinit linteraction entre plusieurs objets daprs un dcoupage bien spcifique. Habituellement ce modle de conception peut tre dfini de la manire suivante : Le modle de conception observateur dfinit une relation entre objets de type un plusieurs, de faon que, lorsque un objet change dtat, tous ceux qui en dpendent en soient notifis et soient mis jour automatiquement . Le modle de conception Observateur met en scne trois acteurs : Le sujet Il est la source de lvnement, nous lappellerons sujet car cest lui qui diffuse les vnements qui peuvent tre couts ou non par les observateurs. Ce sujet peut tre un bouton, ou un clip par exemple. Ne vous inquitez pas, tous ces vnements sont documents, la documentation prsente pour chaque objet les vnements diffuss. Lvnement Nous pouvons conceptualiser la notion dvnement comme un changement dtat au sein du sujet. Un clic sur un bouton, la fin dun chargement de donnes provoquera la diffusion dun vnement par lobjet sujet. Tous les vnements existants en ActionScript 3 sont stocks dans des proprits constantes de classes lies lvnement. Lcouteur Lcouteur a pour mission dcouter un vnement spcifique auprs dun ou plusieurs sujets. Il peut tre une fonction ou une mthode. Lorsque le sujet change dtat, ce dernier diffuse un vnement appropri, si lcouteur est souscrit auprs de lvnement diffus, il en est notifi et excute une action. Il est 6 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

important de souligner quil peut y avoir de multiples couteurs pour un mme vnement. Cest justement lun des points forts existant au sein de lancien modle qui fut conserv dans ce nouveau reprsent par la figure 3-1 :

Figure 3-1. Schma du modle de conception Observateur Nos couteurs sont dpendants du sujet, ils y ont souscrit. Le sujet ne connat rien des couteurs, il sait simplement sil est observ ou non travers sa liste interne dcouteurs. Lorsque celui-ci change dtat un vnement est diffus, nos couteurs en sont notifis et peuvent alors ragir. Tout vnement diffus envoie des informations aux couteurs leur permettant dtre tenus au courant des modifications et donc de rester jour en permanence avec le sujet. Nous dcouvrirons au fil de ces pages, la souplesse apporte par ce nouveau modle vnementiel, et vous apprcierez son fonctionnement trs rapidement. Cela reste encore relativement abstrait, pour y remdier voyons ensemble comment ce modle vnementiel stablit au sein dune application Flash traditionnelle. Prenons un exemple simple constitu dun bouton et dune fonction couteur ragissant lorsque lutilisateur clique sur ce bouton. Nous avons dans ce cas nos trois acteurs mettant en scne le nouveau modle vnementiel ActionScript 3 :
Le sujet : le bouton Lvnement : le clic bouton

7 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Lcouteur : la fonction

Transpos dans une application ActionScript 3 traditionnelle, nous pouvons tablir le schma suivant :

Figure 3-2. Modle vnementiel dans une application ActionScript 3 Notre bouton est un sujet pouvant diffuser un vnement. Celui-ci est cout par une fonction couteur.

A retenir
Il existe un seul et unique modle vnementiel en ActionScript 3. Ce modle vnementiel est bas sur le modle de conception Observateur. Les trois acteurs de ce nouveau modle vnementiel sont : le sujet, lvnement, et lcouteur. Nous pouvons avoir autant dcouteurs que nous le souhaitons. Plusieurs couteurs peuvent couter le mme vnement. Un seul couteur peut tre souscrit diffrents vnements.

Tout est vnementiel


Lessentiel du modle vnementiel rside au sein dune seule et unique classe appele EventDispatcher. Cest elle qui offre la possibilit un objet de diffuser des vnements. Tous les objets issus de lAPI du lecteur 9 et 10 hritent de cette classe, et possdent de ce fait la capacit de diffuser des vnements.

8 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Souvenez-vous que toutes les classes issues du paquetage flash sont relatives lAPI du lecteur Flash. Avant le lecteur 9, ces objets hritaient simplement de la classe Object, si nous souhaitions pouvoir diffuser des vnements nous devions nous mme implmenter la classe EventDispatcher au sein de ces objets. Voici la chane dhritage de la classe MovieClip avant le lecteur Flash 9 :
Object | +-MovieClip

Dans les prcdentes versions du lecteur Flash, la classe MovieClip tendait directement la classe Object, si nous souhaitions pouvoir diffuser des vnements partir dun MovieClip nous devions implmenter nous-mmes un nouveau modle vnementiel. Voyons comme cela a volu avec lActionScript 3 au sein du lecteur 9. Depuis le lecteur 9 et ActionScript 3 :
Object | +-EventDispatcher

La classe EventDispatcher hrite dsormais de la classe Object, tous les autres objets hritent ensuite dEventDispatcher et bnficient donc de la diffusion dvnements en natif. Cest une grande volution pour nous autres dveloppeurs ActionScript, car la plupart des objets que nous manipulerons auront la possibilit de diffuser des vnements par dfaut. Ainsi la classe MovieClip en ActionScript 3 hrite dabord de la classe Object puis dEventDispatcher pour ensuite hriter des diffrentes classes graphiques. Toutes les classes rsidant dans le paquetage flash ne drogent pas la rgle et suivent ce mme principe. Voici la chane dhritage complte de la classe MovieClip en ActionScript 3 :
Object | +-EventDispatcher | +-DisplayObject | +-InteractiveObject |

9 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

+-DisplayObjectContainer | +-Sprite | +-MovieClip

Voyons ensemble comment utiliser ce nouveau modle vnementiel travers diffrents objets courants.

Ecouter un vnement
Lorsque vous souhaitez tre tenu au courant des dernires nouveauts de votre vidoclub, vous avez plusieurs possibilits. La premire consiste vous dplacer jusquau vidoclub afin de dcouvrir les dernires sorties. Une approche classique qui ne savre pas vraiment optimise. Nous pouvons dj imaginer le nombre de visites que vous effectuerez dans le magasin pour vous rendre compte quaucun nouveau DVD ne vous intresse. Une deuxime approche consiste laisser une liste de films que vous attendez au grant du vidoclub et attendre que celui-ci vous appelle lorsquun des films est arriv. Une approche beaucoup plus efficace pour vous et pour le grant qui en a assez de vous voir repartir du. Le grant incarne alors le diffuseur, et vous-mme devenez lcouteur. Le grant du vidoclub ne sait rien sur vous si ce nest votre nom et votre numro de tlphone. Il est donc le sujet, vous tes alors lcouteur car vous tes souscrit un vnement prcis : la disponibilit dun film que vous recherchez . En ActionScript 3 les objets fonctionnent de la mme manire. Pour obtenir une notification lorsque lutilisateur clique sur un bouton, nous souscrivons un couteur auprs dun vnement diffus par le sujet, ici notre bouton. Afin dcouter un vnement spcifique, nous utilisons la mthode addEventListener dont voici la signature :
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

Le premier attend lvnement couter sous la forme dune chane de caractres. Le deuxime paramtre prend en rfrence lcouteur qui sera souscrit auprs de lvnement. Les trois derniers paramtres seront expliqus plus tard. Souvenez-vous que seuls les objets rsidant dans le paquetage flash utilisent ce modle vnementiel. Le schma mmoriser est donc le suivant : 10 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

sujet.addEventListener("evenement", ecouteur);

Voyons laide d'un exemple simple, comment couter un vnement. Dans un nouveau document Flash, crez un symbole bouton, et placez une occurrence de ce dernier sur la scne et nommez la monBouton. Sur un calque AS tapez le code suivant :
monBouton.addEventListener ( "click", clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { // affiche : vnement diffus trace("vnement diffus");

Nous coutons ici lvnement click sur le bouton monBouton et nous passons la fonction clicBouton comme couteur. Lorsque lutilisateur clique sur le bouton, il diffuse un vnement click qui est cout par la fonction clicBouton. Attention ne pas mettre de doubles parenthses lorsque nous passons une rfrence la fonction couteur. Nous devons passer sa rfrence et non le rsultat de son excution. Revenons un instant sur notre code prcdent :
monBouton.addEventListener ( "click", clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { // affiche : vnement diffus trace("vnement diffus");

Mme si cette syntaxe fonctionne, et ne pose aucun problme pour le moment, que se passe t-il si nous spcifions un nom dvnement incorrect ? Imaginons le cas suivant, nous faisons une erreur et coutons lvnement clicck :
monBouton.addEventListener( "clicck", clicBouton );

Si nous testons le code ci-dessus, la fonction clicBouton ne sera jamais dclenche car le nom de lvnement click possde une erreur de saisie et de par sa non-existence nest jamais diffus.

11 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Afin dviter ces problmes de saisie, tous les noms des vnements sont dsormais stocks dans des classes lies chaque vnement. Lorsque vous devez couter un vnement spcifique pensez immdiatement au type dvnement qui sera diffus. Les classes rsidant dans le paquetage flash.events constituent lensemble des classes vnementielles en ActionScript 3. Elles peuvent tre divises en deux catgories. On peut ainsi distinguer les classes vnementielles lies aux vnements autonomes :
flash.events.Event flash.events.FullScreenEvent flash.events.IOErrorEvent

flash.events.HTTPStatusEvent flash.events.NetStatusEvent flash.events.ProgressEvent flash.events.StatusEvent flash.events.SyncEvent flash.events.TimerEvent flash.events.SecurityErrorEvent

Puis, celles lies aux vnements interactifs :


flash.events.FocusEvent flash.events.IMEEvent flash.events.KeyboardEvent flash.events.MouseEvent flash.events.TextEvent

Dans notre cas, nous souhaitons couter un vnement qui relve dune interactivit utilisateur provenant de la souris. Nous nous dirigerons logiquement au sein de la classe MouseEvent. Pour rcuprer le nom dun vnement, nous allons cibler ces proprits constantes statiques, la syntaxe repose sur le systme suivant :
ClasseEvenement.MON_EVENEMENT

Pour bien comprendre ce concept, crez un nouveau document Flash et sur un calque AS tapez la ligne suivante :
// affiche : click trace ( MouseEvent.CLICK );

12 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

La classe MouseEvent contient tous les vnements relatifs la souris, en ciblant la proprit constante statique CLICK nous rcuprons la chane de caractres click que nous passerons en premier paramtre de la mthode addEventListener. En ciblant un nom dvnement par lintermdiaire dune proprit de classe nous bnficions de deux avantages. Si jamais une erreur de saisie survenait de par la vrification du code effectue par le compilateur notre code ne pourrait pas tre compil. Ainsi le code suivant chouerait la compilation :
monBouton.addEventListener ( MouseEvent.CLICCK, clicBouton );

Il afficherait dans la fentre de sortie le message derreur suivant :


1119: Accs la proprit CLICCK peut-tre non dfinie, via la rfrence de type static Class.

Au sein de Flash CS3, Flash CS4 ou Flex Builder le simple fait de cibler une classe vnementielle comme MouseEvent vous affiche aussitt tout les vnements lis cette classe. Vous naurez plus aucune raison de vous tromper dans le ciblage de vos vnements. A plus long terme, si le nom de lvnement click venait changer ou disparatre dans une future version du lecteur Flash, notre ancien code pointant vers la constante continuerait de fonctionner car ces proprits seront mises jour dans les futures versions du lecteur Flash. Pour couter le clic souris sur notre bouton, nous rcuprons le nom de lvnement et nous le passons la mthode addEventListener :
monBouton.addEventListener( MouseEvent.CLICK, clicBouton );

Cette nouvelle manire de stocker le nom des vnements apporte donc une garantie la compilation mais rgle aussi un souci de compatibilit future. Voyons ensemble les diffrentes classes lies aux vnements existant en ActionScript 3.

Lobjet vnementiel
Lorsquun vnement est diffus, un objet est obligatoirement envoy en paramtre la fonction couteur. Cet objet est appel objet vnementiel. Pour cette raison, la fonction couteur doit imprativement contenir dans sa signature un paramtre du type de lvnement diffus. Afin de ne jamais vous tromper, souvenez-vous de la rgle suivante.

13 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Le type de lobjet vnementiel correspond toujours au type de la classe contenant le nom de lvnement. Dans notre exemple il sagit de MouseEvent. Lobjet vnementiel contient des informations lies lvnement en cours de diffusion. Ces informations sont disponibles travers des proprits de lobjet vnementiel. Nous nous attarderons pour linstant sur trois de ces proprits, target, currentTarget et type.
La proprit target fait rfrence lobjet cible de lvnement. De nimporte o nous pouvons savoir qui est lorigine de la propagation de lvnement. La proprit currentTarget fait rfrence lobjet diffuseur de lvnement (le sujet). Il sagit toujours de lobjet sur lequel nous avons appel la mthode addEventListener. La proprit type contient, elle, le nom de lvnement diffus, ici click.

Le code suivant rcupre une rfrence vers lobjet cible, le sujet, ainsi que le nom de lvnement :
monBouton.addEventListener ( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { // affiche : [object SimpleButton] trace( pEvt.target ); // affiche : [object SimpleButton] trace( pEvt.currentTarget ); // affiche : click trace( pEvt.type ); }

Nous reviendrons au cours du chapitre 6 intitul Propagation vnementielle sur les diffrences subtiles entre les proprits target et currentTarget. Le paramtre pEvt dfinit un paramtre de type MouseEvent car lvnement cout est stock au sein de la classe MouseEvent. Les proprits target et currentTarget, prsentent dans tout vnement diffus, permettent en ralit un couplage faible entre les objets. En passant par la proprit currentTarget pour cibler le sujet, nous vitons de le rfrencer directement par son nom, ce qui en cas de modification rendrait notre code rigide et pnible modifier. 14 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Attention, Si la fonction couteur ne possde pas dans sa signature un paramtre cens accueillir lobjet vnementiel, une exception est leve lexcution. Si nous testons le code suivant :
monBouton.addEventListener ( MouseEvent.CLICK, clicBouton ); function clicBouton ():void { }

Lerreur dexcution suivante est leve :


ArgumentError: Error #1063: Non-correspondance du nombre d'arguments sur bouton_fla::MainTimeline/onMouseClick(). 0 prvu(s), 1 dtect(s).

Lorsque lvnement est diffus, aucun paramtre naccueille lobjet vnementiel, une exception est donc leve. Souvenez-vous que la nouvelle machine virtuelle (AVM2) conserve les types lexcution, si le type de lobjet vnementiel dans la signature de la fonction couteur ne correspond pas au type de lobjet vnementiel diffus, le lecteur tentera de convertir implicitement lobjet vnementiel. Une erreur sera aussitt leve si la conversion est impossible. Mettons en vidence ce comportement en spcifiant au sein de la signature de la fonction couteur un objet vnementiel de type diffrent. Ici nous spcifions au sein de la signature de la fonction couteur, un paramtre de type flash.events.TextEvent :
monBouton.addEventListener ( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:TextEvent ):void { }

Ce code lve lerreur dexcution suivante :


TypeError: Error #1034: Echec de la contrainte de type : conversion de flash.events::MouseEvent@2ee7601 en flash.events.TextEvent impossible.

pouvons utiliser flash.events.Event compatible avec vnementielles :


function clicBouton ( pEvt:Event ):void {

linverse,

nous

le type commun toutes les classes

monBouton.addEventListener ( MouseEvent.CLICK, clicBouton );

15 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Nous pouvons nous demander lintrt dune telle criture. Pourquoi utiliser le type Event alors que nous attendons le type MouseEvent ? Imaginons que nous souhaitions utiliser une seule et unique fonction couteur pour de multiples vnements. Prenons lexemple dune fonction ecouteurClicGlobal qui se charge dcouter deux vnements de types diffrents :
monBouton.addEventListener ( MouseEvent.CLICK, ecouteurClicGlobal ); monChampTexte.addEventListener ( TextEvent.LINK, ecouteurClicGlobal ); function ecouteurClicGlobal ( pEvt:Event ):void { }

Lutilisation du type commun Event rsout ici tout problme de contrainte de type. Nous reviendrons sur ce mcanisme au cours du chapitre 13 intitul Chargement de contenu. Si le code prcdent vous pose un problme de comprhension, lisez avec attention les deux prochaines parties ddies la classe Event.

La classe Event
Comme nous lavons vu prcdemment, au sein du paquetage flash.events rside un ensemble de classes contenant tous les types dobjets dvnementiels pouvant tre diffuss. Il est important de retenir que la classe Event est la classe parente de toutes les classes vnementielles. Cette classe dfinit la majorit des vnements diffuss en ActionScript 3, le classique vnement Event.ENTER_FRAME est contenu dans la classe Event, tout comme lvnement Event.COMPLETE indiquant la fin de chargement de donnes externe. Dans un nouveau document Flash, crez un symbole MovieClip et placez une occurrence de ce clip nomm monClip sur la scne.
Event.ENTER_FRAME auprs de ce dernier :
function execute ( pEvt:Event ):void {

Le

code

suivant

permet

lcoute

de

lvnement

monClip.addEventListener ( Event.ENTER_FRAME, execute );

16 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

// affiche : [object MovieClip] : enterFrame trace( pEvt.currentTarget + " : " + pEvt.type ); }

Lvnement Event.ENTER_FRAME a la particularit dtre automatiquement diffus ds lors que le lecteur entre sur une nouvelle image. Comme nous lavons abord prcdemment, certains vnements diffusent en revanche des objets vnementiels de type tendus la classe Event, cest le cas notamment des vnements provenant de la souris ou du clavier. Lorsquun vnement li la souris est diffus, nous ne recevons pas un objet vnementiel de type Event mais un objet de type flash.events.MouseEvent. Pourquoi recevons-nous un objet vnementiel de type diffrent pour les vnements souris ? Cest ce que nous allons dcouvrir ensemble dans cette partie intitule Les sous classes dEvent.

Les sous-classes dEvent


Lorsque lvnement Event.ENTER_FRAME est diffus, lobjet vnementiel de type Event na aucune information supplmentaire renseigner lcouteur. Les proprits target et type commune ce type sont suffisantes. A linverse, si vous souhaitez couter un vnement li au clavier, il y a de fortes chances que notre couteur ait besoin dinformations supplmentaires, comme par exemple la touche du clavier qui vient dtre enfonce et qui a provoqu la diffusion de cet vnement. De la mme manire un vnement diffus par la souris pourra nous renseigner sur sa position ou bien sur quel lment notre curseur se situe. Si nous regardons la dfinition de la classe MouseEvent nous dcouvrons de nouvelles proprits contenant les informations dont nous pourrions avoir besoin. Le code suivant rcupre la position de la souris ainsi que ltat de la touche ALT du clavier :
monBouton.addEventListener ( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { // affiche : x : 40.05 y : 124.45

17 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

trace ( "x : " + pEvt.stageX + " y : " + pEvt.stageY ); // affiche : x : 3 y : 4 trace ( "x : " + pEvt.localX + " y : " + pEvt.localY ); // affiche : ALT enfon : false trace ( "ALT enfon : " + pEvt.altKey ); }

Lorsque lvnement MouseEvent.CLICK est diffus, la fonction couteur clicBouton en est notifie et rcupre les informations relatives lvnement au sein de lobjet vnementiel, ici de type MouseEvent. Nous rcuprons ici la position de la souris par rapport la scne en ciblant les proprits stageX et stageY, la proprit altKey est un boolen renseignant sur ltat de la touche ALT du clavier. Les proprits localX et localY de lobjet vnementiel pEvt nous renseignent sur la position de la souris par rapport au repre local du bouton, nous disposons ici dune trs grande finesse en matire de gestion des coordonnes de la souris. En examinant les autres sous-classes de la classe Event vous dcouvrirez que chacune des sous-classes en hritant intgre de nombreuses proprits supplmentaires riches en informations.

A retenir

18 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Tous les objets issus du paquetage flash peuvent diffuser des vnements. La mthode addEventListener est le seul et unique moyen dcouter un vnement. Le nom de chaque vnement est stock dans une proprit statique de classe correspondant lvnement en question. Les classes contenant le nom des vnements sont appeles classes vnementielles. La classe Event est la classe parente de toutes les classes vnementielles. Lorsquun vnement est diffus, il envoie en paramtre la fonction couteur un objet appel objet vnementiel. Le type de cet objet vnementiel est le mme que la classe contenant le nom de lvnement. Un objet vnementiel possde au minimum une proprit target et currentTarget rfrenant lobjet cible et lobjet auteur de lvnement. La proprit type permet de connatre le nom de lvnement en cours de diffusion.

Arrter lcoute dun vnement


Imaginez que vous ne souhaitiez plus tre mis au courant des dernires nouveauts DVD de votre vidoclub. En informant le grant que vous ne souhaitez plus en tre notifi, vous ne serez plus couteur de lvnement nouveauts DVD . En ActionScript 3, lorsque nous souhaitons ne plus couter un vnement, nous utilisons la mthode removeEventListener dont voici la signature :
removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void

Le premier paramtre appel type attend le nom de lvnement auquel nous souhaitons nous dsinscrire, le deuxime attend une rfrence la fonction couteur, le dernier paramtre sera trait au cours du chapitre 6 intitul Propagation vnementielle. Reprenons ensemble notre exemple prcdent. Lorsquun clic sur le bouton se produit, lvnement MouseEvent.CLICK est diffus, la fonction couteur clicBouton en est notifie et nous supprimons lcoute de lvnement :
monBouton.addEventListener( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:MouseEvent ):void {

19 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

// affiche : x : 40.05 y : 124.45 trace( "x : " + pEvt.stageX + " y : " + pEvt.stageY ); // affiche : ALT enfon : false trace( "ALT enfon : " + pEvt.altKey ); // on supprime l'coute de l'vnement MouseEvent.CLICK pEvt.target.removeEventListener ( MouseEvent.CLICK, clicBouton ); }

Lappel de la mthode removeEventListener sur lobjet monBouton au sein de la fonction couteur clicBouton, supprime lcoute de lvnement MouseEvent.CLICK. La fonction couteur sera donc dclenche une seule et unique fois. Lorsque vous navez plus besoin dun couteur pensez le supprimer de la liste des couteurs laide de la mthode removeEventListener. Le ramasse-miettes ne supprimera pas de la mmoire un objet ayant une de ses mthodes enregistres comme couteur. Pour optimiser votre application, pensez supprimer les couteurs non utiliss, loccupation mmoire sera moindre et vous serez labri de comportements difficiles diagnostiquer.

Mise en application
Afin de reprendre les notions abordes prcdemment nous allons ensemble crer un projet dans lequel une fentre sagrandit avec un effet dinertie selon des dimensions values alatoirement. Une fois le mouvement termin, nous supprimerons lvnement ncessaire afin de librer les ressources et doptimiser lexcution de notre projet. Dans un document Flash, nous crons une occurrence de clip appele myWindow reprsentant un rectangle de couleur unie. Nous coutons lvnement MouseEvent.CLICK sur un bouton nomm monBouton plac lui aussi sur la scne :
monBouton.addEventListener ( MouseEvent.CLICK, clicBouton );

Puis nous dfinissons la fonction couteur :


function clicBouton ( pEvt:MouseEvent ):void { trace("fonction couteur dclenche"); }

Lors du clic souris sur le bouton monBouton, la fonction couteur clicBouton est dclenche, nous affichons un message validant 20 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

lexcution de celle-ci. Il nous faut dsormais couter lvnement Event.ENTER_FRAME auprs de notre clip myWindow. La fonction clicBouton est modifie de la manire suivante :
function clicBouton ( pEvt:MouseEvent ):void { trace("fonction couteur dclenche"); trace("souscription de l'vnement Event.ENTER_FRAME"); myWindow.addEventListener ( Event.ENTER_FRAME, redimensionne ); }

En appelant la mthode addEventListener sur notre clip myWindow, nous souscrivons la fonction couteur redimensionne, celle-ci soccupera du redimensionnement de notre clip. A la suite, dfinissons la fonction redimensionne :
function redimensionne ( pEvt:Event ):void { trace("excution de la fonction redimensionne"); }

Nous obtenons le code complet suivant :


monBouton.addEventListener ( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { trace("fonction couteur dclenche"); trace("souscription de l'vnement Event.ENTER_FRAME"); myWindow.addEventListener ( Event.ENTER_FRAME, redimensionne ); } function redimensionne ( pEvt:Event ):void { trace("excution de la fonction redimensionne"); }

Au clic souris, le message "excution de la fonction redimensionne" saffiche. Ajoutons prsent un effet dinertie pour grer le redimensionnement de notre fentre.

21 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Il nous faut dans un premier temps gnrer une dimension alatoire. Pour cela nous dfinissons deux variables sur notre scnario, afin de stocker la largeur et la hauteur gnres alatoirement. Juste aprs lcoute de lvnement MouseEvent.CLICK nous dfinissons deux variables largeur et hauteur :
var largeur:int; var hauteur:int;

Ces deux variables de scnario serviront stocker les tailles en largeur et hauteur que nous allons gnrer alatoirement chaque clic souris. Notons que lutilisation du type int pour les variables hauteur et largeur nous permet dobtenir par dfaut des valeurs arrondies lentier infrieur. Cela nous vite de faire la conversion manuellement laide de la mthode floor de la classe Math. Nous valuons ces dimensions au sein de la fonction clicBouton en affectant les deux variables :
function clicBouton ( pEvt:MouseEvent ):void { largeur = Math.random()*400; hauteur = Math.random()*400; trace("fonction couteur dclenche"); trace("souscription de l'vnement Event.ENTER_FRAME"); myWindow.addEventListener ( Event.ENTER_FRAME, redimensionne ); }

Nous utilisons la mthode random de la classe Math afin dvaluer une dimension alatoire en largeur et hauteur dans une amplitude de 400 pixels. Nous choisissons ici une amplitude infrieure la taille totale de la scne. Nous modifions la fonction redimensionne afin dajouter leffet voulu :
function redimensionne ( pEvt:Event ):void { trace("excution de la fonction redimensionne"); myWindow.width -= ( myWindow.width largeur ) * .3; myWindow.height -= ( myWindow.height - hauteur ) * .3;

22 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Rafrachissons-nous la mmoire et lisons le code complet :


monBouton.addEventListener ( MouseEvent.CLICK, clicBouton ); var largeur:int; var hauteur:int; function clicBouton ( pEvt:MouseEvent ):void { largeur = Math.random()*400; hauteur = Math.random()*400; trace("fonction couteur dclenche"); trace("souscription de l'vnement Event.ENTER_FRAME"); myWindow.addEventListener ( Event.ENTER_FRAME, redimensionne ); } function redimensionne ( pEvt:Event ):void { trace("excution de la fonction redimensionne"); myWindow.width -= ( myWindow.width - largeur ) *.3; myWindow.height -= ( myWindow.height - hauteur ) *.3; }

Lorsque nous cliquons sur le bouton monBouton, notre fentre se redimensionne une taille alatoire, leffet est visuellement trs sympathique mais noublions pas que nous narrtons jamais lcoute de lvnement Event.ENTER_FRAME ! Il est important de noter quune fois un couteur enregistr auprs dun vnement spcifique, toute nouvelle souscription est ignore. Ceci vite quun couteur ne soit enregistr plusieurs fois auprs dun mme vnement. Ainsi dans notre code, quelque soit le nombre de clic, la fonction redimensionne nest souscrite quune seule fois lvnement Event.ENTER_FRAME. En laissant le code ainsi, mme lorsque le mouvement de la fentre est termin notre fonction couteur redimensionne continue de sexcuter et ralentit grandement notre application. La question que nous devons nous poser est la suivante :

23 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Quand

Event.ENTER_FRAME ?

devons-nous

supprimer

lcoute

de

lvnement

Lastuce consiste tester la diffrence entre la largeur et hauteur actuelle et la largeur et hauteur finale. Si la diffrence est infrieure un demi nous pouvons en dduire que nous sommes quasiment arrivs destination, et donc pouvons supprimer lvnement Event.ENTER_FRAME. Afin dexprimer cela dans notre code, rajoutez cette condition au sein de la fonction redimensionne :
function redimensionne ( pEvt:Event ):void { trace("excution de la fonction redimensionne"); myWindow.width -= ( myWindow.width - largeur ) * .3; myWindow.height -= ( myWindow.height - hauteur ) * .3; if ( Math.abs ( myWindow.width - largeur ) < .5 && Math.abs ( myWindow.height - hauteur ) < .5 ) { myWindow.removeEventListener ( Event.ENTER_FRAME, redimensionne ); trace("redimensionnement termin"); } }

Nous supprimons lcoute de lvnement Event.ENTER_FRAME laide de la mthode removeEventListener. La fonction redimensionne nest plus excute, de cette manire nous optimisons la mmoire, et donc les performances dexcution de notre application. Notre application fonctionne sans problme, pourtant un point essentiel a t nglig ici, nous navons absolument pas tir parti dune fonctionnalit du nouveau modle vnementiel. Notre couplage entre nos objets est dit fort, cela signifie quun changement de nom sur un objet entranera de lourdes modifications en chane au sein de notre code. Voyons comment arranger cela en optimisant nos relations inter-objets.

A retenir

24 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Lorsquun vnement nest plus utilis, pensez arrter son coute laide de la mthode removeEventListener.

La puissance du couplage faible


Notre code prcdent fonctionne pour le moment sans problme. Si nous devions modifier certains lments de notre application, comme par exemple le nom de certains objets, une modification en cascade du code serait invitable. Pour illustrer les faiblesses de notre code prcdent, imaginons le scnario suivant : Marc, un nouveau dveloppeur ActionScript 3 intgre votre quipe et reprend le projet de redimensionnement de fentre que nous avons dvelopp ensemble. Marc dcide alors de renommer le clip myWindow par maFenetre pour des raisons pratiques. Comme tout bon dveloppeur, celui-ci sattend mettre jour une partie minime du code. Marc remplace donc la ligne qui permet la souscription de lvnement Event.ENTER_FRAME au clip fentre. Voici sa nouvelle version de la fonction clicBouton :
function clicBouton ( pEvt:MouseEvent ):void { largeur = Math.random()*400; hauteur = Math.random()*400; trace("fonction couteur dclenche"); trace("souscription de l'vnement Event.ENTER_FRAME"); maFenetre.addEventListener ( Event.ENTER_FRAME, redimensionne ); }

A la compilation, Marc se rend compte des multiples erreurs affiches dans la fentre de sortie, plusieurs reprises lerreur suivante est affiche :
1120: Accs la proprit non dfinie myWindow.

Cette erreur signifie quune partie de notre code fait appel cet objet myWindow qui nest pourtant plus prsent dans notre application. En effet, au sein de notre fonction redimensionne nous ciblons toujours loccurrence myWindow qui nexiste plus car Marc la renomm.

25 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Marc dcide alors de mettre jour la totalit du code de la fonction redimensionne et obtient le code suivant :
function redimensionne ( pEvt:Event ):void { trace("excution de la fonction redimensionne"); maFenetre.width -= ( maFenetre.width largeur ) * .3; maFenetre.height -= ( maFenetre.height hauteur ) * .3; if ( Math.abs ( maFenetre.width largeur ) < .5 && Math.abs ( maFenetre.height hauteur ) < .5 ) { maFenetre.removeEventListener ( Event.ENTER_FRAME, redimensionne ); trace("arriv"); } }

A la compilation, tout fonctionne nouveau ! Malheureusement pour chaque modification du nom doccurrence du clip maFenetre, nous devrons mettre jour la totalit du code de la fonction redimensionne. Cela est d au fait que notre couplage inter-objets est fort. Eric qui vient de lire un article sur le couplage faible propose alors dutiliser au sein de la fonction redimensionne une information essentielle apporte par tout objet vnementiel diffus. Souvenez-vous, lorsquun vnement est diffus, les fonctions couteurs sont notifies de lvnement, puis un objet vnementiel est pass en paramtre chaque fonction couteur. Au sein de tout objet vnementiel rsident les proprits target et currentTarget renseignant la fonction couteur sur lobjet cible ainsi que lauteur de lvnement. Grce la proprit currentTarget ou target, nous rendons le couplage faible entre nos objets. Modifions le code de la fonction redimensionne de manire cibler la proprit currentTarget de lobjet vnementiel :
function redimensionne ( pEvt:Event ):void { trace("excution de la fonction redimensionne"); var objetDiffuseur:DisplayObject = pEvt.currentTarget as DisplayObject;

26 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

objetDiffuseur.width -= ( objetDiffuseur.width - nLargeur ) * .3; objetDiffuseur.height -= ( objetDiffuseur.height - nHauteur ) * .3; if ( Math.abs ( objetDiffuseur.width - nLargeur ) < .5 && Math.abs ( objetDiffuseur.height - nHauteur ) < .5 ) { objetDiffuseur.removeEventListener ( Event.ENTER_FRAME, redimensionne ); trace("arriv"); } }

Si nous compilons, le code fonctionne. Dsormais la fonction redimensionne fait rfrence au clip maFenetre par lintermdiaire de la proprit currentTarget de lobjet vnementiel. Souvenez-vous, la proprit currentTarget fait toujours rfrence lobjet sujet sur lequel nous avons appel la mthode addEventListener. Ainsi, lorsque le nom doccurrence du clip maFenetre est modifi la proprit currentTarget nous permet de cibler lobjet auteur de lvnement sans mme connatre son nom ni son emplacement.
maFenetre, aucune modification ne devra tre apporte la fonction couteur redimensionne. Nous gagnons ainsi en souplesse, notre

Pour toute modification future du nom doccurrence du clip

code est plus simple maintenir.

Quelques jours plus tard, Marc dcide dimbriquer le clip maFenetre dans un autre clip appel conteneur. Heureusement, notre couplage inter-objets est faible, une seule ligne seulement devra tre modifie :
function clicBouton ( pEvt:MouseEvent ):void { largeur = Math.random()*400; hauteur = Math.random()*400; trace("fonction couteur dclenche"); trace("souscription de l'vnement Event.ENTER_FRAME"); conteneur.maFenetre.addEventListener ( Event.ENTER_FRAME, redimensionne ); }

Marc est tranquille, grce au couplage faible son travail de mise jour du code est quasi nul. 27 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

A retenir
Lorsquun vnement na plus besoin dtre observ, nous supprimons lcoute avec la mthode removeEventListener. La mthode removeEventListener est le seul et unique moyen pour supprimer lcoute dun vnement.

Le nom de chaque vnement est stock dans une proprit statique de classe correspondant lvnement en question. Utilisez la proprit target ou currentTarget des objets vnementiels pour garantir un couplage faible entre les objets. Nous dcouvrirons au cours du chapitre 6 intitul Propagation vnementielle, les diffrences subtiles entre les proprits target et currentTarget.

Souplesse de code
Lintrt du nouveau modle vnementiel ActionScript 3 ne sarrte pas l. Auparavant il tait impossible daffecter plusieurs fonctions couteurs un mme vnement. Le code ActionScript suivant met en vidence cette limitation de lancien modle vnementiel :
function clicBouton1 ( ) { trace("click 1 sur le bouton"); } function clicBouton2 ( ) { trace("click 2 sur le bouton"); } monBouton.onRelease = clicBouton1; monBouton.onRelease = clicBouton2;

Nous dfinissions sur le bouton monBouton deux fonctions pour grer lvnement onRelease. En utilisant ce modle vnementiel il nous tait impossible de dfinir plusieurs fonctions pour grer un mme vnement. En ActionScript 1 et 2, une seule et unique fonction pouvait tre dfinie pour grer un vnement spcifique.

28 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Dans notre code, seule la fonction clicBouton2 tait affecte comme gestionnaire de lvnement onRelease, car la dernire affectation effectue sur lvnement onRelease est la fonction clicBouton2. La deuxime affectation crasait la prcdente. Le principe mme du nouveau modle vnementiel apport par ActionScript 3 repose sur le principe dune relation un plusieurs, cest la dfinition mme du modle de conception Observateur. De ce fait nous pouvons souscrire plusieurs couteurs auprs du mme vnement. Prenons un exemple simple constitu dun bouton et de deux fonctions couteurs. Dans un nouveau document Flash, crez un symbole bouton et placez une occurrence de bouton sur la scne et nommez la monBouton. Sur un calque AS tapez le code suivant :
monBouton.addEventListener ( MouseEvent.CLICK, clicBouton1 ); monBouton.addEventListener ( MouseEvent.CLICK, clicBouton2 ); function clicBouton1 ( pEvt:MouseEvent ):void { // affiche : clicBouton1 : [object MovieClip] : monBouton trace( "clicBouton1 : " + pEvt.currentTarget + " : " + pEvt.currentTarget.name ); } function clicBouton2 ( pEvt:MouseEvent ):void { // affiche : clicBouton2 : [object MovieClip] : monBouton trace( "clicBouton2 : " + pEvt.currentTarget + " : " + pEvt.currentTarget.name ); }

Nous souscrivons deux fonctions couteurs auprs de lvnement MouseEvent.CLICK, au clic bouton les deux fonctions couteurs sont notifies de lvnement. Sachez que lordre de notification dpend de lordre dans lequel les couteurs ont t souscris. Ainsi dans notre exemple la fonction clicBouton2 sera excute aprs la fonction clicBouton1. De la mme manire, une seule fonction couteur peut tre rutilise pour diffrents objets. Dans le code suivant la fonction couteur tourner est utilise pour les trois boutons. Un premier rflexe pourrait nous laisser crire le code suivant : 29 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

monBouton1.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); monBouton2.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); monBouton3.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); function tourner ( pEvt:MouseEvent ) { if ( pEvt.currentTarget == monBouton1 ) monBouton1.rotation += 5; else if ( pEvt.currentTarget == monBouton2 ) monBouton2.rotation += 5; else if ( pEvt.currentTarget == monBouton3 ) monBouton3.rotation += 5; }

Souvenez-vous que la proprit currentTarget vous permet de rfrencer dynamiquement lobjet auteur de lvnement :
monBouton1.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); monBouton2.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); monBouton3.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); function tourner ( pEvt:MouseEvent ) { pEvt.currentTarget.rotation += 5; }

En modifiant le nom des boutons, la fonction tourner ne subit, elle, aucune modification :
boutonA.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); boutonB.addEventListener ( MouseEvent.MOUSE_DOWN, tourner ); boutonC.addEventListener ( MouseEvent.MOUSE_DOWN, tourner );

La fonction tourner est ainsi rutilisable pour nimporte quel objet pouvant subir une rotation.

Ordre de notification
Imaginez que vous soyez abonn un magazine, vous souhaitez alors recevoir le magazine en premier lors de sa sortie, puis une fois reu, le magazine est alors distribu aux autres abonns. Certes ce scnario est peu probable dans la vie mais il illustre bien le concept de priorit de notifications du modle vnementiel ActionScript 3. Afin de spcifier un ordre de notification prcis nous pouvons utiliser le paramtre priority de la mthode addEventListener dont nous revoyons la signature :
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

30 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

En reprenant notre exemple prcdent, nous spcifions le paramtre priority :


monBouton.addEventListener ( MouseEvent.CLICK, clicBouton1, false, 0); monBouton.addEventListener ( MouseEvent.CLICK, clicBouton2, false, 1); function clicBouton1 ( pEvt:MouseEvent ):void { // affiche : A [object SimpleButton] : monBouton trace( "A " + pEvt.currentTarget + " : " + pEvt.currentTarget.name ); } function clicBouton2 ( pEvt:MouseEvent ):void { // affiche : B [object SimpleButton] : monBouton trace( "B " + pEvt.currentTarget + " : " + pEvt.currentTarget.name ); }

Lors du clic sur le bouton monBouton, la fonction clicBouton1 est dclenche aprs la fonction clicBouton2, le message suivant est affich :
B [object SimpleButton] : monBouton A [object SimpleButton] : monBouton

Une fonction couteur enregistre avec une priorit de 0 sera excute aprs une fonction couteur enregistre avec une priorit de 1. Il est impossible de modifier la priorit dun couteur une fois enregistr, nous serions obligs de supprimer lcouteur laide de la mthode removeEventListener, puis denregistrer nouveau lcouteur. Cette notion de priorit nexiste pas dans limplmentation officielle DOM3 et a t rajoute uniquement en ActionScript 3 pour offrir plus de flexibilit. Mme si cette fonctionnalit peut savrer pratique dans certains cas prcis, sappuyer sur lordre de notification dans vos dveloppements est considr comme une mauvaise pratique, la mise jour de votre code peut savrer difficile, et rendre le droulement de lapplication complexe.

31 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Rfrences faibles
En ActionScript 3 la gestion de la mmoire est assure par une partie du lecteur appele ramasse-miettes ou Garbage Collector en anglais. Nous avons rapidement abord cette partie du lecteur au cours du chapitre 2 intitul Langage et API. Le ramasse-miettes est charg de librer les ressources en supprimant de la mmoire les objets qui ne sont plus utiliss. Un objet est considr comme non utilis lorsquaucune rfrence ne pointe vers lui, autrement dit lorsque lobjet est devenu inaccessible. Quand nous coutons un vnement spcifique, lcouteur est ajout la liste interne du diffuseur, il sagit en ralit dun tableau stockant les rfrences de chaque couteur. Gardez lesprit que le sujet rfrence lcouteur et non linverse. De ce fait le ramasse-miettes ne supprimera jamais ces couteurs tant que ces derniers seront rfrencs par les sujets et que ces derniers demeurent rfrencs. Lors de la souscription dun couteur auprs dun vnement vous avez la possibilit de modifier ce comportement grce au dernier paramtre de la mthode addEventListener. Revenons sur la signature de cette mthode :
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

Le cinquime paramtre appel useWeakReference permet de spcifier si la fonction couteur sera stocke laide dune rfrence forte ou faible. En utilisant une rfrence faible, la fonction couteur est rfrence faiblement au sein du tableau dcouteurs interne. De cette manire, si la seule ou dernire rfrence la fonction couteur est une rfrence faible lors du passage du ramasse-miettes, ce dernier fait exception, ignore cette rfrence et supprime tout de mme lcouteur de la mmoire. Attention, cela ne veut pas dire que la fonction couteur va obligatoirement tre supprime de la mmoire. Elle le sera uniquement si le ramasse-miettes intervient et procde un nettoyage. Notez bien que cette intervention pourrait ne jamais avoir lieu si le lecteur Flash nen dcidait pas ainsi.

32 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

Cest pour cette raison quil ne faut pas considrer cette technique comme une suppression automatique des fonctions couteurs ds lors quelles ne sont plus utiles. Pensez toujours appeler la mthode removeEventListener pour supprimer lcoute de certains vnements lorsque cela nest plus ncessaire. Si ne nous spcifions pas ce paramtre, sa valeur par dfaut est false. Ce qui signifie que tous nos objets sujets conservent jusqu lappel de la mthode removeEventListener une rfrence forte vers lcouteur. Dans le code suivant, une mthode dinstance de classe personnalise est enregistre comme couteur de lvnement Event.ENTER_FRAME dun MovieClip :
// instanciation d'un objet personnalis var monObjetPerso:ClassePerso = new ClassePerso(); var monClip:MovieClip = new MovieClip(); // coute de l'vnement Event.ENTER_FRAME monClip.addEventListener( Event.ENTER_FRAME, monObjetPerso.ecouteur ); // supprime la rfrence vers l'instance de ClassePerso monObjetPerso = null;

Bien que nous ayons supprim la rfrence linstance de ClassePerso, celle-ci demeure rfrence fortement par lobjet sujet monClip. En sinscrivant comme couteur laide dune rfrence faible, le ramasse-miettes ignorera la rfrence dtenue par le sujet et supprimera linstance de ClassePerso de la mmoire :
// coute de l'vnement Event.ENTER_FRAME monClip.addEventListener( Event.ENTER_FRAME, monObjetPerso.ecouteur, false, 0, true );

Les rfrences faibles ont un intrt majeur lorsque lcouteur ne connat pas lobjet sujet auquel il est souscrit. Nayant aucun moyen de se dsinscrire lvnement, il est recommand dutiliser une rfrence faible afin de garantir que lobjet puisse tout de mme tre libr d la mmoire. Nous reviendrons sur ces subtilits tout au long de louvrage afin de ne pas tre surpris par le ramasse-miettes.

A retenir
En ajoutant un couteur laide dune rfrence faible, ce dernier sera tout de mme supprim de la mmoire, mme si ce dernier na

33 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1.3

pas t dsinscrit removeEventListener.

laide

de

la

mthode

Il ne faut surtout pas considrer lutilisation de rfrences faibles comme remplacement de la mthode removeEventListener. Veillez bien dsinscrire tous vos couteurs lorsquils ne sont plus utiles laide de la mthode removeEventListener.

Subtilits
Comme nous lavons vu au dbut de ce chapitre, en ActionScript 1 et 2 nous avions lhabitude dajouter des couteurs qui ntaient pas des fonctions mais des objets. En ralit, une mthode portant le nom de lvnement tait dclenche lorsque ce dernier tait diffus. Pour couter un vnement li au clavier nous pouvions crire le code suivant :
var monEcouteur = new Object(); monEcouteur.onKeyDown = function ( ) { trace ( Key.getCode() ); } Key.addListener ( monEcouteur );

En passant un objet comme couteur, nous dfinissions une mthode portant le nom de lvnement diffus, le lecteur dclenchait alors la mthode lorsque lvnement tait diffus. Dsormais seule une fonction ou une mthode peut tre passe en tant qucouteur. Si nous testons le code suivant :
var monEcouteur:Object = new Object(); stage.addEventListener ( KeyboardEvent.KEY_DOWN, monEcouteur );

Lerreur de compilation ci-dessous saffiche dans la fentre de sortie :


1118: Contrainte implicite d'une valeur du type statique Object vers un type peut-tre sans rapport Function.

Le compilateur dtecte que le type de lobjet pass en tant qucouteur nest pas une fonction mais un objet et refuse la compilation. En revanche il est techniquement possible de passer en tant qucouteur non pas une fonction mais une mthode cre dynamiquement sur un objet. Le code suivant fonctionne sans problme :

34 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 3 Le modle vnementiel - version 0.1

var monEcouteur:Object = new Object(); monEcouteur.maMethode = function () { trace( "touche clavier enfonce" ); } stage.addEventListener ( KeyboardEvent.KEY_DOWN, monEcouteur.maMethode );

En revanche, une subtilit est noter dans ce cas prcis le contexte dexcution de la mthode maMethode nest pas celui que nous attendons. En faisant appel au mot-cl this pour afficher le contexte en cours nous serons surpris de ne pas obtenir [object Object] mais [object global]. Lorsque nous passons une mthode stocke au sein dune proprit dynamique dun objet, le lecteur Flash na aucun moyen de rattacher cette mthode son objet dorigine, la fonction sexcute alors dans un contexte global, lobjet global. Le code suivant met en avant cette subtilit de contexte :
var monEcouteur:Object = new Object(); monEcouteur.maMethode = function () { // affiche : [object global] trace( this ); } stage.addEventListener ( KeyboardEvent.KEY_DOWN, monEcouteur.maMethode );

Il est donc fortement dconseill dutiliser cette technique pour grer les vnements au sein de votre application. Nous avons dcouvert ensemble le nouveau modle vnementiel, afin de couvrir totalement les mcanismes lis ce modle attardons-nous prsent sur la gestion de laffichage en ActionScript 3.

A retenir
En ActionScript 3, un objet ne peut pas tre utilis comme couteur. Si la mthode passe comme couteur est cre dynamiquement, celle-ci sexcut dans un contexte global.

Nous allons nous intresser dans le chapitre suivant la notion de liste daffichage. Dcouvrons ensemble comment manipuler avec souplesse les objets graphiques au sein du leteur Flash 9.

35 / 35
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

4
La liste daffichage

CLASSES GRAPHIQUES ...................................................................................... 1 LISTE DAFFICHAGE ........................................................................................... 5 INSTANCIATION DES CLASSES GRAPHIQUES............................................................. 7 AJOUT DOBJETS DAFFICHAGE ............................................................................... 8 REATTRIBUTION DE CONTENEUR............................................................................. 9 LISTE INTERNE DOBJETS ENFANTS ....................................................................... 11 ACCEDER AUX OBJETS DAFFICHAGE .................................................................... 13 SUPPRESSION DOBJETS DAFFICHAGE .................................................................. 20 EFFONDREMENT DES PROFONDEURS ..................................................................... 25 GESTION DE LEMPILEMENT DES OBJETS DAFFICHAGE ......................................... 27 ECHANGE DE PROFONDEURS ................................................................................. 30 DESACTIVATION DES OBJETS GRAPHIQUES ............................................................ 32 FONCTIONNEMENT DE LA TETE DE LECTURE ......................................................... 35 SUBTILITES DE LA PROPRIETE STAGE..................................................................... 36 SUBTILITES DE LA PROPRIETE ROOT ...................................................................... 38 LES _LEVEL........................................................................................................... 40

Classes graphiques
En ActionScript 3 comme nous lavons vu prcdemment, toutes les classes graphiques sont stockes dans des emplacements spcifiques et rsident au sein des trois diffrents paquetages : flash.display, flash.media, et flash.text. Le schma suivant regroupe lensemble dentre elles :

1 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Figure 3-1. Classes graphiques de la liste daffichage. Nous remarquons travers ce schma la multitude de classes graphiques disponibles pour un dveloppeur ActionScript 3. La question que nous pouvons nous poser est la suivante. Pourquoi un tel clatement de classes ? ActionScript 3 a t dvelopp dans un souci doptimisation. En offrant un large choix de classes graphiques nous avons la possibilit dutiliser lobjet le plus optimis pour chaque besoin. Plus la classe graphique est enrichie plus celle-ci occupe un poids en mmoire important. Du fait du petit nombre de classes graphiques en ActionScript 1 et 2 beaucoup de dveloppeurs utilisaient la classe MovieClip pour tout raliser, ce qui ntait pas optimis. En ActionScript 1 et 2 seules quatre classes graphiques existaient : MovieClip, Button, TextField, et dernirement BitmapData. Prenons lexemple dune application Flash traditionnelle comme une application de dessin. Avant ActionScript 3 le seul objet nous permettant de dessiner grce lAPI de dessin tait la classe MovieClip. Cette dernire intgre un scnario ainsi que des mthodes lies la manipulation de la tte de lecture qui nous taient inutiles dans ce cas prcis. Un simple objet Shape plus lger en mmoire suffirait pour dessiner. Dans la mme logique, nous prfrerons utiliser un objet SimpleButton plutt quun MovieClip pour la cration de boutons. Nous devons mmoriser trois types dobjets graphiques primordiaux.
Les objets de type flash.display.DisplayObject

Tout lment devant tre affich doit tre de type DisplayObject. Un champ texte, un bouton ou un clip sera forcment de type 2 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

DisplayObject. Durant vos dveloppements ActionScript 3, vous qualifieriez gnralement de DisplayObject tout objet pouvant tre affich. La classe DisplayObject dfinit toutes les proprits de

base lies laffichage, la position, la rotation, ltirement et dautres proprits plus avances. Attention : objet hritant simplement de DisplayObject comme Shape par exemple na pas la capacit de contenir des objets graphiques. Parmi les sous-classes directes de DisplayObject nous pouvons citer les classes Shape, Video, Bitmap.
Les objets de type flash.display.InteractiveObject

un

La classe InteractiveObject dfinit les comportements lis linteractivit. Lorsquun objet hrite de la classe InteractiveObject, ce dernier peut ragir aux entres utilisateur lies la souris ou au clavier. Parmi les objets graphiques descendant directement de la classe InteractiveObject nous pouvons citer les classes SimpleButton et TextField.
Les objets de type flash.display.DisplayObjectContainer

A la diffrence des DisplayObject les DisplayObjectContainer peuvent contenir des objets graphiques. Dornavant nous parlerons dobjet enfant pour qualifier un objet graphique contenu dans un autre. Les objets hritant de cette classe sont donc appels conteneurs dobjets daffichage. La classe DisplayObjectContainer est une sous classe de la classe DisplayObject et dfinit toutes les proprits et mthodes relatives la manipulation des objets enfants. Loader, Sprite et Stage hritent directement de DisplayObjectContainer. Durant nos dveloppements ActionScript 3, certains objets graphiques ne pourront tre instancis tandis que dautres le pourront. Nous pouvons sparer en deux catgories lensemble des classes graphiques : Les objets instanciables :
flash.display.Bitmap : cette classe sert denveloppe lobjet flash.display.BitmapData qui ne peut tre ajout la liste daffichage sans enveloppe Bitmap.

flash.display.Shape : il sagit de la classe de base pour tout contenu vectoriel, elle est fortement lie lAPI de dessin grce sa proprit graphics. Nous reviendrons sur lAPI de dessin trs vite.

3 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

flash.media.Video : la classe Video sert afficher les flux vido, provenant dune webcam, dun serveur de streaming, ou dun simple fichier vido (FLV, MP4, MOV, etc.). flash.text.TextField : la gestion du texte est assure par la classe TextField.

flash.display.SimpleButton : la classe SimpleButton permet de crer des boutons dynamiquement ce qui tait impossible dans les prcdentes versions dActionScript.

flash.display.Loader : la classe Loader gre le chargement de tout contenu graphique externe, chargement de SWF et images (PNG, GIF, JPG) flash.display.Sprite : la classe Sprite est un MovieClip allg car il ne dispose pas de scnario. flash.display.MovieClip : la classe MovieClip est la classe graphique la plus enrichie, elle est lie lanimation traditionnelle.

Les objets non instanciables:


flash.display.AVM1Movie : lorsquune animation ActionScript 1 ou 2 est charge au sein dune animation ActionScript 3, le lecteur Flash 9 enveloppe lanimation dancienne gnration dans un objet de type AVM1Movie il est donc impossible dinstancier un objet de ce type par programmation. flash.display.InteractiveObject : la classe InteractiveObject dfinit les comportements lis linteractivit comme les vnements souris ou clavier par exemple. flash.display.MorphShape : les formes interpoles sont reprsentes par le type MorphShape. Seul lenvironnement auteur de Flash CS3 peut crer des objets de ce type. flash.display.StaticText : la classe StaticText reprsente les champs texte statique crs dans lenvironnement auteur Flash CS3 flash.display.Stage : il est le conteneur principal de tout notre contenu graphique.

A retenir

4 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Il faut utiliser la classe la plus optimise pour chaque cas. Les classes graphiques rsident dans trois paquetages diffrents : flash.display, flash.media, et flash.text.

Les classes DisplayObject, InteractiveObject, et DisplayObjectContainer sont abstraites et ne peuvent tre instancies ni hrites en ActionScript.

Liste daffichage
Lorsquune animation est charge au sein du lecteur Flash, celui-ci ajoute automatiquement la scne principale du SWF en tant que premier enfant du conteneur principal, lobjet Stage. Cette hirarchie dfinit ce quon appelle la liste daffichage de lapplication. La figure 3-2 illustre le mcanisme :

Figure 3-2. Schma du systme daffichage du lecteur Flash. Afin de bien comprendre comment la liste daffichage sorganise, crez un nouveau document Flash CS3 et testez le code suivant sur le scnario principal :
// affiche : [object MainTimeline] trace( this ); // affiche : [object Stage] trace( this.parent );

En faisant appel au mot-cl this, nous faisons rfrence notre scnario principal lobjet MainTimeline contenu par lobjet Stage.

5 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Attention, lobjet Stage nest plus accessible de manire globale comme ctait le cas en ActionScript 1 et 2. Le code suivant gnre une erreur la compilation :
trace( Stage );

Pour faire rfrence lobjet Stage nous devons obligatoirement passer par la proprit stage dun DisplayObject prsent au sein de la liste daffichage :
monDisplayObject.stage

Pour rcuprer le nombre dobjets daffichage contenu dans un objet de type DisplayObjectContainer nous utilisons la proprit numChildren. Ainsi pour rcuprer le nombre dobjets daffichage contenus par lobjet Stage nous crivons :
// affiche : 1 trace( this.stage.numChildren );

Ou bien de manire implicite :


// affiche : 1 trace( stage.numChildren );

Nous reviendrons trs bientt sur laccs cette proprit qui mrite une attention toute particulire. Notre objet Stage contient un seul enfant, notre scnario principal, lobjet MainTimeline. Ainsi lorsquun SWF vide est lu, deux DisplayObject sont prsents par dfaut dans la liste daffichage :
Lobjet Stage Le scnario du SWF, lobjet MainTimeline

Comme lillustre la figure 3-2, chaque application ActionScript 3 possde un seul objet Stage et donc une seule et unique liste daffichage. Il faut considrer lobjet Stage comme le nud principal de notre liste daffichage. En ralit celle-ci peut tre reprsente comme une arborescence XML, un nud principal duquel dcoulent dautres nuds enfants, nos objets daffichages. Lorsque nous posons une occurrence de bouton sur la scne principale de notre animation, nous obtenons la liste daffichage suivante :

6 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Figure 3-3. Liste daffichage avec un bouton. Nous verrons au cours du chapitre 11 intitul Classe du document comment remplacer la classe MainTimeline par une sous classe graphique personnalise. Jusqu' maintenant nous navons pas cr dobjet graphique par programmation. ActionScript 3 intgre un nouveau procd dinstanciation des classes graphiques bien plus efficace que nous allons aborder prsent.

A retenir
Nous accdons lobjet Stage travers la proprit stage de tout DisplayObject. Lobjet Stage nest plus accessible de manire globale.

Instanciation des classes graphiques


Une des grandes nouveauts dActionScript 3 concerne le procd dinstanciation des classes graphiques. Avant ActionScript 3, nous devions utiliser diverses mthodes telles createEmptyMovieClip pour la cration de flash.display.MovieClip, ou encore createTextField pour la classe flash.text.TextField. Il nous fallait mmoriser plusieurs mthodes ce qui ntait pas forcment vident pour une personne dcouvrant ActionScript. Dsormais, nous utilisons le mot cl new pour instancier tout objet graphique. Le code suivant cre une nouvelle instance de MovieClip.
var monClip:MovieClip = new MovieClip();

7 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

De la mme manire, si nous souhaitons crer un champ texte dynamiquement nous crivons :
var monChampTexte:TextField = new TextField();

Pour affecter du texte notre champ nous utilisons la proprit text de la classe TextField :
var monChampTexte:TextField = new TextField(); monChampTexte.text = "Hello World";

Lutilisation

mthodes createTextField ou createEmptyMovieClip nous obligeaient conserver une rfrence un MovieClip afin de pouvoir instancier nos objets graphiques, avec lutilisation du mot-cl new tout cela nest quun mauvais souvenir. Nous navons plus besoin de spcifier de nom doccurrence ni de profondeur lors de la phase dinstanciation. Bien que notre objet graphique soit cr et rside en mmoire, celui-ci na pas encore t ajout la liste daffichage. Si nous testons le code prcdent, nous remarquons que le texte nest pas affich. Un des comportements les plus troublants lors de la dcouverte dActionScript 3 concerne les objets daffichage hors liste.

des

A retenir
Pour quun objet graphique soit visible, ce dernier doit obligatoirement tre ajout la liste daffichage. Tous les objets graphiques sinstancient avec le mot cl new.

Ajout dobjets daffichage


En ActionScript 3, lorsquun objet graphique est cr, celui-ci nest pas automatiquement ajout la liste daffichage comme ctait le cas en ActionScript 1 et 2. Lobjet existe en mmoire mais ne rside pas dans la liste daffichage et nest donc pas rendu. Dans ce cas lobjet est appel objet daffichage hors liste. Pour lafficher nous utilisons une des deux mthodes dfinies par la classe DisplayObjectContainer appeles addChild et addChildAt. Voici la signature de la mthode addChild :
public function addChild(child:DisplayObject):DisplayObject

8 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

La mthode addChild accepte comme seul paramtre une instance de DisplayObject. Pour voir notre objet graphique nous devons lajouter la liste dobjets enfants dun DisplayObjectContainer prsent au sein de la liste daffichage. Nous pouvons ajouter par exemple linstance notre scnario principal :
var monChampTexte:TextField = new TextField(); monChampTexte.text = "Hello World"; addChild ( monChampTexte );

Nous obtenons la liste daffichage illustre par la figure suivante :

Figure 3-4. Liste daffichage simple avec un champ texte. Ce comportement nouveau est extrmement puissant. Tout dveloppeur ActionScript a dj souhait charger une image ou attacher un objet de la bibliothque sans pour autant lafficher. Avec le concept dobjets daffichage hors liste, un objet graphique peut vivre sans pour autant tre rendu laffichage. Ce comportement offre de nouvelles possibilits en matire doptimisation de laffichage. Nous reviendrons sur ce mcanisme lors du chapitre 12 intitul Programmation bitmap.

Rattribution de conteneur
En ActionScript 1 et 2, aucune mthode nexistait pour dplacer un objet graphique au sein de la liste daffichage. Il tait impossible de changer son parent. Le seul moyen tait de supprimer lobjet graphique puis le recrer au sein du conteneur voulu. 9 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

En ActionScript 3, si nous passons la mthode addChild un DisplayObject dj prsent au sein de la liste daffichage, ce dernier est supprim de son conteneur dorigine et plac dans le nouveau DisplayObjectContainer sur lequel nous avons appel la mthode addChild. Prenons un exemple simple, nous crons un clip que nous ajoutons la liste daffichage en lajoutant notre scnario principal :
var monClip:MovieClip = new MovieClip(); // le clip monClip est ajout au scnario principal addChild ( monClip ); // affiche : 1 trace( numChildren );

La proprit numChildren du scnario principal renvoie 1 car le clip monClip est un enfant du scnario principal. Nous crons maintenant, un objet graphique de type Sprite et nous lui ajoutons en tant quenfant notre clip monClip dj contenu par notre scnario principal :
var monClip:MovieClip = new MovieClip(); // le clip monClip est ajout au scnario principal addChild ( monClip ); // affiche : 1 trace( numChildren ); var monSprite:Sprite = new Sprite(); addChild ( monSprite ); // affiche : 2 trace( numChildren ); monSprite.addChild ( monClip ); // affiche : 1 trace( numChildren );

La proprit numChildren du scnario principal renvoie toujours 1 car le clip monClip a quitt le scnario principal afin dtre plac au sein de notre objet monSprite. Dans le code suivant, lappel successif de la mthode addChild ne duplique donc pas lobjet graphique mais le dplace de son conteneur dorigine pour le replacer nouveau :
var monClip:MovieClip = new MovieClip(); // le clip monClip est ajout au scnario principal addChild ( monClip );

10 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

// le clip monClip quitte le scnario principal // puis est repla au sein de ce dernier addChild ( monClip ); // affiche : 1 trace( numChildren );

Il nexiste pas en ActionScript 3 dquivalent la mthode duplicateMovieClip existante dans les prcdentes versions dActionScript. Pour dupliquer un objet graphique, nous utilisons le mot-cl new. La rattribution de conteneurs illustre la puissance apporte par le lecteur Flash 9 et ActionScript 3 en matire de gestion des objets graphiques.

Liste interne dobjets enfants


Chaque DisplayObjectContainer possde une liste dobjets enfants reprsente par un tableau interne. A chaque index se trouve un objet enfant. Visuellement lobjet positionn lindex 0 est en dessous de lobjet positionn lindex 1. La figure 3-5 illustre le concept :

Figure 3-5. Tableau interne daccs aux objets enfants. Ce DisplayObjectContainer contient trois objets enfants. A chaque appel, la mthode addChild place lobjet graphique concern la fin de la liste dobjets enfants, ce qui se traduit par un empilement de ces derniers. En travaillant avec les diffrentes mthodes de gestion des objets enfants nous travaillerons de manire transparente avec ce tableau interne. La mthode addChild empile les objets enfants, mais si nous souhaitons grer lordre dempilement, nous utiliserons la mthode addChildAt dont voici la signature :
public function addChildAt(child:DisplayObject, index:int):DisplayObject

11 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

La mthode addChildAt accepte un deuxime paramtre permettant de grer lindex du DisplayObject au sein de la liste du DisplayObjectContainer. Prenons un exemple simple, nous crons une instance de MovieClip et lajoutons la liste daffichage :
var monClipA:MovieClip = new MovieClip(); addChild ( monClipA );

Lobjet graphique monClipA est aussitt plac lindex 0. Nous souhaitons alors placer un deuxime clip au mme index. La mthode addChildAt place le clip monClipB lindex 0 dplaant monClipA lindex 1 :
var monClipA:MovieClip = new MovieClip(); addChild ( monClipA ); var monClipB:MovieClip = new MovieClip(); addChildAt ( monClipB, 0 );

Afin de faire place lobjet graphique ajout, les objets enfants dj prsents lindex spcifi ne sont pas remplacs mais dplacs dun index. Ce comportement vite dcraser par erreur un objet graphique prsent au sein de la liste daffichage. Si nous souhaitons reproduire le mcanisme d'crasement, nous devons supprimer lobjet graphique puis en placer un nouveau lindex libr. Si lindex pass la mthode addChildAt est ngatif ou ne correspond aucun objet enfant :
var monClipA:MovieClip = new MovieClip(); addChildAt ( monClipA, 10 );

Une erreur lexcution de type RangeError est leve :


RangeError: Error #2006: L'index indiqu sort des limites.

La mthode addChildAt ne peut donc tre utilise quavec un index allant de 0 numChildren-1. Pour placer un DisplayObject devant tous les autres nous pouvons crire :
addChildAt ( monClipA, numChildren-1 );

12 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Aucune profondeur intermdiaire entre deux DisplayObject ne peut demeurer inoccupe. Ce comportement est li la gestion de la profondeur automatique par le lecteur 9 en ActionScript 3. Nous reviendrons plus tard sur cette notion dans la partie intitule Effondrement des profondeurs.

A retenir
Il existe une seule et unique liste daffichage. En son sommet se trouve lobjet Stage. Tous les objets graphiques sinstancient avec le mot cl new

Aucune profondeur ou nom doccurrence nest ncessaire durant la phase dinstanciation. Lorsquun objet graphique est cr, automatiquement la liste daffichage. il nest pas ajout

Pour voir un objet graphique il faut lajouter la liste daffichage. Pour choisir la position du DisplayObject lors de lajout la liste daffichage, nous utilisons la mthode addChildAt.

Accder aux objets daffichage


Ouvrez un nouveau document Flash CS3 et crez une simple forme vectorielle laide de loutil Rectangle. En ciblant la proprit numChildren de notre scne principale, nous rcuprons le nombre denfants correspondant la longueur du tableau interne :
// affiche : 1 trace( numChildren );

La proprit numChildren nous renvoie 1, car le seul objet contenu par notre scne principale est notre forme vectorielle. Celle-ci a t ajoute automatiquement la liste daffichage. Bien que la forme vectorielle nait pas de nom doccurrence nous pouvons y accder grce aux mthodes dfinies par la classe DisplayObjectContainer. Pour accder un objet enfant plac index spcifique nous avons disposition la mthode getChildAt :
public function getChildAt(index:int):DisplayObject

Noubliez pas que la mthode getChildAt ne fait que pointer dans le tableau interne du DisplayObjectContainer selon lindex pass en paramtre :
monDisplayObjectContainer.getChildAt ( index );

13 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Pour accder la forme vectorielle que nous venons de dessiner, nous ciblons lindex 0 laide de la mthode getChildAt :
// accde l'objet graphique plac l'index 0 var forme:Shape = getChildAt ( 0 );

En compilant le code prcdent, une erreur est leve nous indiquant quil est impossible de placer un objet de type DisplayObject au sein dune variable de type Shape :
1118: Contrainte implicite d'une valeur du type statique flash.display:DisplayObject vers un type peut-tre sans rapport flash.display:Shape.

Nous tentons de stocker la rfrence renvoye par la mthode getChildAt au sein dun conteneur de type Shape. En relisant la signature de la mthode getChildAt nous pouvons lire que le type retourn par celle-ci est DisplayObject. Flash refuse alors la compilation car nous tentons de stocker un objet de type DisplayObject dans un conteneur de type Shape. Nous pouvons donc indiquer au compilateur que lobjet sera bien un objet de type Shape en utilisant le mot cl as :
// accde l'objet graphique plac l'index 0 var forme:Shape = getChildAt ( 0 ) as Shape; // affiche : [object Shape] trace( forme );

En ralit, ce code savre dangereux car si lobjet plac lindex 0 est remplac par un objet graphique non compatible avec le type Shape, le rsultat du transtypage choue et renvoie null. Nous devons donc nous assurer que quelque soit lobjet graphique plac lindex 0, notre variable soit de type compatible. Dans cette situation, il convient donc de toujours utiliser le type gnrique DisplayObject pour stocker la rfrence lobjet graphique :
// accde l'objet graphique plac l'index 0 var forme:DisplayObject = getChildAt ( 0 ); // affiche : [object Shape] trace( forme );

De cette manire, nous ne prenons aucun risque, car de par lhritage, lobjet graphique sera forcment de type DisplayObject. Si nous tentons daccder un index inoccup :
// accde l'objet graphique plac l'index 0 var forme:DisplayObject = getChildAt ( 1 );

14 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Une erreur de type RangeError est leve, car lindex 1 spcifi ne contient aucun objet graphique :
RangeError: Error #2006: L'index indiqu sort des limites.

Avant ActionScript 3 lorsquun objet graphique tait cr une profondeur dj occupe, lobjet rsidant cette profondeur tait remplac. Pour viter tout conflit entre graphistes et dveloppeurs les objets graphiques crs par programmation taient placs une profondeur positive et ceux crs dans lenvironnement auteur une profondeur ngative. Ce nest plus le cas en ActionScript 3, tout le monde travaille dans le mme niveau de profondeurs. Lorsque nous posons un objet graphique depuis lenvironnement auteur de Flash CS3 sur la scne, celui-ci est cr et automatiquement ajout la liste daffichage. En ActionScript 1 et 2 il tait impossible daccder une simple forme contenue au sein dun scnario. Une des forces dActionScript 3 est la possibilit de pouvoir accder nimporte quel objet de la liste daffichage. Allons un peu plus loin, et crez sur le scnario principal une seconde forme vectorielle laide de loutil Ovale, le code suivant nous affiche toujours au total un seul objet enfant :
// affiche : 1 trace( numChildren );

Nous aurions pu croire que chaque forme vectorielle cre correspond un objet graphique diffrent, il nen est rien. Toutes les formes vectorielles cres dans lenvironnement auteur ne sont en ralit quun seul objet Shape. Mme si les formes vectorielles sont places sur plusieurs calques, un seul objet Shape est cr pour contenir la totalit des tracs. Le code suivant rend donc invisible nos deux formes vectorielles :
var mesFormes:DisplayObject = getChildAt ( 0 ); mesFormes.visible = false;

Rappelez-vous, ActionScript 3 intgre une gestion de la profondeur automatique. A chaque appel de la mthode addChild le DisplayObject pass en paramtre est ajout au DisplayObjectContainer. Les objets enfants sajoutant les uns derrire les autres au sein du tableau interne.

15 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Chaque objet graphique est plac graphiquement au dessus du prcdent. Il nest donc plus ncessaire de se soucier de la profondeur dun MovieClip au sein dune boucle for :
var monClip:MovieClip; var i:int; for ( i = 0; i< 10; i++ ) { monClip = new MovieClip(); addChild ( monClip ); }

En sortie de boucle, dix clips auront t ajouts la liste daffichage :


var monClip:MovieClip; var i:int; for ( i = 0; i< 10; i++ ) { monClip = new MovieClip(); addChild ( monClip ); } // affiche : 10 trace( numChildren );

Pour faire rfrence chaque clip nous pouvons ajouter le code suivant :
var lng:int = numChildren; for ( i = 0; i< lng; i++ ) { /* affiche : [object MovieClip] [object MovieClip] [object MovieClip] [object MovieClip] [object MovieClip] [object MovieClip] [object MovieClip] [object MovieClip] [object MovieClip] [object MovieClip] */ trace( getChildAt ( i ) ); }

16 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Nous rcuprons le nombre total dobjets enfants avec la proprit numChildren, puis nous passons lindice i comme index la mthode getChildAt pour cibler chaque occurrence. Pour rcuprer lindex associ un DisplayObject, nous le passons en rfrence la mthode getChildIndex :
public function getChildIndex(child:DisplayObject):int

Dans le code suivant nous accdons aux clips prcdemment crs et rcuprons lindex de chacun :
var lng:int = numChildren; var objetEnfant:DisplayObject; for ( i = 0; i< lng; i++ ) { objetEnfant = getChildAt( i ); /*affiche : [object MovieClip] l'index : 0 [object MovieClip] l'index : 1 [object MovieClip] l'index : 2 [object MovieClip] l'index : 3 [object MovieClip] l'index : 4 [object MovieClip] l'index : 5 [object MovieClip] l'index : 6 [object MovieClip] l'index : 7 [object MovieClip] l'index : 8 [object MovieClip] l'index : 9 */ trace ( objetEnfant + " l'index : " + getChildIndex ( objetEnfant ) ); }

Sachez quil est toujours possible de donner un nom un objet graphique. En ActionScript 1 et 2 pour donner un nom un clip, il fallait spcifier le nom de loccurrence en tant que paramtre lors de lappel de la mthode createEmptyMovieClip ou attachMovie. En ActionScript 3, une fois lobjet graphique instanci nous pouvons passer une chane de caractres la proprit name dfinie par la classe DisplayObject. Le code suivant cre une instance de MovieClip et lui affecte un nom doccurrence :
var monClip:MovieClip = new MovieClip(); monClip.name = "monOcurrence"; addChild ( monClip ); // affiche : monOccurrence

17 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

trace(monClip.name);

Pour accder notre MovieClip nous pouvons utiliser la mthode getChildByName permettant de cibler un DisplayObject par son nom et non par son index comme le permet la mthode getChildAt. La
getChildByName dfinie par la classe DisplayObjectContainer accepte comme seul paramtre une

mthode

chane de caractres correspondant au nom doccurrence du DisplayObject auquel nous souhaitons accder. Voici sa signature :
public function getChildByName(name:String):DisplayObject

Le code suivant nous permet de cibler notre MovieClip :


var monClip:MovieClip = new MovieClip(); monClip.name = "monOcurrence"; addChild ( monClip ); // affiche : [object MovieClip] trace( getChildByName ("monOcurrence") );

La mthode getChildByName traverse rcursivement tous les objets de la liste dobjets enfants jusqu ce que le DisplayObject soit trouv. Si lobjet graphique recherch nest pas prsent dans le DisplayObjectContainer concern, la mthode getChildByName renvoie null :
var monClip:MovieClip = new MovieClip(); monClip.name = "monOcurrence"; // affiche : null trace( getChildByName ("monOcurrence") );

Il nest pas recommand dutiliser la mthode getChildByName, celle-ci savre beaucoup plus lente lexcution que la mthode getChildAt qui pointe directement dans le tableau interne du DisplayObjectContainer. Un simple test met en vidence la diffrence significative de performances. Au sein dune boucle nous accdons un clip prsent au sein de la liste daffichage laide de la mthode getChildByName :
var monClip:MovieClip = new MovieClip(); monClip.name = "monOcurrence"; addChild ( monClip );

18 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

var depart:Number = getTimer(); for ( var i:int = 0; i< 5000000; i++ ) { getChildByName ("monOcurrence"); } var arrivee:Number = getTimer(); // affiche : 854 ms trace( (arrivee - depart) + " ms" );

Notre boucle met environ 854 ms sexcuter en utilisant la mthode daccs getChildByName. Procdons au mme test en utilisant cette fois la mthode daccs getChildAt :
var monClip:MovieClip = new MovieClip(); monClip.name = "monOcurrence"; addChild ( monClip ); var depart:Number = getTimer(); for ( var i:int = 0; i< 5000000; i++ ) { getChildAt (0); } var arrivee:Number = getTimer(); // affiche : 466 ms trace( (arrivee - depart) + " ms" );

Nous obtenons une diffrence denviron 400 ms entre les deux boucles. Le bilan de ce test nous pousse utiliser la mthode getChildAt plutt que la mthode getChildByName. Une question que certains dentre vous peuvent se poser est la suivante : Si je ne prcise pas de nom doccurrence, quel nom porte mon objet graphique ? Le lecteur Flash 9 procde de la mme manire que les prcdents lecteurs en affectant un nom doccurrence par dfaut. Prenons lexemple dun MovieClip cr dynamiquement :
var monClip:MovieClip = new MovieClip(); // affiche : instance1 trace( monClip.name );

Un nom doccurrence est automatiquement affect. 19 / 41


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Il est important de noter quil est impossible de modifier la proprit name dun objet cr depuis lenvironnement auteur. Dans le code suivant, nous tentons de modifier la proprit name dun MovieClip cr depuis lenvironnement auteur :
monClip.name = "nouveauNom";

Ce qui gnre lerreur suivante la compilation :


Error: Error #2078: Impossible de modifier la proprit de nom d'un objet plac sur le scnario.

En pratique, cela ne pose pas de rel problme car nous utiliserons trs rarement la proprit name qui est fortement lie la mthode getChildByName.

Suppression dobjets daffichage


Pour supprimer un DisplayObject de laffichage nous appelons la mthode removeChild sur son conteneur, un objet DisplayObjectContainer. La mthode removeChild prend comme paramtre le DisplayObject supprimer de la liste daffichage et renvoie sa rfrence :
public function removeChild(child:DisplayObject):DisplayObject

Il est essentiel de retenir que la suppression dun objet graphique est toujours ralise par lobjet parent. La mthode removeChild est donc toujours appele sur lobjet conteneur :
monConteneur.removeChild ( enfant );

De ce fait, un objet graphique nest pas en mesure de se supprimer luimme comme ctait le cas avec la mthode removeMovieClip existante en ActionScript 1 et 2. Il est en revanche capable de demander son parent de le supprimer :
parent.removeChild ( this );

Il est important de retenir que lappel de la mthode removeChild procde une simple suppression du DisplayObject au sein de la liste daffichage mais ne le dtruit pas. Un ancien rflexe li ActionScript 1 et 2 pourrait vous laisser penser que la mthode removeChild est lquivalent de la mthode removeMovieClip, ce nest pas le cas. Pour nous rendre compte de ce comportement, ouvrez un nouveau document Flash CS3 et crez un symbole de type clip. Posez une 20 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

occurrence de ce dernier sur le scnario principal et nommez la monRond. Sur un calque AS nous ajoutons un appel la mthode removeChild pour supprimer le clip de la liste daffichage.
removeChild ( monRond );

Nous pourrions penser que notre clip monRond a aussi t supprim de la mmoire. Mais si nous ajoutons la ligne suivante :
removeChild ( monRond ); // affiche : [object MovieClip] trace( monRond );

Nous voyons que le clip monRond existe toujours et na pas t supprim de la mmoire. En ralit nous avons supprim de laffichage notre clip monRond. Ce dernier nest plus rendu mais continue dexister en mmoire. Si le dveloppeur ne prend pas en compte ce mcanisme, les performances dune application peuvent tre mises en pril. Prenons
Event.ENTER_FRAME MovieClip :

le

cas

classique suivant : un vnement est cout auprs dune instance de

var monClip:MovieClip = new MovieClip(); monClip.addEventListener( Event.ENTER_FRAME, ecouteur ); addChild ( monClip ); function ecouteur ( pEvt:Event ):void { trace("excution"); }

En supprimant linstance de MovieClip de la liste daffichage laide de la mthode removeChild, nous remarquons que lvnement est toujours diffus :
var monClip:MovieClip = new MovieClip(); monClip.addEventListener( Event.ENTER_FRAME, ecouteur ); addChild ( monClip ); function ecouteur ( pEvt:Event ):void {

21 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

trace("excution"); } removeChild ( monClip );

Pour librer de la mmoire un DisplayObject supprim de la liste daffichage nous devons passer ses rfrences null et attendre le passage du ramasse-miettes. Rappelez-vous que ce dernier supprime les objets nayant plus aucune rfrence au sein de lapplication. Le code suivant supprime notre clip de la liste daffichage et passe sa rfrence null. Ce dernier nest donc plus rfrenc au sein de lapplication le rendant donc ligible pour le ramasse miettes :
var monClip:MovieClip = new MovieClip(); monClip.addEventListener( Event.ENTER_FRAME, ecouteur ); addChild ( monClip ); function ecouteur ( pEvt:Event ):void { trace("excution"); } removeChild ( monClip ); monClip = null; // affiche : null trace( monClip );

Bien que nous ayons supprim toutes les rfrences vers notre MovieClip, celui-ci nest pas supprim de la mmoire immdiatement. Rappelez-vous que le passage du ramasse-miettes est diffr ! Nous ne pouvons pas savoir quand ce dernier interviendra. Le lecteur gre cela de manire autonome selon les ressources systme. Il est donc impratif de prvoir un mcanisme de dsactivation des objets graphiques, afin que ces derniers consomment un minimum de ressources lorsquils ne sont plus affichs et attendent dtre supprims par le ramasse-miettes. Durant nos dveloppements, nous pouvons nanmoins dclencher manuellement le passage du ramasse-miettes au sein du lecteur de dbogage laide de la mthode gc de la classe System. 22 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Dans le code suivant, nous dclenchons le ramasse-miettes lors du clic souris sur la scne :
var monClip:MovieClip = new MovieClip(); monClip.addEventListener( Event.ENTER_FRAME, ecouteur ); addChild ( monClip ); function ecouteur ( pEvt:Event ):void { trace("excution"); } removeChild ( monClip ); monClip = null; stage.addEventListener ( MouseEvent.CLICK, clicSouris ); function clicSouris ( pEvt:MouseEvent ):void { // dclenchement du ramasse-miettes System.gc(); }

En cliquant sur la scne, lvnement Event.ENTER_FRAME cesse dtre diffus car le passage du ramasse-miettes a entran une suppression dfinitive du MovieClip. Nous reviendrons sur ce point dans une prochaine partie intitule Dsactivation des objets graphiques. Imaginons le cas suivant : nous devons supprimer un ensemble de clips que nous venons dajouter la liste daffichage. Ouvrez un nouveau document Flash CS3 et placez plusieurs occurrences de symbole MovieClip sur la scne principale. Il nest pas ncessaire de leur donner des noms doccurrences. En ActionScript 1 et 2, nous avions plusieurs possibilits. Une premire technique consistait stocker dans un tableau les rfrences aux clips ajouts, puis le parcourir pour appeler la mthode removeMovieClip sur chaque rfrence. Autre possibilit, parcourir le clip conteneur laide dune boucle for in pour rcuprer chaque proprit faisant rfrence aux clips pour pouvoir les cibler puis les supprimer.

23 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Lorsque vous souhaitez supprimer un objet graphique positionn un index spcifique nous pouvons utiliser la mthode removeChildAt dont voici la signature :
public function removeChildAt(index:int):DisplayObject

En dcouvrant cette mthode nous pourrions tre tents dcrire le code suivant :
var lng:int = numChildren; for ( var i:int = { removeChildAt ( i ); } 0; i< lng; i++ )

A lexcution le code prcdent gnre lerreur suivante :


RangeError: Error #2006: L'index indiqu sort des limites.

Une erreur de type RangeError est leve car lindex que nous avons pass la mthode getChildIndex est considr comme horslimite. Cette erreur est leve lorsque lindex pass ne correspond aucun objet enfant contenu par le DisplayObjectContainer. Notre code ne peut pas fonctionner car nous navons pas pris en compte un comportement trs important en ActionScript 3 appel effondrement des profondeurs.

A retenir

24 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Chaque DisplayObjectContainer contient un tableau interne contenant une rfrence chaque objet enfant. Toutes les mthodes de manipulation des objets daffichage travaillent avec le tableau interne dobjets enfants de manire transparente. La profondeur est gre automatiquement. La mthode la plus rapide pour accder un objet enfant est getChildAt La mthode addChild ne ncessite aucune profondeur et empile chaque objet enfant. La mthode removeChild supprime un DisplayObject de la liste daffichage mais ne le dtruit pas. Pour supprimer un DisplayObject de la mmoire, nous devons passer ses rfrences null.

Le ramasse miettes interviendra quand il le souhaite et supprimera les objets non rfrencs de la mmoire.

Effondrement des profondeurs


En ActionScript 1 et 2, lorsquun objet graphique tait supprim, les objets placs au dessus de ce dernier conservaient leur profondeur. Ce comportement paraissait tout fait logique mais soulevait un problme doptimisation car des profondeurs demeuraient inoccupes entres les objets graphiques. Ainsi les profondeurs 6, 8 et 30 pouvaient tres occupes alors que les profondeurs intermdiaires restaient inoccupes. En ActionScript 3 aucune profondeur intermdiaire ne peut rester vide au sein de la liste daffichage. Pour comprendre le concept deffondrement des objets daffichage prenons un exemple de la vie courante, une pile dassiettes. Si nous retirons une assiette situe en bas de la pile, les assiettes situes au dessus de celle-ci descendront dun niveau. Au sein de la liste daffichage, les objets daffichage fonctionnent de la mme manire. Lorsque nous supprimons un objet daffichage, les objets situs au dessus descendent alors dun niveau. On dit que les objets daffichage seffondrent. De cette manire aucune profondeur entre deux objets daffichage ne peut demeurer inoccupe. En prenant en considration cette nouvelle notion, relisons notre code cens supprimer la totalit des objets enfants contenus dans un DisplayObjectContainer :
var lng:int = numChildren;

25 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

for ( var i:int = 0; i< lng; i++ ) { removeChildAt ( i ); }

A chaque itration la mthode removeChildAt supprime un objet enfant, faisant descendre dun index tous les objets enfants suprieurs. Lobjet en dbut de pile tant supprim, le suivant devient alors le premier de la liste dobjets enfants, le mme processus se rpte alors pour chaque itration. En milieu de boucle, notre index pointe donc vers un index inoccup levant une erreur de type RangeError. En commenant par le haut de la pile nous nentranons aucun effondrement de pile. Nous pouvons supprimer chaque objet de la liste :
var lng:int = numChildren-1; for ( var i:int = lng; i>= 0; i-- ) { removeChildAt ( i ); } // affiche : 0 trace( numChildren );

La variable lng stocke lindex du dernier objet enfant, puis nous supprimons chaque objet enfant en commenant par le haut de la pile puis en descendant pour chaque itration. Cette technique fonctionne sans problme, mais une approche plus concise et plus lgante existe. Ne considrons pas leffondrement automatique comme un inconvnient mais plutt comme un avantage, nous pouvons crire :
while ( numChildren > 0 ) { removeChildAt ( 0 ); } // affiche : 0 trace( numChildren );

26 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Nous supprimons chaque itration lobjet graphique positionn lindex 0. Du fait de leffondrement, chaque objet enfant passera forcment par cet index.

A retenir
La suppression dun DisplayObject au sein de la liste daffichage provoque un effondrement des profondeurs. Leffondrement des profondeurs permet de ne pas laisser de profondeurs intermdiaires inoccupes entre les objets graphiques.

Gestion de lempilement des objets daffichage


Pour modifier la superposition dobjets enfants au sein dun DisplayObjectContainer, nous disposons de la mthode setChildIndex dont voici la signature :
public function setChildIndex(child:DisplayObject, index:int):void

La mthode setChildIndex est appele sur le conteneur des objets enfants manipuler. Si lindex pass est ngatif ou ne correspond aucun index occup, son appel lve une exception de type RangeError. Pour bien comprendre comment fonctionne cette mthode, passons un peu de pratique. Ouvrez un nouveau document Flash CS3 et crez deux clips de formes diffrentes. Superposez-les comme lillustre la figure 3.6

Figure 3-6. Clips superposs Nous donnerons comme noms doccurrence monRond et monRectangle. Nous souhaitons faire passer le clip monRond devant le clip monRectangle. Nous pouvons en dduire facilement que ce dernier est positionn lindex 1, et le premier lindex 0.

27 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Pour placer le rond lindex 1 nous le passons la mthode setChildIndex tout en prcisant lindex de destination. Sur un calque AS, nous dfinissons le code suivant :
setChildIndex( monRond, 1 );

Le rond est supprim temporairement de la liste faisant chuter notre rectangle lindex 0 pour laisser place au rond lindex 1. Ce dernier est donc plac en premier plan comme lillustre la figure 3.7

Figure 3-7. Changement de profondeurs. Le changement dindex dun DisplayObject est divis en plusieurs phases. Prenons lexemple de huit objets graphiques. Nous souhaitons passer le DisplayObject de lindex 1 lindex 6. La figure 3-8 illustre lide :

Figure 3-8. Changement dindex pour un DisplayObject. Si nous ne connaissons pas le nom des objets graphiques, nous pouvons crire :
setChildIndex ( getChildAt ( 1 ), 6 );

Nous rcuprons le DisplayObject lindex 1 pour le passer lindex 6. Lorsque ce dernier est retir de son index dorigine, les 28 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

DisplayObject suprieurs vont alors descendre dun index.

Exactement comme si nous retirions une assiette dune pile dassiettes. Il sagit du comportement que nous avons voqu prcdemment appel effondrement des profondeurs.

La figure 3-9 illustre le rsultat du changement dindex du DisplayObject :

Figure 3-9. Effondrement des profondeurs.


DisplayObject de lindex 5 lindex 0.

Prenons une autre situation. Cette fois nous souhaitons dplacer un

La figure 3-10 illustre lide :

Figure 3-10. Dplacement vers le bas de la pile. Pour procder ce dplacement au sein de la liste dobjets enfants nous pouvons crire :
setChildIndex ( monDisplayObject, 0 );

Lorsquun DisplayObject est dplac un index infrieur son index dorigine, les objets graphiques intermdiaires ne seffondrent pas mais remontent dun index. Le code prcdent provoque donc lorganisation prsente par la figure 3-11 :

29 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Figure 3-11. Objets graphiques pousss dun index vers le haut de la pile. Lors de lutilisation de la mthode setChildIndex les objets graphiques intermdiaires compris entre lindex dorigine et darrive peuvent tre pousss ou descendus dun niveau.

Echange de profondeurs
Dans certaines situations nous pouvons avoir besoin dintervertir lordre dempilement de deux objets graphiques. Pour cela nous utiliserons les mthodes swapChildren ou swapChildrenAt. Nous allons nous attarder sur la premire dont voici la signature :
public function swapChildren(child1:DisplayObject, child2:DisplayObject):void

La mthode swapChildren accepte deux paramtres de type DisplayObject. Contrairement la mthode setChildIndex, les mthodes swapChildren et swapChildrenAt ne modifient pas lindex des autres objets graphiques lors de lchange de profondeur. Nous appelons la mthode swapChildren sur notre scnario principal qui contient nos deux objets graphiques. Le code suivant procde lchange des profondeurs :
swapChildren ( monRond, monRectangle );

Si nous testons notre animation, nous obtenons laffichage illustr par la figure 3-12.

30 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Figure 3-12. Echange des profondeurs. Un appel successif de la mthode swapChildren intervertit chaque appel les deux DisplayObject. Si nous rajoutons un appel la mthode swapChildren nous obtenons laffichage de dpart. Sur notre calque, nous rajoutons un deuxime appel la mthode swapChildren :
swapChildren ( monRond, monRectangle ); swapChildren ( monRond, monRectangle );

Nous obtenons lorganisation de dpart comme lillustre la figure 313 :

Figure 3-13. Organisation de dpart. Lorsque nous navons pas de rfrence aux DisplayObject intervertir nous pouvons utiliser la mthode swapChildrenAt. Cette dernire accepte deux index en paramtres :
public function swapChildrenAt(index1:int, index2:int):void

Si lindex est ngatif ou nexiste pas dans la liste denfants, lappel de la mthode lvera une exception de type RangeError. Nous pouvons ainsi arriver au mme rsultat que notre exemple prcdent sans spcifier de nom doccurrence mais directement lindex des DisplayObject :
swapChildrenAt ( 1, 0 );

Si nous avions seulement une rfrence et un index nous pourrions obtenir le mme rsultat laide des diffrentes mthodes que nous avons abordes :
swapChildrenAt ( getChildIndex (monRectangle), 0 );

Ou encore, mme si lintrt est moins justifi :

31 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

swapChildren ( getChildAt (1), getChildAt (0) );

A retenir

Pour changer lordre dempilement des DisplayObject au sein de la liste dobjets enfants, nous utilisons la mthode setChildIndex. La mthode setChildIndex pousse dun index vers le haut ou vers le bas les autres objets graphiques de la liste denfants.

Pour intervertir lordre dempilement de deux DisplayObject, les mthodes swapChildren et swapChildrenAt seront utilises. Les mthodes swapChildren et swapChildrenAt ne modifient pas lordre dempilements des autres objets graphiques de la liste dobjets enfants.

Dsactivation des objets graphiques


Lors du dveloppement dapplications ActionScript 3, il est essentiel de prendre en considration la dsactivation des objets graphiques. Comme nous lavons vu prcdemment, lorsquun objet graphique est supprim de la liste daffichage, ce dernier continue de vivre . Afin de dsactiver un objet graphique supprim de la liste daffichage, nous utilisons les deux vnements suivants :
Event.ADDED_TO_STAGE : diffus lorsquun objet graphique est affich. Event.REMOVED_FROM_STAGE : diffus lorsquun objet graphique est supprim de laffichage.

Ces deux vnements extrmement utiles sont diffuss automatiquement lorsque le lecteur affiche ou supprime de laffichage un objet graphique. Dans le code suivant, la dsactivation de lobjet graphique nest pas gre, lorsque le MovieClip est supprim de laffichage lvnement Event.ENTER_FRAME est toujours diffus :
var monClip:MovieClip = new MovieClip(); monClip.addEventListener( Event.ENTER_FRAME, ecouteur ); addChild ( monClip ); function ecouteur ( pEvt:Event ):void { trace("excution");

32 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

} removeChild ( monClip );

En prenant en considration la dsactivation de lobjet graphique, nous coutons lvnement Event.REMOVED_FROM_STAGE afin de supprimer lcoute de lvnement Event.ENTER_FRAME lorsque lobjet est supprim de laffichage :
var monClip:MovieClip = new MovieClip(); monClip.addEventListener( Event.ENTER_FRAME, ecouteur ); // coute de l'vnement Event.REMOVED_FROM_STAGE monClip.addEventListener( Event.REMOVED_FROM_STAGE, desactivation ); addChild ( monClip ); function ecouteur ( pEvt:Event ):void { trace("excution"); } function desactivation ( pEvt:Event ):void { // suppression de l'coute de l'vnement Event.ENTER_FRAME pEvt.target.removeEventListener ( Event.ENTER_FRAME, ecouteur ); } stage.addEventListener ( MouseEvent.CLICK, supprimeAffichage ); function supprimeAffichage ( pEvt:MouseEvent ):void { // lors de la suppression de l'objet graphique // l'vnement Event.REMOVED_FROM_STAGE est automatiquement // diffus par l'objet removeChild ( monClip ); }

De cette manire, lorsque lobjet graphique nest pas affich, ce dernier ne consomme quasiment aucune ressource car nous avons pris le soin de supprimer lcoute de tous les vnements consommateurs des ressources. A linverse, nous pouvons couter lvnement Event.ADDED_TO_STAGE pour ainsi grer lactivation et la dsactivation de lobjet graphique :
var monClip:MovieClip = new MovieClip();

33 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

// coute de l'vnement Event.REMOVED_FROM_STAGE monClip.addEventListener( Event.REMOVED_FROM_STAGE, desactivation ); // coute de l'vnement Event.ADDED_TO_STAGE monClip.addEventListener( Event.ADDED_TO_STAGE, activation ); addChild ( monClip ); function ecouteur ( pEvt:Event ):void { trace("excution"); } function activation ( pEvt:Event ):void { // coute de l'vnement Event.ENTER_FRAME pEvt.target.addEventListener ( Event.ENTER_FRAME, ecouteur ); } function desactivation ( pEvt:Event ):void { // suppression de l'coute de l'vnement Event.ENTER_FRAME pEvt.target.removeEventListener ( Event.ENTER_FRAME, ecouteur ); } stage.addEventListener ( MouseEvent.CLICK, supprimeAffichage ); function supprimeAffichage ( pEvt:MouseEvent ):void { // // // if lors de la suppression de l'objet graphique l'vnement Event.REMOVED_FROM_STAGE est automatiquement diffus par l'objet ( contains ( monClip ) ) removeChild ( monClip );

// lors de l'affichage de l'objet graphique // l'vnement Event.ADDED_TO_STAGE est automatiquement // diffus par l'objet else addChild ( monClip ); }

Au sein de la fonction couteur supprimeAffichage, nous testons si le scnario contient le clip monClip laide de la mthode contains. Si ce nest pas le cas nous laffichons, dans le cas inverse nous le supprimons de laffichage. Nous reviendrons sur le concept dactivation et dsactivation des objets graphiques tout au long de louvrage.

34 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Afin de totalement dsactiver notre MovieClip nous devons maintenant supprimer les rfrences pointant vers lui. Pour cela, nous modifions la fonction supprimeAffichage :
function supprimeAffichage ( pEvt:MouseEvent ):void { // lors de la suppression de l'objet graphique // l'vnement Event.REMOVED_FROM_STAGE est automatiquement // diffus par l'objet, l'objet est dsactiv removeChild ( monClip ); // suppression de la rfrence pointant vers le MovieClip monClip = null; }

Certains dentre vous peuvent se demander lintrt de ces deux vnements par rapport une simple fonction personnalise qui se chargerait de dsactiver lobjet graphique. Au sein dune application, une multitude de fonctions ou mthodes peuvent entraner la suppression dun objet graphique. Grce ces deux vnements, nous savons que, quelque soit la manire dont lobjet est supprim ou affich, nous le saurons et nous pourrons grer son activation et sa dsactivation. Nous allons nous attarder au cours de cette nouvelle partie, au comportement de la tte de lecture afin de saisir tout lintrt des deux vnements que nous venons daborder.

Fonctionnement de la tte de lecture


D au nouveau mcanisme de liste daffichage du lecteur Flash 9. Le fonctionnement interne de la tte de lecture a lui aussi t modifi. Ainsi lorsque la tte de lecture rencontre une image ayant des objets graphiques celle-ci ajoute les objets graphiques laide de la mthode addChild :

Figure 3-14. Ajout dobjets graphiques.

35 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

A linverse lorsque la tte de lecture rencontre une image cl vide, le lecteur supprime les objets graphiques laide de la mthode removeChild :

Figure 3-15. Suppression dobjets graphiques. De par la suppression ou ajout des objets graphiques, ces derniers diffusent automatiquement les vnements Event.ADDED_TO_STAGE et Event.REMOVED_FROM_STAGE. Nous reviendrons sur ce mcanisme dactivation et dsactivation des objets graphiques au cours du chapitre 9 intitul Etendre les classes natives.

A retenir
Il est fortement recommand de grer lactivation et la dsactivation des objets graphiques. Pour cela, nous utilisons les vnements Event.ADDED_TO_STAGE et Event.REMOVED_FROM_STAGE.

Si de nouveaux objets sont placs sur une image cl, la tte de lecture les ajoute laide de la mthode addChild.

Si la tte de lecture rencontre une image cl vide, le lecteur supprime automatiquement les objets graphiques laide de la mthode removeChild.

Subtilits de la proprit stage


Nous allons revenir dans un premier temps sur la proprit stage dfinie par la classe DisplayObject. Nous avons appris prcdemment que lobjet Stage nest plus accessible de manire globale comme ctait le cas en ActionScript 1 et 2. Pour accder lobjet Stage nous ciblons la proprit stage sur nimporte quel DisplayObject :
monDisplayObject.stage

36 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

La subtilit rside dans le fait que ce DisplayObject doit obligatoirement tre ajout la liste daffichage afin de pouvoir retourner une rfrence lobjet Stage. Ainsi, si nous ciblons la proprit stage sur un DisplayObject non prsent dans la liste daffichage, celle-ci nous renvoie null :
var monClip:MovieClip = new MovieClip(); // affiche : null trace( monClip.stage );

Une fois notre clip ajout la liste daffichage, la proprit stage contient une rfrence lobjet Stage :
var monClip:MovieClip = new MovieClip(); addChild ( monClip ); // affiche : [object Stage] trace( monClip.stage );

Grce lvnement Event.ADDED_TO_STAGE, nous pouvons accder de manire scurise lobjet Stage, car la diffusion de cet vnement nous garantit que lobjet graphique est prsent au sein de la liste daffichage.

Figure 3-16. Objet Stage. Contrairement aux scnarios qui peuvent tre multiples dans le cas de multiples SWF, lobjet Stage est unique.

37 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Afin de tester si un objet graphique est affich actuellement, il suffit donc de tester si sa proprit renvoie null ou non.

A retenir
La proprit stage dun DisplayObject renvoie null, tant que ce dernier nest pas plac au sein de la liste daffichage.

Subtilits de la proprit root


Les dveloppeurs ActionScript 1 et 2 se souviennent surement de la proprit _root. En ActionScript 3 son fonctionnement a t entirement revu, rendant son utilisation totalement scurise. Souvenez vous, la proprit root tait accessible de manire globale et permettait de rfrencer rapidement le scnario principal dune animation. Son utilisation tait fortement dconseille pour des raisons de portabilit de lapplication dveloppe. Prenons un cas typique : nous dveloppions une animation en ActionScript 1 ou 2 qui tait plus tard charge dans un autre SWF. Si nous ciblions notre scnario principal laide de _root, une fois lanimation charge par le SWF, _root pointait vers le scnario du SWF chargeur et non plus vers le scnario de notre SWF charg. Cest pour cette raison quil tait conseill de toujours travailler par ciblage relatif laide de la proprit _parent et non par ciblage absolu avec la proprit _root. Comme pour la proprit stage, la proprit root renvoie null tant que le DisplayObject na pas t ajout la liste daffichage.
var monClip:MovieClip = new MovieClip(); // affiche : null trace( monClip.root );

Pour que la proprit root pointe vers le scnario auquel le DisplayObject est rattach il faut imprativement lajouter la liste daffichage. Si lobjet graphique est un enfant du scnario principal, c'est--dire contenu par lobjet MainTimeline, alors sa proprit root pointe vers ce dernier. Le code suivant est dfini sur le scnario principal :
var monClip:MovieClip = new MovieClip(); // le clip monClip est ajout au scnario principal addChild ( monClip );

38 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

// affiche : [object MainTimeline] trace( monClip.root );

Si lobjet graphique nest pas un enfant du scnario principal, mais un enfant de lobjet Stage, sa proprit root renvoie une rfrence vers ce dernier :
var monClip:MovieClip = new MovieClip(); // le clip monClip est ajout l'objet Stage stage.addChild ( monClip ); // affiche : [object Stage] trace( monClip.root );

Ainsi, lorsque lobjet graphique nest pas un enfant du scnario principal, les proprits root et stage sont quivalentes car pointent toutes deux vers lobjet Stage. Lusage de la proprit root en ActionScript 3 est tout fait justifi et ne pose aucun problme de ciblage comme ctait le cas en ActionScript 1 et 2. En ActionScript 3, la proprit root rfrence le scnario principal du SWF en cours. Mme dans le cas de chargement externe, lorsquun SWF est charg au sein dun autre, nous pouvons cibler la proprit root sans aucun problme, nous ferons toujours rfrence au scnario du SWF en cours. La figure 3-17 illustre lide :

Figure 3-17. Scnarios. 39 / 41


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Nous reviendrons sur ce cas plus tard au cours du chapitre 13 intitul Chargement de contenu externe.

A retenir
La proprit root dun DisplayObject renvoie null, tant que ce dernier nest pas plac au sein de la liste daffichage. Lorsque lobjet graphique nest pas un enfant du scnario principal, les proprits root et stage renvoient toutes deux une rfrence lobjet Stage. En ActionScript 3 lusage de la proprit root ne souffre pas des faiblesses existantes dans les prcdentes versions dActionScript. La proprit root rfrence le scnario du SWF en cours.

Les _level
En ActionScript 3 la notion de _level nest plus disponible, souvenez vous, en ActionScript 1 et 2 nous pouvions crire :
_level0.createTextField ( "monChampTexte", 0, 0, 0, 150, 25 );

Ce code cre un champ texte au sein du _level0. En ActionScript 3 si nous tentons daccder lobjet _level, nous obtenons une erreur la compilation :
var monChampTexte:TextField = new TextField(); monChampTexte.text = "Bonjour !"; _level0.addChild ( monChampTexte );

Affiche dans la fentre de sortie :


1120: Accs la proprit non dfinie _level0.

Il nexiste pas en ActionScript 3 dobjet similaire aux _level que nous connaissions. En revanche le mme rsultat est facilement ralisable en ActionScript 3. En ajoutant nos objets graphiques lobjet Stage plutt qu notre scnario, nous obtenons le mme rsultat :
var monChampTexte:TextField = new TextField(); monChampTexte.text = "Bonjour !"; stage.addChild ( monChampTexte );

Le code prcdent dfinit la liste daffichage illustre par la figure suivante :

40 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 4 La liste daffichage version 0.1.2

Figure 3-17. Simulation de _level. Notre champ texte est ici un enfant direct de lobjet Stage, nous obtenons donc le mme concept dempilement de scnario tablit par les _level. Dans cette situation, lobjet Stage contient alors deux enfants directs :
var monChampTexte:TextField = new TextField(); monChampTexte.text = "Bonjour !"; stage.addChild ( monChampTexte ); // affiche : 2 trace ( stage.numChildren );

Mme si cette technique nous permet de simuler la notion de _level en ActionScript 3, son utilisation nest pas recommande sauf cas spcifique, comme le cas dune fentre dalerte qui devrait tre affiche au dessus de tous les lments de notre application.

A retenir
En ActionScript 3, la notion de _level nexiste plus.

41 / 41
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

5
Symboles prdfinis

LES TYPES DE SYMBOLE ................................................................................... 1 LE SYMBOLE CLIP.................................................................................................... 2 LA PROPRIT NAME ............................................................................................... 4 INSTANCIATION DE SYMBOLES PAR PROGRAMMATION ...................... 6 INSTANCIATION DE SYMBOLES PRDFINIS ............................................................. 8 EXTRAIRE UNE CLASSE DYNAMIQUEMENT ............................................................ 13 LE SYMBOLE BOUTON ........................................................................................... 14 LE SYMBOLE BOUTON ........................................................................................... 21 LE SYMBOLE GRAPHIQUE ...................................................................................... 22 LES IMAGES BITMAP .............................................................................................. 24 AFFICHER UNE IMAGE BITMAP .................................................................... 26 LE SYMBOLE SPRITE ........................................................................................ 28 DEFINITION DU CODE DANS UN SYMBOLE ............................................... 30

Les types de symbole


Durant nos dveloppements ActionScript nous avons trs souvent besoin dutiliser des symboles prdfinis. Crs depuis lenvironnement auteur de Flash, un logo, une animation, ou encore une image pourront tre stocks au sein de la bibliothque pour une utilisation future durant lexcution de lapplication. Pour convertir un objet graphique en symbole, nous slectionnons ce dernier et appuyons sur F8. Le panneau convertir en symbole souvre, comme lillustre la figure 5.1 :

1 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-1. Panneau Convertir en symbole. Trois types de symboles sont proposs :
Clip Bouton Graphique

Le type bitmap existe aussi pour les symboles, mais napparait pas ici car il est impossible de crer un bitmap depuis le panneau Convertir en symbole. Seules les images importes dans la bibliothque sont intgres en tant que type bitmap. Le symbole clip savre tre le plus courant dans les dveloppements, nous allons commencer par ce dernier en dcouvrant les nouveauts apportes par ActionScript 3.

Le symbole clip
Le symbole clip est sans doute lobjet graphique le plus utilis dans les animations Flash. Si son fonctionnement na pas chang en ActionScript 3, son instanciation et sa manipulation par programmation a t entirement revue. Dans un nouveau document Flash CS3 nous crons un symbole de type clip que nous appelons Balle. Celui-ci est aussitt ajout la bibliothque, comme lillustre la figure 5-2 :

2 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-2. Symbole clip en bibliothque. En posant une occurrence de ce dernier sur la scne nous avons la possibilit de lui affecter travers linspecteur de proprits un nom doccurrence comme lillustre la figure 5-3 :

3 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-3. Occurrence du symbole clip sur la scne principale. Aussitt le nom doccurrence affect, Flash ajoutera au scnario concern une proprit du mme nom pointant vers loccurrence. Le code suivant nous permet daccder cette dernire :
// affiche : [object MovieClip] trace( maBalle );

Si nous dfinissons une variable portant le mme nom quun nom doccurrence, une erreur la compilation est gnre nous indiquant un conflit de variables. Cette scurit vite de dfinir une variable portant le mme nom quune occurrence ce qui provoquait avec les prcdentes versions dActionScript un crasement de variables difficile dboguer. Prenons le cas suivant : au sein dune animation ActionScript 1 ou 2 un clip pos sur la scne possdait monMc comme nom doccurrence. Le code suivant retournait une rfrence vers ce dernier :
// affiche : _level0.monMc trace( monMc );

Si une variable du mme nom tait dfinie, laccs notre clip tait perdu :
var monMc:String = "bob"; // affiche : bob trace( monMc );

En ActionScript 3 cette situation ne peut pas se produire grce la gestion des conflits de variables la compilation. Ici, nous dfinissons une variable appele maBalle sur le mme scnario que notre occurrence :
var maBalle:MovieClip;

Lerreur la compilation suivante est gnre :


1151: Conflit dans la dfinition maBalle dans l'espace de nom internal.

Nous allons nous intresser prsent quelques subtilits lies la proprit name de la classe flash.display.DisplayObject.

La proprit name
Lors du chapitre 3 intitul La liste daffichage nous avons vu que la proprit name dun DisplayObject pouvait tre modifie dynamiquement. 4 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Une autre nouveaut dActionScript 3 intervient dans la manipulation de la proprit name des occurrences de symboles places depuis lenvironnement auteur de Flash CS3. En ActionScript 1 et 2 nous pouvions modifier dynamiquement le nom dun objet graphique grce la proprit _name. En procdant ainsi nous changions en mme temps la variable permettant de le cibler. Dans lexemple suivant, un clip possdait monMc comme nom doccurrence :
// affiche : monMc trace( monMc._name ) ; monMc._name = "monNouveauNom"; // affiche : _level0.monNouveauNom trace( monNouveauNom ); // affiche : undefined trace( monMc );

En ActionScript 3, cela nest pas possible pour les occurrences de symboles places sur la scne manuellement. Seuls les symboles graphiques crs par programmation permettent la modification de leur proprit name. Dans le code suivant nous tentons de modifier la proprit name dune occurrence de symbole pose manuellement :
// affiche : maBalle trace( maBalle.name ); maBalle.name = "monNouveauNom";

Ce qui gnre lerreur suivante lexcution :


Error: Error #2078: Impossible de modifier la proprit de nom d'un objet plac sur le scnario.

La liaison qui existait en ActionScript 1 et 2 entre la proprit _name et laccs loccurrence nest plus valable en ActionScript 3. Si nous crons un Sprite en ActionScript 3, et que nous lui affectons un nom par la proprit name, aucune variable du mme nom nest cre pour y accder :
var monSprite:Sprite = new Sprite; monSprite.name = "monNomOccurrence"; trace( monNomOccurrence );

Lerreur la compilation suivante est gnre : 5 / 31


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

1120: Accs la proprit non dfinie monNomOccurrence.

Pour y accder par ce nom, nous utilisons la mthode getChildByName dfinie par la classe flash.display.DisplayObjectContainer.
var monSprite:Sprite = new Sprite; monSprite.name = "monNomOccurrence"; addChild ( monSprite ); // affiche : [object Sprite] trace( getChildByName ("monNomOccurrence") );

Une autre nouveaut dActionScript 3 concerne la suppression des objets graphiques poss depuis lenvironnement auteur. En ActionScript 1 et 2 il tait normalement impossible de supprimer de laffichage ces derniers. Les dveloppeurs devaient utiliser une astuce consistant passer une profondeur positive lobjet avant dappeler une mthode comme removeMovieClip. En ActionScript 3 nous pouvons supprimer tous les objets graphiques poss en dur sur la scne laide de la mthode removeChild. Le code suivant supprime notre occurrence de Balle pose manuellement :
removeChild ( maBalle );

Alors que les graphistes se contenteront danimer et manipuler des occurrences de symboles manuellement, un dveloppeur voudra manipuler par programmation les symboles prsents au sein de la bibliothque. Dcouvrons ensemble le nouveau mcanisme apport par ActionScript 3.

A retenir
Il est impossible de modifier la proprit name dun objet graphique plac manuellement sur la scne.

Instanciation de symboles par programmation


Comme en ActionScript 1 et 2, un symbole clip ne peut tre attach dynamiquement par programmation si loption Exporter pour ActionScript du panneau Proprits de liaison nest pas active. Notons quen ralit nous ne donnons plus de nom de liaison au clip en ActionScript 3, mais nous spcifions dsormais un nom de classe. 6 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

En mode de publication ActionScript 3, le champ Identifiant est gris, nous ne lutiliserons plus. En slectionnant notre symbole Balle dans la bibliothque nous choisissons loption Liaison, puis nous cochons la case Exporter pour ActionScript. La figure 5-4 illustre le panneau Proprits de Liaison :

Figure 5-4. Panneau Proprits de liaison. Le champ Classe de base indique la classe dont notre symbole hritera, une fois loption Exporter pour ActionScript coche, ce dernier est renseign automatiquement. Notre symbole Balle est un clip et hrite donc de la classe flash.display.MovieClip. Le champ Classe contient par dfaut le nom du symbole en bibliothque. La grande nouveaut ici, est la possibilit de dfinir un nom de classe associe au symbole. Nous conservons Balle. En cliquant sur OK pour valider, Flash CS3 recherche aussitt une classe appele Balle proximit de notre fichier .fla. Sil ne trouve aucune classe de ce nom il en gnre une automatiquement. Le panneau de la figure 5-5 nous rapporte cette information :

7 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-5. Panneau de gnration automatique de classe. Attention, cette classe ne sera pas accessible pour ldition. Elle sera utilise en interne par le compilateur pour linstanciation de notre symbole.

Instanciation de symboles prdfinis


En ActionScript 1 et 2 la mthode attachMovie permettait dattacher des symboles par programmation. Ds le dpart cette mthode ntait pas trs simple apprhender, ses nombreux paramtres rendaient sa mmorisation difficile. De plus, nous tions obligs dappeler cette mthode sur une occurrence de MovieClip, nous forant conserver une rfrence un clip pour pouvoir instancier dautres clips issus de la bibliothque. En ActionScript 3 le mot-cl new nous permet aussi dinstancier des symboles prsents dans la bibliothque et offre donc beaucoup plus de souplesse que la mthode attachMovie. De nimporte o, sans aucune rfrence un MovieClip existant, nous pouvons crire le code suivant pour instancier notre symbole Balle :
var maBalle:MovieClip = new Balle();

Lutilisation du mot cl new pour linstanciation des objets graphiques et plus particulirement des symboles rsout une autre grande faiblesse dActionScript 2 que nous traiterons plus tard dans le chapitre 9 intitul Etendre les classes natives. Nous avons utilis le type MovieClip pour stocker la rfrence notre occurrence de symbole Balle, alors que ce symbole possde un type spcifique que nous avons renseign travers le panneau Proprits de liaison.

8 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Dans la ligne ci-dessous, nous typons la variable laide du type Balle :


var maBalle:Balle = new Balle();

En testant le type de notre clip Balle nous voyons que ce dernier possde plusieurs types communs :
var maBalle:Balle = new Balle(); // affiche : true trace( maBalle is MovieClip ); // affiche : true trace( maBalle is Balle );

Souvenons-nous que notre objet graphique est, pour le moment hors de la liste daffichage. Pour le voir nous ajoutons notre symbole notre scnario principal laide de la mthode addChild :
var maBalle:Balle = new Balle(); addChild ( maBalle );

Notre symbole est positionn en coordonnes 0 pour laxe des x et 0 pour laxe des y. Notre instance de la classe Balle est donc constitue dune enveloppe MovieClip contenant notre forme vectorielle, ici de forme circulaire. Il parait donc logique que cette enveloppe contienne un objet graphique enfant de type flash.display.Shape. Le code suivant rcupre le premier objet enfant de notre clip Balle laide de la mthode getChildAt :
var maBalle:Balle = new Balle(); addChild (maBalle); // affiche : [object Shape] trace( maBalle.getChildAt ( 0 ) );

Nous pourrions supprimer le contenu de notre clip avec la mthode removeChildAt :


var maBalle:Balle = new Balle(); addChild (maBalle); // affiche : [object Shape] trace( maBalle.removeChildAt ( 0 ) );

Comme nous le dcouvrons depuis le dbut de cet ouvrage, ActionScript 3 offre une souplesse sans prcdent pour la manipulation des objets graphiques.

9 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Il serait intressant dinstancier plusieurs objets Balle et de les positionner alatoirement sur la scne avec une taille diffrente. Pour cela, nous allons au sein dune boucle for crer de multiples instances de la classe Balle et les ajouter la liste daffichage :
var maBalle:Balle; for ( var i:int = 0; i< 10; i++ ) { maBalle = new Balle(); addChild( maBalle ); }

Si nous testons le code prcdent nous obtenons dix instances de notre classe Balle positionnes en 0,0 sur notre scne. Il sagit dun comportement qui a toujours exist dans le lecteur Flash. Tous les objets affichs sont positionns par dfaut en coordonnes 0 pour les axes x et y. Nous allons les positionner alatoirement sur la scne laide de la mthode Math.random(). Nous devons donc rcuprer la taille totale de la scne pour savoir sur quelle amplitude gnrer notre valeur alatoire pour les axes x et y. En ActionScript 1 et 2 nous aurions crit :
maBalle._x = Math.random()*Stage.width; maBalle._y = Math.random()*Stage.height;

En

1 et 2, les proprits Stage.width et Stage.height nous renvoyaient la taille de la scne. En ActionScript 3 lobjet Stage possde quatre proprits relatives sa taille, ce qui peut paratre relativement droutant. Les deux proprits width et height existent toujours mais renvoient la largeur et hauteur occupe par lensemble des DisplayObject contenus par lobjet Stage. La figure 5-6 illustre lide :

ActionScript

10 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-6. Comportement des proprits stage.width et stage.height. Ainsi dans un SWF vide, les proprits stage.width et stage.height renvoient 0. La figure 5-6 montre une animation o trois instances du symbole Balle sont poses sur la scne. Les proprits stage.width et stage.height renvoient la surface occupe par les objets graphiques prsents dans la liste daffichage. Pour rcuprer la taille totale de la scne et non la surface occupe par les objets graphiques nous utilisons les proprits stage.stageWidth et stage.stageHeight :
// affiche : 550 trace( stage.stageWidth ); // affiche : 400 trace( stage.stageHeight );

En gnrant une valeur alatoire sur la largueur et la hauteur totale nous positionnons alatoirement nos instances du symbole Balle :
var maBalle:Balle; for ( var i:int = 0; i< 10; i++ ) {

11 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

maBalle = new Balle(); maBalle.x = Math.random()*stage.stageWidth; maBalle.y = Math.random()*stage.stageHeight; addChild( maBalle ); }

Le code gnre lanimation suivante :

Figure 5-7. Positionnement alatoire des instances de la classe Balle. Si nous ne souhaitons pas voir nos occurrences sortir de la scne, nous allons intgrer une contrainte en gnrant un alatoire prenant en considration la taille des occurrences.
var maBalle:Balle; for ( var i:int = 0; i< 10; i++ ) { maBalle = new Balle(); maBalle.x = Math.random()*(stage.stageWidth - maBalle.width); maBalle.y = Math.random()*(stage.stageHeight - maBalle.height); addChild( maBalle ); }

La figure 5-8 illustre le rsultat :

12 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-8. Positionnement alatoire sans dbordement des instances de la classe Balle.

A retenir
Les proprits stage.width et stage.height renvoient la taille occupe par les DisplayObject prsent au sein de la liste daffichage. Pour rcuprer les dimensions de la scne, nous utilisons les proprits stage.stageWidth et stage.stageHeight. Pour instancier un symbole prdfini, nous utilisons le mot-cl new.

Extraire une classe dynamiquement


Dans les prcdentes versions dActionScript, nous pouvions stocker les noms de liaisons de symboles au sein dun tableau, puis boucler sur ce dernier afin dattacher les objets graphiques :
// tableau contenant les identifiants de liaison des symboles var tableauLiaisons:Array = ["polygone", "balle", "polygone", "carre", "polygone", "carre", "carre"]; var lng:Number = tableauLiaisons.length; var ref:MovieClip; for ( var i:Number = 0; i< lng; i++ ) { // affichage des symboles ref = this.attachMovie ( tableauLiaisons[i], tableauLiaisons[i] + i, i );

13 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

De par lutilisation du mot-cl new afin dinstancier les objets graphiques, nous ne pouvons plus instancier un objet graphique laide dune simple chane de caractres. Pour raliser le code quivalent en ActionScript 3, nous devons au pralable extraire une dfinition de classe, puis instancier cette dfinition. Pour cela nous utilisons la fonction
flash.utils.getDefinitionByName :
// tableau contenant le noms des classes var tableauLiaisons:Array = ["Polygone", "Balle", "Polygone", "Carre", "Polygone", "Carre", "Carre"]; var lng:Number = tableauLiaisons.length; var Reference:Class; for ( var i:Number = 0; i< lng; i++ ) { // extraction des rfrences de classe Reference = Class ( getDefinitionByName ( tableauLiaisons[i] ) ); // instanciation var instance:DisplayObject = DisplayObject ( new Reference() ); // ajout la liste d'affichage addChild ( instance ); }

Cette fonctionnalit permet lvaluation du nom de la classe extraire de manire dynamique. Nous pourrions imaginer un fichier XML contenant le nom des diffrentes classes instancier. Un simple fichier XML pourrait ainsi dcrire toute une interface graphique en spcifiant les objets graphiques devant tres instancis.

A retenir
La fonction getDefinitionByName permet dextraire une dfinition de classe de manire dynamique.

Le symbole bouton
Lorsque nous crons un bouton dans lenvironnement auteur, celui-ci nest plus de type Button comme en ActionScript 1 et 2 mais de type flash.display.SimpleButton.

14 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Il est tout fait possible de crer et denrichir graphiquement des boutons par programmation, chose impossible avec les prcdents lecteurs Flash. Nous reviendrons sur cette fonctionnalit dans le prochain chapitre 7 intitul Interactivit. Nous allons crer un bouton dans le document en cours, puis poser une occurrence de ce dernier sur la scne principale. Dans notre exemple le bouton est de forme rectangulaire, illustre en figure 5-9 :

Figure 5-9. Bouton pos sur la scne. Nous lui donnons monBouton comme nom doccurrence puis nous testons le code suivant :
// affiche : [object SimpleButton] trace( monBouton );

En testant notre animation nous remarquons que notre bouton monBouton affiche un curseur reprsentant un objet cliquable lors du survol. Pour dclencher une action spcifique lorsque nous cliquons dessus, nous utilisons le modle vnementiel que nous avons dcouvert ensemble au cours du chapitre 3 intitul Le modle vnementiel. Lvnement diffus par lobjet SimpleButton lorsquun clic est dtect est lvnement MouseEvent.CLICK. Nous souscrivons une fonction couteur auprs de ce dernier en ciblant la classe flash.events.MouseEvent :
monBouton.addEventListener( MouseEvent.CLICK, clicBouton );

15 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

function clicBouton ( pEvt:MouseEvent ):void { // affiche : [object SimpleButton] trace( pEvt.target ); }

Notre fonction clicBouton sexcute chaque clic effectu sur notre bouton monBouton. La proprit target de lobjet vnementiel diffus nous renvoie une rfrence lobjet auteur de lvnement, ici notre bouton. Afin dattacher dynamiquement nos instances du symbole Balle lors du clic sur notre bouton monBouton, nous plaons au sein de la fonction clicBouton le processus dinstanciation auparavant cr :
monBouton.addEventListener( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { var maBalle:Balle for ( var i:int = 0; i< 10; i++ ) { maBalle = new Balle(); maBalle.x = Math.random()*(stage.stageWidth - maBalle.width); maBalle.y = Math.random()*(stage.stageHeight - maBalle.height); addChild ( maBalle ); } }

Au clic souris sur notre bouton, dix instances du symbole Balle sont attaches sur le scnario principal. Comme lillustre la figure 5-10 :

16 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-10. Occurrences de symbole Balle. Nous risquons dtre confronts un problme de chevauchement, il serait intressant de remonter notre bouton au premier niveau pour viter quil ne soit masqu par les instances de la classe Balle, comme lillustre la figure 5-11 :

Figure 5-11. Bouton masqu par les instances de la classe Balle.

17 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Pour ramener un objet graphique au premier plan nous devons travailler sur son index au sein de la liste daffichage. Nous savons que la proprit numChildren nous renvoie le nombre denfants total, numChildren-1 correspond donc lindex de lobjet le plus haut de la pile. En changeant lindex de notre bouton et du dernier objet enfant avec la mthode setChildIndex nous obtenons le rsultat escompt :
monBouton.addEventListener( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { var maBalle:Balle for ( var i:int = 0; i< 10; i++ ) { maBalle = new Balle(); maBalle.x = Math.random()*(stage.stageWidth - maBalle.width); maBalle.y = Math.random()*(stage.stageHeight - maBalle.height); addChild( maBalle ); } setChildIndex ( monBouton, numChildren - 1 ); }

La figure 5-12 illustre le rsultat :

18 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-12. Bouton plac au premier plan. La mthode addChild empile chaque instance sans jamais supprimer les objets graphiques dj prsents sur la scne. Pour supprimer tous les objets graphiques nous pourrions parcourir la scne en supprimant chaque occurrence de type Balle. Lide serait dajouter une fonction nettoie avant la boucle for, afin de supprimer les occurrences du symbole Balle dj prsentes sur la scne :
monBouton.addEventListener( MouseEvent.CLICK, clicBouton ); function nettoie ( pConteneur:DisplayObjectContainer, pClasse:Class ):void { var monDisplayObject:DisplayObject; for ( var i:int = pConteneur.numChildren-1; i >= 0; i-- ) { monDisplayObject = pConteneur.getChildAt ( i ); if ( monDisplayObject is pClasse ) pConteneur.removeChild (monDisplayObject); } } function clicBouton ( pEvt:MouseEvent ):void { nettoie ( this, Balle ); var maBalle:Balle; for ( var i:int = 0; i< 10; i++ ) { maBalle = new Balle(); maBalle.x = Math.random()*(stage.stageWidth - maBalle.width); maBalle.y = Math.random()*(stage.stageHeight - maBalle.height); addChild( maBalle ); } setChildIndex ( monBouton, numChildren - 1 ); }

La fonction nettoie parcourt le conteneur pass en paramtre ici notre scne, puis rcupre chaque objet enfant et stocke sa rfrence 19 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

dans

une variable monDisplayObject flash.display.DisplayObject.

de

type

Nous utilisons ce type pour notre variable monDisplayObject car celle-ci peut tre amene rfrencer diffrents sous-types de la classe DisplayObject. Nous choisissons ce type qui est le type commun tout objet enfant. Nous testons son type laide du mot-cl is. Si celui-ci est de type Balle alors nous supprimons linstance de la liste daffichage. Nous pourrions obtenir une structure plus simple en crant un objet graphique conteneur afin daccueillir les occurrences du symbole Balle, puis vider entirement ce dernier sans faire de test. De plus si nous souhaitons dplacer tous les objets plus tard, cette approche savrera plus judicieuse :
monBouton.addEventListener( MouseEvent.CLICK, clicBouton ); var conteneur:Sprite = new Sprite(); addChildAt ( conteneur, 0 ); function nettoie ( pConteneur:DisplayObjectContainer ):void { while ( pConteneur.numChildren ) pConteneur.removeChildAt ( 0 ); } function clicBouton ( pEvt:MouseEvent ):void { nettoie ( conteneur ); var maBalle:Balle; for ( var i:int = 0; i< 10; i++ ) { maBalle = new Balle(); maBalle.x = Math.random()*(stage.stageWidth - maBalle.width); maBalle.y = Math.random()*(stage.stageHeight - maBalle.height); conteneur.addChild( maBalle ); } }

La fonction nettoie intgre une boucle while supprimant chaque objet enfant, tant quil en existe. En cliquant sur notre bouton nous 20 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

nettoyons le conteneur de type flash.display.Sprite puis nous y ajoutons nos occurrences du symbole Balle. Revenons sur certaines parties du code prcdent, dans un premier temps nous crons un conteneur de type flash.display.Sprite. Nous utilisons un objet Sprite car utiliser un MovieClip ici ne serait daucune utilit, un objet Sprite suffit car nous devons simplement y stocker des objets enfants :
var conteneur:Sprite = new Sprite; addChildAt ( conteneur, 0 );

Nous crons un conteneur puis nous lajoutons la liste daffichage laide de la mthode addChildAt en le plaant lindex 0 dj occup par notre bouton seul objet graphique prsent ce moment l. Notre conteneur prend donc lindex du bouton dplaant ce dernier lindex 1. Notre bouton se retrouve ainsi au-dessus de lobjet conteneur, vitant ainsi dtre cach par les instances de la classe Balle. La fonction nettoie prend en paramtre le conteneur nettoyer, et supprime chaque enfant tant que celui-ci en possde :
function nettoie ( pConteneur:DisplayObjectContainer ):void { while ( pConteneur.numChildren ) pConteneur.removeChildAt ( 0 ); }

Cette fonction nettoie peut tre rutilise pour supprimer tous les objets enfants de nimporte quel DisplayObjectContainer.

A retenir
En ActionScript 3, il est recommand de crer des conteneurs afin de manipuler plus facilement un ensemble dobjets graphiques.

Le symbole bouton
Notre symbole bouton en bibliothque peut aussi tre attach dynamiquement un scnario en lui associant une classe spcifique grce au panneau de liaison. En slectionnant loption Liaison sur notre symbole Bouton nous faisons apparatre le panneau Proprits de liaison.

21 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-13. Proprits de liaison dun symbole de type Bouton. En cochant loption Exporter pour ActionScript nous rendons notre symbole disponible par programmation. Nous spcifions MonBouton comme nom de classe, puis nous cliquons sur OK. Pour instancier notre bouton dynamiquement nous crivons :
var monBouton:MonBouton = new MonBouton(); addChild ( monBouton );

Souvenez-vous, il tait impossible en ActionScript 1 et 2, de crer de vritables boutons par programmation. Les dveloppeurs devaient obligatoirement utiliser des clips ayant un comportement bouton. Nous verrons au cours du chapitre 7 intitul Interactivit que lutilisation de la classe SimpleButton savre en ralit rigide et noffre pas une souplesse quivalente la classe MovieClip.

Le symbole graphique
Lorsquun symbole graphique est plac en bibliothque, celui-ci ne peut tre manipul par programmation et instanci dynamiquement au sein de lapplication. De par sa non-interactivit ce dernier est de type flash.display.Shape. Pour
Shape nhrite pas de la classe flash.display.interactiveObject et ne peut donc avoir une

rappel,

la

classe

quelconque interactivit lie au clavier ou la souris.

Lorsque nous slectionnons loption Liaison sur un symbole graphique toutes les options sont grises, comme le dmontre la figure 5-14 :

22 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-14. Options de liaisons grises pour le symbole graphique. Il est cependant possible de manipuler un graphique pos sur la scne en y accdant grce aux mthodes daccs de la liste daffichage comme getChildAt comme nous lavons vu durant le chapitre 4. Nous sommes obligs de pointer notre objet graphique en passant un index car aucun nom doccurrence ne peut tre affect une occurrence de symbole graphique. Si nous posons une occurrence de graphique sur une scne vide nous pouvons tout de mme y accder par programmation laide du code suivant :
var monGraphique:DisplayObject = getChildAt ( 0 ); // affiche : [object Shape] trace( monGraphique );

Nous pourrions tre tents de lui affecter un nom par la proprit name, et dy faire rfrence laide de la mthode getChildByName dfinie par la classe flash.display.DisplayObjectContainer. Malheureusement, comme nous lavons vu prcdemment, la modification de la proprit name dun objet graphique pos depuis lenvironnement auteur est impossible. Notez que si dans lenvironnement auteur de Flash nous crons un graphique et imbriquons lintrieur un clip, lexcution notre imbrication ne pourra tre conserve. En regardant de plus prs lhritage de la classe Shape nous remarquons que celle-ci nhrite pas de la classe flash.display.DisplayObjectContainer et ne peut donc contenir des objets enfants. 23 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Il peut pourtant arriver quun clip soit imbriqu depuis lenvironnement auteur dans un graphique, mme si dans lenvironnement auteur cela ne pose aucun problme, cette imbrication ne pourra tre conserve lexcution. Le clip sortira du graphique pour devenir un enfant direct du parent du graphique. Les deux objets se retrouvant ainsi au mme niveau dimbrication, ce comportement bien que troublant savre tout fait logique et doit tre connu afin quil ne soit pas source dinterrogations.

A retenir
Le symbole graphique ne peut pas tre associ une sous classe.

Les images bitmap


Depuis toujours nous pouvons intgrer au sein de sa bibliothque des images bitmap de diffrents types. En ralit, il faut considrer dans Flash une image bitmap comme un symbole. Depuis Flash 8 nous avions la possibilit dattribuer un nom de liaison une image et de lattacher dynamiquement laide de la mthode BitmapData.loadBitmap puis de lafficher laide de la mthode MovieClip.attachBitmap. Pour attacher une image de la bibliothque nous devions appeler la mthode loadBitmap de la classe BitmapData :
import flash.display.BitmapData; var ref:BitmapData = BitmapData.loadBitmap ("LogoWiiFlash");

En lui passant un nom didentifiant affect par le panneau liaison, nous rcuprions notre image sous la forme de BitmapData puis nous laffichions avec la mthode attachBitmap :
import flash.display.BitmapData; var monBitmap:BitmapData = BitmapData.loadBitmap ("LogoWiiFlash"); attachBitmap ( monBitmap, 0 );

En ActionScript 3, les images sinstancient comme tout objet graphique avec le mot-cl new. Importez une image de type quelconque dans la bibliothque puis faites un clic-droit sur celle-ci pour slectionner loption Liaison comme illustre en figure 5-15 :

24 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-15. Proprits de liaison dun symbole de type bitmap. En cochant loption Exporter pour ActionScript nous rendons les champs Classe et Classe de base ditable. Nous spcifions LogoWiiFlash comme nom de classe, et laissons comme classe de base flash.display.BitmapData. Notre image sera donc de type LogoWiiFlash hritant de BitmapData. Puis nous instancions notre image :
var monBitmapData:LogoWiiFlash = new LogoWiiFlash();

Si nous testons le code prcdent, le message derreur suivant saffiche :


1136: Nombre d'arguments incorrect. 2 attendus.

En ActionScript 3, un objet BitmapData ne peut tre instanci sans prciser une largeur et hauteur spcifique au constructeur. Afin de ne pas tre bloqu la compilation nous devons obligatoirement passer une largeur et hauteur de 0,0 :
var monBitmapData:LogoWiiFlash = new LogoWiiFlash(0,0);

A lexcution, le lecteur affiche limage sa taille dorigine.

A retenir

25 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Afin dinstancier une image bitmap issue de la bibliothque, nous devons obligatoirement spcifier une hauteur et une largeur de 0 pixels. A lexcution, le lecteur affiche limage sa taille dorigine.

Afficher une image bitmap


En ActionScript 1 et 2, une image BitmapData pouvait tre affiche en tant enveloppe dans un MovieClip grce la mthode MovieClip.attachBitmap. En ActionScript 3 si nous tentons dajouter la liste daffichage un objet graphique de type BitmapData nous obtenons lerreur suivante la compilation :
1067: Contrainte implicite d'une valeur du type flash.display:BitmapData vers un type sans rapport flash.display:DisplayObject.

Pour ajouter la liste daffichage un BitmapData nous devons obligatoirement lenvelopper dans un objet de type Bitmap. Celui-ci nous permettra plus tard de positionner lobjet BitmapData et deffectuer dautres manipulations sur ce dernier. Nous instancions un objet Bitmap, puis nous passons au sein de son constructeur lobjet BitmapData envelopper :
var monBitmapData:LogoWiiFlash = new LogoWiiFlash(0, 0); var monBitmap:Bitmap = new Bitmap( monBitmapData ); addChild ( monBitmap );

Une fois compil, notre image est bien affiche :

26 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-16. Image bitmap ajoute la liste daffichage. La classe flash.display.Bitmap hritant de la classe flash.display.DisplayObject possde toutes les proprits et mthodes ncessaires la manipulation dun objet graphique. Pour procder une translation de notre image, puis une rotation nous pouvons crire :
var monBitmapData:LogoWiiFlash = new LogoWiiFlash(0, 0); var monBitmap:Bitmap = new Bitmap( monBitmapData ); addChild ( monBitmap ); monBitmap.smoothing = true; monBitmap.rotation = 45; monBitmap.x += 250;

Le code suivant, gnre le rendu suivant :

27 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Figure 5-17.Translation et rotation sur un objet Bitmap. Nous reviendrons en dtail sur la classe Bitmap au cours du chapitre 12 intitul Programmation bitmap.

A retenir
Un symbole de type graphique nest pas manipulable depuis la bibliothque par programmation. Une image est associe au type flash.display.BitmapData, pour afficher celle-ci nous devons lenvelopper dans un objet flash.display.Bitmap.

Le symbole Sprite
Lobjet graphique Sprite est trs proche du classique MovieClip et possde quasiment toutes ses fonctionnalits mais ne contient pas de scnario et donc aucune des mthodes lies sa manipulation telles gotoAndStop, gotoAndPlay, etc. Cest en quelque sorte un MovieClip version allge. Le dveloppeur Flash qui a toujours eu lhabitude de crer des clips vides dynamiquement se dirigera dsormais vers lobjet Sprite plus lger en mmoire et donc plus optimis. 28 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Lorsque nous transformons une forme en symbole lenvironnement de Flash CS3 nous propose trois types de symboles :
Clip Bouton Graphique

Le type Sprite napparat pas, mais il est pourtant possible de crer un symbole de type Sprite depuis lenvironnement auteur de Flash CS3. Pour cela, nous allons ouvrir un nouveau document et crer un symbole de type clip. Dans le panneau Proprits de liaison, nous cochons la case Exporter pour ActionScript. Nous dfinissons comme nom de classe MonSprite, dans le champ Classe de base nous remplaons la classe flash.display.MovieClip par la classe flash.display.Sprite, comme lillustre la figure 5-18 :

Figure 5-18.Panneau de proprits de liaison. De cette manire notre symbole hritera de la classe Sprite au lieu de la classe MovieClip. Nous instancions notre symbole :
var monSprite:MonSprite = new MonSprite();

Puis nous laffichons :


var monSprite:MonSprite = new MonSprite(); monSprite.x = 200; monSprite.y = 100; addChild ( monSprite );

29 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

Si nous testons son type avec loprateur is, nous voyons que notre occurrence est bien de type Sprite :
var monSprite:MonSprite = new MonSprite(); monSprite.x = 200; monSprite.y = 100; addChild ( monSprite ); // affiche : true trace( monSprite is Sprite );

Au sein de lenvironnement de Flash CS3, le symbole Sprite est similaire au symbole clip mais labsence de scnario nest pas rpercute graphiquement. Comme nous lavons vu prcdemment, lobjet graphique Sprite na pas de scnario, mais que se passe t-il si nous ajoutons une animation au sein de ce dernier ? A lexcution le symbole ne lira pas lanimation, si nous passons la classe de base du symbole flash.display.MovieClip lanimation est alors joue.

A retenir
Mme si le symbole de type flash.display.Sprite nest pas disponible depuis le panneau Convertir en symbole il est possible de crer des symboles Sprite depuis lenvironnement auteur de Flash CS3. Pour cela, nous utilisons le champ Classe de base en spcifiant la classe flash.display.Sprite. Si une animation est place au sein dun symbole de type Sprite, celle-ci nest pas joue.

Dfinition du code dans un symbole


Dans les prcdentes versions dActionScript, la dfinition de code tait possible sur les occurrences de la manire suivante :
on (release) { trace("cliqu"); }

Mme si cette technique tait dconseille dans de larges projets, elle permettait nanmoins dattacher simplement le code aux objets. En ActionScript 3, la dfinition de code est impossible sur les 30 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 5 Symboles prdfinis version 0.1.2

occurrences, mais il est possible de reproduire le mme comportement en ActionScript 3. En plaant le code suivant sur le scnario dun MovieClip nous retrouvons le mme comportement :
// coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.CLICK, clic ); // activation du comportement bouton buttonMode = true; function clic ( pEvt:MouseEvent ):void { trace("cliqu"); }

Nous reviendrons sur la proprit buttonMode au cours du chapitre 7 intitul Interactivit.

31 / 31
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

6
Propagation vnementielle

CONCEPT ................................................................................................................ 1 LA PHASE DE CAPTURE ..................................................................................... 3 LA NOTION DE NUDS............................................................................................. 6 DETERMINER LA PHASE EN COURS .......................................................................... 7 OPTIMISER LE CODE AVEC LA PHASE DE CAPTURE .............................. 9 LA PHASE CIBLE ................................................................................................ 14 INTERVENIR SUR LA PROPAGATION .......................................................... 18 LA PHASE DE REMONTEE ............................................................................... 23 ECOUTER PLUSIEURS PHASES ...................................................................... 27 LA PROPRIETE EVENT.BUBBLES ............................................................................ 29 SUPPRESSION DECOUTEURS ........................................................................ 30

Concept
Nous avons abord la notion dvnements au cours du chapitre 3 intitul Modle vnementiel et trait les nouveauts apportes par ActionScript 3. La propagation vnementielle est un concept avanc dActionScript 3 qui ncessite une attention toute particulire. Nous allons au cours de ce chapitre apprendre matriser ce nouveau comportement. Le concept de propagation vnementielle est hrit du Document Object Model (DOM) du W3C dont la dernire spcification est disponible ladresse suivante : http://www.w3.org/TR/DOM-Level-3-Events/

1 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

En ActionScript 3, seuls les objets graphiques sont concerns par la propagation vnementielle. Ainsi, lorsquun vnement survient au sein de la liste daffichage, le lecteur Flash propage ce dernier de lobjet flash.display.Stage jusquau parent direct de lobjet auteur de la propagation. Cette descente de lvnement est appele phase de capture. Grce ce mcanisme, nous pouvons souscrire un couteur auprs de lun des parents de lobjet ayant initi la propagation, afin den tre notifi durant la phase descendante. Le terme de capture est utilis car nous pouvons capturer lvnement durant sa descente et stopper sa propagation si ncessaire. La phase cible intervient lorsque lvnement a parcouru tous les objets parents et atteint lobjet ayant provoqu sa propagation, ce dernier est appel objet cible ou nud cible. Une fois la phase cible termine, le lecteur Flash propage lvnement dans le sens inverse de la phase de capture. Cette phase est appele phase de remonte. Durant celle-ci lvnement remonte jusqu' lobjet Stage c'est--dire la racine de la liste daffichage. Ces trois phases constituent schmatise sur la figure 6-1 : la propagation vnementielle,

Figure 6-1. Les trois phases du flot vnementiel. Les vnements diffuss en ActionScript 2 ne disposaient que dune seule phase cible. Les deux nouvelles phases apportes par 2 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

ActionScript 3 vont nous permettre de mieux grer linteraction entre les objets graphiques au sein de la liste daffichage. Attention, la propagation vnementielle ne concerne que les objets graphiques. Il est important de noter quActionScript 3 nest pas le seul langage intgrer la notion de propagation vnementielle, des langages comme JavaScript ou Java, intgrent ce mcanisme depuis plusieurs annes dj. Nous allons nous attarder sur chacune des phases, et dcouvrir ensemble quels sont les avantages lis cette propagation vnementielle et comment en tirer profit dans nos applications.

A retenir
La propagation vnementielle ne concerne que les objets graphiques. La propagation vnementielle nest pas propre ActionScript 3. Dautres langages comme JavaScript, Java ou C# lintgre depuis plusieurs annes dj. Le flot vnementiel se divise en trois phases distinctes : la phase de capture, la phase cible, et la phase de remonte.

La phase de capture
Comme nous lavons abord prcdemment, la phase de capture est la premire phase du flot vnementiel. En ralit, lorsque nous cliquons sur un bouton prsent au sein de la liste daffichage, tous les objets graphiques parents ce dernier sont dabord notifis de lvnement et peuvent ainsi le diffuser. La figure 6-2 illustre une situation classique, un bouton est pos sur le scnario principal :

3 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

Figure 6-2. Phase de capture. Lorsque lutilisateur clique sur le bouton monBouton, la phase de capture dmarre. Le lecteur Flash propage lvnement du haut vers le bas de la hirarchie. Notons que la descente de lvnement sarrte au parent direct du bouton, la phase suivante sera la phase cible. Lobjet Stage, puis lobjet MainTimeline (root) sont ainsi notifis de lvnement. Ainsi, si nous coutons lvnement en cours de propagation sur lun de ces derniers, il nous est possible de lintercepter. En lisant la signature de la mthode addEventListener nous remarquons la prsence dun paramtre appel useCapture :
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

Par dfaut la mthode addEventListener ne souscrit pas lcouteur pass la phase de capture. Pour en profiter nous devons passer la valeur boolenne true au troisime paramtre nomm useCapture. Lide est dcouter lvnement sur lun des objets parents lobjet cible :
objetParent.addEventListener ( MouseEvent.CLICK, clicBouton, true );

Passons un peu de pratique, dans un nouveau document Flash CS3 nous crons un symbole de type bouton et nous posons une occurrence de ce dernier, appele monBouton, sur la scne principale. Dans le code suivant nous souscrivons un couteur auprs du scnario principal en activant la capture : 4 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

// souscription l'vnement MouseEvent.CLICK auprs // du scnario principal pour la phase de capture addEventListener ( MouseEvent.CLICK, clicBouton, true ); function clicBouton ( pEvt:MouseEvent ) { // affiche : [MouseEvent type="click" bubbles=true cancelable=false eventPhase=1 localX=13 localY=13 stageX=75.95 stageY=92 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); }

Lors

scnario principal qui le diffuse aussitt. La fonction couteur clicBouton est dclenche.

du clic sur notre bouton monBouton, lvnement MouseEvent.CLICK entame sa phase de descente puis atteint le

Contrairement la mthode hasEventListener, la mthode willTrigger permet de dterminer si un vnement spcifique est cout auprs de lun des parents lors de la phase de capture ou de remonte :
// souscription l'vnement MouseEvent.CLICK auprs // du scnario principal pour la phase de capture addEventListener ( MouseEvent.CLICK, clicBouton, true ); function clicBouton ( pEvt:MouseEvent ) { } trace( pEvt );

// aucun couteur n'est enregistr auprs du bouton // affiche : false trace( monBouton.hasEventListener (MouseEvent.CLICK) ); // un couteur est enregistr auprs d'un des parents du bouton // affiche : true trace( monBouton.willTrigger( MouseEvent.CLICK ) ); // un couteur est enregistr auprs du scnario principal // affiche : true trace( willTrigger( MouseEvent.CLICK ) );

Nous prfrerons donc lutilisation de la mthode willTrigger dans un contexte de propagation vnementielle. Au cours du chapitre 3 intitul Modle vnementiel nous avons dcouvert la proprit target de lobjet vnementiel correspondant lobjet auteur du flot vnementiel que nous appelons gnralement objet cible. Attention, durant la phase de capture ce dernier nest pas celui qui a diffus lvnement, mais celui qui est la source de la propagation vnementielle. Dans notre cas, lobjet MainTimeline a diffus 5 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

lvnement MouseEvent.CLICK, suite un dclenchement de la propagation de lvnement par notre bouton. Lintrt majeur de la phase de capture rside donc dans la possibilit dintercepter depuis un parent nimporte quel vnement provenant dun enfant.

La notion de nuds
Lorsquun vnement se propage, les objets graphiques notifis sont appels objets nuds. Pour rcuprer le nud sur lequel se trouve lvnement au cours de sa propagation, nous utilisons la proprit currentTarget de lobjet vnementiel. La proprit currentTarget renvoie toujours lobjet sur lequel nous avons appel la mthode addEventListener. Grace aux proprits target et currentTarget nous pouvons donc savoir vers quel objet graphique se dirige lvnement ainsi que le nud trait actuellement. La figure 6-3 illustre lide :

Figure 6-3. Propagation de lvnement au sein des objets nuds. En testant le code suivant, nous voyons que le scnario principal est notifi de lvnement MouseEvent.CLICK se dirigeant vers lobjet cible :
// souscription l'vnement MouseEvent.CLICK auprs // du scnario principal pour la phase de capture

6 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

addEventListener ( MouseEvent.CLICK, clicBouton, true ); function clicBouton ( pEvt:MouseEvent ) { /* // affiche : Objet cible : [object SimpleButton] Noeud en cours : [object MainTimeline] */ trace( "Objet cible : " + pEvt.target ) trace( "Noeud en cours : " + pEvt.currentTarget ); }

Bien que nous connaissions les objets graphiques associs la propagation de lvnement, nous ne pouvons pas dterminer pour le moment la phase en cours. Lvnement actuellement diffus correspond til la phase de capture, cible, ou bien de remonte ?

Dterminer la phase en cours


Afin de savoir quelle phase correspond un vnement nous utilisons la proprit eventPhase de lobjet vnementiel. Durant la phase de capture, la proprit eventPhase vaut 1, 2 pour la phase cible et 3 pour la phase de remonte. Dans le mme document que prcdemment nous coutons lvnement MouseEvent.CLICK auprs du scnario principal durant la phase de capture :
// souscription l'vnement MouseEvent.CLICK auprs // du scnario principal pour la phase de capture addEventListener ( MouseEvent.CLICK, clicBouton, true ); function clicBouton ( pEvt:MouseEvent ) { // affiche : Phase en cours : 1 trace ( "Phase en cours : " + pEvt.eventPhase ); }

Lorsque nous cliquons sur notre bouton, la proprit eventPhase de lobjet vnementiel vaut 1. Pour afficher une information relative la phase en cours nous pourrions tre tents dcrire le code suivant :
// souscription l'vnement MouseEvent.CLICK auprs // du scnario principal pour la phase de capture addEventListener ( MouseEvent.CLICK, clicBouton, true ); function clicBouton ( pEvt:MouseEvent )

7 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

{ // affiche : phase de capture if ( pEvt.eventPhase == 1 ) trace("phase de capture"); }

Attention, comme pour la souscription dvnements, nous utilisons toujours des constantes de classes pour dterminer la phase en cours. Pour cela, la classe flash.events.EventPhase contient trois proprits associes chacune des phases. En testant la valeur de chaque proprit nous rcuprons les valeurs correspondantes :
// affiche : 1 trace( EventPhase.CAPTURING_PHASE ); // affiche : 2 trace( EventPhase.AT_TARGET); // affiche : 3 trace( EventPhase.BUBBLING_PHASE);

La figure 6-4 illustre les constantes de la classe EventPhase lies chaque phase :

Figure 6-4. Les trois phases de la propagation vnementielle. En prenant cela en considration, nous pouvons rcrire la fonction couteur clicBouton de la manire suivante :
function clicBouton ( pEvt:MouseEvent ) { switch ( pEvt.eventPhase ) { case EventPhase.CAPTURING_PHASE :

8 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

trace("phase de capture"); break; case EventPhase.AT_TARGET : trace("phase cible"); break; case EventPhase.BUBBLING_PHASE : trace("phase de remonte"); break; } }

Tout cela reste relativement abstrait et nous pouvons nous demander lintrt dun tel mcanisme dans le dveloppement dapplications ActionScript 3. Grce ce processus nous allons rendre notre code souple et optimis. Nous allons en tirer profit au sein dune application dans la partie suivante.

A retenir
La phase de capture dmarre de lobjet Stage jusqu'au parent de lobjet cible. La phase de capture ne concerne que les objets parents. La proprit target de lobjet vnementiel renvoie toujours une rfrence vers lobjet cible. La proprit currentTarget de lobjet vnementiel renvoie toujours une rfrence vers lobjet sur lequel nous avons appel la mthode addEventListener. Durant la propagation dun vnement, la proprit target ne change pas et fait toujours rfrence au mme objet graphique (objet cible).

La proprit eventPhase de lobjet vnementiel renvoie une valeur allant de 1 3 associes chaque phase : CAPTURING_PHASE, AT_TARGET et BUBBLING_PHASE. Pour dterminer la phase lie un vnement nous comparons la proprit eventPhase aux trois constantes de la classe flash.events.EventPhase.

Optimiser le code avec la phase de capture


Afin de mettre en vidence lintrt de la phase de capture, nous allons prendre un cas simple dapplication ActionScript o nous souhaitons couter lvnement MouseEvent.CLICK auprs de diffrents

9 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

boutons. Lorsque nous cliquons sur chacun dentre eux, nous les supprimons de la liste daffichage. Dans un nouveau document Flash CS3 nous crons un symbole bouton de forme rectangulaire li une classe Fenetre par le panneau Proprits de liaison. Puis nous ajoutons plusieurs instances de celle-ci notre scnario principal :
// nombre de fentres var lng:int = 12; var maFenetre:Fenetre; for ( var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.x = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.y = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 ); addChild ( maFenetre ); }

Nous obtenons le rsultat illustr par la figure 6-5 :

Figure 6-5. Instances de la classe Fenetre.

10 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

Notre liste daffichage pourrait tre reprsente de la manire suivante :

Figure 6-6. Liste daffichage avec instances de la classe Fenetre. Chaque clic sur une instance de symbole Fenetre provoque une propagation de lvnement MouseEvent.CLICK de lobjet Stage jusqu notre scnario principal MainTimeline. Ce dernier est donc notifi de chaque clic sur nos boutons durant la phase de capture. Comme nous lavons vu prcdemment, la phase de capture prsente lintrt de pouvoir intercepter un vnement durant sa phase descendante auprs dun objet graphique parent. Cela nous permet donc de centraliser lcoute de lvnement MouseEvent.CLICK en appelant une seule fois la mthode addEventListener. De la mme manire, pour arrter lcoute dun vnement, nous appelons la mthode removeEventListener sur lobjet graphique parent seulement. Ainsi nous navons pas besoin de souscrire un couteur auprs de chaque instance de la classe Fenetre mais seulement auprs du scnario principal :
// nombre de fentres var lng:int = 12; var maFenetre:Fenetre; for ( var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.x = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.y = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 ); addChild ( maFenetre );

11 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

} // souscription l'vnement MouseEvent.CLICK auprs // du scnario principal pour la phase de capture addEventListener ( MouseEvent.CLICK, clicFenetre, true ); function clicFenetre ( pEvt:MouseEvent ):void { // affiche : [MouseEvent type="click" bubbles=true cancelable=false eventPhase=1 localX=85 localY=15 stageX=92 stageY=118 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); }

Si nous navions pas utilis la phase de capture, nous aurions du souscrire un couteur auprs de chaque symbole Fenetre. En ajoutant un couteur auprs de lobjet parent nous pouvons capturer lvnement provenant des objets enfants, rendant ainsi notre code centralis. Si les boutons viennent tre supprims nous navons pas besoin dappeler la mthode removeEventListener sur chacun dentre eux afin de librer les ressources, car seul le parent est cout. Si par la suite les boutons sont recrs, aucun code supplmentaire nest ncessaire. Attention, en capturant lvnement auprs du scnario principal nous coutons tous les vnements MouseEvent.CLICK des objets interactifs enfants. Si nous souhaitons seulement couter les vnements provenant des instances de la classe Fenetre, il nous faut alors les isoler dans un objet conteneur, et procder la capture des vnements auprs de ce dernier. Nous prfrerons donc lapproche suivante :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; for ( var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.x = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 );

12 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

maFenetre.y = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 ); conteneurFenetres.addChild ( maFenetre ); } // souscription l'vnement MouseEvent.CLICK auprs // du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, clicFenetre, true ); function clicFenetre ( pEvt:MouseEvent ):void { // affiche : [MouseEvent type="click" bubbles=true cancelable=false eventPhase=1 localX=119 localY=46 stageX=126 stageY=53 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); }

Afin de supprimer de laffichage chaque fentre clique, nous ajoutons linstruction removeChild au sein de la fonction couteur clicFenetre :
function clicFenetre ( pEvt:MouseEvent ):void { // l'instance de Fenetre clique est supprime de l'affichage pEvt.currentTarget.removeChild ( DisplayObject ( pEvt.target ) ); }

La proprit target fait ainsi rfrence linstance de Fenetre clique, tandis que la proprit currentTarget rfrence le conteneur. A chaque clic, linstance clique est supprime de la liste daffichage :

13 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

Figure 6-7. Suppression dinstances de la classe Fenetre. Nous reviendrons tout au long de louvrage sur lutilisation de la phase de capture afin de matriser totalement ce concept. Nous allons nous attarder prsent sur la phase cible.

A retenir
Grce la phase de capture, nous souscrivons lcouteur auprs de lobjet graphique parent afin de capturer les vnements des objets enfants. Notre code est plus optimis et plus centralis.

La phase cible
La phase cible correspond la diffusion de lvnement depuis lobjet cible. Certains vnements associs des objets graphiques ne participent qu celle-ci. Cest le cas des quatre vnements suivants qui ne participent ni la phase de capture ni la phase de remonte :
Event.ENTER_FRAME Event.ACTIVATE

14 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

Event.DEACTIVATE Event.RENDER

Nous allons reprendre lexemple prcdent, en prfrant cette fois la phase cible la phase de capture :

Figure 6-8. Phase cible. Afin dcouter lvnement MouseEvent.CLICK, nous souscrivons un couteur auprs de chaque instance de la classe Fenetre :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); // souscription auprs de chaque instance pour la phase cible maFenetre.addEventListener ( MouseEvent.CLICK, clicFenetre ); maFenetre.x = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.y = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 ); conteneurFenetres.addChild ( maFenetre ); } function clicFenetre ( pEvt:MouseEvent ):void

15 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

{ // affiche : [object Fenetre] trace( pEvt.target ); }

Au clic, lvnement commence sa propagation puis atteint lobjet cible, notre fonction couteur clicFenetre est dclenche. nous appelons la mthode addEventListener sans spcifier le paramtre useCapture, nous souscrivons lcouteur la phase cible ainsi qu la phase de remonte que nous traiterons juste aprs. Si nous affichons le contenu de la proprit target de lobjet vnementiel, celui-ci nous renvoie une rfrence vers lobjet cible, ici notre objet Fenetre. Pour supprimer chaque instance, nous rajoutons linstruction removeChild au sein de notre fonction couteur en passant lobjet rfrenc par la proprit target de lobjet vnementiel :
function clicFenetre ( pEvt:MouseEvent ):void { // l'instance de Fenetre clique est supprime de l'affichage conteneurFenetres.removeChild ( DisplayObject ( pEvt.target ) ); // dsinscription de la fonction clicFenetre lvnement // MouseEvent.CLICK pEvt.target.removeEventListener ( MouseEvent.CLICK, clicFenetre ); }

Ainsi,

lorsque

Il est important de noter que durant la phase cible, lobjet cible est aussi le diffuseur de lvnement. En testant le code suivant, nous voyons que les proprits target et currentTarget de lobjet vnementiel rfrencent le mme objet graphique :
function clicFenetre ( pEvt:MouseEvent ):void { // affiche : true trace( pEvt.target == pEvt.currentTarget ); // l'instance de Fenetre clique est supprime de l'affichage conteneurFenetres.removeChild ( DisplayObject ( pEvt.target ) ); // dsinscription de la fonction clicFenetre lvnement // MouseEvent.CLICK

16 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

pEvt.target.removeEventListener ( MouseEvent.CLICK, clicFenetre ); }

Pour tester si lvnement diffus est issu de la phase cible nous pouvons comparer la proprit eventPhase de lobjet vnementiel la constante EventPhase.AT_TARGET :
function clicFenetre ( pEvt:MouseEvent ):void { // affiche : true trace( pEvt.target == pEvt.currentTarget ); // affiche : phase cible if ( pEvt.eventPhase == EventPhase.AT_TARGET ) { trace("phase cible"); } // l'instance de Fenetre clique est supprime de l'affichage conteneurFenetres.removeChild ( DisplayObject ( pEvt.target ) ); // dsinscription de la fonction clicFenetre lvnement // MouseEvent.CLICK pEvt.target.removeEventListener ( MouseEvent.CLICK, clicFenetre ); }

En utilisant la phase cible nous avons perdu en souplesse en souscrivant la fonction couteur clicFenetre auprs de chaque instance de la classe Fenetre, et gr la dsinscription des couteurs lors de la suppression de la liste daffichage. Bien entendu, la phase de capture ne doit et ne peut pas tre utilise systmatiquement. En utilisant la phase cible nous pouvons enregistrer une fonction couteur un bouton unique, ce qui peut savrer primordial lorsque la logique associe chaque bouton est radicalement diffrente. Bien que trs pratique dans certains cas, la phase de capture intercepte les clics de chaque bouton en excutant une seule fonction couteur ce qui peut savrer limitatif dans certaines situations. Chacune des phases doit donc tre considre et utilise lorsque la situation vous parat la plus approprie.

A retenir

17 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

La phase cible correspond la diffusion de lvnement par lobjet cible. Les objets graphiques diffusent des vnements pouvant se propager. Les objets non graphiques diffusent des vnements ne participant qu la phase cible. Il convient de bien tudier limbrication des objets graphiques au sein de lapplication, et dutiliser la phase la plus adapte nos besoins.

ActionScript 3 offre en plus la possibilit dintervenir sur la propagation dun vnement. Cette fonctionnalit peut tre utile lorsque nous souhaitons empcher un vnement datteindre lobjet cible en empchant la poursuite de sa propagation. Nous allons dcouvrir laide dun exemple concret comment utiliser cette nouvelle notion.

Intervenir sur la propagation


Il est possible de stopper la propagation dun vnement depuis nimporte quel nud. Quel pourrait tre lintrt de stopper la propagation dun vnement durant sa phase de capture ou sa phase de remonte ? Dans certains cas nous avons besoin de verrouiller lensemble dune application, comme par exemple la slection dlments interactifs dans une application ou dans un jeu. Deux mthodes dfinies par la classe flash.events.Event permettent dintervenir sur la propagation :
objtEvenementiel.stopPropagation() objtEvenementiel.stopImmediatePropagation()

Ces deux mthodes sont quasiment identiques mais diffrent quelque peu, nous allons nous y attarder prsent. Imaginons que nous souhaitions verrouiller tous les boutons de notre exemple prcdent. Dans lexemple suivant nous allons intervenir sur la propagation dun vnement MouseEvent.CLICK durant la phase de capture. En stoppant sa propagation au niveau du conteneur, nous allons lempcher datteindre lobjet cible. Ainsi la phase cible ne sera jamais atteinte.

18 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

La figure 6-9 illustre linterruption de propagation dun vnement :

Figure 6-9. Intervention sur la propagation dun vnement. Afin dintercepter lvnement MouseEvent.CLICK avant quil ne parvienne jusqu lobjet cible, nous coutons lvnement MouseEvent.CLICK lors de la phase de capture auprs du conteneur conteneurFenetres :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); // souscription auprs de chaque instance pour la phase cible maFenetre.addEventListener ( MouseEvent.CLICK, clicFenetre ); maFenetre.x = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.y = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 ); conteneurFenetres.addChild ( maFenetre ); } // souscription l'vnement MouseEvent.CLICK auprs // du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, captureClic, true );

19 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

function clicFenetre ( pEvt:Event ):void { // l'instance de Fenetre clique est supprime de l'affichage conteneurFenetres.removeChild ( DisplayObject ( pEvt.target ) ); // dsinscription de la fonction clicFenetre lvnement // MouseEvent.CLICK pEvt.target.removeEventListener ( MouseEvent.CLICK, clicFenetre ); } function captureClic ( pEvt:MouseEvent ):void { // affiche : Capture de l'vnement : click trace("Capture de l'vnement : " + pEvt.type ); }

Nous remarquons que la fonction captureClic est bien dclenche chaque clic bouton.
stopPropagation sur lobjet vnementiel diffus :
function captureClic ( pEvt:MouseEvent ):void { // affiche : Objet cible : [object Fenetre] trace( "Objet cible : " + pEvt.target ); // affiche : Objet notifi : [object Sprite] trace( "Objet notifi : " + pEvt.currentTarget ); // affiche : Capture de l'vnement : click trace("Capture de l'vnement : " + pEvt.type ); pEvt.stopPropagation(); }

Pour

stopper

la

propagation,

nous

appelons

la

mthode

Lorsque la mthode stopPropagation est excute, lvnement interrompt sa propagation sur le nud en cours, la fonction clicFenetre nest plus dclenche. Grce au code suivant, les instances de la classe Fenetre sont supprimes uniquement lorsque la touche ALT est enfonce :
function captureClic ( pEvt:MouseEvent ):void { // affiche : Objet cible : [object Fenetre] trace( "Objet cible : " + pEvt.target );

20 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

// affiche : Objet notifi : [object Sprite] trace( "Objet notifi : " + pEvt.currentTarget ); // affiche : Capture de l'vnement : click trace("Capture de l'vnement : " + pEvt.type ); if ( !pEvt.altKey ) pEvt.stopPropagation(); }

Nous reviendrons sur les proprits de la classe MouseEvent au cours du prochain chapitre intitul Interactivit. Attention, la mthode stopPropagation interrompt la propagation de lvnement auprs des nuds suivants, mais autorise lexcution des couteurs souscrits au nud en cours. Si nous ajoutons un autre couteur lobjet conteneurFenetres, bien que la propagation soit stoppe, la fonction couteur est dclenche. Afin de mettre en vidence la mthode stopImmediatePropagation nous ajoutons une fonction captureClicBis comme autre couteur du conteneur :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); // souscription auprs de chaque instance pour la phase cible maFenetre.addEventListener ( MouseEvent.CLICK, clicFenetre ); maFenetre.x = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.y = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 ); conteneurFenetres.addChild ( maFenetre ); } // souscription l'vnement MouseEvent.CLICK auprs // du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, captureClic, true ); // souscription l'vnement MouseEvent.CLICK auprs // du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, captureClicBis, true );

21 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

function clicFenetre ( pEvt:Event ):void { // l'instance de Fenetre clique est supprime de l'affichage conteneurFenetres.removeChild ( DisplayObject ( pEvt.target ) ); // dsinscription de la fonction clicFenetre lvnement // MouseEvent.CLICK pEvt.target.removeEventListener ( MouseEvent.CLICK, clicFenetre ); } function captureClic ( pEvt:MouseEvent ):void { // affiche : Objet cible : [object Fenetre] trace( "Objet cible : " + pEvt.target ); // affiche : Objet notifi : [object Sprite] trace( "Objet notifi : " + pEvt.currentTarget ); // affiche : Capture de l'vnement : click trace("Capture de l'vnement : " + pEvt.type ); if ( !pEvt.altKey ) pEvt.stopPropagation(); } function captureClicBis ( pEvt:MouseEvent ):void { trace("fonction couteur captureClicBis dclenche"); }

En testant le code prcdent, nous voyons que notre fonction captureClicBis est bien dclenche bien que la propagation de lvnement soit interrompue. A linverse, si nous appelons la mthode stopImmediatePropagation, la fonction captureClicBis nest plus dclenche :
function captureClic ( pEvt:MouseEvent ):void { // affiche : Objet cible : [object Fenetre] trace( "Objet cible : " + pEvt.target ); // affiche : Objet notifi : [object Sprite] trace( "Objet notifi : " + pEvt.currentTarget ); // affiche : Capture de l'vnement : click trace("Capture de l'vnement : " + pEvt.type ); if ( !pEvt.altKey ) pEvt.stopImmediatePropagation(); }

22 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

Grce aux mthodes stopPropagation et stopImmediatePropagation nous pouvons ainsi matriser le flot vnementiel avec prcision.

A retenir
Il est possible dintervenir sur la propagation dun vnement laide des mthodes stopPropagation et stopImmediatePropagation.

La mthode stopPropagation interrompt la propagation dun vnement mais nempche pas sa diffusion auprs des couteurs du nud en cours. La mthode stopImmediatePropagation interrompt la propagation dun vnement et empche sa diffusion mme auprs des couteurs du mme nud.

La phase de remonte
La phase de remonte constitue la dernire phase de la propagation. Durant cette phase, lvnement parcourt le chemin inverse de la phase de capture, notifiant chaque nud parent de lobjet cible. Le parcourt de lvnement durant cette phase sapparente celui dune bulle remontant la surface de leau. Cest pour cette raison que cette phase est appele en anglais bubbling phase . Lvnement remontant de lobjet cible jusqu lobjet Stage. La figure 6-10 illustre la phase de remonte :

Figure 6-10. Phase de remonte. Pour souscrire un couteur auprs de la phase de remonte nous appelons la mthode addEventListener sur un des objets 23 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

graphiques parent en passant la valeur boolenne false au paramtre useCapture :


monObjetParent.addEventListener ( MouseEvent.CLICK, clicRemontee, false );

En lisant la signature de la mthode addEventListener nous voyons que le paramtre useCapture vaut false par dfaut :
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

Ainsi, la souscription auprs de la phase de remonte peut scrire sans prciser le paramtre useCapture :
objetParent.addEventListener ( MouseEvent.CLICK, clicRemontee );

Cela signifie que lorsque nous coutons un vnement pour la phase cible, la phase de remonte est automatiquement coute. La fonction clicRemontee recevra donc aussi les vnements MouseEvent.CLICK provenant des enfants durant leur phase de remonte. Dans suivant, nous coutons lvnement MouseEvent.CLICK lors de la phase de capture et de remonte auprs du conteneur :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.x = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.y = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 ); conteneurFenetres.addChild ( maFenetre ); } // souscription auprs du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, captureClic, true ); // souscription auprs du conteneur pour la phase de remonte conteneurFenetres.addEventListener ( MouseEvent.CLICK, clicRemontee );

lexemple

24 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

function captureClic ( pEvt:MouseEvent ):void { /* affiche : phase de capture de l'vnement : click noeud en cours de notification : [object Sprite] */ if ( pEvt.eventPhase == EventPhase.CAPTURING_PHASE ) { trace("phase de capture de l'vnement : " + pEvt.type ); trace("noeud en cours de notification : " + pEvt.currentTarget ); } } function clicRemontee ( pEvt:MouseEvent ):void { /* affiche : phase de remonte de l'vnement : click noeud en cours de notification : [object Sprite] */ if ( pEvt.eventPhase == EventPhase.BUBBLING_PHASE ) { trace("phase de remonte de l'vnement : " + pEvt.type ); trace("noeud en cours de notification : " + pEvt.currentTarget ); } }

Nous allons aller plus loin en ajoutant une rorganisation automatique des fentres lorsquune dentre elles est supprime. Pour cela nous modifions le code en intgrant un effet dinertie afin de disposer chaque instance de la classe Fenetre avec un effet de ralenti :
// modification de la cadence de l'animation stage.frameRate = 30; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.destX = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.destY = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 maFenetre.addEventListener ( Event.ENTER_FRAME, mouvement ); conteneurFenetres.addChild ( maFenetre );

);

25 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

} function mouvement ( pEvt:Event ):void { // algorithme dinertie pEvt.target.x -= ( pEvt.target.x - pEvt.target.destX ) * .3; pEvt.target.y -= ( pEvt.target.y - pEvt.target.destY ) * .3; }

Puis nous intgrons la logique ncessaire au sein des fonctions couteurs captureClic et clicRemontee :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; // modification de la cadence de l'animation stage.frameRate = 30; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.destX = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.destY = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 maFenetre.addEventListener ( Event.ENTER_FRAME, mouvement ); conteneurFenetres.addChild ( maFenetre ); } function mouvement ( pEvt:Event ):void { // algorithme dinertie pEvt.target.x -= ( pEvt.target.x - pEvt.target.destX ) * .3; pEvt.target.y -= ( pEvt.target.y - pEvt.target.destY ) * .3; } // souscription auprs du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, captureClic, true ); // souscription auprs du conteneur pour la phase de remonte conteneurFenetres.addEventListener ( MouseEvent.CLICK, clicRemontee );

);

26 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

function captureClic ( pEvt:MouseEvent ):void { pEvt.currentTarget.removeChild ( DisplayObject ( pEvt.target ) ); } function clicRemontee ( pEvt:MouseEvent ):void { var lng:int = pEvt.currentTarget.numChildren; var objetGraphique:DisplayObject; var maFenetre:Fenetre; while ( lng-- ) { // rcupration des objets graphiques objetGraphique = pEvt.currentTarget.getChildAt ( lng ); // si l'un d'entre eux est de type Fenetre if ( objetGraphique is Fenetre ) { // nous le transtypons en type Fenetre maFenetre = Fenetre ( objetGraphique ); // repositionnement de chaque occurrence maFenetre.destX = 7 + Math.round ( lng % 3 ) * ( maFenetre.width maFenetre.destY = 7 + Math.floor ( lng / 3 ) * ( maFenetre.height

+ 10 ); + 10 ); } } }

Nous profitons de la phase de remonte pour que le conteneur rorganise ses objets enfants lorsque lun dentre eux a t supprim de la liste daffichage. Nous verrons au cours du chapitre 8 intitul Programmation oriente objet comment notre code actuel pourrait tre encore amlior.

Ecouter plusieurs phases


Afin de suivre la propagation dun vnement, nous pouvons souscrire un couteur auprs de chacune des phases. En affichant les valeurs des proprits eventPhase, target et currentTarget de lobjet vnementiel nous obtenons le chemin parcouru par lvnement. 27 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

Dans le code suivant, la fonction ecoutePhase est souscrite auprs des trois phases de lvnement MouseEvent.CLICK :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; // modification de la cadence de l'animation stage.frameRate = 30; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.destX = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.destY = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 // souscription auprs de chaque instance pour la phase cible maFenetre.addEventListener ( MouseEvent.CLICK, ecoutePhase ); maFenetre.addEventListener ( Event.ENTER_FRAME, mouvement ); conteneurFenetres.addChild ( maFenetre ); } function mouvement ( pEvt:Event ):void { // algorithme dinertie pEvt.target.x -= ( pEvt.target.x - pEvt.target.destX ) * .3; pEvt.target.y -= ( pEvt.target.y - pEvt.target.destY ) * .3; } // souscription auprs du conteneur pour la phase de remonte conteneurFenetres.addEventListener ( MouseEvent.CLICK, ecoutePhase ); // souscription auprs du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, ecoutePhase, true ); function ecoutePhase ( pEvt:MouseEvent ):void { /* affiche : 1 : Phase de capture de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Sprite] 2 : Phase cible de l'vnement : click

);

28 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

Objet cible : [object Fenetre] Objet notifi : [object Fenetre] 3 : Phase de remonte de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Sprite] */ switch ( pEvt.eventPhase ) { case EventPhase.CAPTURING_PHASE : trace ( pEvt.eventPhase + " : Phase de capture de l'vnement : " + pEvt.type ); break; case EventPhase.AT_TARGET : trace ( pEvt.eventPhase + " : Phase cible de l'vnement : " + pEvt.type ); break; case EventPhase.BUBBLING_PHASE : trace ( pEvt.eventPhase + " : Phase de remonte de l'vnement : " + pEvt.type ); break; } trace( "Objet cible : " + pEvt.target ); trace( "Objet notifi : " + pEvt.currentTarget ); }

Le panneau de sortie nous affiche lhistorique de la propagation de lvnement MouseEvent.CLICK.

A retenir
A linstar de la phase de capture, la phase de remonte ne concerne que les objets parents. La phase de remonte permet un objet parent dtre notifi dun vnement provenant de lun de ses enfants. Un mme couteur peut tre souscrit aux diffrentes phases dun vnement.

La proprit Event.bubbles
Pour savoir si un vnement participe ou non la phase de remonte nous pouvons utiliser la proprit bubbles de lobjet vnementiel. Nous pouvons mettre jour la fonction ecoutePhase afin de savoir si lvnement en cours participe la phase de remonte :
function ecoutePhase ( pEvt:MouseEvent ):void {

29 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

/* affiche : 1 : Phase de capture de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Sprite] Evnement participant la phase de remonte : true 2 : Phase cible de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Fenetre] Evnement participant la phase de remonte : true 3 : Phase de remonte de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Sprite] Evnement participant la phase de remonte : true */ switch ( pEvt.eventPhase ) { case EventPhase.CAPTURING_PHASE : trace ( pEvt.eventPhase + " : Phase de capture de l'vnement : " + pEvt.type ); break; case EventPhase.AT_TARGET : trace ( pEvt.eventPhase + " : Phase cible de l'vnement : " + pEvt.type ); break; case EventPhase.BUBBLING_PHASE : trace ( pEvt.eventPhase + " : Phase de remonte de l'vnement : " + pEvt.type ); break; } trace( "Objet cible : " + pEvt.target ); trace( "Objet notifi : " + pEvt.currentTarget ); trace ( "Evnement participant la phase de remonte : " + pEvt.bubbles ); }

Notons que tout vnement se propageant vers le haut participe forcment aux phases de capture et cible.

Suppression dcouteurs
Comme nous lavons trait lors du chapitre 3 intitul Le modle vnementiel, lorsque nous souhaitons arrter lcoute dun vnement nous utilisons la mthode removeEventListener. Lorsque nous avons souscrit un couteur pour une phase spcifique nous devons spcifier la mme phase lors de lappel de la mthode removeEventListener. Si nous coutons un vnement pour la phase de capture : 30 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

objetParent.addEventListener ( MouseEvent.CLICK, clicBouton, true );

Pour supprimer lcoute nous devons spcifier la phase concerne :


objetParent.removeEventListener ( MouseEvent.CLICK, clicBouton, true );

Il en est de mme pour les phases de remonte et cible :


objetParent.removeEventListener ( MouseEvent.CLICK, clicBouton, false ); objetCible.removeEventListener ( MouseEvent.CLICK, clicBouton, false );

Ou bien de manire implicite :


objetParent.removeEventListener ( MouseEvent.CLICK, clicBouton ); objetCible.removeEventListener ( MouseEvent.CLICK, clicBouton );

En intgrant cela dans notre exemple prcdent, nous obtenons le code suivant :
// nombre de fentres var lng:int = 12; // cration d'un conteneur var conteneurFenetres:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurFenetres ); var maFenetre:Fenetre; // modification de la cadence de l'animation stage.frameRate = 30; for (var i:int = 0; i< lng; i++ ) { maFenetre = new Fenetre(); maFenetre.destX = 7 + Math.round ( i % 3 ) * ( maFenetre.width + 10 ); maFenetre.destY = 7 + Math.floor ( i / 3 ) * ( maFenetre.height + 10 // souscription auprs de chaque instance pour la phase cible maFenetre.addEventListener ( MouseEvent.CLICK, ecoutePhase ); // dsinscription auprs de chaque occurrence pour la phase cible maFenetre.removeEventListener ( MouseEvent.CLICK, ecoutePhase ); maFenetre.addEventListener ( Event.ENTER_FRAME, mouvement ); conteneurFenetres.addChild ( maFenetre ); } function mouvement ( pEvt:Event ):void { // algorithme dinertie

);

31 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

pEvt.target.x -= ( pEvt.target.x - pEvt.target.destX ) * .3; pEvt.target.y -= ( pEvt.target.y - pEvt.target.destY ) * .3; } // souscription auprs du conteneur pour la phase de capture conteneurFenetres.addEventListener ( MouseEvent.CLICK, ecoutePhase, true ); // dsinscription auprs du conteneur pour la phase de capture conteneurFenetres.removeEventListener ( MouseEvent.CLICK, ecoutePhase, true ); // souscription auprs du conteneur pour la phase de remonte conteneurFenetres.addEventListener ( MouseEvent.CLICK, ecoutePhase ); // dsinscription auprs du conteneur pour la phase de remonte conteneurFenetres.removeEventListener ( MouseEvent.CLICK, ecoutePhase ); function ecoutePhase ( pEvt:MouseEvent ):void { /* affiche : 1 : Phase de capture de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Sprite] 2 : Phase cible de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Fenetre] 3 : Phase de remonte de l'vnement : click Objet cible : [object Fenetre] Objet notifi : [object Sprite] */ switch ( pEvt.eventPhase ) { case EventPhase.CAPTURING_PHASE : trace ( pEvt.eventPhase + " : Phase de capture de l'vnement : " + pEvt.type ); break; case EventPhase.AT_TARGET : trace ( pEvt.eventPhase + " : Phase cible de l'vnement : " + pEvt.type ); break; case EventPhase.BUBBLING_PHASE : trace ( pEvt.eventPhase + " : Phase de remonte de l'vnement : " + pEvt.type ); break; } trace( "Objet cible : " + pEvt.target ); trace( "Objet notifi : " + pEvt.currentTarget ); trace ( "Evnement participant la phase de remonte : " + pEvt.bubbles ); }

32 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 6 Propagation vnementielle version 0.1.1

A retenir
La proprit bubbles dun objet vnementiel permet de savoir si lvnement en cours participe la phase de remonte. Lorsque nous avons souscrit un couteur pour une phase spcifique nous devons spcifier la mme phase lors de lappel de la mthode removeEventListener.

Nous avons abord au cours des chapitres prcdents un ensemble de concepts cls que nous allons pouvoir mettre profit ds maintenant. En route pour le nouveau chapitre intitul Interactivit !

33 / 33
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

7
Interactivit

AU CUR DE FLASH............................................................................................ 2 INTERACTIVITE AVEC SIMPLEBUTTON ...................................................... 2 ENRICHIR GRAPHIQUEMENT UN SIMPLEBUTTON ..................................................... 4 CREER UN MENU DYNAMIQUE ....................................................................... 9 MOUVEMENT PROGRAMMATIQUE ......................................................................... 14 LES PIEGES DU RAMASSE-MIETTES ............................................................ 19 AJOUT DE COMPORTEMENT BOUTON ....................................................... 20 ZONE RACTIVE .................................................................................................... 23 GESTION DU FOCUS .......................................................................................... 29 POUR ALLER PLUS LOIN ................................................................................. 34 ESPACE DE COORDONNEES ........................................................................... 37 EVENEMENT GLOBAL ...................................................................................... 40 MISE EN APPLICATION .................................................................................... 41 MISE A JOUR DU RENDU .................................................................................. 43 GESTION DU CLAVIER ..................................................................................... 45 DTERMINER LA TOUCHE APPUYE ....................................................................... 46 GESTION DE TOUCHES SIMULTANES .................................................................... 48 SIMULER LA METHODE KEY.ISDOWN .................................................................... 54 SUPERPOSITION ................................................................................................. 55 LVNEMENT EVENT.RESIZE ...................................................................... 58

1 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Au cur de Flash
Linteractivit est lune des forces majeures et le cur du lecteur Flash. En un temps rduit, nous avons toujours pu raliser une combinaison dobjets ractifs la souris, au clavier ou autres. La puissance apporte par ActionScript 3 introduit quelques nuances relatives linteractivit, que nous allons dcouvrir ensemble travers diffrents exercices pratiques.

Interactivit avec SimpleButton


Comme nous lavons dcouvert lors du chapitre 5 intitul Les symboles, la classe SimpleButton reprsente les symboles de type boutons en ActionScript 3. Cette classe savre trs pratique en offrant une gestion avance des boutons crs depuis lenvironnement auteur ou par programmation. Il faut considrer lobjet SimpleButton comme un bouton constitu de quatre DisplayObject affects chacun de ses tats. Chacun dentre eux est dsormais accessible par des proprits dont voici le dtail :
SimpleButton.upState : dfinit ltat haut SimpleButton.overState : dfinit ltat dessus

SimpleButton.downState : dfinit ltat abaiss

SimpleButton.hitTestState : dfinit ltat cliqu

Pour nous familiariser avec cette nouvelle classe nous allons tout dabord crer un simple bouton, puis laide de ce dernier nous construirons un menu dynamique. Dans un nouveau document Flash CS3, nous crons un symbole bouton, celui-ci est aussitt ajout la bibliothque :

2 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-1. Symbole bouton. Puis nous dfinissons une classe Bouton associe, laide du panneau Proprits de liaison. En dfinissant une classe associe nous pourrons instancier plus tard notre bouton par programmation. La figure 7-2 illustr le panneau :

Figure 7-2. Dfinition de classe associe. Nous posons une occurrence de ce dernier sur le scnario principal et lui donnons monBouton comme nom doccurrence. Chaque proprit relative ltat du bouton renvoie un objet de type flash.display.Shape :
/*affiche :

3 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

[object Shape] [object Shape] [object Shape] [object Shape] */ trace( monBouton.upState ); trace( monBouton.overState ); trace( monBouton.downState ); trace( monBouton.hitTestState );

Chaque tat peut tre dfini par nimporte quel objet de type DisplayObject. Lorsque nous crons un bouton dans lenvironnement auteur et quune simple forme occupe ltat Haut, nous obtenons un objet Shape pour chaque tat. Si nous avions cr un clip pour ltat Haut nous aurions rcupr un objet de type flash.display.MovieClip. La figure 7-3 illustre la correspondance entre chaque tat et chaque proprit :

Figure 7-3. Correspondance des proprits dtats. Il tait auparavant impossible daccder dynamiquement aux diffrents tats dun bouton. Nous verrons plus tard quun bouton, ainsi que ces tats et les sons associs, peuvent tre entirement crs ou dfinis par programmation. Nous reviendrons trs vite sur les autres nouveauts apportes par la classe SimpleButton. Pour enrichir graphiquement un bouton, nous devons comprendre comment celui-ci fonctionne, voyons prsent comment dcorer notre bouton afin de le rendre utilisable pour notre menu.

Enrichir graphiquement un SimpleButton


Un bouton ne se limite gnralement pas une simple forme cliquable, une lgende ou une animation ou dautres lments peuvent tre ajouts afin denrichir graphiquement le bouton. Avant de commencer coder, il faut savoir que la classe SimpleButton est 4 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

une classe particulire. Nous pourrions penser que celle-ci hrite de la classe DisplayObjectContainer, mais il n'en est rien. La classe SimpleButton hrite de la classe InteractiveObject et ne peut se voir ajouter du contenu travers les mthodes traditionnelles telles addChild, addChildAt, etc. Seules
upState, downState, overState et hitTestSate permettent dajouter et SimpleButton.

les

proprits

daccder

au

contenu

dune

occurrence

de

Le seul moyen de supprimer un tat du bouton est de passer la valeur null un tat du bouton :
// supprime l'tat Abaiss monBouton.downState = null;

Si nous testons le code prcdent, nous voyons que le bouton ne dispose plus dtat abaiss lorsque nous cliquons dessus. Pour ajouter une lgende notre occurrence de SimpleButton nous devons ajouter un champ texte chaque objet graphique servant dtat. Si nous ajoutons directement un objet TextField lun des tats nous le remplaons :
// cration du champ texte servant de lgende var maLegende:TextField = new TextField(); // nous affectons le contenu maLegende.text = "Lgende Bouton"; // nous passons la lgende en tant qu'tat (Haut) du bouton monBouton.upState = maLegende;

En testant le code prcdent, nous obtenons un bouton cliquable constitu dune lgende comme tat Haut. La figure 7-4 illustre le rsultat :

5 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-4. Bouton avec lgende comme tat Haut. Lorsque nous survolons le bouton, les trois autres tats sont conservs seul ltat upState (Haut) a t cras. Ce comportement nous apprend que mme si un seul tat a t dfini depuis lenvironnement auteur, le lecteur copie ltat Haut pour chaque tat du bouton. Si nous altrons un tat les autres continuent de fonctionner. Ce nest pas le rsultat que nous avions escompt, il va nous falloir utiliser une autre technique. Le problme vient du fait que nous najoutons pas le champ texte un objet servant dtat mais nous remplaons un tat par un champ texte. Afin dobtenir ce que nous souhaitons nous allons depuis lenvironnement auteur convertir ltat Haut en clip et y imbriquer un champ texte auquel nous donnons maLegende comme nom doccurrence.

6 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-4. Clip avec champ texte imbriqu pour ltat Haut. Nous devons obtenir un clip positionn sur ltat Haut, avec un champ texte imbriqu comme lillustre la figure 7-5.

Figure 7-5. Liste daffichage du bouton. Si nous ciblons ltat upState de notre occurrence, nous rcuprons notre clip tout juste cr :
// rcupration du clip positionn pour l'tat Haut // affiche : [object MovieClip] trace( monBouton.upState );

Puis nous ciblons le champ texte par la syntaxe pointe traditionnelle :


// variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); // affectation de contenu etatHaut.maLegende.text = "Ma lgende";

7 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Nous pourrions tre tents de donner un nom doccurrence au clip positionn sur ltat haut, mais souvenons-nous que seules les proprits upState, downState, overState et hitTestState permettent daccder aux diffrents tats. Mme si notre clip imbriqu sappelait monClipImbrique le code suivant renverrait undefined :
// affiche : undefined trace( monBouton.monClipImbrique );

Par dfaut le lecteur Flash duplique ltat Haut pour chaque tat la compilation. Ce qui garantie que lorsquun tat vient tre modifi lexcution, comme pour laffectation de contenu au sein du champ texte, les autres tats ne refltent pas la modification. Lorsque nous souhaitons avoir un seul tat global au bouton, nous trompons le lecteur en affectant notre clip servant dtat Haut chaque tat :
// variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); // affectation de contenu etatHaut.maLegende.text = "Ma lgende"; //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut;

Nous obtenons le rsultat suivant :

8 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-6. Occurrence de SimpleButton dcor. Une fois notre symbole bouton dcor, passons maintenant son intgration au sein dun menu.

A retenir
La classe SimpleButton est une classe particulire et nhrite pas de la classe DisplayObjectContainer. Chaque tat dun SimpleButton est dfini par quatre proprits : upState, overState, downState et hitTestState.

Crer un menu dynamique


Toute application ou site internet Flash se doit dintgrer une interface de navigation permettant lutilisateur de naviguer au sein du contenu. Le menu figure parmi les classiques du genre, nous avons tous dvelopp au moins une fois un menu. La mise en application de ce dernier va savrer intressante afin de dcouvrir de nouveaux comportements apports par ActionScript 3. Nous allons ensemble crer un menu dynamique en intgrant le bouton sur lequel nous avons travaill jusqu maintenant. Notre menu sera dploy verticalement, nous instancions chaque occurrence travers une boucle for : 9 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// cration du conteneur var conteneur:Sprite = new Sprite(); conteneur.x = 20; addChild ( conteneur ); function creeMenu ():void { var lng:int = 5; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut; // disposition des instances monBouton.y = 20 + i * (monBouton.height + 10); conteneur.addChild ( monBouton ); } } creeMenu();

En testant notre code, nous obtenons le rsultat illustr par la figure suivante :

10 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-7. Instances de symboles Bouton. Durant la boucle for nous dcalons chaque bouton du menu grce la valeur de la variable i qui est incrmente. En multipliant la hauteur de chaque occurrence par i nous obtenons une position y spcifique chaque bouton. Trs souvent, un menu est gnr dynamiquement partir de donnes externes provenant dun tableau local, dun fichier XML local, ou de donnes provenant dun serveur. Nous allons modifier le code prcdent en dfinissant un tableau contenant le nom des rubriques reprsenter au sein du menu. Le nombre de boutons du menu sera li au nombre dlments du tableau :
// les rubriques var legendes:Array = new Array ( "Accueil", "Photos", "Liens", "Contact" ); // cration du conteneur var conteneur:Sprite = new Sprite(); conteneur.x = 20; addChild ( conteneur ); function creeMenu ():void {

11 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

var lng:int = legendes.length; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut; // disposition des instances monBouton.y = 20 + i * (monBouton.height + 10); conteneur.addChild ( monBouton ); } } creeMenu();

Notre menu est maintenant li au nombre dlments du menu, si nous retirons ou ajoutons des lments au tableau source de donnes, notre menu sera mis jour automatiquement. Afin que chaque bouton affiche une lgende spcifique nous rcuprons chaque valeur contenue dans le tableau et laffectons chaque champ texte contenu dans les boutons. Nous pouvons facilement rcuprer le contenu du tableau au sein de la boucle, grce la syntaxe crochet :
function creeMenu ():void { var lng:int = legendes.length; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); /* affiche : Accueil Photos Liens

12 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Contact */ trace( legendes[i] ); // variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut; // disposition des instances monBouton.y = 20 + i * (monBouton.height + 10); conteneur.addChild ( monBouton ); } }

Au sein de la boucle nous ciblons notre champ texte et nous lui affectons le contenu :
function creeMenu ():void { var lng:int = legendes.length; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); // affectation du contenu etatHaut.maLegende.text = legendes[i]; //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut; // disposition des instances monBouton.y = 20 + i * (monBouton.height + 10); conteneur.addChild ( monBouton ); } }

13 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

En testant le code prcdent nous obtenons le rsultat illustr par la figure 7-8 :

Figure 7-8. Menu dynamique avec lgendes. Notre menu est termin, nous pourrions en rester l mais il faut avouer que ce dernier manque de vie. Nous allons lui donner vie en ajoutant un peu de mouvement. A laide dun effet de glissement, nous allons rendre ce dernier plus attrayant. En route vers la notion de mouvement programmatique !

Mouvement programmatique
Afin de crer diffrents mouvements par programmation nous pouvons utiliser nos propres algorithmes, diffrentes librairies opensource ou bien la classe Tween intgre Flash CS3. La classe Tween rside dans le paquetage fl.transitions et doit tre importe afin dtre utilise. Afin de donner du mouvement un objet graphique dans Flash, nous pouvons utiliser les vnements Event.ENTER_FRAME ou TimerEvent.TIMER. La
Tween utilise en interne un vnement Event.ENTER_FRAME afin de modifier la proprit de lobjet. Un

classe

large nombre de mouvements sont disponibles, de type cintique, lastique, rebond ou bien constant. 14 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Nous allons ajouter un effet dlasticit permettant notre menu dtre dploy de manire ludique. Pour cela nous associons un objet Tween chaque instance de bouton. Chaque rfrence lobjet Tween est stocke au sein de loccurrence bouton pour pouvoir tre rcupre facilement plus tard. La classe Tween nest pas automatiquement importe pour le compilateur, nous devons donc le faire afin de lutiliser. Nous coutons lvnement MouseEvent.CLICK des boutons :
// import des classes Tween et Elastic pour le type de mouvement import fl.transitions.Tween; import fl.transitions.easing.Elastic; // les rubriques var legendes:Array = new Array ( "Accueil", "Photos", "Liens", "Contact" ); // cration du conteneur var conteneur:Sprite = new Sprite(); conteneur.x = 20; addChild ( conteneur ); function creeMenu ():void { var lng:int = legendes.length; var monBouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); etatHaut.maLegende.text = legendes[i]; //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut; // disposition des instances monBouton.tween = new Tween ( monBouton, "y", Elastic.easeOut, 0, 20 + i * (monBouton.height + 10), 3, true ); conteneur.addChild ( monBouton );

15 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

} } creeMenu(); // capture de l'vnement MouseEvent.CLICK auprs du conteneur conteneur.addEventListener ( MouseEvent.CLICK, clicMenu, true ); function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object Bouton] trace( pEvt.target ); // affiche : [object Sprite] trace( pEvt.currentTarget ); }

La position dans laxe des y de chaque bouton est dsormais gre par notre objet Tween. En stockant chaque objet Tween cr au sein des boutons nous pourrons y faire rfrence nimporte quel moment en ciblant plus tard la proprit tween. Notre menu se dploie avec un effet dlasticit et devient beaucoup plus interactif. Nous ne sommes pas restreints un seul type de mouvement, nous allons aller plus loin en ajoutant un effet similaire lors du survol. Cette fois, le mouvement se fera sur la largeur des boutons. Nous stockons une nouvelle instance de la classe Tween pour grer leffet de survol de chaque bouton :
// import des classes Tween et Elastic pour le type de mouvement import fl.transitions.Tween; import fl.transitions.easing.Elastic; // les rubriques var legendes:Array = new Array ( "Accueil", "Photos", "Liens", "Contact" ); // cration du conteneur var conteneur:Sprite = new Sprite(); conteneur.x = 20; addChild ( conteneur ); function creeMenu ():void { var lng:int = legendes.length; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) {

16 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// cration des occurrences du symbole Bouton monBouton = new Bouton(); // variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); etatHaut.maLegende.text = legendes[i]; //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut; // disposition des instances monBouton.tween = new Tween ( monBouton, "y", Elastic.easeOut, 0, 20 + i * (monBouton.height + 10), 3, true ); // un objet Tween est cr pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); conteneur.addChild ( monBouton ); } } creeMenu(); // capture de l'vnement MouseEvent.CLICK auprs du conteneur conteneur.addEventListener ( MouseEvent.CLICK, clicMenu, true ); conteneur.addEventListener ( MouseEvent.ROLL_OVER, survolBouton, true ); conteneur.addEventListener ( MouseEvent.ROLL_OUT, quitteBouton, true ); function survolBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo ( 1.1, 2 ); } function quitteBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo ( 1, 2 ); } function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object Bouton] trace( pEvt.target );

17 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// affiche : [object Sprite] trace( pEvt.currentTarget ); }

Au sein des fonctions survolBouton et quitteBouton nous rcuprons lobjet Tween associ lobjet survol au sein de la variable monTween. Grce la mthode continueTo de lobjet Tween, nous pouvons redmarrer, stopper ou bien lancer lanimation dans le sens inverse. Dans le code prcdent nous avions dfini les valeurs de dpart et darrive pour ltirement du bouton. Nous augmentons de 10% la taille du bouton au survol et nous ramenons sa taille 100% lorsque nous quittons le survol du bouton. Nous obtenons un menu lastique ragissant au survol, mais il nous reste une chose optimiser. Si nous regardons bien, nous voyons que le champ texte interne au bouton est lui aussi redimensionn. Ce problme intervient car nous redimensionnons lenveloppe principale du bouton dans lequel se trouve notre champ texte. En tirant lenveloppe conteneur nous tirons les enfants et donc la lgende. Nous allons modifier notre symbole Bouton afin de pouvoir redimensionner la forme de fond de notre bouton sans altrer le champ texte. Pour cela, au sein du symbole Bouton nous ditons le clip plac sur ltat Haut et transformons la forme de fond en clip auquel nous donnons fondBouton comme nom doccurrence. Nous allons ainsi redimensionner le clip interne ltat upState, sans altrer le champ texte. Nous modifions notre code en associant lobjet Tween au clip fondBouton :
// un objet Tween est cr pour les effets de survol monBouton.tweenSurvol = new Tween ( etatHaut.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true );

Au final, la classe SimpleButton savre trs pratique mais ne facilite pas tellement la tche ds lors que nos boutons sont quelque peu travaills. Contrairement la classe Button en ActionScript 1 et 2, la classe SimpleButton peut diffuser un vnement Event.ENTER_FRAME. Lutilisation de la classe SimpleButton savre limit de par son manque de souplesse en matire de dcoration. Nous allons refaire le mme exercice en utilisant cette fois des instances de Sprite auxquels nous ajouterons un comportement bouton, une fois termin nous ferons un bilan.

18 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

A retenir
La classe Tween permet dajouter du mouvement nos objets graphiques. Celle-ci rside dans le paquetage fl.transitions et doit tre importe explicitement afin dtre utilise. Les diffrentes mthodes et proprits de la classe Tween permettent de grer le mouvement.

Les piges du ramasse-miettes


Comme nous lavons vu depuis le dbut de louvrage, le ramassemiettes figure parmi les lments essentiels prendre en considration lors du dveloppement dapplications ActionScript 3. Dans lexemple prcdent, nous avons utilis la classe Tween afin de grer les mouvements de chaque bouton. Une ancienne habitude provenant des prcdentes versions dActionScript pourrait nous pousser ne pas conserver de rfrences aux objets Tween. Dans le code suivant, nous avons modifi la fonction creeMenu de manire ne pas stocker de rfrences aux objets Tween :
function creeMenu ():void { var lng:int = legendes.length; var monBouton:Bouton; var tweenMouvement:Tween; var tweenSurvol:Tween; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // variable rfrenant le clip utilis pour l'tat Haut var etatHaut:MovieClip = MovieClip ( monBouton.upState ); etatHaut.maLegende.text = legendes[i]; //affectation du clip pour tous les tats monBouton.upState = etatHaut; monBouton.downState = etatHaut; monBouton.overState = etatHaut; monBouton.hitTestState = etatHaut; // disposition des instances tweenMouvement = new Tween ( monBouton, "y", Elastic.easeOut, 0, 20 + i * (monBouton.height + 10), 3, true );

19 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// un objet Tween est cr pour les effets de survol tweenSurvol = new Tween ( etatHaut.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); conteneur.addChild ( monBouton ); } }

Une fois lexcution de la fonction creeMenu, les variables tweenMouvement et tweenSurvol sont supprimes. Nos objets Tween ne sont plus rfrencs au sein de notre application. Si nous dclenchons manuellement le passage du ramasse-miettes aprs la cration du menu :
creeMenu(); // dclenchement du ramasse-miettes System.gc();

Nous remarquons que le mouvement de chaque bouton est interrompu, car le ramasse-miettes vient de supprimer les objets Tween de la mmoire.

A retenir
Veillez bien rfrencer les objets ncessaires afin quils ne soient pas supprims par le ramasse-miettes sans que vous ne layez dcid.

Ajout de comportement bouton


Un grand nombre de dveloppeurs Flash utilisaient en ActionScript 1 et 2 des symboles clips en tant que boutons. En dfinissant lvnement onRelease ces derniers devenaient cliquables. En ActionScript 3 le mme comportement peut tre obtenu en activant la proprit buttonMode sur une occurrence de Sprite ou MovieClip. Dans un nouveau document, nous crons un symbole Sprite grce la technique aborde lors du chapitre 5 intitul Les symboles. Nous lui associons Bouton comme nom de classe grce au panneau Proprits de Liaison, puis nous transformons la forme contenue dans ce dernier en un nouveau clip. Nous lui donnons fondBouton comme nom doccurrence. Au dessus de ce clip nous crons un champ texte dynamique que nous appelons maLegende : 20 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-9. Symbole clip. Puis nous disposons nos instances de Bouton verticalement :
// les rubriques var legendes:Array = new Array ( "Accueil", "Nouveauts", "Photos", "Liens", "Contact" ); // cration du conteneur var conteneur:Sprite = new Sprite(); conteneur.x = 20; addChild ( conteneur ); function creeMenu ():void { // nombre de rubriques var lng:int = legendes.length; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) {

21 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// instanciation du symbole Bouton monBouton = new Bouton(); monBouton.y = 20 + i * (monBouton.height + 10); // ajout la liste d'affichage conteneur.addChild ( monBouton ); } } creeMenu();

Afin dactiver le comportement bouton sur des objets autres que SimpleButton nous devons activer la proprit buttonMode :
// activation du comportement bouton monBouton.buttonMode = true;

Puis nous ajoutons le texte :


function creeMenu ():void { // nombre de rubriques var lng:int = legendes.length; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) { // instanciation du symbole Bouton monBouton = new Bouton(); monBouton.y = 20 + i * (monBouton.height + 10); // activation du comportement bouton monBouton.buttonMode = true; // affectation du contenu monBouton.maLegende.text = legendes[i]; // ajout la liste d'affichage conteneur.addChild ( monBouton ); } }

En utilisant dautres objets que la classe SimpleButton pour crer des boutons nous devons prendre en considration certains comportements comme la ractivit des objets imbriqus avec la souris. La manire dont les objets ragissent aux vnements souris a t modifie en ActionScript 3. Nous allons prsent dcouvrir ces subtilits.

22 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Zone ractive
Si nous survolons les boutons de notre menu, nous remarquons que le curseur reprsentant une main ne saffiche pas sur la totalit de la surface des boutons. Le comportement est illustr par la figure suivante :

Figure 7-10. Zone ractive. Si nous cliquons sur la zone occupe par le champ texte imbriqu dans chaque bouton, lobjet cible nest plus le bouton mais le champ texte. A linverse si nous cliquons sur la zone non occupe par le champ texte, lobjet cible est le clip fondBouton. Ainsi, en cliquant sur les boutons de notre menu, lobjet ractif peut tre le clip ou le champ texte imbriqu. Nous capturons conteneur : lvnement
MouseEvent.CLICK

auprs

du

// capture de l'vnement MouseEvent.CLICK auprs du conteneur conteneur.addEventListener ( MouseEvent.CLICK, clicMenu, true ); function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object TextField] trace( pEvt.target );

23 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// affiche : [object Sprite] trace( pEvt.currentTarget ); }

A chaque clic la fonction clicMenu est dclenche, la proprit target de lobjet vnementiel renvoie une rfrence vers lobjet cible de lvnement. La proprit currentTarget rfrence le conteneur actuellement notifi de lvnenement MouseEvent.CLICK. Souvenez-vous, la proprit target rfrence lobjet cible de lvnement. La proprit currentTarget rfrence lobjet sur lequel nous avons appel la mthode addEventListener. Si nous cliquons sur le bouton, les objets enfants ragissent aux entres souris. La proprit target renvoie une rfrence vers chaque objet interactif. Si nous cliquons ct du champ texte imbriqu, le clip fondBouton ragit :
function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object MovieClip] trace( pEvt.target ); }

Si nous cliquons sur le champ texte imbriqu la proprit target renvoie une rfrence vers ce dernier :
function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object TextField] trace( pEvt.target ); }

Afin dtre srs que notre zone sensible sera uniquement lenveloppe parente nous dsactivons la sensibilit la souris pour tous les objets enfants grce la proprit mouseChildren. Ainsi, lobjet cible sera lenveloppe principale du bouton :
// dsactivation des objets enfants monBouton.mouseChildren = false;

Une fois la proprit mouseChildren passe false, lensemble des objets enfants au bouton ne ragissent plus la souris. Notre bouton est parfaitement cliquable sur toute sa surface : 24 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-11. Dsactivation des objets enfants. En cliquant sur chacun des boutons, la proprit target rfrence le bouton cliqu :
function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object Bouton] trace( pEvt.target ); }

Les boutons du menu sont dsormais parfaitement cliquables, nous allons intgrer la classe Tween que nous avons utilis dans lexemple prcdent :
// import des classes Tween et Elastic pour le type de mouvement import fl.transitions.Tween; import fl.transitions.easing.Elastic; // cration du conteneur var conteneur:Sprite = new Sprite(); conteneur.x = 20; addChild ( conteneur ); // les rubriques var legendes:Array = new Array ( "Accueil", "Nouveauts", "Photos", "Liens", "Contact" );

25 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

function creeMenu () { var lng:int = legendes.length; var monBouton:Bouton; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton monBouton.buttonMode = true; // dsactivation des objets enfants monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = legendes[i]; // disposition des instances monBouton.tween = new Tween ( monBouton, "y", Elastic.easeOut, 0, 20 + i * (monBouton.height + 10), 3, true ); // un objet Tween est cr pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); conteneur.addChild ( monBouton ); } } creeMenu(); // capture de l'vnement MouseEvent.CLICK auprs du conteneur conteneur.addEventListener ( MouseEvent.CLICK, clicMenu, true ); conteneur.addEventListener ( MouseEvent.ROLL_OVER, survolBouton, true ); conteneur.addEventListener ( MouseEvent.ROLL_OUT, quitteBouton, true ); function survolBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo ( 1.1, 2 ); } function quitteBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo ( 1, 2 );

26 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

} function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object Bouton] trace( pEvt.target ); // affiche : [object Sprite] trace( pEvt.currentTarget ); }

Nous obtenons le mme menu quauparavant laide doccurrences de SimpleButton. Si nous souhaitons donner un style diffrent notre menu, nous pouvons jouer avec les proprits et mthodes des objets Tween. Dans le code suivant nous disposons le menu avec un effet de rotation. Pour cela nous modifions simplement les lignes grant la disposition des occurrences :
function creeMenu () { var lng:int = legendes.length; var monBouton:Bouton; var angle:int = 360 / lng; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton monBouton.buttonMode = true; // dsactivation des objets enfants monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = legendes[i]; // disposition des instances monBouton.tween = new Tween ( monBouton, "rotation", Elastic.easeOut, 0, i * angle, 3, true ); // un objet Tween est cr pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); conteneur.addChild ( monBouton ); }

27 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Puis nous dplacons le conteneur :


conteneur.x = 150; conteneur.y = 150;

Si le texte des boutons disparat, cela signifie que nous navons pas intgr les contours de polices pour nos champs texte. Le lecteur Flash ne peut rendre laffichage un champ texte ayant subi une rotation ou un masque, sil contient une police non embarque. Il faut toujours sassurer davoir bien intgr les contours de polices. Une fois les contours de polices intgrs, en modifiant simplement ces quelques lignes nous obtenons un menu totalement diffrent comme lillustre la figure 7-12 :

Figure 7-12. Disposition du menu avec effet de rotation. Libre vous dimaginer toutes sortes deffets en travaillant avec les diffrentes proprits de la classe DisplayObject. Dans le code prcdent nous avons modifi la proprit rotation. Une application Flash peut prendre en compte des comportements interactifs relativement subtils comme la perte de focus de 28 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

lanimation. Le lecteur Flash 9 peut en ActionScript 3 dtecter facilement la perte et le gain du focus de lanimation. Nous allons adapter notre menu afin quil prenne en considration cette fonctionnalit.

A retenir
La proprit buttonMode affecte un comportement bouton aux objets MovieClip et Sprite. Pour sassurer que lenveloppe principale seulement reoive les entres souris, nous passons la valeur false la proprit mouseChildren.

Gestion du focus
Nous allons travailler sur la gestion du focus prsent, deux nouveaux vnements ont vu le jour en ActionScript 3 :
Event.ACTIVATE : vnement diffus lorsque le lecteur Flash

gagne le focus.

Event.DEACTIVATE : vnement diffus lorsque le lecteur

Flash perd le focus.

Ces deux vnements sont diffuss par tout DisplayObject prsent ou non au sein de la liste daffichage. Grce ces vnements nous allons pouvoir fermer le menu lorsque lanimation perdra le focus puis le rouvrir linverse. Pour savoir quand lutilisateur na plus le focus sur le lecteur il suffit dcouter lvnement Event.DEACTIVATE de nimporte quel DisplayObject, quil soit sur la liste daffichage ou non :
// souscription auprs de l'vnement Event.DEACTIVATE auprs du conteneur conteneur.addEventListener ( Event.DEACTIVATE, perteFocus ); function perteFocus ( pEvt:Event ):void { // rcupration du nombre de boutons var lng:int = pEvt.target.numChildren; var bouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // nous rcuprons chaque bouton du menu bouton = Bouton ( pEvt.target.getChildAt ( i ) ); // nous ciblons chaque objet Tween var myTween:Tween = bouton.tween;

29 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

myTween.func = Strong.easeOut; // nous dfinissons les points de dpart et d'arrive myTween.continueTo ( i * 10, 1 ); } }

Attention, la classe Strong doit tre importe :


import fl.transitions.Tween; import fl.transitions.easing.Elastic; import fl.transitions.easing.Strong;

Lorsque lanimation perd le focus notre menu se referme avec un effet dinertie, la figure suivante illustre le rsultat :

Figure 7-13. Menu referm. Il nous faut ouvrir nouveau le menu lorsque lapplication rcupre le focus, pour cela nous coutons lvnement Event.ACTIVATE :
function perteFocus ( pEvt:Event ):void { // rcupration du nombre de boutons var lng:int = pEvt.target.numChildren; var bouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // nous rcuprons chaque bouton du menu bouton = Bouton ( pEvt.target.getChildAt ( i ) ); // nous ciblons chaque objet Tween var myTween:Tween = bouton.tween; myTween.func = Strong.easeOut; // nous dfinissons les points de dpart et d'arrive myTween.continueTo ( i * 10, 1 ); } if( pEvt.target.hasEventListener( Event.ACTIVATE) == false )

30 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

{ // souscription auprs de l'vnement Event.ACTIVATE auprs du conteneur pEvt.target.addEventListener ( Event.ACTIVATE, gainFocus ); } } function gainFocus ( pEvt:Event ):void { // rcupration du nombre de boutons var lng:int = pEvt.target.numChildren; var bouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // nous rcuprons chaque bouton du menu bouton = Bouton ( pEvt.target.getChildAt ( i ) ); // nous ciblons chaque objet Tween var myTween:Tween = bouton.tween; myTween.func = Elastic.easeOut; // nous dfinissons les points de dpart et d'arrive myTween.continueTo ( (360/lng) * i, 2 ); } }

Nous dclenchons les diffrents mouvements laide la mthode continueTo de lobjet Tween, nous pouvons jouer avec les diffrentes valeurs et proprits altres par le mouvement pour obtenir dautres effets :
Voici le code complet de notre menu dynamique : // import des classes Tween et Elastic pour le type de mouvement import fl.transitions.Tween; import fl.transitions.easing.Elastic; import fl.transitions.easing.Strong; // cration du conteneur var conteneur:Sprite = new Sprite(); conteneur.x = 150; conteneur.y = 150; addChild ( conteneur ); // les rubriques var legendes:Array = new Array ( "Accueil", "Nouveauts", "Photos", "Liens", "Contact" ); function creeMenu () {

31 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

var lng:int = legendes.length; var monBouton:Bouton; var angle:int = 360 / lng; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton monBouton.buttonMode = true; // dsactivation des objets enfants monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = legendes[i]; // disposition des instances monBouton.tween = new Tween ( monBouton, "rotation", Elastic.easeOut, 0, i * angle, 3, true ); // un objet Tween est cr pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); conteneur.addChild ( monBouton ); } } creeMenu(); // capture de l'vnement click sur le scnario principal conteneur.addEventListener ( MouseEvent.CLICK, clicMenu, true ); conteneur.addEventListener ( MouseEvent.ROLL_OVER, survolBouton, true ); conteneur.addEventListener ( MouseEvent.ROLL_OUT, quitteBouton, true ); function survolBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo ( 1.1, 2 ); } function quitteBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo ( 1, 2 ); }

32 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

function clicMenu ( pEvt:MouseEvent ):void { // affiche : [object Bouton] trace( pEvt.target ); // affiche : [object Sprite] trace( pEvt.currentTarget ); } // souscription auprs de l'vnement Event.DEACTIVATE auprs du conteneur conteneur.addEventListener ( Event.DEACTIVATE, perteFocus ); function perteFocus ( pEvt:Event ):void { // rcupration du nombre de boutons var lng:int = pEvt.target.numChildren; var bouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // nous rcuprons chaque bouton du menu bouton = Bouton ( pEvt.target.getChildAt ( i ) ); // nous ciblons chaque objet Tween var myTween:Tween = bouton.tween; myTween.func = Strong.easeOut; // nous dfinissons les points de dpart et d'arrive myTween.continueTo ( i * 10, 1 ); } if( pEvt.target.hasEventListener( Event.ACTIVATE) == false ) { // souscription auprs de l'vnement Event.ACTIVATE auprs du conteneur pEvt.target.addEventListener ( Event.ACTIVATE, gainFocus ); } } function gainFocus ( pEvt:Event ):void { // rcupration du nombre de boutons var lng:int = pEvt.target.numChildren; var bouton:Bouton; for (var i:int = 0; i< lng; i++ ) {

33 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// nous rcuprons chaque bouton du menu bouton = Bouton ( pEvt.target.getChildAt ( i ) ); // nous ciblons chaque objet Tween var myTween:Tween = bouton.tween; myTween.func = Elastic.easeOut; // nous dfinissons les points de dpart et d'arrive myTween.continueTo ( 60 * (i+1), 2 ); } }

Notre menu dynamique ragit dsormais la perte ou au gain du focus de lanimation. Nous pourrions varier les diffrents effets et augmenter les capacits du menu sans limites, cest ici que rside la puissance de Flash !

A retenir
Les vnements Event.ACTIVATE et Event.DEACTIVATE permettent de grer la perte ou le gain de focus du lecteur.

Pour aller plus loin


Nous navons pas encore utilis la fonction clicMenu souscrite auprs de lvnement MouseEvent.CLICK de chaque bouton. En associant un lien chaque bouton nous ouvrirons une fentre navigateur. Il nous faut dans un premier temps stocker chaque lien, pour cela nous allons crer un second tableau spcifique :
// les liens var liens:Array = new Array ("http://www.oreilly.com", "http://www.bytearray.org", "http://www.flickr.com", "http://www.linkdup.com", "http://www.myspace.com");

Chaque bouton contient au sein de sa proprit lien un lien associ :


function creeMenu () { var lng:int = legendes.length; var monBouton:Bouton; var angle:int = 360 / lng; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton

34 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

monBouton.buttonMode = true; // dsactivation des objets enfants monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = legendes[i]; // chaque bouton stocke son lien associ monBouton.lien = liens[i]; // disposition des instances monBouton.tween = new Tween ( monBouton, "rotation", Elastic.easeOut, 0, i * angle, 3, true ); // un objet Tween est cr pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); conteneur.addChild ( monBouton ); } }

Lorsque la fonction clicMenu est dclenche, nous ouvrons une nouvelle fentre navigateur :
function clicMenu ( pEvt:MouseEvent ):void { // ouvre une fentre navigateur pour le lien cliqu navigateToURL ( new URLRequest ( pEvt.target.lien ) ); }

Nous rcuprons le lien au sein du bouton cliqu, rfrenc par la proprit target de lobjet vnementiel. La fonction navigateToURL stocke au sein du paquetage flash.net nous permet douvrir une nouvelle fentre navigateur et datteindre le lien spcifi. En ActionScript 3 tout lien HTTP doit tre envelopp dans un objet URLRequest. Pour plus dinformations concernant les changes externes, rendezvous au chapitre 15 intitul Communication externe. Notons quil est possible de crer une proprit lien car la classe MovieClip est dynamique. La cration dune telle proprit au sein dune instance de SimpleButton est impossible. Notre code pourrait tre amlior en prfrant un tableau associatif aux deux tableaux utiliss actuellement. Nous pourrions regrouper nos deux tableaux liens et legendes en un seul tableau : 35 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// rubriques et liens var donnees:Array = new Array (); // ajout des donnees.push donnees.push ); donnees.push donnees.push donnees.push rubriques et liens associs ( { rubrique : "Accueil", lien : "http://www.oreilly.com" } ); ( { rubrique : "Nouveauts", lien : "http://www.bytearray.org" } ( { rubrique : "Photos", lien : "http://www.flickr.com" } ); ( { rubrique : "Liens", lien : "http://www.linkdup.com" } ); ( { rubrique : "Contact", lien : "http://www.myspace.com" } );

En utilisant un tableau associatif nous organisons mieux nos donnes et rendons laccs simplifi. Nous modifions la fonction creeMenu afin de cibler les informations au sein du tableau associatif :
function creeMenu () { var lng:int = donnees.length; var monBouton:Bouton; var angle:int = 360 / lng; for ( var i:int = 0; i< lng; i++ ) { // cration des occurrences du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton monBouton.buttonMode = true; // dsactivation des objets enfants monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = donnees[i].rubrique; // chaque bouton stocke son lien associ monBouton.lien = donnees[i].lien; // disposition des instances monBouton.tween = new Tween ( monBouton, "rotation", Elastic.easeOut, 0, i * angle, 3, true ); // un objet Tween est cr pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); conteneur.addChild ( monBouton ); } }

Notre menu est maintenant termin ! Intressons-nous maintenant lespace de coordonnes. 36 / 60


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

A retenir
Il est prfrable dutiliser des tableaux associatifs plutt que des tableaux index. Lorganisation et laccs aux donnes seront optimiss et simplifis.

Espace de coordonnes
Pour toutes les entres souris, les objets graphiques diffusent des objets vnementiels de type flash.events.MouseEvent. Cette classe possde de nombreuses proprits dont voici le dtail :
MouseEvent.altKey : cense indiquer si la touche ALT est

enfonce au moment du clic.

MouseEvent.buttonDown : indique si le bouton principal de la

souris est enfonc au moment du clic.

MouseEvent.delta : indique le nombre de lignes qui doivent

dfiler chaque fois que l'utilisateur fait tourner la molette de sa souris dun cran. par rapport lespace de coordonnes de lobjet cliqu.

MouseEvent.localX : indique les coordonnes X de la souris

par rapport lespace de coordonnes de lobjet cliqu.

MouseEvent.localY : indique les coordonnes Y de la souris MouseEvent.relatedObject : indique lobjet sur lequel la souris pointe lors de lvnement MouseEvent.MOUSE_OUT.

: indique si la touche SHIFT est enfonce au moment du clic.


MouseEvent.shiftKey

MouseEvent.stageX : indique les coordonnes X de la souris par rapport lespace de coordonnes de lobjet Stage. MouseEvent.stageY : indique les coordonnes Y de la souris par rapport lespace de coordonnes de lobjet Stage.

En traant lobjet vnementiel, une reprsentation toString() est effectue :


// souscription auprs de l'vnement MouseMove du bouton monBouton.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); function bougeSouris ( pEvt:MouseEvent ):void { //affiche : [MouseEvent type="mouseMove" bubbles=true cancelable=false eventPhase=2 localX=28 localY=61 stageX=158.95000000000002 stageY=190 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace(pEvt);

37 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Ces proprits nous permettent par exemple de savoir quelle position se trouve la souris par rapport aux coordonnes de lobjet survol :
// souscription auprs de l'vnement MouseMove du bouton monBouton.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); function bougeSouris ( pEvt:MouseEvent ):void { /*affiche : Position X de la souris Position Y de la souris */ trace("Position X de la trace("Position Y de la } par rapport au bouton : 14 par rapport au bouton : 14 souris par rapport au bouton : " + pEvt.localX); souris par rapport au bouton : " + pEvt.localY);

La figure 7-14 illustre lintrt des proprits localX et localY :

Figure 7-14. Proprits localX et localY. Afin de savoir o se trouve la souris par rapport au stage et donc au scnario principal nous utilisons toujours les proprits stageX et stageY de lobjet vnementiel de type MouseEvent :
// souscription auprs de l'vnement MouseMove du bouton monBouton.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); function bougeSouris ( pEvt:MouseEvent ):void

38 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

{ /*affiche : Position X par rapport la scne : 184 Position Y par rapport la scne : 204 */ trace("Position X de la souris par rapport la scne : " + pEvt.stageX); trace("Position Y de la souris par rapport la scne : " + pEvt.stageY); }

La figure 7-15 illustre les proprits stageX et stageY :

Figure 7-15. Proprits stageX et stageY. Notons que lorsque lobjet Stage est la cible dun vnement souris, les proprits stageX, stageY et localX et localY de lobjet vnementiel renvoient la mme valeur.

A retenir

39 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Les proprits localX et localY renvoient les coordonnes de la souris par rapport aux coordonnes locale de lobjet cliqu. Les proprits stageX et stageY renvoient les coordonnes de la souris par rapport au scnario principal.

Evnement global
En ActionScript 1 et 2 les clips taient capables dcouter tous les vnements souris, mme si ces derniers taient invisibles ou non cliquables. Pour couter les dplacements de la souris sur la scne nous pouvions crire :
// dfinition d'un gestionnaire d'vnement monClip.onMouseMove = function () { // affiche : 78 : 211 trace( _xmouse + " : " + _ymouse ); }

En dfinissant une fonction anonyme sur la proprit onMouseMove nous disposions dun moyen efficace dcouter les dplacements de la souris sur toute lanimation. En ActionScript 3 les comportements souris ont t modifis, si nous coutons des vnements souris sur un objet non prsent au sein de la liste daffichage, lvnement nest pas diffus, car aucune interaction nintervient entre la souris et lobjet graphique. A linverse, si lobjet est visible lvnement nest diffus que lorsque la souris se dplace au-dessus de lobjet. Pour nous en rendre compte, nous posons une occurrence de symbole bouton sur le scnario principal que nous appelons monBouton et nous coutons lvnement MouseEvent.MOUSE_MOVE :
// souscription auprs de l'vnement MouseMove du bouton monBouton.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); function bougeSouris ( pEvt:MouseEvent ):void { trace("Dclench uniquement lors du dplacement de la souris sur le bouton monBouton"); }

Afin de mettre en application la notion dvnements globaux, nous allons dvelopper une application de dessin, nous ajouterons plus tard de nouvelles fonctionnalits celle-ci comme lexport JPEG et PNG afin de sauvegarder notre dessin. 40 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Mise en application
Pour mettre en application la notion dvnement global nous allons crer ensemble une application de dessin dans laquelle lutilisateur dessine lcran laide de la souris. La premire tape pour ce type dapplication consiste utiliser les capacits de dessin offertes par la classe proprit graphics de tout objet de type flash.display.DisplayObject. Notons que lAPI de dessin nest plus directement disponible sur la classe MovieClip mais depuis la proprit graphics de tout DisplayObject. Dans un nouveau document Flash CS3 nous allons tout dabord nous familiariser avec lAPI de dessin. Contrairement lAPI disponible en ActionScript 1 et 2 qui savrait limite, la version ActionScript 3 a t enrichie. Afin de dessiner nous allons crer un DisplayObject le plus simple possible et le plus optimis, pour cela nous nous orientons vers la classe flash.display.Shape :
// cration du conteneur de tracs vectoriels var monDessin:Shape = new Shape(); // ajout la liste d'affichage addChild ( monDessin );

Nous allons reproduire le mme mcanisme que dans la ralit en traant partir du point cliqu. Le premier vnement couter est donc lvnement MouseEvent.MOUSE_DOWN, pour dtecter le premier clic et dplacer la mine cette position :
// cration du conteneur de tracs vectoriels var monDessin:Shape = new Shape(); // ajout la liste d'affichage addChild ( monDessin ); // souscription auprs de l'vnement MouseEvent.MOUSE_DOWN du scnario stage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); function clicSouris ( pEvt:MouseEvent ):void { // position de la souris var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; }

A chaque clic souris nous rcuprons la position de la souris, nous allons prsent dplacer la mine en appelant la mthode moveTo :
// cration du conteneur de tracs vectoriels

41 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

var monDessin:Shape = new Shape(); // ajout la liste d'affichage addChild ( monDessin ); // souscription auprs de l'vnement MouseEvent.MOUSE_DOWN du scnario stage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); function clicSouris ( pEvt:MouseEvent ):void { // position de la souris var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // la mine est dplace cette position // pour commencer dessiner partir de cette position monDessin.graphics.moveTo ( positionX, positionY ); }

Pour linstant rien nest dessin lorsque nous cliquons car nous ne faisons que dplacer la mine de notre stylo imaginaire mais nous ne lui ordonnons pas de tracer, afin de dessiner nous devons appeler la mthode lineTo en permanence lorsque notre souris est en mouvement. Il nous faut donc couter un nouvel vnement. ajouter est lcoute de lvnement MouseEvent.MOUSE_MOVE qui va nous permettre de dclencher une fonction qui dessinera lorsque la souris sera dplace :
// cration du conteneur de tracs vectoriels var monDessin:Shape = new Shape(); // ajout la liste d'affichage addChild ( monDessin ); // souscription auprs de l'vnement MouseEvent.MOUSE_DOWN du scnario stage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); function clicSouris ( pEvt:MouseEvent ):void { // position de la souris var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // la mine est dplace cette position // pour commencer dessiner partir de cette position monDessin.graphics.moveTo ( positionX, positionY ); // lorsque la souris est clique nous commencons l'coute de celle ci pEvt.currentTarget.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } function bougeSouris ( pEvt:MouseEvent ):void

La

premire

chose

42 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

{ var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // la mine est dplace cette position // pour commencer dessiner partir de cette position monDessin.graphics.lineTo ( positionX, positionY ); }

Si nous testons le code prcdent, aucun trac napparatra car nous navons pas dfini le style du trac avec la mthode lineStyle de la classe Graphics :
// initialisation du style de trac monDessin.graphics.lineStyle ( 1, 0x990000, 1 );

Afin de ne pas continuer tracer lorsque lutilisateur relche la souris nous devons supprimer lcoute de lvnement MouseEvent.MOUSE_MOVE lorsque lvnement MouseEvent.MOUSE_UP est diffus :
// souscription auprs de l'vnement MouseEvent.MOUSE_UP du scnario stage.addEventListener ( MouseEvent.MOUSE_UP, relacheSouris ); function relacheSouris ( pEvt:MouseEvent ):void { // dsinscription auprs de l'vnement MouseEvent.MOUSE_MOVE pEvt.currentTarget.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); }

Tout fonctionne trs bien, mais en regardant attentivement nous voyons que le rendu du trac nest pas trs fluide. Si nous augmentons la cadence de lanimation environ 60 img/sec nous remarquons que le rendu est plus fluide. Il existe nanmoins une mthode beaucoup plus optimise pour amliorer le rafrachissement du rendu, nous allons intgrer cette fonctionnalit dans notre application.

A retenir
Seul lobjet Stage permet dcouter la souris de manire globale.

Mise jour du rendu


Cest du lecteur Flash 5 que la fonction updateAfterEvent a vu le jour. Cette fonction permet de mettre jour le rendu du lecteur indpendamment de la cadence de lanimation. Ds que la fonction updateAfterEvent est dclenche 43 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

lpoque

Chapitre 7 Interactivit version 0.1.2

le lecteur rend nouveau les vecteurs, en rsum cette fonction permet de mettre jour laffichage entre deux images cls. Cette fonction na dintrt quau sein de fonctions ntant pas lies la cadence de lanimation. Cest pour cette raison quen ActionScript 3, seules les classes MouseEvent, KeyboardEvent et TimerEvent possdent une mthode updateAfterEvent. Afin doptimiser le rendu des tracs dans notre application de dessin nous forons le rafrachissement du rendu au sein de la fonction bougeSouris :
function bougeSouris ( pEvt:MouseEvent ):void { var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // la mine est dplace cette position // pour commencer dessiner partir de cette position monDessin.graphics.lineTo ( positionX, positionY ); // force le rendu pEvt.updateAfterEvent(); }

Voici le code final de notre application de dessin :


// cration du conteneur de tracs vectoriels var monDessin:Shape = new Shape(); // ajout la liste d'affichage addChild ( monDessin ); // initialisation du style de trac monDessin.graphics.lineStyle ( 1, 0x990000, 1 ); // souscription auprs de l'vnement MouseEvent.MOUSE_DOWN du scnario stage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); // souscription auprs de l'vnement MouseEvent.MOUSE_UP du scnario stage.addEventListener ( MouseEvent.MOUSE_UP, relacheSouris ); function clicSouris ( pEvt:MouseEvent ):void { // position de la souris var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // la mine est dplae cette position // pour commencer dessiner partir de cette position monDessin.graphics.moveTo ( positionX, positionY ); // lorsque la souris est clique nous commencons l'coute de celle ci

44 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

pEvt.currentTarget.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } function bougeSouris ( pEvt:MouseEvent ):void { var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // la mine est dplae cette position // pour commencer dessiner partir de cette position monDessin.graphics.lineTo ( positionX, positionY ); // force le rendu pEvt.updateAfterEvent(); } function relacheSouris ( pEvt:MouseEvent ):void { // dsinscription auprs de l'vnement MouseEvent.MOUSE_MOVE pEvt.currentTarget.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); }

Sans augmenter la cadence de notre animation, nous amliorons le rendu des tracs, en conservant une cadence rduite notre application consomme peu de ressources sans mettre de ct les performances. Grce la proprit frameRate de la classe Stage nous rduisons la cadence de lanimation lexcution :
stage.frameRate = 2;

Avec une cadence rduite deux image/sec nos tracs restent fluides. Intressons-nous dsormais une autre notion lie linteractivit, la gestion du clavier.

A retenir
Grce la mthode updateAfterEvent, le rendu peut tre for entre deux images. Il en resulte dun meilleur rafraichissement de laffichage.

Gestion du clavier
Grce au clavier nous pouvons ajouter une autre dimension nos applications, cette fonctionnalit nest dailleurs aujourdhui pas assez utilise dans les sites traditionnels, nous allons capturer certaines

45 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

touches du clavier afin dintgrer de nouvelles fonctionnalits dans notre application de dessin cre auparavant. Il serait judicieux dintgrer un raccourci clavier afin deffacer le dessin en cours. Par dfaut, lobjet Stage gre les entres clavier. Deux vnements sont lis lcoute du clavier : clavier est enfonce. est relche.
KeyboardEvent.KEY_DOWN : diffus lorsquune touche du KeyboardEvent.KEY_UP : diffus lorsquune touche du clavier

Nous allons couter lvnement KeyboardEvent.KEY_DOWN auprs de lobjet Stage, chaque fois quune touche est enfonce la fonction couteur ecouteClavier est dclenche :
// souscription auprs de l'objet Stage pour l'vnement KEY_DOWN stage.addEventListener ( KeyboardEvent.KEY_DOWN, ecouteClavier ); function ecouteClavier ( pEvt:KeyboardEvent ):void { // affiche : [KeyboardEvent type="keyDown" bubbles=true cancelable=false eventPhase=2 charCode=114 keyCode=82 keyLocation=0 ctrlKey=false altKey=false shiftKey=false] trace( pEvt ); }

Un objet vnementiel de type KeyboardEvent est diffus, cette classe possde de nombreuses proprits dont voici le dtail :
KeyboardEvent.altKey : indique si la touche ALT est

enfonce, cette proprit nest pas prise en charge pour le moment. correspondant la touche du clavier. clavier est enfonce.

KeyboardEvent.charCode : contient le code caractre Unicode KeyboardEvent.ctrlKey : indique si la touche CTRL du KeyboardEvent.keyCode : valeur de code correspondant la

touche enfonce ou relche. touche sur le clavier. clavier est enfonce.

KeyboardEvent.keyLocation : indique lemplacement de la KeyboardEvent.shiftKey : indique si la touche SHIFT du

Dterminer la touche appuye


La proprit charCode de lobjet vnementiel diffus lors dun vnement clavier nous permet de dterminer la touche enfonce. 46 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Grce la mthode String.fromCharCode nous valuons le code Unicode et rcuprons le caractre correspondant :
// souscription auprs de l'objet stage pour l'vnement KEY_DOWN stage.addEventListener ( KeyboardEvent.KEY_DOWN, ecouteClavier ); function ecouteClavier ( pEvt:KeyboardEvent ):void { // affiche : d,f,g, etc... trace( String.fromCharCode( pEvt.charCode ) ); }

Pour tester la touche appuye, nous utilisons les proprits statiques de la classe Keyboard. Les codes de touche les plus courants sont stocks au sein de proprits statiques de la classe Keyboard. Pour savoir si la touche espace est enfonce nous crivons :
// souscription auprs de l'objet stage pour l'vnement KEY_DOWN stage.addEventListener ( KeyboardEvent.KEY_DOWN, ecouteClavier ); function ecouteClavier ( pEvt:KeyboardEvent ):void { if ( pEvt.keyCode == Keyboard.SPACE ) { trace("Touche ESPACE enfonce"); } }

Nous allons supprimer le dessin lorsque la touche ESPACE sera enfonce, afin de pouvoir commencer un nouveau dessin :
// souscription auprs de l'objet stage pour l'vnement KEY_DOWN stage.addEventListener ( KeyboardEvent.KEY_DOWN, ecouteClavier ); function ecouteClavier ( pEvt:KeyboardEvent ):void { if ( pEvt.keyCode == Keyboard.SPACE ) { // non effaons tous les prcdent tracs monDessin.graphics.clear(); // Attention, nous devons obligatoirement redfinir un style monDessin.graphics.lineStyle ( 1, 0x990000, 1 ); } }

47 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Nous pourrions changer la couleur des tracs pour chaque nouveau dessin :
// souscription auprs de l'objet stage pour l'vnement KEY_DOWN stage.addEventListener ( KeyboardEvent.KEY_DOWN, ecouteClavier ); function ecouteClavier ( pEvt:KeyboardEvent ):void { if ( pEvt.keyCode == Keyboard.SPACE ) { // non effaons tout les prcdent tras monDessin.graphics.clear(); // Attention, nous devons obligatoirement redfinir un style monDessin.graphics.lineStyle ( 1, Math.random()*0xFFFFFF, 1 ); } }

Nous reviendrons sur la manipulation avance des couleurs au cours du chapitre 12 intitul Programmation bitmap.

Gestion de touches simultanes


En ActionScript 1 et 2 la gestion des touches simultanes avec le clavier ntait pas facile. En ActionScript 3 grce aux proprits de la classe MouseEvent nous pouvons trs facilement dtecter la combinaison de touches. Celle-ci est rendue possible grce aux proprits altKey, ctrlKey et shiftKey. Nous allons ajouter la notion dhistorique dans notre application de dessin. Lorsque lutilisateur cliquera sur CTRL+Z nous reviendrons en arrire, pour repartir en avant lutilisateur cliquera sur CTRL+Y. Pour concevoir cette fonctionnalit nous avons besoin de pouvoir dissocier chaque trac, pour cela nous allons intgrer chaque trac dans un objet Shape diffrent et lajouter un conteneur principal. Pour pouvoir contenir des objets Shape, notre conteneur principal devra tre un DisplayObjectContainer, nous modifions donc les premires lignes de notre application pour intgrer un conteneur de type Sprite :
// cration du conteneur de tracs vectoriels var monDessin:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( monDessin );

Nous devons stocker au sein de deux tableaux les formes supprimes et les formes affiches : 48 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// tableau rfrenant les formes traces et supprimes var tableauTraces:Array = new Array(); var tableauAncienTraces:Array = new Array();

A chaque fois que la souris sera clique, nous devons crer un objet Shape spcifique chaque trac, ce dernier est alors rfrenc au sein du tableau tableauTraces. Nous modifions le corps de la fonction clicSouris :
function clicSouris ( pEvt:MouseEvent ):void { // position de la souris var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // un nouvel objet Shape est cr pour chaque trac var monNouveauTrace:Shape = new Shape(); // nous ajoutons le conteneur de trac au conteneur principal monDessin.addChild ( monNouveauTrace ); // puis nous rfrenons le trac au sein du tableau // rfrenant les tracs affichs tableauTraces.push ( monNouveauTrace ); // nous dfinisson un style de trac monNouveauTrace.graphics.lineStyle ( 1, 0x990000, 1 ); // la mine est dplae cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.moveTo ( positionX, positionY ); // si un nouveau trac intervient alors que nous sommes // repartis en arrire nous repartons de cet tat if ( tableauAncienTraces.length ) tableauAncienTraces = new Array(); // lorsque la souris est clique nous commencons l'coute de celle ci pEvt.currentTarget.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); }

Lorsque la souris est en mouvement nous devons tracer au sein du dernier objet Shape ajout, grce notre tableau tableauTraces nous rcuprons le dernier objet Shape et traons au sein de ce dernier :
function bougeSouris ( pEvt:MouseEvent ):void { var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // nous rcuprons le dernier objet Shape ajout // prt accueillir le nouveau trac var monNouveauTrace:Shape = tableauTraces[tableauTraces.length-1]; // la mine est dplae cette position

49 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// pour commencer dessiner partir de cette position monNouveauTrace.graphics.lineTo ( positionX, positionY ); // force le rafraichissement du rendu pEvt.updateAfterEvent(); }

La dernire fonction que nous devons modifier est la fonction ecouteClavier, cest au sein de celle-ci que nous testons la combinaison de touches et dcidons sil faut supprimer des tracs ou les rafficher :
function ecouteClavier ( pEvt:KeyboardEvent ):void { // si la barre espace est enfonce if ( pEvt.keyCode == Keyboard.SPACE ) { // nombre d'objets Shape contenant des tracs var lng:int = tableauTraces.length; // suppression des tras de la liste d'affichage while ( lng-- ) monDessin.removeChild ( tableauTraces[lng] ); // les tableaux d'historiques sont reinitialiss // les rfrences supprimes tableauTraces = new Array(); tableauAncienTraces = new Array(); } // code des touches Z et Y var ZcodeTouche:int = 90; var YcodeTouche:int = 89; if ( pEvt.ctrlKey ) { // si retour en arrire (CTRL+Z) if( pEvt.keyCode == ZcodeTouche && tableauTraces.length ) { // nous supprimons le dernier trac var aSupprimer:Shape = tableauTraces.pop() // nous stockons chaque trac supprim dans le tableau spcifique tableauAncienTraces.push ( aSupprimer ); // nous supprimons le trac de la liste d'affichage monDessin.removeChild( aSupprimer ); // si retour en avant (CTRL+Y) } else if ( pEvt.keyCode == YcodeTouche && tableauAncienTraces.length ) {

50 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// nous recuprons le dernier trac ajout var aAfficher:Shape = tableauAncienTraces.pop(); // nous le replaons dans le tableau de tracs l'affichage tableauTraces.push ( aAfficher ); // puis nous l'affichons monDessin.addChild ( aAfficher ); } } }

Voici le code complet de notre application de dessin avec gestion de lhistorique :


// cration du conteneur de tracs vectoriels var monDessin:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( monDessin ); // souscription auprs de l'vnement MouseEvent.MOUSE_DOWN du scnario stage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); // souscription auprs de l'vnement MouseEvent.MOUSE_UP du scnario stage.addEventListener ( MouseEvent.MOUSE_UP, relacheSouris ); // tableaux rfrenant les formes traces et supprimes var tableauTraces:Array = new Array(); var tableauAncienTraces:Array = new Array(); function clicSouris ( pEvt:MouseEvent ):void { // position de la souris var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // un nouvel objet Shape est cr pour chaque trac var monNouveauTrace:Shape = new Shape(); // nous ajoutons le conteneur de trac au conteneur principal monDessin.addChild ( monNouveauTrace ); // puis nous rfrenons le trac au sein du tableau // rfrenant les tracs affichs tableauTraces.push ( monNouveauTrace ); // nous dfinisson un style de trac monNouveauTrace.graphics.lineStyle ( 1, 0x990000, 1 ); // la mine est dplae cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.moveTo ( positionX, positionY ); // si un nouveau trac intervient alors que nous sommes repartis // en arrire nous repartons de cet tat if ( tableauAncienTraces.length ) tableauAncienTraces = new Array;

51 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

// lorsque la souris est clique nous commencons l'coute de celle ci pEvt.currentTarget.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } function bougeSouris ( pEvt:MouseEvent ):void { var positionX:Number = pEvt.stageX; var positionY:Number = pEvt.stageY; // nous rcuprons le dernier objet Shape ajout // prt accueillir le nouveau trac var monNouveauTrace:Shape = tableauTraces[tableauTraces.length-1]; // la mine est dplae cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.lineTo ( positionX, positionY ); // force le rafraichissement du rendu pEvt.updateAfterEvent(); } function relacheSouris ( pEvt:MouseEvent ):void { // dsinscription auprs de l'vnement MOUSE_MOVE pEvt.currentTarget.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } // souscription auprs de l'objet stage pour l'vnement KEY_DOWN stage.addEventListener ( KeyboardEvent.KEY_DOWN, ecouteClavier ); function ecouteClavier ( pEvt:KeyboardEvent ):void { // si la barre espace est enfonce if ( pEvt.keyCode == Keyboard.SPACE ) { // nombre d'objets Shape contenant des tracs var lng:int = tableauTraces.length; // suppression des tras de la liste d'affichage while ( lng-- ) monDessin.removeChild ( tableauTraces[lng] ); // les tableaux d'historiques sont reinitialiss // les rfrences supprimes tableauTraces = new Array(); tableauAncienTraces = new Array(); } // code des touches Z et Y var ZcodeTouche:int = 90;

52 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

var YcodeTouche:int = 89; if ( pEvt.ctrlKey ) { // si retour en arrire (CTRL+Z) if( pEvt.keyCode == ZcodeTouche && tableauTraces.length ) { // nous supprimons le dernier trac var aSupprimer:Shape = tableauTraces.pop() // nous stockons chaque trac supprim dans le tableau spcifique tableauAncienTraces.push ( aSupprimer ); // nous supprimons le trac de la liste d'affichage monDessin.removeChild( aSupprimer ); // si retour en avant (CTRL+Y) } else if ( pEvt.keyCode == YcodeTouche && tableauAncienTraces.length ) { // nous recuprons le dernier trac ajout var aAfficher:Shape = tableauAncienTraces.pop(); // nous le replaons dans le tableau de tracs l'affichage tableauTraces.push ( aAfficher ); // puis nous l'affichons monDessin.addChild ( aAfficher ); } } }

Nous verrons au cours du chapitre 10 intitul Programmation Bitmap comment capturer des donnes vectorielles afin de les compresser et les exporter en un format dimage spcifique type PNG ou JPEG. Attention : Lors du test de cette application, veillez bien cocher loption Dsactiver les raccourcis clavier du menu Contrle du lecteur Flash. Autrement les touches correspondant aux outils de lenvironnement auteur de Flash ne pourront tre dtectes par le lecteur. Nous pourrions imaginer une application permettant lutilisateur de dessiner, puis de sauvegarder sur son ordinateur le dessin au format favori. ActionScript nous rserve encore bien des surprises !

A retenir
53 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

La classe MouseEvent permet grce aux proprits shiftKey et ctrlKey la pression de touches simultanes.

Simuler la mthode Key.isDown


ActionScript 3 nintgre pas de mthode quivalente la mthode isDown de la classe Key existante en ActionScript 1 et 2. Dans le cas de dveloppements de jeux il peut tre important de simuler cet ancien comportement. Nous allons nous y attarder prsent. Afin de dvelopper cette fonctionnalit, nous utilisons un symbole de type clip afin de le dplacer sur la scne laide du clavier. La figure 7-16 illustre le symbole :

Figure 7-16. Occurrence de symbole clip. Grce un objet de mmorisation, nous pouvons reproduire le mme comportement que la mthode isDown laide du code suivant :
// coute des vnements clavier stage.addEventListener ( KeyboardEvent.KEY_DOWN, toucheEnfoncee ); stage.addEventListener ( KeyboardEvent.KEY_UP, toucheRelachee ); // objet de mmorisation de l'tat des touches var touches:Object = new Object(); function toucheEnfoncee ( pEvt:KeyboardEvent ):void { // marque la touche en cours comme enfonce touches [ pEvt.keyCode ] = true; }

54 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

function toucheRelachee ( pEvt:KeyboardEvent ):void { // marque la touche en cours comme relache touches [ pEvt.keyCode ] = false; } monPersonnage.addEventListener ( Event.ENTER_FRAME, deplace ); var etat:Number = personnage_mc.scaleX; var vitesse:Number = 15; function deplace ( pEvt:Event ):void { // si la touche Keyboard.RIGHT est enfonce if ( touches [ Keyboard.RIGHT ] ) { pEvt.target.x += vitesse; pEvt.target.scaleX = etat; // si la touche Keyboard.LEFT est enfonce } else if ( touches [ Keyboard.LEFT ] ) { pEvt.target.x -= vitesse; pEvt.target.scaleX = -etat; } }

En testant le code prcdent, le symbole est manipul par le clavier. Nous venons de simuler le fonctionnement de la mthode Key.isDown qui nexiste plus en ActionScript 3.

A retenir
La proprit keyCode de lobjet vnementiel KeyboardEvent renvoie le code de la touche. de type Il nexiste pas dquivalent la mthode isDown de la classe Key en ActionScript 3. Il est en revanche facile de simuler un comportement quivalent.

Superposition
Le lecteur 9 en ActionScript 3 intgre un nouveau comportement concernant la superposition des objets graphiques. En ActionScript 1 et 2 lorsquun objet non cliquable tait superpos sur un bouton, le bouton recouvert recevait toujours les entres souris.

55 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-17. Le bouton reste cliquable mme recouvert. La figure 7-17 illustre une animation ActionScript 1/2, un clip de forme rectangulaire opacit rduite recouvre un bouton qui demeure cliquable et affiche le curseur reprsentant une main. Si nous cliquions sur le bouton, son vnement onRelease tait dclench. En ActionScript 3 le lecteur 9 prend lobjet le plus haut de la hirarchie, les objets tant censs recevoir des entres souris recouverts par un objet graphique mme non cliquable ne reoivent plus les entres les souris et naffichent pas le curseur main. La figure 7-18 illustre le comportement en ActionScript 3 :

56 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-18. Le bouton nest plus cliquable si recouvert. Si lobjet plac au-dessus dcouvre une partie du bouton, cette partie devient alors cliquable :

57 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Figure 7-19. Zone cliquable sur bouton. Afin de rendre un objet superpos par un autre cliquable nous devons passer par la proprit mouseEnabled dfinie par la classe InteractiveObject. En passant la valeur false la proprit mouseEnabled de lobjet superpos nous rendant les objets placs en dessous sensibles aux entres souris. En ayant au pralable nomm le clip superpos monClip, nous rendons cliquable le bouton situ en dessous :
// dsactive la raction aux entres souris monClip.mouseEnabled = false;

Nous pouvons en dduire que lorsquune occurrence de symbole recouvre un objet cliquable, ce dernier ne peut recevoir dvnement souris. A linverse lorsquune occurrence de symbole contient un objet cliquable, ce dernier continue de recevoir des vnements souris. Cest un comportement que nous avons trait en dbut de chapitre lors de la construction du menu. Les objets enfants des occurrences de Sprite continuaient de recevoir les entres souris et rendaient nos boutons partiellement cliquables. Afin de dsactiver tous les objets enfants, nous avions utilis la proprit mouseChildren, qui revient passer la proprit mouseEnabled de tous les objets enfants dun DisplayObjectContainer.

Lvnement Event.RESIZE
Lorsque nous pouvons grer le redimensionnement de notre animation. Dans un nouveau document Flash CS3 nous crons un clip contenant le logo de notre site et nommons loccurrence monLogo.
Event.RESIZE est diffus par lobjet Stage. Grace cet vnement

lanimation

est

redimensionne

un

vnement

Le code suivant nous permet de conserver notre logo toujours centr quel que soit le redimensionnement effectu sur notre animation :
// import des classes de mouvement import fl.transitions.Tween; import fl.transitions.easing.Strong; // alignement de la scne en haut gauche stage.align = StageAlign.TOP_LEFT; // nous empchons le contenu d'tre tir stage.scaleMode = StageScaleMode.NO_SCALE; // coute de l'vnement auprs de l'objet Stage stage.addEventListener ( Event.RESIZE, redimensionne ); // calcul de la position en x et y

58 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

var initCentreX:int = (stage.stageWidth - monLogo.width)/2; var initCentreY:int = (stage.stageHeight - monLogo.height)/2; // deux objets Tween sont crs pour chaque axe var tweenX:Tween = new Tween ( monLogo, "x", Strong.easeOut, monLogo.x, initCentreX, 15 ); var tweenY:Tween = new Tween ( monLogo, "y", Strong.easeOut, monLogo.y, initCentreY, 15 ); function redimensionne ( pEvt : Event ):void { // calcul de la position en x et y var centreX:int = (stage.stageWidth - monLogo.width)/2; var centreY:int = (stage.stageHeight - monLogo.height)/2; // nous affectons les valeurs de dpart et d'arrive et relanons le mouvement tweenX.begin = monLogo.x; tweenX.finish = centreX; tweenX.start(); tweenY.begin = monLogo.y; tweenY.finish = centreY; tweenY.start(); }

La figure 7-20 illustre le rsultat :

Figure 7-20. Logo centr automatiquement. Nous pouvons utiliser lvnement Event.RESIZE afin de grer le repositionnement dun ensemble dlments graphiques au sein dune application ou dun site.

59 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 7 Interactivit version 0.1.2

Les prcdents chapitres nous ont permis de comprendre certains mcanismes avancs dActionScript 3 que nous avons traits de manire procdurale. Nous allons durant les prochains chapitres concevoir nos applications laide dobjets communiquant, ce style de programmation est appel programmation oriente objet ou plus communment POO.

60 / 60
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

8
Programmation oriente objet

CONCEVOIR AUTREMENT ................................................................................ 1 TOUT EST OBJET ...................................................................................................... 4 NOTRE PREMIERE CLASSE ............................................................................... 9 INTRODUCTION AUX PAQUETAGES ........................................................................ 10 DFINITION DE PROPRITS .................................................................................. 12 ATTRIBUTS DE PROPRITS DE CLASSE ................................................................. 13 ATTRIBUTS DE CLASSE .......................................................................................... 15 LE CONSTRUCTEUR ............................................................................................... 16 UTILISATION DU MOT CL THIS ............................................................................. 21 DFINITION DE MTHODES .................................................................................... 22 LENCAPSULATION ........................................................................................... 25 MISE EN PRATIQUE DE LENCAPSULATION ............................................................ 28 LES MTHODES DACCS ....................................................................................... 28 CONTRLE DAFFECTATION .................................................................................. 34 MTHODES EN LECTURE/CRITURE ....................................................................... 39 CAS DUTILISATION DE LATTRIBUT STATIC .......................................... 45 LA CLASSE JOUEURMANAGER ..................................................................... 51 LHERITAGE ........................................................................................................ 57 SOUS-TYPES ET SUPER-TYPE ................................................................................. 60 SPCIALISER UNE CLASSE ..................................................................................... 63 LE TRANSTYPAGE ............................................................................................. 65 SURCHARGE ........................................................................................................ 70

Concevoir autrement
La programmation oriente objet doit tre considre comme une manire de penser et de concevoir une application, certains la considre dailleurs comme un paradigme de programmation. Cette 1 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

nouvelle manire de penser nest pas limite ActionScript 3 et peut tre applique un grand nombre de langages. Avant de dmarrer, nous allons tout dabord nous familiariser avec le vocabulaire puis nous entamerons notre aventure oriente objet travers diffrents exemples concrets. Cest en 1967 que le premier langage orient objet vu le jour sous le nom de Simula-67. Comme son nom lindique, Simula permettait de simuler toutes sortes de situations pour les applications de lpoque telles les applications de gestions, de comptabilit ou autres. Nous devons la notion dobjets, de classes, de sous-classes, dvnements ainsi que de ramasse-miettes (garbage collector) Simula. Alan Key, inventeur du terme programmation oriente objet et ingnieur chez Xerox tendit les concepts apports par Simula et dveloppa le langage Smalltalk, aujourdhui considr comme le rel point de dpart de la programmation oriente objet. Lintrt de la programmation oriente objet rside dans la sparation des tches, lorganisation et la rutilisation du code. Nous pourrions rsumer ce chapitre en une seule phrase essentielle la base du concept de la programmation oriente objet : A chaque type dobjet une tche spcifique. Afin de dvelopper un programme ActionScript nous avons le choix entre deux approches. La premire, appele couramment programmation procdurale ou fonctionnelle ne dfinit aucune sparation claire des tches. Les diffrentes fonctionnalits de lapplication ne sont pas affectes un objet spcifique, des fonctions sont dclenches dans un ordre prcis et sassurent que lapplication sexcute correctement. En rsum, il sagit de la premire approche de programmation que tout dveloppeur chevronn a connue. Au moindre bug ou mise jour, cest langoisse, aucun moyen disoler les comportements et les bogues, bienvenue dans le code spaghetti. La seconde approche, appele programmation oriente objet dfinit une sparation claire des tches. Chaque objet soccupe dune tche spcifique, dans le cas dune galerie photo un objet peut grer la connexion au serveur afin de rcuprer les donnes, un autre soccupe de laffichage, enfin un dernier objet traite les entres souris et clavier de lutilisateur. En connectant ces objets entre eux, nous donnons vie une application. Nous verrons plus tard que toute la difficult de la programmation oriente objet, rside dans la communication inter objets. La figure 8-1 illustre un exemple de galerie photo compose de trois objets principaux. 2 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Figure 8-1. Concept de sparation des tches. Mme si cela nous parat naturel, la notion de programmation oriente objet reste abstraite sans un cas concret appliqu aux dveloppements de tous les jours. Afin de simplifier tout cela, nous pouvons regarder les objets qui nous entourent. Nous retrouvons peu prs partout le concept de sparation des tches. Afin doptimiser le temps de dveloppement et de maintenance, un programme peut tre considr comme un ensemble dobjets communicants pouvant tout moment tre remplacs ou rutiliss. Une simple voiture en est lexemple parfait. Le moteur, les roues, lembrayage, les pdales, permettent notre voiture de fonctionner. Si celle-ci tombe en panne, chaque partie sera teste, au cas o lune dentre elles savre dfectueuse, elle est aussitt remplace ou rpare permettant une rparation rapide de la voiture. En programmation, la situation reste la mme, si notre application objet est bogue, grce la sparation des tches nous pouvons trs facilement isoler les erreurs et corriger lapplication ou encore ajouter de nouvelles fonctionnalits en ajoutant de nouveaux objets.

3 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Tout est objet


Dans le monde qui nous entoure tout peut tre reprsent sous forme dobjet, une ampoule, une voiture, un arbre, la terre elle-mme peut tre considre comme un objet. En programmation, il faut considrer un objet comme une entit ayant un tat et permettant deffectuer des oprations. Pour simplifier nous pouvons dire quun objet est capable deffectuer certaines tches et possde des caractristiques spcifiques. Une tlvision peut tre considre comme un objet, en possdant une taille, une couleur, ainsi que des fonctionnalits, comme le fait dtre allume, teinte ou en veille. Ici encore, la sparation des tches est utilise, le tube cathodique soccupe de crer limage, lcran se charge de lafficher, le capteur soccupe de grer les informations envoyes par la tlcommande. Cet ensemble dobjets produit un rsultat fonctionnel et utilisable. Nous verrons que de la mme manire nous pouvons concevoir une application ActionScript 3 en associant chaque objet une tche spcifique. Afin de porter notre exemple de tlvision en ActionScript 3, nous pourrions dfinir une classe Television permettant de crer des objets tlvisions. Chaque objet tlvision serait donc de type Television. Lorsque un dveloppeur parle de la classe Array nous savons de quel type dobjet il sagit et de ses capacits, comme ajouter, supprimer ou trier des lments. De la mme manire, en parlant de la classe Television nous savons quelles sont les fonctionnalits offertes par ce type. Si nous regardons lintrieur dune tlvision, nous y trouvons toutes sortes de composants. Des hauts parleurs, des boutons, ainsi quun ensemble de composants lectroniques. En programmation, nous pouvons exactement reproduire cette conception, nous verrons que plusieurs objets travaillant ensemble peuvent donner naissance une application concrte. Chaque composant peut ainsi tre rutilis dans un autre programme, les hauts parleurs dun modle de tlvision pourront tre rutiliss dans une autre. Cest ce que nous appelons la notion de composition, nous reviendrons sur ce sujet au cours du chapitre 10 intitul Diffusion dvnements personnaliss. Une fois notre objet cr, nous pouvons lui demander dexcuter certaines actions, lorsque nous achetons une tlvision nous la choisissons selon ses capacits, et nous nous attendons au moins pouvoir lallumer, lteindre, monter le son ou baisser le son. Ces capacits propres un objet sont dfinies par son interface, les 4 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

capacits de celle-ci sont directement lies au type. La figure 8-2 illustre lexemple dune tlvision.

Figure 8-2. Classe Television. Afin dinstancier un objet partir dune classe nous utilisons le mot cl new :
// cration d'une instance de la classe Television au sein de la variable maTV var maTV:Television = new Television();

Le type Television dtermine linterface de notre tlvision, c'est-dire ce que lobjet est capable de faire. Les fonctions allumer, eteindre, monterVolume, et baisserVolume sont appeles mthodes car elles sont rattaches un objet et dfinissent ses capacits. En les appelants, nous excutons tout un ensemble de mcanismes totalement transparents pour lutilisateur. Dans lexemple suivant, nous instancions une tlvision, sil est trop tard nous lteignons :
// cration d'une instance de la classe Television au sein de la variable maTV var maTV:Television = new Television(); // nous allumons la tl maTV.allumer() // si il est trop tard if ( heureActuelle > 12 ) { // nous tignons la tl maTV.eteindre(); }

Comment stockons-nous les informations telles la taille, la couleur, ou bien le numro de srie de la tlvision ?

5 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Bonne question, ces donnes reprsentent les caractristiques dun objet et sont stockes au sein de proprits. Nous pouvons imaginer quune tlvision ait une taille, un poids, une couleur, et un numro de srie.

Figure 8-3. Mthodes et proprits du type Television. Toutes les instances de tlvisions cres partir de la mme classe possdent ainsi les mmes fonctionnalits, mais des caractristiques diffrentes comme la couleur, la taille ou encore le poids. Pour rsumer, nous pouvons dire que les mthodes et proprits dterminent le type. Pour rcuprer la couleur ou le poids dune tlvision, nous ciblons la proprit voulue :
// nous rcuprons la taille var taille:Number = maTV.taille; // nous rcuprons la couleur var couleur:Number = maTV.couleur;

Au sein de Flash nous retrouvons partout le concept dobjets, prenons le cas de la classe MovieClip, dans le code suivant nous crons une instance de MovieClip :
// instanciation d'un clip var monClip:MovieClip = new MovieClip();

Nous savons daprs le type MovieClip les fonctionnalits disponibles sur une instance de MovieClip :
// mthodes de la classe MovieClip monClip.gotoAndPlay(2); monClip.startDrag();

6 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

En crant plusieurs instances de MovieClip, nous obtenons des objets ayant les mmes capacits mais des caractristiques diffrentes comme par exemple la taille, la position ou encore la transparence :
// instanciation d'un premier clip var monPremierClip:MovieClip = new MovieClip(); // utilisation de l'api de dessin monPremierClip.graphics.lineStyle ( 1 ); monPremierClip.graphics.beginFill ( 0x880099, 1); monPremierClip.graphics.drawCircle ( 30, 30, 30 ); // caractristiques du premier clip monPremierClip.scaleX = .8; monPremierClip.scaleY = .8; monPremierClip.alpha = .5; monPremierClip.x = 150; monPremierClip.y = 150; // affichage du premier clip addChild ( monPremierClip ); // instanciation d'un second clip var monSecondClip:MovieClip = new MovieClip(); // utilisation de l'api de dessin monSecondClip.graphics.lineStyle ( 1 ); monSecondClip.graphics.beginFill ( 0x997711, 1); monSecondClip.graphics.drawCircle ( 60, 60, 60 ); // caractristiques du second clip monSecondClip.scaleX = .8; monSecondClip.scaleY = .8; monSecondClip.alpha = 1; monSecondClip.x = 300; monSecondClip.y = 190; // affichage du second clip addChild ( monSecondClip );

Si nous testons le code prcdent nous obtenons deux clips de forme circulaire ayant des fonctionnalits communes mais des caractristiques diffrentes, comme lillustre la figure 8-4 :

7 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Figure 8-4. Deux clips de caractristiques diffrentes. Au sein de Flash tout est objet, toutes les classes permettent dinstancier des objets ayant des fonctionnalits spcifiques comme la gestion de laffichage, le chargement de donnes externe, etc. Nous allons prsent dcouvrir comment concevoir nos propres objets en ActionScript 3 afin de les rendre rutilisable et facile dutilisation. Dans un nouveau fichier ActionScript nous allons crer une classe Joueur qui nous permettra de reprsenter un joueur dans un jeu. En quelques minutes notre classe sera dfinie et prte tre utilise.

A retenir

8 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Tout est objet. A chaque objet une tche spcifique. Les mthodes dfinissent les capacits dun objet. Les proprits dfinissent ses caractristiques. Une instance de classe est un objet cr partir dune classe. Pour instancier un objet partir dune classe, nous utilisons le mot cl new.

Notre premire classe


Pour crer un nouveau type dobjet en ActionScript 3 nous devons crer une classe. Il faut considrer celle-ci comme un moule permettant de crer des objets de mme type. A partir dune classe nous pouvons crer autant dinstances de cette classe que nous voulons. Avant de commencer coder, il est important de noter que les classes ActionScript 3 rsident dans des fichiers externes portant lextension .as. A cot de notre document Flash nous allons dfinir une classe ActionScript 3 avec un diteur tel FlashDevelop ou Eclipse. Pour cela nous utilisons le mot cl class :
class Joueur {

Nous sauvons cette classe cot de notre document en cours et nous lappelons Joueur.as. Sur le scnario principal nous instancions notre joueur avec le mot cl new :
var monJoueur:Joueur = new Joueur();

A la compilation nous obtenons les deux erreurs suivantes :


1046: Ce type est introuvable ou n'est pas une constante de compilation : Joueur. 1180: Appel une mthode qui ne semble pas dfinie, Joueur.

Le compilateur ne semble pas apprcier notre classe, il ne reconnat pas cette classe que nous venons de dfinir. Contrairement ActionScript 2, en ActionScript 3, une classe doit tre contenue dans un conteneur de classes appel paquetage.

9 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Introduction aux paquetages


Les paquetages (packages en anglais) permettent dans un premier temps dorganiser les classes en indiquant dans quel rpertoire est situe une classe. Nous pouvons imaginer une application dynamique dans laquelle une classe Connector serait place dans un rpertoire serveur, une autre classe PNGEncoder serait elle place dans un rpertoire encodage. Le compilateur trouvera la classe selon le chemin renseign par le paquetage, afin de dfinir un paquetage nous utilisons le mot cl package. Si notre classe Joueur tait place dans un rpertoire jeu nous devrions dfinir notre classe de la manire suivante :
package jeu { public class Joueur { } }

Il serait alors ncessaire de spcifier son chemin au compilateur en utilisant linstruction import avant de pouvoir lutiliser :
import jeu.Joueur;

Lorsque notre classe nest pas place dans un rpertoire spcifique mais rside simplement cot de notre document FLA aucun import nest ncessaire, le compilateur vrifie automatiquement ct du document Flash si les classes existent. Dans notre cas, la classe Joueur est place cot du document Flash et ne rside dans aucun rpertoire, nous devons donc spcifier un paquetage vide :
package { class Joueur { } }

Dans ce cas, aucun import pralable nest ncessaire. En ActionScript 2, le mot cl package nexistait pas, pour indiquer le chemin daccs 10 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

une classe nous spcifions le chemin daccs directement lors de sa dclaration :


class jeu.Joueur { }

La notion de paquetages an ActionScript 3 ne sert pas uniquement reflter lemplacement dune classe, en ActionScript 2, une seule classe pouvait tre dfinie au sein dun fichier source. En rsum, une seule et unique classe pour chaque fichier .as. En ActionScript 3, quelques subtilits on t ajoutes, nous pouvons dsormais dfinir autant de classes que nous souhaitons pour chaque fichier .as. Une seule classe rsidera l'intrieur d'une dclaration de paquetage, les autres devront tre dfinies lextrieur de celui-ci et ne pourront pas tre utilises par un code extrieur. Ne vous inquitez pas, cela parait relativement abstrait au dpart, nous allons au cours des prochains chapitres apprendre matriser la notion de paquetages et dcouvrir nouveau de nouvelles fonctionnalits. Si nous tentons de compiler notre classe nouveau, les mmes erreurs saffichent. Afin de pouvoir instancier notre classe depuis lextrieur nous devons utiliser lattribut de classe public. Nous allons revenir sur la notion dattributs de classe trs bientt :
package { public class Joueur { } }

Une fois le mot cl public ajout, la compilation seffectue sans problme. Lintrt de notre classe est de pouvoir tre instancie et recevoir diffrents paramtres lorsque nous utiliserons cette classe dans nos applications. Il ya de fortes chances quun joueur ait un nom, un prnom, ainsi que dautres proprits. Pour instancier notre instance de Joueur nous utilisons le mot cl new :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur();

Notre objet Joueur est cr pour savoir si un objet correspond un type spcifique nous utilisons le mot cl is : 11 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

// instanciation d'un joueur var monJoueur:Joueur = new Joueur (); // est-ce que notre objet est de type Joueur ? // affiche : true trace( monJoueur is Joueur );

Le mot cl instanceof utilis en ActionScript 1 et 2 nexiste plus et a t remplac par le mot cl is. En tant de type Joueur notre instance nous garantit quelle possde toutes les capacits et proprits propres au type Joueur.

A retenir :
Les classes ActionScript 3 rsident dans des fichiers externes portant lextension .as. Les classes ActionScript 3 doivent rsider dans des paquetages Le nom de la classe doit tre le mme que le fichier .as Afin dutiliser une classe nous devons limporter avec le mot cl import. Afin de dfinir un paquetage, nous utilisons le mot cl package. Pour tester le type dun objet nous utilisons le mot cl is. Pour dfinir une classe, nous utilisons le mot cl class.

Dfinition de proprits
Comme nous lavons vu prcdemment, les proprits dun objet dcrivent ses caractristiques. Un tre humain peut tre considr comme une instance de la classe Humain o chaque caractristique propre ltre humain telle la taille, la couleur de ses yeux, ou son nom seraient stockes au sein de proprits. Nous allons dfinir des proprits au sein de la classe Joueur afin que tous les joueurs crs aient leurs propres caractristiques. Chaque joueur aura un nom, un prnom, une ville dorigine, un ge, et un identifiant les caractrisant. Nous dfinissons donc cinq proprits au sein de la classe Joueur. Pour dfinir des proprits au sein dune classe, nous utilisons la dfinition de classe, un espace situ juste en dessous de la ligne dfinissant la classe :
package { public class Joueur { // dfinition des proprits de la classe var nom:String;

12 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

var prenom:String; var age:int; var ville:String; } }

En lisant les lignes prcdentes nous nous rendons compte que la syntaxe nous rappelle la dfinition de simples variables. Les proprits sont en ralit des variables comme les autres, mais voluant dans le contexte dun objet. En dfinissant ces proprits, nous garantissons que chaque instance de la classe Joueur les possde. A tout moment, nous pouvons rcuprer ces informations en ciblant les proprits sur linstance de classe :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur(); // recupration du prnom du joueur var prenom = monJoueur.prenom;

A la compilation le code prcdent choue, car contrairement ActionScript 2, lorsque nous ne spcifions pas dattributs pour chaque proprit, celles-ci sont considres comme non visible depuis lextrieur du paquetage en cours. Pour grer laccs aux proprits dune classe nous utilisons les attributs de proprits.

Attributs de proprits de classe


Nous dfinissons comme membres dune classe, les proprits ainsi que les mthodes. Afin de grer laccs chaque membre, nous utilisons des attributs de proprits. Au sein du modle dobjet ActionScript les mthodes sont aussi dfinies par des proprits. Une mthode ntant quune fonction voluant dans le contexte dun objet. Cinq attributs de proprits de classe existent en ActionScript 3 :
internal : Par dfaut, le membre est accessible uniquement depuis le paquetage en cours. public : Accessible depuis nimporte quelle partie du code. private : Le membre nest accessible que depuis la mme classe. Les sous-classes nont pas accs au membre. protected : Le membre est accessible depuis la mme classe, et les sous-classes.

13 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

static : Le membre est accessible uniquement depuis la classe, non depuis les instances.

Nous navons pas dfini pour le moment dattributs pour les proprits de notre classe Joueur, lorsquaucun attribut nest dfini, la proprit ou mthode est considre comme internal et nest accessible que depuis une classe appartenant au mme paquetage. Nous reviendrons sur ces subtilits plus tard, pour le moment nous souhaitons accder nos proprits depuis notre scnario nous les dfinissons comme public :
package { public class Joueur { // dfinition des proprits publiques de la classe public var nom:String; public var prenom:String; public var age:int; public var ville:String; } }

Cest pour cette raison que nous avons dfini notre classe publique afin que celle-ci puisse tre instancie depuis lextrieur. Une fois nos proprits rendues publiques nous pouvons y accder depuis lextrieur, nous instancions un objet Joueur puis nous accdons sa proprit prenom :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur(); // recupration du prnom du joueur var prenom = monJoueur.prenom; // affiche : null trace( prenom );

En ciblant la proprit prenom dun joueur nous rcuprons la valeur null. Ce comportement est tout fait normal, car pour linstant aucunes donnes ne sont contenues par les proprits. Nous les avons simplement dfinies. En ActionScript 2 le code prcdent aurait retourn undefined au lieu de null. Nous venons de voir qu laide dattributs de proprits de classe nous pouvons grer laccs et le comportement des membres dune 14 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

classe. Il existe en ActionScript 3 dautres attributs lis cette fois aux classes, ces derniers sont appels attributs de classe.

Attributs de classe
Certaines rgles peuvent tre appliques aux classes directement laide de quatre attributs, notons que lattribut abstract nexiste pas en ActionScript 3 :
dynamic : La classe accepte lajout de proprits ou mthodes lexcution. final : La classe ne peut pas tre tendue. internal (par dfaut) : La classe nest accessible que depuis les classes appartenant au mme paquetage. public : La classe est accessible depuis nimporte quel emplacement.

Afin de rendre la classe Joueur instanciable depuis lextrieur nous avons du la rendre publique laide de lattribut public. Celle-ci est dailleurs considre comme non dynamique, cela signifie quil est impossible dajouter lexcution de nouvelles mthodes ou proprits une occurrence de la classe ou la classe mme. Imaginons que nous souhaitions ajouter une nouvelle proprit appele sSexe lexcution :
// cration d'un joueur et d'un administrateur var premierJoueur:Joueur = new Joueur (); // ajout d'une nouvelle proprit l'excution premierJoueur.sSexe = "H";

Le compilateur se plaint et gnre lerreur suivante :


1119: Accs la proprit sSexe peut-tre non dfinie, via la rfrence de type static Joueur.

Si nous rendons la classe dynamique laide de lattribut dynamic, lajout de membres lexcution est possible :
package { dynamic public class Joueur { // dfinition des proprits publiques de la classe public var nom:String; public var prenom:String; public var age:int; public var ville:String; }

15 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Dans le code suivant nous crons une nouvelle proprit sSexe au sein linstance de classe Joueur et rcuprons sa valeur :
// cration d'un joueur et d'un administrateur var premierJoueur:Joueur = new Joueur (); // ajout d'une nouvelle proprit l'excution premierJoueur.sSexe = "H"; // rcupration de la valeur // affiche : H trace( premierJoueur.sSexe );

Bien que cela soit possible, il est fortement dconseill dajouter de nouveaux membres lexcution une classe ou instance de classe, la lecture de la classe ne refltera pas les relles capacits ou caractristiques de la classe. En lisant la dfinition de la classe Joueur le dveloppeur pensera trouver cinq proprits, en ralit une sixime est rajoute lexcution. Ce dernier serait oblig de parcourir tout le code de lapplication afin de dnicher toutes les ventuelles modifications apportes la classe lexcution. Afin dinitialiser notre objet Joueur lors de sa cration nous devons lui passer des paramtres. Mme si notre objet Joueur est bien cr, son intrt pour le moment reste limit car nous ne passons aucun paramtre lors son instanciation. Pour passer des paramtres un objet lors de son instanciation nous dfinissons une mthode au sein de la classe appele mthode constructeur.

Le constructeur
Le but du constructeur est dinitialiser lobjet cr. Celui-ci sera appel automatiquement ds que la classe sera instancie. Attention, le constructeur doit imprativement avoir le mme nom que la classe. Si ce nest pas le cas, il sera considr comme une mthode classique et ne sera pas dclench. Nous allons prvoir quatre paramtres dinitialisation pour chaque joueur :
Nom : Une chane de caractres reprsentant son nom. Prnom : Une chane de caractres reprsentant son prnom. Age : Un entier reprsentant son ge. Ville : Une chane de caractres reprsentant sa location.

16 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Au sein du constructeur nous dfinissons quatre paramtres, qui serviront accueillir les futurs paramtres passs :
package { public class Joueur { // dfinition des proprits de la classe public var nom:String; public var prenom:String; public var age:int; public var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { trace( this ); } } }

Si nous tentons dinstancier notre joueur sans passer les paramtres ncessaires :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur();

Le compilateur nous renvoie une erreur :


1136: Nombre d'arguments incorrect. 4 attendus.

Le compilateur ActionScript 2 ne nous renvoyait aucune erreur lorsque nous tentions dinstancier une classe sans paramtres alors que celle-ci en ncessitait. ActionScript 3 est plus strict et ne permet pas linstanciation de classes sans paramtres si cela nest pas spcifi laide du mot cl rest. Nous verrons plus tard comment dfinir une classe pouvant recevoir ou non des paramtres linitialisation. Pour obtenir un joueur, nous crons une instance de la classe Joueur en passant les paramtres ncessaires :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // recupration du prnom du joueur var prenom = monJoueur.prenom; // affiche : null trace( prenom );

17 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Si nous tentons dinstancier un joueur en passant plus de paramtres que prvus :


// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan", 50);

Lerreur de compilation suivante est gnre :


1137: Nombre d'arguments incorrect. Nombre maximum attendu : 4.

Le compilateur ActionScript 2 tait moins strict et permettait de passer un nombre de paramtres en plus de ceux prsents au sein de la signature dune mthode, en ActionScript 3 cela est impossible. Nous avons instanci notre premier joueur, mais lorsque nous tentons de rcuprer son prnom, la proprit prenom nous renvoie nouveau null. Nous avons bien pass les paramtres au constructeur, et pourtant nous rcuprons toujours null lorsque nous ciblons la proprit prenom. Est-ce bien normal ? Cest tout fait normal, il ne suffit pas de simplement dfinir un constructeur pour que linitialisation se fasse comme par magie. Cest nous de grer cela, et dintgrer au sein du constructeur une affectation des paramtres aux proprits correspondantes. Une fois les paramtres passs, nous devons les stocker au sein de lobjet afin de les mmoriser. Cest le travail du constructeur, ce dernier doit recevoir les paramtres et les affecter des proprits correspondantes dfinies au pralable. Pour cela nous modifions le constructeur de manire affecter chaque paramtre pass aux proprits de lobjet :
package { public class Joueur { // dfinition des proprits de la classe public var nom:String; public var prenom:String; public var age:int; public var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { // affectation de chaque proprit prenom = pPrenom; nom = pNom; age = pAge; ville = pVille;

18 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

} } }

Une fois les proprits affectes, nous pouvons rcuprer les valeurs associes :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // rcupration du prnom du joueur var prenom:String = monJoueur.prenom; var nom:String = monJoueur.nom; var age:int = monJoueur.age; var ville:String = monJoueur.ville; // affiche : Stevie trace( prenom ); // affiche : Wonder trace( nom ); // affiche : 57 trace( age ); // affiche : Michigan trace( ville );

Lorsquun objet Joueur est instanci nous passons des paramtres dinitialisation, chaque caractristique est stocke au sein de proprits. A linverse si nous rendons ces proprits prives :
package { public class Joueur { // dfinition des proprits de la classe private var nom:String; private var prenom:String; private var age:int; private var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } } }

19 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

En dfinissant lattribut private, les proprits deviennent inaccessibles depuis lextrieur de la classe :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan" ); // affiche une erreur la compilation : // 1178: Tentative d'accs la proprit inaccessible nom, via la rfrence de type static Joueur. trace( monJoueur.nom );

Lorsque le compilateur est en mode strict, une erreur la compilation est leve, il est impossible de compiler. Si nous passons le compilateur en mode non strict, laide de la syntaxe crochet il devient possible de compiler :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan"); // provoque une erreur l'excution : trace( monJoueur['nom'] );

Mais la machine virtuelle 2 nous rattrape en levant une erreur lexcution :


ReferenceError: Error #1069: La proprit nom est introuvable sur Joueur et il n'existe pas de valeur par dfaut.

Souvenons-nous que la machine virtuelle dActionScript 3 (VM2) conserve les types lexcution. Lastuce ActionScript 2 consistant utiliser la syntaxe crochet pour viter les erreurs de compilation nest plus valable. Nous verrons tout au long des prochains chapitres, diffrents cas dutilisation dautres attributs de proprits. Mais quel serait lintrt de dfinir des proprits prives ? Quel est lintrt si nous ne pouvons pas y accder ? Nous allons dcouvrir trs vite dautres moyens plus scuriss daccder aux proprits dun objet. Nous allons traiter cela plus en dtail dans quelques instants. Nous venons de dcouvrir comment dfinir des proprits au sein dune classe ActionScript 3, abordons maintenant la dfinition de mthodes.

A retenir :

20 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Le constructeur dune classe sert recevoir des paramtres afin dinitialiser lobjet. La prsence de constructeur nest pas obligatoire, si omis, le compilateur en dfinit un par dfaut. Dans ce cas, aucun paramtre ne peut tre pass lors de linstanciation de la classe.

Utilisation du mot cl this


Afin de faire rfrence lobjet courant nous pouvons utiliser le mot cl this au sein dune classe. Nous aurions pu dfinir la classe Joueur de la manire suivante :
package { public class Joueur { // dfinition des proprits de la classe public var nom:String; public var prenom:String; public var age:int; public var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { // affectation de chaque proprit this.prenom = pPrenom; this.nom = pNom; this.age = pAge; this.ville = pVille; } } }

Bien que son utilisation soit intressante dans certaines situations comme pour passer une rfrence, son utilisation peut savrer redondante et rendre le code verbeux. Certains dveloppeurs apprcient tout de mme son utilisation afin de diffrencier facilement les proprits doccurrences des variables locales. En voyant une variable prcde du mot cl this, nous sommes assurs quil sagit dune proprit membre de la classe. En rsum, il sagit dun choix propre chaque dveloppeur, il nexiste pas de vritable rgle dutilisation.

21 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Dfinition de mthodes
Alors que les proprits dun objet dfinissent ses caractristiques, les mthodes dune classe dfinissent ses capacits. Nous parlons gnralement de fonctions membres. Dans notre prcdent exemple de tlvision, les mthodes existantes taient allumer, eteindre, changerChaine, baisserVolume, et monterVolume. En appliquant le mme concept notre classe Joueur il parait logique quun joueur puisse se prsenter, nous allons donc dfinir une mthode sePresenter. Une mthode est un membre rgit aussi par les attributs de proprits. Nous allons rendre cette mthode publique car elle devra tre appele depuis lextrieure :
package { public class Joueur { // dfinition des proprits de la classe public var nom:String; public var prenom:String; public var age:int; public var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } // mthode permettant au joueur de se prsenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } } }

Pour quun joueur se prsente nous appelons la mthode sePresenter :


// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan");

22 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

// Je m'appelle Stevie, j'ai 57 ans. monJoueur.sePresenter();

Ainsi, chaque instance de Joueur a la capacit de se prsenter. La mthode sePresenter rcupre les proprits prenom et age et affiche leurs valeurs. Toutes les proprits dfinies au sein de la classe sont accessibles par toutes les mthodes de celle-ci. Nous allons dfinir une deuxime mthode nous permettant dafficher une reprsentation automatique de lobjet Joueur. Par dfaut lorsque nous traons un objet, le lecteur Flash reprsente lobjet sous une forme simplifie, indiquant simplement le type de lobjet :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan" ); // affiche : [object Joueur] trace( monJoueur );

Notons quen ActionScript 2, le lecteur affichait simplement [objet Object] il fallait ensuite tester laide de linstruction instanceof afin de dterminer le type dun objet. En ActionScript 3 laffichage du type est automatique. En dfinissant une mthode toString au sein de la classe Joueur nous redfinissons la description que le lecteur fait de lobjet :
package { public class Joueur { // dfinition des proprits de la classe public var nom:String; public var prenom:String; public var age:int; public var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } // mthode permettant au joueur de se prsenter public function sePresenter ( ):void {

23 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

La mthode toString retourne une chaine de caractres utilise par le lecteur Flash lorsque celui-ci affiche la description dun objet. Une fois dfinie, lorsque nous traons une instance de la classe Joueur, nous obtenons une reprsentation dtaille de lobjet. Cela rend notre code plus lgant, lorsquun dveloppeur tiers cible une instance de la classe Joueur, la description suivante est faite :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan" ); // affiche : [Joueur prenom : Stevie, Michigan] trace( monJoueur ); nom : Wonder, age : 57, ville :

Dans la quasi-totalit des classes que nous creront, nous intgrerons une mthode toString afin dassurer une description lgante de nos objets. Pour le moment nous accdons directement depuis lextrieur aux proprits de lobjet Joueur car nous les avons dfinies comme publique. Afin de rendre notre conception plus solide nous allons aborder prsent un nouveau point essentiel de la programmation oriente objet appel encapsulation.

A retenir :

24 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Les mthodes de la classe ont accs aux proprits dfinies au sein de la classe. En dfinissant une mthode toString sur nos classes nous modifions la reprsentation de notre objet faite par le lecteur Flash.

Lencapsulation
Lorsque nous utilisons une tlvision nous utilisons des fonctionnalits sans savoir ce qui se passe en interne, et tant mieux. Lobjet nous est livr mettant disposition des fonctionnalits, nous ne savons rien de ce qui se passe en interne. Le terme dencapsulation dtermine la manire dont nous exposons les proprits dun objet envers le monde extrieur. Un objet bien pens doit pouvoir tre utilis sans montrer les dtails de son implmentation. En dautres termes, un dveloppeur tiers utilisant notre classe Joueur na pas savoir que nous utilisons une proprit nomme age ou prenom ou autres. Il faut tout dabord sparer les personnes utilisant les classes en deux catgories :
Le ou les auteurs de la classe. Les personnes lutilisant.

Le code interne une classe, est appel implmentation. En tant quauteur de classes, nous devons imprativement nous assurer de ne pas montrer les dtails de limplmentation de notre classe. Pourquoi ? Afin dviter que la modification dun dtail de limplmentation nentraine de lourdes rpercussions pour les dveloppeurs utilisant nos classes. Prenons un scnario classique, Mathieu, Nicolas et Eric sont dveloppeurs au sein dune agence Web et dveloppent diffrentes classes ActionScript 3 quils rutilisent au cours des diffrents projets quils doivent raliser. Nicolas vient de dvelopper une classe permettant de gnrer des PDF en ActionScript 3. Toute lquipe de dveloppement Flash utilise cette classe dans les nouveaux projets et il faut bien avouer que tout le monde est plutt satisfait de cette nouvelle librairie. La figure 8-4 illustre la classe que Nicolas a dveloppe, ainsi que les diffrentes fonctionnalits disponibles :

25 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Figure 8-4. Schma UML simplifi de la classe PDF. La proprit nbPages permet de savoir combien de pages comporte le PDF en cours de cration. A chaque page cre, Nicolas incrmente la valeur de cette proprit. Eric vient justement de livrer un projet un client qui souhaitait gnrer des PDF depuis Flash. Eric a utilis la proprit nbPages afin de savoir combien de pages sont prsentes dans le PDF en cours de cration, voici une partie du code dEric :
// rcupration du nombre de pages var nombresPages:int = monPDF.nbPages; // affichage du nombre de pages legende_pages.text = String ( nombresPages );

Un matin, Nicolas, auteur de la classe se rend compte que la classe possde quelques faiblesses et dcide de rajouter des fonctionnalits. Sa gestion des pages internes au document PDF ne lui parait pas optimise, dsormais Nicolas stocke les pages dans un tableau accessible par la proprit tableauPages contenant chaque page du PDF. Voici la nouvelle version de la classe PDF :

Figure 8-5. Nouvelle version de la classe PDF. 26 / 74


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Dsormais Nicolas utilise un tableau pour stocker chaque page cre. Pour rcuprer le nombre de pages, il faut cibler le tableau tableauPages et rcuprer sa longueur pour connaitre le nombre de pages du PDF. Eric et Mathieu rcuprent la nouvelle version mais tous leurs projets ne compilent plus, car la proprit nbPages nexiste plus ! Eric et Mathieu auront beau tenter de convaincre Nicolas de remettre la proprit afin que tout fonctionne nouveau, Nicolas, trs content de sa nouvelle gestion interne des pages refuse de rajouter cette proprit nbPages qui naurait plus de sens dsormais. Qui est le fautif dans ce scnario ? Nicolas est fautif davoir expos lextrieur cette proprit nbPages. En modifiant limplmentation de sa classe, chaque dveloppeur utilisant la classe risque de voir son code obsolte. Nicolas aurait du dfinir une mthode daccs getNbPages qui peut importe limplmentation aurait toujours retourn le nombre de pages. Nicolas se rendant compte de sa maladresse, dcide de sortir une nouvelle version prenant en compte lencapsulation. La figure 8-6 illustre la nouvelle classe.

Figure 8-6. Nouvelle version encapsule de la classe PDF. Eric et Mathieu en appelant la mthode getNbPages ne savent pas ce qui se passe en interne et tant mieux ! Nicolas est tranquille et pourra modifier tout ce quil souhaite en interne, son seul soucis sera de toujours retourner le nombre de pages quelque soit limplmentation. Ainsi nous pouvons modifier limplmentation interne de lobjet sans modifier linterface de programmation.

27 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Nous allons mettre en pratique la notion dencapsulation et dcouvrir dautres avantages lis ce nouveau concept.

Mise en pratique de lencapsulation


Nous venons de voir quil est trs important de cacher limplmentation afin de rendre notre conception plus solide. Les parties caches du code ne sont pas utilises par les dveloppeurs, ainsi nous sommes libres de le modifier sans entraner de rpercussions ngatives. Lencapsulation des proprits permet ainsi de rendre notre code plus intuitif, nous rendons visible uniquement ce qui est utile et utilisable. Dans le cas de notre classe Joueur nous allons conserver la plupart des proprits accessibles mais sous une forme diffrente. Pour accder lge ou au nom dun joueur nous accdons directement des proprits publiques :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affiche : Stevie trace( monJoueur.prenom ); // affiche : 57 trace( monJoueur.age );

Si un dveloppeur tiers vient utiliser notre classe Joueur, ce dernier pourra utiliser ces deux proprits. Si nous changeons plus tard limplmentation de notre classe, ces proprits pourraient peut tre disparatre, ou bien tre renommes rendant le code du dveloppeur caduc. Afin dviter tout risque, nous allons dfinir des mthodes daccs qui tcheront de renvoyer la valeur escompte, quelque soit limplmentation.

Les mthodes daccs


Deux groupes de mthodes daccs existent :
Les mthodes rcuprant la valeur dune proprit, appeles mthodes de rcupration. Les mthodes affectant une valeur une proprit, appeles mthodes daffectation.

Prenons un exemple simple, au lieu de cibler directement la proprit age, le dveloppeur tiers appellera une mthode getAge. Nous rendons dans un premier temps toutes nos proprits prives afin que celles-ci soit caches :
package

28 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ public class Joueur { // dfinition des proprits de la classe private var nom:String; private var prenom:String; private var age:int; private var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } // mthode permettant au joueur de se prsenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

En programmation oriente objet, il est fortement conseill de protger les proprits en les rendant prives. Puis nous dfinissons une mthode getAge afin de rcuprer lge du joueur :
package { public class Joueur { // dfinition des proprits de la classe private var nom:String; private var prenom:String;

29 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

private var age:int; private var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } // rcupre l'age du joueur public function getAge ( ):int { return age; } // mthode permettanrt au joueur de se presenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

Si nous tentons daccder aux proprits prives, le compilateur empche de compiler :


// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affiche : 1178: Tentative d'accs la proprit inaccessible prenom, via la rfrence de type static Joueur. trace( monJoueur.prenom ); // affiche : 1178: Tentative d'accs la proprit inaccessible age, via la rfrence de type static Joueur. trace( monJoueur.age );

30 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Dsormais, afin de rcuprer lge dun joueur nous appelons la mthode getAge :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affiche : 57 trace( monJoueur.getAge() );

En utilisant des mthodes daccs nous cachons limplmentation de la classe et rendons les modifications futures scurises et plus faciles. Les dveloppeurs ne savent pas que la proprit age est utilise en interne. Si nous dcidons de changer limplmentation pour des raisons doptimisations ou de conception, aucun risque pour les dveloppeurs utilisant la classe. Dans lexemple suivant les proprits du joueur sont dsormais stockes au sein dun objet :
package { public class Joueur { // dfinition des proprits de la classe private var infosJoueur:Object; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { // les paramtres sont dsormais stocks // dans un objet infosJoueur infosJoueur = new Object(); // chaque paramtre est stock dans l'objet infoJoueurs infosJoueur.prenom = pPrenom; infosJoueur.nom = pNom; infosJoueur.age = pAge; infosJoueur.ville = pVille; } // rcupre l'age du joueur public function getAge ( ):int { //rcupre la proprit age au sein de lobjet infosJoueur return infosJoueur.age; } // mthode permettanrt au joueur de se presenter public function sePresenter ( ):void

31 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ trace("Je m'appelle " + infosJoueur.prenom + ", j'ai " + infosJoueur.age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + infosJoueur.prenom +", infosJoueur.nom + ", age : " + infosJoueur.age + ", ville : " + infosJoueur.ville + "]"; } } } nom : " +

Laccs aux proprits ne change pas, notre code continue de fonctionner :


// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affiche : 57 trace( monJoueur.getAge() );

Ainsi, notre implmentation change sans intervenir sur linterface de programmation. Il est important de signaler quune mthode de rcupration peut porter nimporte quel nom, lutilisation du mot get au sein de la mthode getAge nest pas obligatoire. Nous aurions pu appeler cette mme mthode recupAge. Nous dfinissons une mthode de rcupration spcifique chaque proprit :
package { public class Joueur { // dfinition des proprits de la classe private var nom:String; private var prenom:String; private var age:int; private var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String

32 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } // rcupre le prnom du joueur public function getPrenom ( ):String { return prenom; } // rcupre le nom du joueur public function getNom ( ):String { return nom; } // rcupre l'age du joueur public function getAge ( ):int { return age; } // rcupre la ville du joueur public function getVille ( ):String { return ville; } // mthode permettant au joueur de se presenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; nom : " + nom + ", age :

33 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

} } }

De cette manire, nous pouvons accder toutes les proprits dune instance de Joueur :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan" ); // affiche : Stevie trace( monJoueur.getPrenom() ); // affiche : Wonder trace( monJoueur.getNom() ); // affiche : 57 trace( monJoueur.getAge() ); // affiche : Michigan trace( monJoueur.getVille() );

Grace aux diffrentes mthodes de rcupration les proprits prives sont rendues accessibles de manire encapsule. Pour linstant il est impossible de les modifier, car nous navons pas encore intgr de mthodes daffectation.

Contrle daffectation
Nous venons de voir comment rendre notre code encapsul grce aux mthodes de rcupration et daffectation, lautre grand intrt de lencapsulation concerne le contrle daffectation des proprits. Sans mthodes daffectation, il est impossible de rendre laffectation ou laccs nos proprits intelligentes. Imaginons que le nom de la ville doive toujours tre format correctement. Pour cela nous ajoutons une mthode daccs setVille qui intgre une logique spcifique :
package { public class Joueur { // dfinition des proprits de la classe private var nom:String; private var prenom:String; private var age:Number; private var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:Number, pVille:String )

34 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } // rcupre le prnom du joueur public function getPrenom ( ):String { return prenom; } // rcupre le nom du joueur public function getNom ( ):String { return nom; } // rcupre l'age du joueur public function getAge ( ):Number { return age; } // rcupre la ville du joueur public function getVille ( ):String { return ville; } // rcupre la ville du joueur public function setVille ( pVille:String ):void { ville = pVille.charAt(0).toUpperCase()+pVille.substr ( 1 ).toLowerCase(); } // mthode permettant au joueur de se presenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." );

35 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

} // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

Le changement de ville est dsormais contrl, si un nom de ville est pass, le formatage est automatique :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan" ); // affectation d'une ville ma formate monJoueur.setVille ( "ClEEVELAnd"); // affiche : Cleeveland trace( monJoueur.getVille() );

La mthode setVille formate automatiquement la ville passe au format attendu. En dfinissant cette mthode, nous sommes assurs du formatage des villes associes chaque joueur. Pratique nest-ce pas ? Afin dviter les erreurs dexcution, nous pouvons affecter les proprits dun objet en exerant un contrle. Nous rendons notre objet intelligent. Une autre situation pourrait tre imagine. Imaginons que lapplication serveur grant la connexion de nos joueurs ne puisse grer des noms ou prnoms de plus de 30 caractres, nous intgrons ce contrle au sein des mthodes setNom et setPrenom. Nous rajoutons au passage une mthode setAge arrondissant automatiquement lge pass :
package { public class Joueur { // dfinition des proprits de la classe private var nom:String; private var prenom:String; private var age:Number; private var ville:String; // fonction constructeur

36 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

function Joueur ( pPrenom:String, pNom:String, pAge:Number, pVille:String ) { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; } // rcupre le prnom du joueur public function getPrenom ( ):String { return prenom; } // rcupre le nom du joueur public function getNom ( ):String { return nom; } // rcupre l'age du joueur public function getAge ( ):Number { return age; } // rcupre la ville du joueur public function getVille ( ):String { return ville; } // permet de changer le prnom du joueur public function setPrenom ( pPrenom:String ):void { if ( pPrenom.length <= 30 ) prenom = pPrenom; else trace ("Le prnom spcifi est trop long"); } // permet de changer le nom du joueur public function setNom ( pNom:String ):void

37 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ if ( pNom.length <= 30 ) nom = pNom; else trace ("Le nom spcifi est trop long"); } // permet de changer lge du joueur public function setAge ( pAge:Number ):void { // l'age pass est automatiquement arrondi age = Math.floor ( pAge ); } // rcupre la ville du joueur public function setVille ( pVille:String ):void { ville = pVille.charAt(0).toUpperCase()+pVille.substr ( 1 ).toLowerCase(); } // mthode permettant au joueur de se prsenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

Si un nom ou un prnom trop long est pass nous pouvons afficher un message avertissant le dveloppeur tiers :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affectation du nouveau nom // affiche : Le nom spcifi est trop long monJoueur.setNom ( "UnNomTresTresTresTresTresTresLong" ); // affiche : Wonder

38 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

trace( monJoueur.getNom() ); // affectation d'un nouvel age monJoueur.setAge ( 24.8 ); // affiche : 24 trace( monJoueur.getAge() );

En effectuant ce contrle, nous sommes assurs que la proprit nom ne contiendra aucune chane de caractres de plus de 30 caractres. Sans cette vrification faite au moment de laffectation, nous serions obligs de tester la proprit nom avant de faire quoi que ce soit. Les mthodes daffectation et de rcupration savrent trs pratiques, elles offrent la possibilit de dlivrer un code scuris, portable et lgant. Pourtant certains dveloppeurs prfrent utiliser les mthodes en lecture/criture quils considrent comme plus pratiques. Lutilisation dune mthode pour rcuprer ou affecter une proprit ne leur convient pas. ActionScript 3 propose une solution alternative, appeles mthodes en lecture/criture.

Mthodes en lecture/criture
Les mthodes de lecture et dcriture plus communment appeles getter/setter sont la prolongation des mthodes daccs. Leur intrt reste le mme, elles permettent dencapsuler notre code en grant laffectation et la rcupration de proprits dun objet mais laide dune syntaxe diffrente. Les mthodes de lecture et dcriture permettent au dveloppeur dappeler de manire transparente ces mthodes comme si ce dernier ciblait une proprit. Afin de bien comprendre ce concept nous allons intgrer des mthodes de lecture et dcriture dans notre classe Joueur. Afin de dfinir ces mthodes nous utilisons deux mots cls :
get : Dfinit une mthode de lecture set : Dfinit une mthode dcriture

Une mthode de lecture se dfinit de la manire suivante :


// affecte une proprit public function get maPropriete ( ):type { return proprieteInterne; }

39 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Celle-ci doit obligatoirement retourner une valeur, et ne possder aucun paramtre au sein de sa signature. Si ce nest pas le cas, une erreur la compilation est gnre. Une mthode dcriture se dfinit de la manire suivante :
// affecte une proprit public function set maPropriete ( pNouvelleValeur:type ):void { proprieteInterne = pNouvelleValeur; }

Celle-ci doit obligatoirement possder au moins un paramtre au sein de sa signature et ne retourner aucune valeur. Si ce nest pas le cas, une erreur la compilation est gnre. Nous allons modifier les mthodes de rcupration et daffectation setNom et getNom par des mthodes en lecture/criture. Afin dviter un conflit de dfinition de variables, nous devons nous assurer que les mthodes en lecture/criture ne possdent pas le mme nom que les proprits que nous souhaitons externaliser :
package { public class Joueur { // dfinition des proprits de la classe private var _nom:String; private var prenom:String; private var age:Number; private var ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:Number, pVille:String ) { prenom = pPrenom; _nom = pNom; age = pAge; ville = pVille; } // rcupre le prnom du joueur public function getPrenom ( ):String { return prenom;

40 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

} // rcupre l'age du joueur public function getAge ( ):Number { return age; } // rcupre la ville du joueur public function getVille ( ):String { return ville; } // permet de changer le prnom du joueur public function setPrenom ( pPrenom:String ):void { if ( pPrenom.length <= 30 ) prenom = pPrenom; else trace ("Le prnom spcifi est trop long"); } // rcupre le nom du joueur public function get nom ( ):String { return _nom; } // permet de changer le nom du joueur public function set nom ( pNom:String ):void { if ( pNom.length <= 30 ) _nom = pNom; else trace ("Le nom spcifi est trop long"); } // permet de changer l'age du joueur public function setAge ( pAge:Number ):void { // l'age pass est automatiquement arrondi age = Math.floor ( pAge ); }

41 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

// rcupre la ville du joueur public function setVille ( pVille:String ):void { ville = pVille.charAt(0).toUpperCase()+pVille.substr ( 1 ).toLowerCase(); } // mthode permettant au joueur de se prsenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

En utilisant le mot cl set nous avons dfini une mthode dcriture. Ainsi, le dveloppeur limpression daffecter une proprit, en ralit notre mthode dcriture est dclenche :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affectation du nouveau nom monJoueur.nom = "Womack";

Cela permet de conserver une affectation des donnes, dans la mme criture que les proprits, en conservant le contrle des donnes passes :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affectation du nouveau nom // affiche : Le nom spcifi est trop long monJoueur.nom = "UnNomTresTresTresTresTresTresLong";

En utilisant le mot cl get nous avons dfini une mthode de lecture. Ainsi, le dveloppeur limpression de cibler une simple proprit, en ralit notre mthode de lecture est dclenche :
// instanciation d'un joueur

42 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affectation du nouveau nom // affiche : Le nom spcifi est trop long monJoueur.nom = "UnNomTresTresTresTresTresTresLong"; // rcupration du nom // affiche : Wonder trace( monJoueur.nom ) ;

Voici le code final de notre classe Joueur :


package { public class Joueur { // dfinition des proprits de la classe private var _nom:String; private var _prenom:String; private var _age:Number; private var _ville:String; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:Number, pVille:String ) { _prenom = pPrenom; _nom = pNom; _age = pAge; _ville = pVille; } // rcupre le nom du joueur public function get nom ( ):String { return _nom; } // permet de changer le nom du joueur public function set nom ( pNom:String ):void { if ( pNom.length <= 30 ) _nom = pNom; else trace ("Le nom spcifi est trop long"); } // rcupre le prnom du joueur public function get prenom ( ):String {

43 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

return _prenom; } // permet de changer le prnom du joueur public function set prenom ( pPrenom:String ):void { if ( pPrenom.length <= 30 ) _prenom = pPrenom; else trace ("Le prnom spcifi est trop long"); } // permet de changer l'age du joueur public function set age ( pAge:Number ):void { // l'age pass est automatiquement arrondi _age = Math.floor ( pAge ); } // rcupre l'age du joueur public function get age ( ):Number { return _age; } // rcupre la ville du joueur public function set ville ( pVille:String ):void { _ville = pVille.charAt(0).toUpperCase()+pVille.substr ( 1 ).toLowerCase(); } // rcupre la ville du joueur public function get ville ( ):String { return _ville; } // mthode permettant au joueur de se prsenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); }

44 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

// affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

Lencapsulation fait partie des points essentiels de la programmation oriente objet, les mthodes de rcupration et daffectation permettent de cacher limplmentation et dexercer un contrle lors de laffectation de proprits. Les mthodes de lecture/criture tendent ce concept en proposant une syntaxe alternative. Libre vous de choisir ce que vous prfrez, de nombreux dbats existent quand au choix dutilisateur de mthodes de rcupration et daffectation et de lecture/criture.

A retenir
Dans la plupart des cas, pensez rendre prives les proprits dun objet. Afin dencapsuler notre code nous pouvons utiliser des mthodes de rcupration ou daffectation ou bien des mthodes de lecture et dcriture. Lintrt est dexercer un contrle sur la lecture et lcriture de proprits. Un code encapsul rend la classe volutive et solide.

Cas dutilisation de lattribut static


Lattribut de proprit static permet de rendre un membre utilisable dans un contexte de classe et non doccurrence. En dautres termes, lorsquune mthode ou une proprit est dfinie avec lattribut static celle-ci ne peut tre appele que depuis le constructeur de la classe. Les proprits et mthodes statiques sopposent aux mthodes et proprits doccurrences qui ne peuvent tre appeles que sur des instances de classes. Lintrt dune mthode ou proprit statique est dtre globale la classe, si nous dfinissons une proprit statique nVitesse au sein dune classe Vaisseau, lors de sa modification tout les vaisseaux voient leur vitesse modifie. 45 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Parmi les mthodes statiques les plus connues nous pouvons citer :
Math.abs Math.round

Math.floor

ExternalInterface.call

Nous utilisons trs souvent ses proprits statiques au sein de classes comme Math ou System :
Math.PI System.totalMemory

Security.sandboxType

Voici un exemple canonique dutilisation dune proprit statique. Afin de savoir combien dinstances de classes ont t cres, nous dfinissons une proprit statique i au sein de la classe Joueur. A chaque instanciation nous incrmentons cette proprit et affectons la valeur la proprit doccurrence id :
package { public class Joueur { // dfinition des proprits de la classe private var nom:String; private var prenom:String; private var age:Number; private var ville:String; // proprit statique private static var i:int = 0; // proprit id private var id:int; // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; // chaque objet Joueur cr, nous incrmentons // la proprit statique i id = i++;

46 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

} // reste du code de la classe non montr } }

A chaque instanciation dun objet Joeur, le constructeur est dclench et incrmente la proprit statique i aussitt affecte la proprit doccurrence id. Pour pouvoir rcuprer le nombre de joueurs crs, nous allons crer une mthode statique nous renvoyant la valeur de la proprit statique i :
// renvoie le nombre de joueurs crs public static function getNombreJoueurs ( ):int { return i; }

Afin de cibler une proprit statique au sein dune classe nous prcisons par convention toujours le constructeur de la classe avant de cibler la proprit :
// fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; id = Joueur.i++; } // renvoie le nombre de joueurs crs public static function getNombreJoueurs ( ):int { return Joueur.i; }

En spcifiant le constructeur avant la proprit, un dveloppeur tiers serait tout de suite averti de la nature statique dune proprit. Voici le code final de la classe Joueur : 47 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

package { public class Joueur { ) // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; id = Joueur.i++; } // renvoie le nombre de joueurs crs public static function getNombreJoueurs ( ):int { return Joueur.i; } // rcupre le prnom du joueur public function getPrenom ( ):String { return prenom; } // rcupre le nom du joueur public function getNom ( ):String { return nom; } // rcupre l'age du joueur public function getAge ( ):int { return age; } // rcupre la ville du joueur public function getVille ( ):String {

48 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

return ville; } // rcupre la ville du joueur public function setVille ( pVille:String ):void { ville = pVille.charAt(0).toUpperCase()+pVille.substr ( 1 ).toLowerCase(); } // permet de changer le nom du joueur public function setNom ( pNom:String ):void { if ( pNom.length <= 30 ) nom = pNom; else trace ("Le nom spcifi est trop long"); } // permet de changer le prnom du joueur public function setPrenom ( pPrenom:String ):void { if ( pPrenom.length <= 30 ) prenom = pPrenom; else trace ("Le prnom spcifi est trop long"); } // mthode permettant au joueur de se presenter public function sePresenter ( ):void { trace("Je m'appelle " + prenom + ", j'ai " + age + " ans." ); } // affiche une description de l'objet Joueur public function toString ( ):String { return "[Joueur prenom : " + prenom +", " + age + ", ville : " + ville + "]"; } } } nom : " + nom + ", age :

Afin de savoir combien de joueurs ont t crs, nous appelons la mthode getNombreJoueurs directement depuis la classe Joueur : 49 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

// instanciation d'un joueur var monJoueur:Joueur = new Joueur("Stevie", "Wonder", 57, "Michigan"); // affiche : 1 trace( Joueur.getNombreJoueurs() ); // instanciation d'un joueur var monSecondJoueur:Joueur = new Joueur("Bobby", "Womack", 57, "Detroit"); // affiche : 2 trace( Joueur.getNombreJoueurs() );

Si nous tentons dappeler une mthode de classe sur une occurrence de classe :
// cration d'un joueur connu :) var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan"); // appel d'une mthode statique sur une instance de classe monJoueur.getNombreJoueurs();

Le compilateur nous renvoie lerreur suivante :


1061: Appel la mthode getNombreJoueurs peut-tre non dfinie, via la rfrence de type static Joueur.

Contrairement aux proprits doccurrences de classes, les proprits de classes peuvent tre initialises directement aprs leur dfinition. Ceci est du au fait quune mthode statique nexiste quau sein dune classes et non des occurrences, ainsi si nous crons dix joueurs le constructeur de la classe Joueur sera dclenche dix fois, mais linitialisation de la classe et de ses proprits statiques une seule fois. Ainsi notre tableau tableauJoueurs nest pas recr chaque dclenchement du constructeur. Il est impossible dutiliser le mot cl this au sein dune mthode de classe, seules les mthodes dinstances le permettent. Une mthode statique volue dans le contexte dune classe et non dune instance. Si nous tentons de rfrencer this au sein dune mthode statique, la compilation est impossible :
// renvoie le nombre de joueurs crs public static function getNombreJoueurs ( ):int { return this.i; }

Le message derreur suivant saffiche :


1042: Il est impossible d'utiliser le mot-cl this dans une mthode statique. Il ne peut tre utilis que dans les mthodes d'une instance, la fermeture d'une fonction et le code global.

50 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

A linverse les mthodes dinstances peuvent cibler une proprit ou une mthode statique. Le constructeur de la classe Joueur incrmente la proprit statique i :
) // fonction constructeur function Joueur ( pPrenom:String, pNom:String, pAge:int, pVille:String { prenom = pPrenom; nom = pNom; age = pAge; ville = pVille; statique i } // A chaque objet Joueur cr, nous incrmentons la proprit id = Joueur.i++;

Lorsque nous avons besoin de dfinir une proprit ou une mthode ayant une signification globale toutes les instances de classes nous utilisons lattribut de proprits static. Certaines classes ne contiennent que des mthodes statiques, nous pourrions imaginer une classe VerifFormulaire disposant de diffrentes mthodes permettant de valider un formulaire. Au lieu de crer une instance de classe puis dappeler la mthode sur celle-ci, nous appelons directement la mthode verifEmail sur la classe :
// vrification du mail directement l'aide d'une mthode statique var mailValide:Boolean = OutilsFormulaire.verifEmail( "bobby@groove.com" );

Cette technique permet par exemple la cration de classes utilitaires, rendant laccs des fonctionnalits sans instancier dobjet spcifique. Nous piochons directement sur la classe la fonctionnalit qui nous intresse.

A retenir :
Lutilisation du mot cl this est interdite au sein dune mthode statique. A linverse, une mthode dinstance de classe peut cibler ou appeler une mthode ou proprit de classe. Les mthodes ou proprits statiques ont un sens global la classe.

La classe JoueurManager
La programmation objet prend tout son sens lorsque nous faisons travailler plusieurs objets ensemble, nous allons maintenant crer une 51 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

classe qui aura comme simple tche dajouter ou supprimer nos joueurs au sein de lapplication, dautres fonctionnalits comme le tri ou autres pourront tre ajoutes plus tard. Le but de la classe JoueurManager sera de centraliser la gestion des joueurs de notre application. A cot de notre classe Joueur nous dfinissons une nouvelle classe appele JoueurManager :
package { public class JoueurManager { public function JoueurManager ( ) {

} } }

Nous dfinissons une proprit doccurrence tableauJoueurs afin de contenir chaque joueur :
package { public class JoueurManager { // tableau contenant les rfrences de joueurs private var tableauJoueurs:Array = new Array(); public function JoueurManager ( ) {

} } }

La mthode ajouteJoueur ajoute la rfrence au joueur pass en paramtre au tableau interne tableauJoueurs et appelle la mthode sePresenter sur chaque joueur entrant :
package

52 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ public class JoueurManager { // tableau contenant les rfrences de joueurs private var tableauJoueurs:Array = new Array(); public function JoueurManager ( ) {

} public function ajouteJoueur ( pJoueur:Joueur ):void { pJoueur.sePresenter(); tableauJoueurs.push ( pJoueur ); } } }

La mthode ajouteJoueur accepte un paramtre de type Joueur, seuls des objets de type Joueur pourront donc tre passs. Lorsquun joueur est ajout, la mthode sePresenter est appele sur celui-ci. Ainsi, pour ajouter des joueurs nous crons chaque instance puis les ajoutons au gestionnaire de joueurs, la classe JoueurManager :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan"); var monDeuxiemeJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var monTroisiemeJoueur:Joueur = new Joueur ("Michael", "Jackson", 48, "Los Angeles"); // gestion des joueurs var monManager:JoueurManager = new JoueurManager(); // ajout des joueurs // affiche : /* Je m'appelle Stevie, j'ai 57 ans. Je m'appelle Bobby, j'ai 66 ans. Je m'appelle Michael, j'ai 48 ans. */ monManager.ajouteJoueur ( monJoueur ); monManager.ajouteJoueur ( monDeuxiemeJoueur ); monManager.ajouteJoueur ( monTroisiemeJoueur );

53 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Lorsquun joueur est ajout, il se prsente automatiquement. Au cas o un joueur serait supprim par le systme, nous devons le supprimer du gestionnaire, nous dfinissons une mthode supprimeJoueur. Celle-ci recherche le joueur dans le tableau interne grce la mthode indexOf et le supprime si celui-ci est trouv. Autrement, nous affichons un message indiquant que le joueur nest plus prsent dans le gestionnaire :
package { public class JoueurManager { // tableau contenant les rfrences de joueurs private var tableauJoueurs:Array = new Array(); public function JoueurManager ( ) {

} public function ajouteJoueur ( pJoueur:Joueur ):void { pJoueur.sePresenter(); tableauJoueurs.push ( pJoueur ); } public function supprimeJoueur ( pJoueur:Joueur ):void { var positionJoueur:int = tableauJoueurs.indexOf ( pJoueur ); if ( positionJoueur != -1 ) tableauJoueurs.splice ( positionJoueur, 1 ); else trace("Joueur non prsent !"); } } }

Nous pouvons supprimer un joueur en passant sa rfrence. Afin dexternaliser le tableau contenant tout les joueurs, nous dfinissons une mthode daccs getJoueurs : 54 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

package { public class JoueurManager { // tableau contenant les rfrences de joueurs private var tableauJoueurs:Array; public function JoueurManager ( ) { tableauJoueurs = new Array(); } public function ajouteJoueur ( pJoueur:Joueur ):void { pJoueur.sePresenter(); tableauJoueurs.push ( pJoueur ); } public function supprimeJoueur ( pJoueur:Joueur ):void { var positionJoueur:int = tableauJoueurs.indexOf ( pJoueur ); if ( positionJoueur != -1 ) tableauJoueurs.splice ( positionJoueur, 1 ); else trace("Joueur non prsent !"); } public function getJoueurs ( ):Array { return tableauJoueurs; } } }

Afin de rcuprer lensemble des joueurs, nous appelons la mthode getJoueurs sur linstance de classe JoueurManager :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan"); var monDeuxiemeJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var monTroisiemeJoueur:Joueur = new Joueur ("Michael", "Jackson", 48, "Los Angeles"); // gestion des joueurs

55 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

var monManager:JoueurManager = new JoueurManager(); // ajout des joueurs monManager.ajouteJoueur ( monJoueur ); monManager.ajouteJoueur ( monDeuxiemeJoueur ); monManager.ajouteJoueur ( monTroisiemeJoueur ); // rcupration de l'ensemble des joueurs // affiche : [Joueur prenom : Stevie, nom : Wonder, age : 57, ville : Michigan],[Joueur prenom : Bobby, nom : Womack, age : 66, ville : Detroit],[Joueur prenom : Michael, nom : Jackson, age : 48, ville : Los Angeles] trace( monManager.getJoueurs() );

Afin de supprimer des joueurs nous appelons la mthode supprimeJoueur en passant le joueur supprimer en rfrence :
// instanciation d'un joueur var monJoueur:Joueur = new Joueur ("Stevie", "Wonder", 57, "Michigan"); var monDeuxiemeJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var monTroisiemeJoueur:Joueur = new Joueur ("Michael", "Jackson", 48, "Los Angeles"); // gestion des joueurs var monManager:JoueurManager = new JoueurManager(); // ajout des joueurs monManager.ajouteJoueur ( monJoueur ); monManager.ajouteJoueur ( monDeuxiemeJoueur ); monManager.ajouteJoueur ( monTroisiemeJoueur ); // suppression de deux joueurs monManager.supprimeJoueur ( monJoueur ); monManager.supprimeJoueur ( monTroisiemeJoueur ); // rcupration de l'ensemble des joueurs // affiche : [Joueur prenom : Bobby, nom : Womack, age : 66, ville : Detroit] trace( monManager.getJoueurs() );

La classe JoueurManager nous permet de grer les diffrents joueurs crs, nous pourrions ajouter de nombreuses fonctionnalits. En programmation oriente objet il nexiste pas une seule et unique manire de concevoir chaque application. Cest justement la richesse du dveloppement orient objet, nous pouvons discuter des heures durant, de la manire dont nous avons dvelopp une application, certains seront daccord avec votre raisonnement dautres ne le seront pas. Lintrt est dchanger ides et point de vue sur une conception. Lorsque nous devons dvelopper une application, nous sommes souvent confronts des problmes quun autre dveloppeur a surement rencontrs avant nous. Pour apporter des solutions concrtes des problmes rcurrents nous pouvons utiliser des modles de conceptions. Ces derniers dfinissent la manire dont nous devons sparer et concevoir notre application toujours dans un objectif doptimisation, de portabilit et de rutilisation. Nous reviendrons bientt sur les modles de conception les plus utiliss. 56 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Nos classes Joueur et JoueurManager sont dsormais utilises dans nos applications, mais le client souhaite introduire la notion de modrateurs. En y rflchissant quelques instants nous pouvons facilement admettre quun modrateur est une sorte de joueur, mais disposant de droits spcifiques comme le fait dexclure un autre joueur ou de stopper une partie en cours. Un administrateur possde donc toutes les capacits dun joueur. Il serait redondant dans ce cas prcis, de redfinir toutes les mthodes et proprits dj dfinies au sein de la classe Joueur au sein de la classe Administrateur, pour rutiliser notre code nous allons utiliser lhritage.

Lhritage
La notion dhritage est un concept cl de la programmation oriente objet tir directement du monde qui nous entoure. Tout lment du monde rel hrite dun autre en le spcialisant, ainsi en tant qutre humain nous hritons de toutes les caractristiques dun mammifre, au mme titre quune pomme hrite des caractristiques du fruit. Lhritage est utilis lorsquune relation de type est-un est possible. Nous pouvons considrer bien quun administrateur est un type de joueur et possde toutes ses capacits, et dautres qui font de lui un modrateur. Lorsque cette relation nest pas vrifie, lhritage ne doit pas tre considr, dans ce cas nous prfrerons gnralement la composition. Nous traiterons au cours du chapitre 10 intitul Hritage versus composition les diffrences entre les deux approches, nous verrons que lhritage peut savrer rigide et difficile maintenir dans certaines situations. Dans un contexte dhritage, des classes filles hritent automatiquement des fonctionnalits des classes mres. Dans notre cas, nous allons crer une classe Administrateur hritant de toutes les fonctionnalits de la classe Joueur. La classe mre est gnralement appele super-classe, tandis que la classe fille est appele sous-classe. Nous allons dfinir une classe Administrateur cot de la classe Joueur, afin de traduire lhritage nous utilisons le mot cl extends :
package { // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur {

57 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

public function Administrateur ( ) { } } }

Puis nous instancions un objet administrateur :


var monModo:Administrateur = new Administrateur();

A la compilation, lerreur suivante est gnre :


1203: Aucun constructeur par dfaut n'a t dfini dans la classe de base Joueur.

Lorsquune classe fille tend une classe mre, nous devons obligatoirement passer au constructeur parent, les paramtres ncessaires linitialisation de la classe mre, pour dclencher le constructeur parent nous utilisons le mot cl super :
package { // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } } }

Lintrt de lhritage rside dans la rutilisation du code. La figure 85 illustre le concept dhritage de classes :

58 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Figure 8-5. Hritage de classes. Sans lhritage nous aurions du redfinir lensemble des mthodes de la classe Joueur au sein de la classe Administrateur. Grce lhritage exprim par le mot cl extends, la classe fille Administrateur hrite de toutes les fonctionnalits de la classe mre Joueur :
// nous crons un objet administrateur var monModo:Administrateur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // le modrateur possde toutes les capacits d'un joueur // affiche : Je m'appelle Michael, j'ai 48 ans. monModo.sePresenter(); // affiche : Jackson trace( monModo.nom ); // affiche : Michael trace( monModo.prenom ); // affiche : 48 trace( monModo.age ); // affiche : Los Angeles trace( monModo.ville );

Il existe quelques exceptions lies lhritage, les mthodes et proprits statiques ne sont pas hrites. La mthode statique 59 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

getNombreJoueurs dfinie au sein de la classe Joueur nest pas disponible sur la classe Administrateur :
// instanciation dun administrateur var monModo:Administrateur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // la mthode statique Joueur.getNombreJoueurs n'est pas hrite monModo.getNombreJoueurs();

Lerreur la compilation suivante est gnre :


1061: Appel la mthode getNombreJoueurs peut-tre non dfinie, via la rfrence de type static Administrateur.

Notons que lhritage multiple nest pas gr en ActionScript 3, une classe ne peut hriter de plusieurs classes directes. Lhritage ne se limite pas aux classes personnalises, nous verrons au cours du chapitre 9 intitul Etendre Flash comment tendre des classes natives de Flash afin daugmenter les capacits de classes telles Array, BitmapData ou MovieClip et bien dautres.

A retenir :
Lhritage permet de rutiliser facilement les fonctionnalits dune classe existante. La classe mre est appele super-classe, la classe fille est appele sous-classe. On parle alors de super-type et de sous-type. Afin dhriter dune classe nous utilisons le mot cl extends.

Sous-types et super-type
Grce lhritage, la classe Administrateur possde dsormais deux types, Joueur considr comme le super-type et Administrateur comme sous-type :
// la classe Administrateur est aussi de type Joueur var monModo:Administrateur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // un modrateur est un joueur // affiche : true trace( monModo is Joueur ); // un modrateur est aussi un administrateur // affiche : true trace( monModo is Administrateur );

Partout o le super-type est attendu nous pouvons passer une instance de sous-type, ainsi une variable de type Joueur peut stocker un objet de type Administrateur :
// la classe Administrateur est aussi de type Joueur

60 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

var monModo:Joueur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // un modrateur est un joueur // affiche : true trace( monModo is Joueur ); // un modrateur est aussi un administrateur // affiche : true trace( monModo is Administrateur );

Tous les administrateurs sont des joueurs, le compilateur sait que toutes les fonctionnalits du type Joueur sont prsentes au sein de la classe Administrateur et nous permet de compiler. Linverse nest pas vrai, les joueurs ne sont pas forcment des administrateurs, il est donc impossible daffecter un super-type une variable de sous-type. Le code suivant ne peut tre compil :
// la classe Joueur n'est pas de type Administrateur var monModo:Administrateur = new Joueur("Michael", "Jackson", 48, "Los Angeles");

Lerreur la compilation suivante est gnre :


1118: Contrainte implicite d'une valeur du type statique Joueur vers un type peut-tre sans rapport Administrateur.

Le compilateur ne peut nous garantir que les fonctionnalits de la classe Administrateur seront prsentes sur lobjet Joueur et donc interdit la compilation. Nous retrouvons le mme concept en utilisant les classes graphiques natives comme flash.display.Sprite et flash.display.MovieClip, le code suivant peut tre compil sans problme :
// une instance de MovieClip est aussi de type Sprite var monClip:Sprite = new MovieClip();

La classe MovieClip hrite de la classe Sprite ainsi une instance de MovieClip est aussi de type Sprite. A linverse, un Sprite nest pas de type MovieClip :
// une instance de Sprite n'est pas de type MovieClip var monClip:MovieClip = new Sprite();

Si nous testons le code prcdant, lerreur la compilation suivante est gnre :


1118: Contrainte implicite d'une valeur du type statique flash.display:Sprite vers un type peut-tre sans rapport flash.display:MovieClip.

La question que nous pouvons nous poser est la suivante, quel est lintrt de stocker un objet correspondant un sous-type au sein

61 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

dune variable de super-type ? Pourquoi ne pas simplement utiliser le type correspondant ? Afin de bien comprendre ce concept, imaginons quune mthode nous renvoie des objets de diffrents types, prenons un cas trs simple comme la mthode getChildAt de la classe DisplayObjectContainer. Si nous regardons sa signature, nous voyons que celle-ci renvoie un objet de type DisplayObject :
public function getChildAt(index:int):DisplayObject

En effet, la mthode getChildAt peut renvoyer toutes sortes dobjets graphiques tels Shape, MovieClip, Sprite ou bien SimpleButton. Tous ces types on quelque chose qui les lient, le type DisplayObject est le type commun tous ces objets graphiques, ainsi lorsque plusieurs types sont attendus, nous utilisons un type commun aux diffrentes classes. Nous allons justement mettre cela en application au sein de notre classe JoueurManager, si nous regardons la signature de la mthode ajouterJoueur nous voyons que celle-ci accepte un paramtre de type Joueur :
public function ajouteJoueur ( pJoueur:Joueur ):void { pJoueur.sePresenter(); tableauJoueurs.push ( pJoueur ); }

Joueur est le type commun aux deux classes Joueur et Administrateur, nous pouvons donc sans problme lui passer des instances de type Administrateur :
// cration des joueurs et du modrateur var premierJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var deuxiemeJoueur:Joueur = new Joueur ("Michael", "Jackson", 48, "Michigan"); var troisiemeJoueur:Joueur = new Joueur ("Lenny", "Williams", 50, "New York"); var monModo:Administrateur = new Administrateur ("Stevie", "Wonder", 48, "Los Angeles"); // gestion des joueurs var monManager:JoueurManager = new JoueurManager(); // /* Je Je ajout des joueurs affiche : m'appelle Bobby, j'ai 66 ans. m'appelle Michael, j'ai 48 ans.

62 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Je m'appelle Bobby, j'ai 30 ans. Je m'appelle Stevie, j'ai 48 ans. Je suis modrateur */ monManager.ajouteJoueur ( premierJoueur ); monManager.ajouteJoueur ( deuxiemeJoueur ); monManager.ajouteJoueur ( troisiemeJoueur ); monManager.ajouteJoueur ( monModo );

Si nous devons plus tard crer de nouveaux types de joueurs en tendant la classe Joueur, aucune modification ne sera ncessaire au sein de la fonction ajouteJoueur.

A retenir :
Grce lhritage, une classe peut avoir plusieurs types. Partout o une classe mre est attendue nous pouvons utiliser une classe fille. Cest ce que nous appelons le polymorphisme.

Spcialiser une classe


Pour le moment, la classe Administrateur possde les mmes fonctionnalits que la classe Joueur. Il est inutile dhriter simplement dune classe mre sans ajouter de nouvelles fonctionnalits, en dfinissant de nouvelles mthodes au sein de la classe Administrateur nous spcialisons la classe Joueur. Nous allons dfinir une nouvelle mthode appele kickJoueur qui aura pour but de supprimer un joueur de la partie en cours :
package { // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } // mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { trace ("Kick " + pJoueur ); }

63 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

} }

Dsormais la classe Administrateur possde une mthode kickJoueur en plus des fonctionnalits de la classe Joueur. Nous avons spcialis la classe Joueur travers la classe Administrateur. La figure 8-6 illustre les deux classes :

Figure 8-6. Spcialisation de la classe Joueur. Lorsque la mthode kickJoueur est excute, nous devons appeler la mthode supprimeJoueur de la classe JoueurManager car cest celle qui centralise et gre les joueurs connects. Chaque administrateur pourrait avoir une rfrence la classe JoueurManager mais cela serait rigide, dans ce cas nous prfrerons une approche vnementielle en utilisant dans nos classes personnalises le modle vnementiel dActionScript 3 laide de la classe flash.events.EventDispatcher. La notion de diffusion dvnements personnaliss est traite en dtail lors du chapitre 12 intitul Diffusion dvnements personnaliss. Une 64 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

fois achev, nhsitez pas revenir sur cet exemple pour ajouter le code ncessaire. Lide est que la classe Administrateur diffuse un vnement indiquant la classe JoueurManager de supprimer le joueur en question. Nous retrouverons ici le concept de diffuseur couteur trait dans les chapitres prcdents. La figure 8-6 illustre le concept :

Figure 8-6. Lobjet JoueurManager est souscrit un vnement auprs dun administrateur. Lorsquun administrateur diffuse lvnement appropri, lobjet JoueurManager supprime le joueur de la partie en cours. Nous allons nous intresser maintenant un processus appel transtypage trs utile dans un contexte dhritage.

A retenir :
Il ne faut pas confondre hritage et spcialisation. Mme si cela est dconseill, une classe peut hriter dune autre sans ajouter de nouvelles fonctionnalits. Lorsquune sous-classe ajoute de nouvelles fonctionnalits, on dit que celle-ci spcialise la super-classe.

Le transtypage
Le transtypage est un processus trs simple qui consiste faire passer un objet pour un autre auprs du compilateur. Afin de comprendre le transtypage prenons la situation suivante, comme nous lavons vu prcdemment il peut arriver quune variable dun type parent stocke des instances de types enfants. 65 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

Dans le code suivant nous stockons au sein dune variable de type Joueur une instance de classe Administrateur :
// cration d'un joueur et d'un administrateur var premierJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var monModo:Joueur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // la mthode kickJoueur n'existe pas pour le compilateur, car la classe Joueur ne la dfinit pas monModo.kickJoueur(premierJoueur);

Cela ne pose aucun problme, sauf lorsque nous tentons dappeler la mthode kickJoueur sur la variable monModo, le compilateur nous gnre lerreur suivante :
1061: Appel la mthode kickJoueur peut-tre non dfinie, via la rfrence de type static Joueur.

Pour le compilateur, la classe Joueur ne possde pas de mthode kickJoueur, la compilation est donc impossible. Pourtant nous savons qu lexcution la variable monModo contiendra une instance de la classe Administrateur qui possde bien la mthode kickJoueur. Afin de faire taire le compilateur nous allons utiliser le transtypage en disant en quelque sorte au compilateur Fais moi confiance, il ya bien la mthode kickJoueur sur lobjet, laisse moi compiler . La syntaxe du transtypage est simple, nous prcisions le type puis nous plaons deux parenthses comme pour une fonction traditionnelle :
Type ( monObjet ) ;

Ainsi, nous transtypons la variable monModo vers le type Administrateur afin de pouvoir compiler :
// cration d'un joueur et d'un administrateur var premierJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var monModo:Joueur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // affiche : Kick [Joueur prenom : Bobby, nom : Womack, age : 66, ville : Detroit] Administrateur ( monModo ).kickJoueur(premierJoueur);

A lexcution, la mthode est bien excute. L encore, nous pouvons nous demander dans quel cas concret nous devrions avoir recourt au transtypage ? Nous allons dfinir une nouvelle mthode jouerSon sur la classe Administrateur qui sera automatiquement appele au sein de la mthode ajouteJoueur. Lorsquun administrateur sera ajout

66 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

lapplication un son sera jou afin de notifier de larrive du modrateur :


package { // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } // mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { trace ("Kick " + pJoueur ); } // mthode permettant de jouer un son public function jouerSon ( ):void { trace("Joue un son"); } } }

Au sein de la classe JoueurManager nous devons appeler la mthode jouerSon lorsquun objet de type Administrateur est pass, pour cela nous testons si le type correspond puis nous appelons la mthode voulue :
package { public class JoueurManager { // tableau contenant les rfrences de joueurs private var tableauJoueurs:Array; public function JoueurManager ( ) {

67 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

tableauJoueurs = new Array(); } public function ajouteJoueur ( pJoueur:Joueur ):void { pJoueur.sePresenter(); if ( pJoueur is Administrateur ) pJoueur.jouerSon(); tableauJoueurs.push ( pJoueur ); } public function supprimeJoueur ( pJoueur:Joueur ):void { var positionJoueur:int = tableauJoueurs.indexOf ( pJoueur ); if ( positionJoueur != -1 ) tableauJoueurs.splice ( positionJoueur, 1 ); else throw new Error ("Joueur non prsent !"); } public function getJoueurs ( ):Array { return tableauJoueurs; } } }

Une fois nos classes modifies, nous pouvons initialiser notre application :
// cration d'un joueur et d'un administrateur var premierJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var deuxiemeJoueur:Joueur = new Joueur ("Michael", "Jackson", 48, "Michigan"); var troisiemeJoueur:Joueur = new Joueur ("Lenny", "Williams", 50, "New York"); var monModo:Administrateur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // gestion des joueurs var monManager:JoueurManager = new JoueurManager(); // ajout des joueurs et monManager.ajouteJoueur monManager.ajouteJoueur monManager.ajouteJoueur monManager.ajouteJoueur administrateurs ( premierJoueur ); ( deuxiemeJoueur ); ( troisiemeJoueur ); ( monModo );

68 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

A la compilation lerreur suivante est gnre :


1061: Appel la mthode jouerSon peut-tre non dfinie, via la rfrence de type static Joueur.

Le compilateur se plaint car pour lui nous tentons dappeler une mthode inexistante sur la classe Joueur. Nous savons qu lexcution si la condition est valide, nous serons assurs que lobjet possde bien la mthode jouerSon, pour nous permettre de compiler nous transtypons vers le type Administrateur:
package { public class JoueurManager { // tableau contenant les rfrences de joueurs private var tableauJoueurs:Array; public function JoueurManager ( ) { tableauJoueurs = new Array(); } public function ajouteJoueur ( pJoueur:Joueur ):void { pJoueur.sePresenter(); if ( pJoueur is Administrateur ) Administrateur ( pJoueur ).jouerSon(); tableauJoueurs.push ( pJoueur ); } public function supprimeJoueur ( pJoueur:Joueur ):void { var positionJoueur:int = tableauJoueurs.indexOf ( pJoueur ); if ( positionJoueur != -1 ) tableauJoueurs.splice ( positionJoueur, 1 ); else throw new Error ("Joueur non prsent !"); } public function getJoueurs ( ):Array { return tableauJoueurs;

69 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

} } }

Nous pouvons initialiser notre application :


// cration d'un joueur et d'un administrateur var premierJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); var deuxiemeJoueur:Joueur = new Joueur ("Michael", "Jackson", 48, "Michigan"); var troisiemeJoueur:Joueur = new Joueur ("Lenny", "Williams", 50, "New York"); var monModo:Administrateur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // gestion des joueurs var monManager:JoueurManager = new JoueurManager(); // ajout des joueurs et administrateurs /* affiche : Je m'appelle Bobby, j'ai 66 ans. Je m'appelle Michael, j'ai 48 ans. Je m'appelle Lenny, j'ai 50 ans. Je m'appelle Michael, j'ai 48 ans. Joue un son */ monManager.ajouteJoueur ( premierJoueur ); monManager.ajouteJoueur ( deuxiemeJoueur ); monManager.ajouteJoueur ( troisiemeJoueur ); monManager.ajouteJoueur ( monModo );

Nous reviendrons trs souvent sur la notion de transtypage, couramment utilise au sein la liste daffichage.

A retenir :
Il ne faut pas confondre conversion et transtypage. Le transtypage ne convertit pas un objet en un autre, mais permet de faire passer un objet pour un autre.

Surcharge
Le concept de surcharge (override en anglais) intervient lorsque nous avons besoin de modifier certaines fonctionnalits hrites. Imaginons quune mthode hrite ne soit pas assez complte, nous pouvons surcharger celle-ci dans la sous-classe afin dintgrer notre nouvelle version. Nous allons dfinir au sein de notre classe Administrateur une mthode sePresenter, afin de surcharger la version hrite, pour cela nous utilisons le mot cl override :
package

70 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } // mthode permettant ladministrateur de se prsenter override public function sePresenter ( ):void { trace("Je suis modrateur"); } } }

En appelant la mthode sePresenter sur notre instance de modrateur nous dclenchons la version redfinie :
// nous instancions un modrateur var monModo:Administrateur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // nous lui demandons de se prsenter // affiche : Je suis modrateur monModo.sePresenter();

En ActionScript 2, il nexistait pas de mot cl pour surcharger une mthode, le simple fait de dfinir une mthode du mme nom au sein de la sous-classe surchargeait la mthode hrite. Grce au mot cl override introduit par ActionScript 3 il est beaucoup plus simple pour un dveloppeur de savoir si une mthode est une mthode surchargeante ou non. Attention, la mthode surchargeante doit avoir le mme nom et la mme signature que la mthode surcharge, sinon la surcharge est dite non compatible. Dans lexemple suivant nous retournons une valeur lorsque la mthode sePresenter est excute :
package { // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur

71 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } // mthode permettant l'administrateur de se prsenter override public function sePresenter ( ):String { // dclenche la mthode surcharge super.sePresenter(); return "Je suis modrateur"; } // mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { trace ("Kick " + pJoueur ); } // mthode permettant de jouer un son public function jouerSon ( ):void { trace("Joue un son"); } } }

La mthode surchargeante na pas la mme signature que la mthode surcharge, la compilation est impossible, si nous testons le code suivant :
var monModo:Administrateur "Los Angeles"); = new Administrateur("Michael", "Jackson", 48,

Lerreur suivante est gnre la compilation :


1023: override non compatible.

Dans un contexte de surcharge, la mthode surcharge nest pas perdue. Si nous souhaitons au sein de notre mthode surchargeante dclencher la mthode surcharge, nous utilisons le mot cl super :
package

72 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

{ // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } // mthode permettant au joueur de se prsenter override public function sePresenter ( ):void { // dclenche la mthode sePresenter surcharge super.sePresenter(); trace("Je suis modrateur"); } } }

Ainsi, lorsque la mthode sePresenter est dclenche, celle-ci dclenche aussi la version surcharge :
// nous instancions un modrateur var monModo:Administrateur = new Administrateur("Michael", "Jackson", 48, "Los Angeles"); // nous lui demandons de se prsenter // affiche : /* Je m'appelle Michael, j'ai 48 ans. Je suis modrateur */ monModo.sePresenter();

Dans le code prcdent nous augmentons les capacits de la mthode sePresenter en ajoutant le message Je suis modrateur . Grce au mot cl super nous pouvons excuter la mthode surcharge, celle-ci nest pas perdue. En surchargeant laide dune mthode vide, nous pouvons supprimer une fonctionnalit hrite :
package {

73 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 8 Programmation oriente objet version 0.1.2

// l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } // mthode surchargeante vide override public function sePresenter ( ):void { } } }

Lorsque la mthode sePresenter est appele sur une instance de la classe Administrateur, la version vide est excute. Celle-ci ne contenant aucune logique, nous avons annul lhritage de la mthode sePresenter.

A retenir :
La surcharge permet de redfinir une fonctionnalit hrite. Afin que la surcharge soit possible, la mthode surcharge et surchargeante doivent avoir la mme signature. Afin de surcharger une mthode nous utilisons le mot cl override.

74 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

9
Etendre les classes natives

INTERETS ............................................................................................................... 1 LE VIEIL AMI PROTOTYPE ........................................................................................ 2 ETENDRE LES CLASSES NON GRAPHIQUES ................................................ 5 ETENDRE LES CLASSES GRAPHIQUES........................................................ 11 ACCDER LOBJET STAGE DE MANIRE SCURISE ............................................ 19 AJOUTER DES FONCTIONNALITS .......................................................................... 23 RUTILISER LE CODE ............................................................................................. 47 CLASSE DYNAMIQUE ............................................................................................. 49 UN VRAI CONSTRUCTEUR ...................................................................................... 51 CRER DES BOUTONS DYNAMIQUES ...................................................................... 52

Intrts
Nous avons dcouvert au cours du chapitre prcdent les principaux concepts cl de la programmation oriente objet. Cependant, une des principales difficults rside souvent dans la mise en application de ces notions dans un projet concret ActionScript. La grande puissance de Flash rside dans sa capacit lier graphisme et programmation. Nous allons profiter de cette force pour appliquer ce que nous avons appris au cours du chapitre prcdent travers diffrents cas pratiques. La notion dhritage ne se limite pas aux classes personnalises et peut tre applique nimporte quelle classe de lAPI du lecteur Flash. Il est par exemple possible dtendre la classe native MovieClip, en dfinissant de nouvelles mthodes afin dobtenir un MovieClip amlior.

1 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Le but de ce chapitre est dapprendre tendre les classes natives telles MovieClip, Sprite, BitmapData mais aussi des classes non graphiques comme Array afin daugmenter les fonctionnalits offertes par celle-ci. Attention, nous verrons que certaines classes natives sont considres comme scelles et ne peuvent tre tendues.

Le vieil ami prototype


En ActionScript 1, les dveloppeurs avaient pour habitude dutiliser le prototype dune classe afin daugmenter ses capacits. En ActionScript 3, cette pratique fonctionne toujours mais nest pas officiellement recommande. Dans le code suivant nous dfinissons une mthode hello sur le prototype de la classe MovieClip :
// ajout d'une mthode hello MovieClip.prototype.hello = function () { // affiche : [object MovieClip] trace( this ); } // cration d'un clip var monClip:MovieClip = new MovieClip(); // le clip possde automatiquement la mthode ajoute au prototype monClip.hello();

Automatiquement, le clip cr possde la mthode hello. Bien quefficace, cette technique pollue les autres instances de la mme classe, car la mthode hello est alors disponible sur tous les MovieClip de lanimation. Dans le code suivant nous appelons la mthode hello sur le scnario principal :
// ajout d'une mthode hello MovieClip.prototype.hello = function () { // affiche : [object MainTimeline] trace( this ); } // le scnario possde automatiquement la mthode ajoute au prototype this.hello();

2 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Dans un concept dhritage, lide est dobtenir un nouveau type dobjet, dot de nouvelles fonctionnalits. En utilisant cette technique nous ne crons aucune nouvelle varit dobjet, nous ajoutons simplement des fonctionnalits une classe existante. Imaginons que nous devions crer une balle ayant la capacit de rebondir de manire lastique. Nous pourrions dfinir sur le prototype de la classe MovieClip toutes les mthodes de collision ncessaires notre balle, mais serait-ce rellement optimis ? En utilisant cette approche, tous les MovieClip de notre application seraient dots de capacits de rebond. Pourtant, seule la balle a vritablement besoin de telles capacits. Il serait plus intressant dtendre la classe MovieClip et de lier notre balle la sous-classe. Le prototypage possde en revanche un intrt majeur li sa simplicit et son efficacit. Prenons le cas de la classe DisplayObjectContainer. Comme nous lavons vu lors du chapitre 4 intitul Liste daffichage, la classe DisplayObjectContainer ne dfinit pas de mthode permettant de supprimer la totalit des objets enfants. A laide dune mthode ajoute au prototype de la classe DisplayObjectContainer nous pouvons ajouter trs facilement cette fonctionnalit :
DisplayObjectContainer.prototype.supprimeEnfants = function ( ) { var nbEnfants:int = this.numChildren; while ( this.numChildren > 0 ) this.removeChildAt ( 0 ); return nbEnfants; }

Ainsi, toutes les instances de la classe DisplayObjectContainer diposent dsormais dune mthode supprimeEnfants. En plus de permettre la suppression de tous les enfants, celle-ci retourne le nombre denfants supprims. Si nous disposons plusieurs objets graphiques sur le scnario principal, nous pouvons les supprimer en appelant la mthode supprimeEnfants :
DisplayObjectContainer.prototype.supprimeEnfants = function ( ) {

3 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

var nbEnfants:int = this.numChildren; while ( this.numChildren > 0 ) this.removeChildAt ( 0 ); return nbEnfants; } // supprime tous les objets enfants du scnario principal this.supprimeEnfants();

De la mme manire nous pouvons supprimer tous les objets enfants dune instance de la classe Sprite :
// cration d'un conteneur de type Sprite var monSprite:Sprite = new Sprite (); // cration d'un objet Shape enfant var maForme:Shape = new Shape(); maForme.graphics.lineStyle ( 1, 0x990000, 1 ); maForme.graphics.beginFill ( 0x990000, .2 ); maForme.graphics.drawCircle ( 50, 50, 50 ); monSprite.addChild( maForme ); addChild ( monSprite ); // suppression des objets enfants var nbEnfantsSupprimes:int = monSprite.supprimeEnfants();

En testant le code prcdent, une erreur la compilation est gnre :


1061: Appel la mthode supprimeEnfants peut-tre non dfinie, via la rfrence de type static flash.display:Sprite.

Le compilateur empche la compilation car aucune mthode du nom de supprimeEnfants nest trouve. Afin de faire taire le compilateur nous pouvons exceptionellement transtyper vers la classe dynamique Object non soumise la vrification de type la compilation :
// cration d'un conteneur de type Sprite var monSprite:Sprite = new Sprite (); // cration d'un objet Shape enfant var maForme:Shape = new Shape(); maForme.graphics.lineStyle ( 1, 0x990000, 1 ); maForme.graphics.beginFill ( 0x990000, .2 ); maForme.graphics.drawCircle ( 50, 50, 50 ); monSprite.addChild( maForme ); addChild ( monSprite ); // suppression des objets enfants var nbEnfantsSupprimes:int = Object(monSprite).supprimeEnfants(); // affiche : 1 trace( nbEnfantsSupprimes );

4 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Nous reviendrons sur lintrt du prototypage au cours du chapitre 16 intitul Le texte.

A retenir
Lutilisation du prototype est toujours possible en ActionScript 3. Son utilisation est officiellement dconseille, mais offre une souplesse intressante dans certains cas prcis. Nous prfrerons dans la majorit des cas lutilisation de sousclasses afin dtendre les capacits.

Etendre les classes non graphiques


Dautres classes natives peuvent aussi tre tendues afin daugmenter leurs capacits, cest le cas de la classe Array, dont les diffrentes mthodes ne sont quelquefois pas suffisantes. Ne vous est-il jamais arriv de vouloir rapidement mlanger les donnes dun tableau ? En tendant la classe Array nous allons ajouter un ensemble de mthodes pratiques, qui seront disponibles pour toute instance de sous-classe. A ct dun nouveau document Flash CS3 nous dfinissons une classe MonTableau au sein du paquetage org.bytearray.outils. Voici le code de la sous-classe MonTableau :
package org.bytearray.outils { dynamic public class MonTableau extends Array { public function MonTableau ( ...rest ) {

} } }

La sous-classe MonTableau est une classe dynamique car laccs aux donnes par lcriture crochet requiert lutilisation dune classe dynamique.

5 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Etendre la classe Array ncessite une petite astuce qui na pas t corrige avec ActionScript 3. Au sein du constructeur de la sousclasse nous devons ajouter la ligne suivante :
package org.bytearray.outils { dynamic public class MonTableau extends Array { public function MonTableau ( ...rest ) { splice.apply(this, [0, 0].concat(rest)); } } }

Cette astuce permet dinitialiser correctement le tableau lorsque des paramtres sont passs au constructeur. A ce stade, nous bnficions dune sous-classe oprationnelle, qui possde toutes les capacits dun tableau standard :
// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var premierTableau:MonTableau = new MonTableau (58, 48, 10); // affiche : 48 trace( premierTableau[1] ); // ajout d'une valeur premierTableau.push ( 25 ); // affiche : 4 trace( premierTableau.length ); // affiche : 25 trace( premierTableau.pop() ) ; // affiche : 3 trace( premierTableau.length );

Pour linstant, lutilisation de la sous-classe MonTableau nest pas vraiment justifie, celle-ci ne possde aucune fonctionnalit en plus de la classe Array. Afin de copier un tableau, nous pourrions tre tents dcrire le code suivant :
// cration d'un premier tableau

6 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

var monTableau:Array = new Array (58, 48, 10); // cration d'une copie ? var maCopie:Array = monTableau;

Cette erreur fait rfrence la notion de variables composites et primitives que nous avons trait lors du chapitre 2 intitul Langage et API du lecteur Flash. Souvenez-vous, lors de la copie de donnes composites, nous copions les variables par rfrence et non par valeur. Afin de crer une vraie copie de tableau nous dfinissons une nouvelle mthode copie au sein de la classe MonTableau :
package org.bytearray.outils { dynamic public class MonTableau extends Array { public function MonTableau ( ...rest ) { splice.apply(this, [0, 0].concat(rest)); } public function copie ( ):MonTableau { var maCopie:MonTableau = new MonTableau(); var lng:int = length; for ( var i:int = 0; i< lng; i++ ) maCopie[i] = this[i]; return maCopie; } } }

En testant le code suivant, nous voyons quune relle copie du tableau est retourne par la mthode copie :
// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var tableau:MonTableau = new MonTableau (58, 48, 10); // cration d'une copie

7 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

var copieTableau:MonTableau = tableau.copie(); // modification d'une valeur copieTableau[0] = 100; // affiche : 58,48,10 100,48,10 trace ( tableau, copieTableau );

Nous aurions pu utiliser la mthode Array.slice mais celle-ci nous aurait renvoy un tableau de type Array et non pas une nouvelle instance de MonTableau. En modifiant une valeur du tableau retourn nous voyons que les deux tableaux sont bien distincts. Bien entendu, cette mthode ne fonctionne que pour la copie de tableaux contenant des donnes de types primitifs. Si nous souhaitons copier des tableaux contenant des donnes de types composites nous utiliserons la mthode writeObject de la classe flash.utils.ByteArray. Nous reviendrons sur cette fonctionnalit au cours du chapitre 20 intitul ByteArray. Nous allons maintenant ajouter une nouvelle mthode melange permettant de mlanger les donnes du tableau :
public function melange ( ):void { var i:int = length; var aleatoire:int; var actuel:*; while ( i-- ) { aleatoire = Math.floor ( Math.random()*length ); actuel = this[i]; this[i] = this[aleatoire]; this[aleatoire] = actuel; } }

Dans le code suivant nous mlangeons diffrentes valeurs, nous pourrions utiliser cette mthode dans un jeu. Nous pourrions imaginer une srie de questions stockes dans un tableau, chaque lancement le tableau est mlang afin que les joueurs naient pas les questions dans le mme ordre :
// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var tableau:MonTableau = new MonTableau(58, 48, 10); // mlange les valeurs

8 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

tableau.melange(); // affiche : 85,12,58 trace( tableau );

La mthode egal nous permet de tester si deux tableaux contiennent les mmes valeurs :
public function egal ( pTableau:Array ):Boolean { if ( this == pTableau ) return true; var i:int = this.length; if ( i != pTableau.length ) return false; while( i-- ) { if ( this[i] != pTableau[i] ) return false; } return true; }

Dans le code suivant nous comparons deux tableaux :


// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var premierTableau:MonTableau = new MonTableau(12, 58, 85); // cration d'un autre tableau de nombres var secondTableau:MonTableau = new MonTableau(12, 58, 85); // affiche : true trace( premierTableau.egal ( secondTableau ) );

Lorsque le tableau pass contient les mmes valeurs, la mthode egal renvoie true. A linverse si nous modifions les donnes du premier tableau, la mthode egal renvoie false :
// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var premierTableau:MonTableau = new MonTableau(100, 58, 85); // cration d'un autre tableau de nombres var secondTableau:MonTableau = new MonTableau(12, 58, 85); // affiche : false trace( premierTableau.egal ( secondTableau ) );

Dans cet exemple nous ne prenons pas en charge les tableaux plusieurs dimensions, diffrentes approches pourraient tre utilises, vous de jouer ! Il serait pratique davoir une mthode qui se chargerait de calculer la moyenne des valeurs contenues dans le tableau. Avant de dmarrer le

9 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

calcul nous devons nous assurer que le tableau ne contienne que des nombres. Pour cela nous utilisons la nouvelle mthode every de la classe Array :
public function moyenne ( ):Number { if ( ! every ( filtre ) ) throw new TypeError ("Le tableau actuel ne contient pas que des nombres"); var i:int = length; var somme:Number = 0; while ( i-- ) somme += this[i]; return somme/length; } private function filtre ( pElement:*, pIndex:int, pTableau:Array ):Boolean { return pElement is Number; }

Lorsque la mthode moyenne est excute, celle-ci vrifie dans un premier temps si la totalit des donnes du tableau sont bien des nombres. Pour cela la mthode filtre est excute sur chaque lment du tableau, et renvoie true tant que llment parcouru est un nombre. Si ce nest pas le cas, celle-ci renvoie false et nous levons une erreur de type TypeError. Si aucune erreur nest leve, nous pouvons entamer le calcul de la moyenne. Dans le code suivant, le calcul est possible :
// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var premierTableau:MonTableau = new MonTableau(100, 58, 85); // affiche : 81 trace( premierTableau.moyenne() );

A linverse, si une valeur du tableau nest pas un nombre :


// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var premierTableau:MonTableau = new MonTableau(this, 58, false); trace( premierTableau.moyenne() );

10 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Une erreur lexcution est leve :


TypeError: Le tableau actuel ne contient pas que des nombres

Afin de grer lerreur, nous pouvons placer lappel de la mthode moyenne au sein dun bloc try catch :
// import de la classe MonTableau import org.bytearray.outils.MonTableau; // cration d'un tableau de nombres var premierTableau:MonTableau = new MonTableau(this, 58, false); try { trace( premierTableau.moyenne() ); } catch ( pError:Error ) { trace("une erreur de calcul est survenue !"); }

Nous pourrions ajouter autant de mthodes que nous souhaitons au sein de la classe MonTableau, libre vous dajouter les fonctionnalits dont vous avez besoin. Cela nous permet de substituer la classe MonTableau la classe Array afin de toujours avoir disposition ces fonctionnalits.

A retenir
Toutes les classes natives ne sont pas sous-classables. La composition peut tre utilise afin daugmenter les capacits dune classe qui nest pas sous-classable. Etendre une classe native est un moyen lgant dtendre les capacits dActionScript 3 ou du lecteur.

Etendre les classes graphiques


Lextension de classes natives prend tout son sens dans le cas de sousclasses graphiques. Nous avons dvelopp une application de dessin au cours du chapitre 7 intitul Intractivit, nous allons prsent lenrichir en ajoutant un objet graphique tel un stylo. Le graphisme a t ralis sous 3D Studio Max puis ensuite export en image pour tre utilis dans Flash. Dans un nouveau document Flash CS3 nous importons le graphique puis nous le transformons en symbole clip.

11 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-1. Graphique du stylo converti en symbole clip. Attention bien modifier le point denregistrement, de manire ce que la mine du stylo soit en coordonne 0,0. Ce clip va nous servir de graphisme afin de reprsenter le stylo de lapplication. Nous devons pour cela le rendre disponible par programmation. Au sein du panneau Proprits de liaison nous cochons la case Exporter pour ActionScript et renseignons Stylo comme nom de classe, nous laissons flash.display.MovieClip comme classe de base. La classe Stylo est donc une sous-classe de MovieClip :

Figure 9-2. Panneau proprits de liaison.

12 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Lorsque nous cliquons sur le bouton OK, Flash tente de trouver une classe du mme nom qui aurait pu tre dfinie. Si il nen trouve aucune, Flash affiche le message illustr en figure 9-3 :

Figure 9-3. Gnration automatique de classe. Comme nous lavons vu au cours du chapitre 5 intitul Les symboles, Flash gnre automatiquement une classe interne utilise pour instancier le symbole. Dans notre cas, une classe Stylo est gnre par Flash, afin de pouvoir instancier notre stylo puis lafficher de la manire suivante :
// instanciation du stylo var monStylo:Stylo = new Stylo(); // ajout la liste d'affichage addChild ( monStylo );

Flash gnre automatiquement une classe Stylo qui hrite de la classe MovieClip, rappelez-vous que cette classe est inaccessible. Si nous avions accs celle-ci nous pourrions lire le code suivant :
package { import flash.display.MovieClip; public class Stylo extends MovieClip { public function Stylo () {

} }

13 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

La classe Stylo possde donc toutes les capacits dun MovieClip :


// instanciation du stylo var monStylo:Stylo = new Stylo(); // ajout la liste d'affichage addChild ( monStylo ); // la classe stylo possde toutes les capacits d'un clip monStylo.stop(); monStylo.play(); monStylo.gotoAndStop (2); monStylo.prevFrame ();

Cette gnration automatique de classe savre trs pratique lorsque nous ne souhaitons pas dfinir de classe manuellement pour chaque objet, Flash sen charge et cela nous permet de gagner un temps prcieux. En revanche Flash nous laisse aussi la possibilit de dfinir manuellement les sous classe graphiques, et dy ajouter tout ce que nous souhaitons. A cot de notre document Flash CS3 nous crons une classe Stylo hritant de MovieClip :
package { import flash.display.MovieClip; public class Stylo extends MovieClip { public function Stylo () { trace( this ); } } }

Nous sauvons la classe sous le nom Stylo.as, attention, le nom de la classe doit tre le mme que le fichier .as. Lorganisation de nos fichiers de travail doit tre la suivante :

14 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-4. Organisation des fichiers. Flash va dsormais utiliser notre dfinition de classe et non celle gnre automatiquement. Pour sen assurer, nous utilisons le panneau Proprits de liaison. A droite du champ Classe sont situes deux icnes. En cliquant sur la premire, nous pouvons valider la dfinition de la classe afin de vrifier que Flash utilise bien notre dfinition de classe. La figure 9-5 illustre les deux icnes :

Figure 9-5. Organisation des fichiers. Une fois la dfinition de classe valide, un message nous indiquant que la classe a bien t dtecte saffiche. Si nous cliquons sur licne ddition reprsentant un stylo situe droite de licne de validation, la classe souvre au sein de Flash afin dtre dite :

15 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-6. Edition de la classe au sein de Flash CS3. Le symbole est correctement li notre classe, nous pouvons ds prsent linstancier :
// affiche : [object Stylo] var monStylo:Stylo = new Stylo(); // ajout la liste d'affichage addChild ( monStylo ); // positionnement en x et y monStylo.x = 250; monStylo.y = 200;

Lorsque le stylo est cr le constructeur est dclench, linstruction trace que nous avions plac affiche : [object Stylo]. Notre symbole est affich comme lillustre la figure 9-7 :

16 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-7. Symbole Stylo affich. Nous navons dfini pour le moment aucune nouvelle fonctionnalit au sein de la classe Stylo, celle-ci tend simplement la classe flash.display.MovieClip. Toutes les fonctionnalits que nous ajouterons au sein de la classe Stylo seront automatiquement disponibles au sein du symbole, celui-ci est dsormais li la classe. En ajoutant une mthode test au sein de la classe Stylo :
package { import flash.display.MovieClip; public class Stylo extends MovieClip { public function Stylo () { trace( this ); } public function test ( ):void

17 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

{ trace("ma position dans l'axe des x est de : " + x ); trace("ma position dans l'axe des y est de : " + y ); } } }

Celle-ci est automatiquement disponible auprs de linstance :


// affiche : [object Stylo] var monStylo:Stylo = new Stylo(); // ajout la liste d'affichage addChild ( monStylo ); // positionnement en x et y monStylo.x = 250; monStylo.y = 200; /* affiche : ma position dans l'axe des x est de : 250 ma position dans l'axe des y est de : 200 */ monStylo.test();

La premire chose que notre stylo doit savoir faire est de suivre la souris. Nous nallons pas utiliser linstruction startDrag car nous verrons que nous devons travailler sur le mouvement du stylo, linstruction startDrag ne nous le permettrait pas. Pour cela nous allons utiliser lvnement MouseEvent.MOUSE_MOVE. Notre classe est une sous-classe de MovieClip et possde donc tous les vnements interactifs ncessaires dont nous avons besoin, au sein du constructeur nous coutons lvnement MouseEvent.MOUSE_MOVE :
package { import flash.display.MovieClip; import flash.events.MouseEvent; public class Stylo extends MovieClip { public function Stylo () { trace( this ); addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris );

18 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

} private function bougeSouris ( pEvt:MouseEvent ):void { trace( pEvt ); } } }

Dans le code prcdent la mthode bougeSouris est lcoute de lvnement MouseEvent.MOUSE_MOVE. Pourquoi prfrons-nous lvenement MouseEvent.MOUSE_MOVE lvenement Event.ENTER_FRAME ? Ce dernier se dclenche en continu indpendamment du mouvement de la souris. Ainsi, mme si le stylo est immobile lvnement Event.ENTER_FRAME est diffus. A terme, cela pourrait ralentir les performances de notre application. Souvenez-vous que lvnement MouseEvent.MOUSE_MOVE nest plus global comme en ActionScript 1 et 2, et nest diffus que lors du survol du stylo. Si nous avons besoin dcouter le mouvement de la souris sur toute la scne nous devons accder lobjet Stage.

A retenir
Il est possible de lier un symbole existant une sous-classe graphique dfinie manuellement. Le symbole hrite de toutes les fonctionnalits de la sous-classe.

Accder lobjet Stage de manire scurise


Comme nous lavons vu lors du chapitre 7 intitul Interactivit, seul lobjet Stage permet une coute globale de la souris ou du clavier. Nous devons donc modifier notre code afin dcouter lvnement MouseEvent.MOUSE_MOVE auprs de lobjet Stage :
package { import flash.display.MovieClip; import flash.events.MouseEvent; public class Stylo extends MovieClip {

19 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

public function Stylo () { trace( this ); stage.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } private function bougeSouris ( pEvt:MouseEvent ):void { trace( pEvt ); } } }

En testant le code prcdent, nous obtenons lerreur suivante lexcution :


TypeError: Error #1009: Il est impossible d'accder la proprit ou la mthode d'une rfrence d'objet nul.

Souvenez-vous, lors du chapitre 4 intitul La liste daffichage nous avons vu que la proprit stage propre tout objet de type flash.display.DisplayObject renvoyait null tant que lobjet graphique ntait pas ajout la liste daffichage. Ainsi, lorsque nous crons le symbole :
var monStylo:Stylo = new Stylo();

Le constructeur est dclench et nous tentons alors daccder lobjet Stage. A ce moment l, notre symbole nest pas encore prsent au sein de la liste daffichage, la proprit stage renvoie donc null et lappel la mthode addEventListener choue. Comment allons-nous procder ? Lors du chapitre 4 intitul La liste daffichage nous avons dcouvert deux vnements importants lis lactivation et la dsactivation des objets graphiques. Voici un rappel de ces deux vnements fondamentaux :
Event.ADDED_TO_STAGE : cet vnement est diffus lorsque lobjet graphique est plac au sein dun DisplayObjectContainer prsent au sein de la liste daffichage. Event.REMOVED_FROM_STAGE : cet vnement est diffus lorsque lobjet graphique est supprim de la liste daffichage.

20 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Nous devons attendre que le symbole soit ajout la liste daffichage pour pouvoir accder lobjet Stage. Le symbole va se souscrire luimme auprs de lvnement Event.ADDED_TO_STAGE quil diffusera lorsquil sera ajout la liste daffichage. Cela peut paratre trange, mais dans ce cas lobjet scoute lui-mme. Nous modifions la classe Stylo afin dintgrer ce mcanisme :
package { import flash.display.MovieClip; import flash.events.Event; public class Stylo extends MovieClip { public function Stylo () { trace( this ); // le stylo coute l'vnement Event.ADDED_TO_STAGE, diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); } private function activation ( pEvt:Event ):void { trace( pEvt ); } }

Lors dinstanciation du symbole Stylo, le constructeur est dclench :


// affiche : [object Stylo] var monStylo:Stylo = new Stylo();

Lorsque linstance est ajoute la liste daffichage, lvnement Event.ADDED_TO_STAGE est diffus :
// affiche : [object Stylo] var monStylo:Stylo = new Stylo(); // ajout la liste d'affichage // affiche : [Event type="addedToStage" bubbles=false cancelable=false eventPhase=2] addChild ( monStylo );

21 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Nous dfinissons la mthode couteur activation comme prive car nous ny accderons pas depuis lextrieur. Gardez bien en tte de rendre prives toutes les mthodes et proprits qui ne seront pas utilises depuis lextrieur de la classe. Lvnement Event.ADDED_TO_STAGE est diffus, nous remarquons au passage que nous coutons la phase cible et quil sagit dun vnement qui ne participe pas la phase de remonte. Lorsque la mthode activation est dclenche nous pouvons cibler en toute scurit la proprit stage et ainsi couter lvnement MouseEvent.MOUSE_MOVE :
package { import flash.display.MovieClip; import flash.events.MouseEvent; import flash.events.Event; public class Stylo extends MovieClip { public function Stylo () { // le stylo coute l'vnement Event.ADDED_TO_STAGE // diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); } private function activation ( pEvt:Event ):void { // coute de l'vnement MouseEvent.MOUSE_MOVE stage.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } private function bougeSouris ( pEvt:MouseEvent ):void { trace( pEvt ); } }

Si nous testons le code prcdent, nous remarquons que la mthode bougeSouris est bien dclenche lorsque la souris est dplace sur la totalit de la scne. 22 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Ajouter des fonctionnalits


Nous allons prsent nous intresser au mouvement du stylo en rcuprant les coordonnes de la souris afin de le positionner. Nous dfinissons deux proprits positionX et positionY :
// position en cours de la souris private var positionX:Number; private var positionY:Number;

Celles-ci permettent de stocker la position de la souris :


private function bougeSouris ( pEvt:MouseEvent ):void { // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // affectation de la position x = positionX; y = positionY;

Le stylo suit dsormais la souris comme lillustre la figure 9-8 :

Figure 9-8. Stylo attach au curseur. En testant lapplication nous remarquons que le mouvement nest pas totalement fluide, nous allons utiliser la mthode

23 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

intitul Interactivit.

updateAfterEvent que nous avons tudi au cours du chapitre 7

Souvenez-vous, cette mthode de la classe MouseEvent nous permet de forcer le rafrachissement du lecteur :
private function bougeSouris ( pEvt:MouseEvent ):void { // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // affectation de la position x = positionX; y = positionY; // force le rafrachissement pEvt.updateAfterEvent(); }

Le mouvement est maintenant fluide mais le curseur de la souris demeure affich, nous le masquons laide de la mthode statique hide de la classe Mouse lors de lactivation de lobjet graphique. Veillez ne pas placer cet appel en continu au sein de la mthode bougeSouris cela serait redondant. Nous importons la classe Mouse :
import flash.ui.Mouse;

Puis nous modifions la mthode activation :


private function activation ( pEvt:Event ):void { // cache le curseur Mouse.hide(); // coute de l'vnement MouseEvent.MOUSE_MOVE stage.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); }

A ce stade, nous avons tendu les capacits de la classe MovieClip, les symboles lis la classe Stylo peuvent dsormais suivrent la souris. Nous pouvons tout moment lier notre classe Stylo nimporte quel autre symbole celui-ci bnficiera aussitt de toutes les fonctionnalits dfinies par celle-ci. Nous avons pourtant oubli un lment essentiel, voyez-vous de quoi il sagit ? 24 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Nous devons grer la dsactivation de lobjet graphique afin de librer les ressources. Si nous supprimons le stylo de la liste daffichage, lvnement MouseEvent.MOUSE_MOVE est toujours diffus, le curseur souris demeure masqu :
var monStylo:Stylo = new Stylo(); // ajout la liste d'affichage // affiche : [Event type="addedToStage" bubbles=false cancelable=false eventPhase=2] addChild ( monStylo ); // suppression du stylo, celui ci n'intgre aucune logique de dsactivation removeChild ( monStylo );

Pour

cela Event.REMOVED_FROM_STAGE :
public function Stylo () {

grer

proprement

nous

coutons

lvnement

// le stylo coute l'vnement Event.ADDED_TO_STAGE // diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); // le stylo coute l'vnement Event.REMOVED_FROM_STAGE // diffus lorsque celui-ci est supprim de la liste d'affichage addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); }

Puis nous ajoutons une mthode couteur desactivation afin dintgrer la logique de dsactivation ncessaire :
private function desactivation ( pEvt:Event ):void { // affiche le curseur Mouse.show(); // arrte l'coute de l'vnement MouseEvent.MOUSE_MOVE stage.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); }

Il serait intressant dajouter un effet de rotation au stylo lorsque celui-ci arrive en haut de la scne afin de simuler une rotation du poignet. La figure 9-9 illustre lide :

25 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-9. Rotation du stylo. En intgrant une simple condition nous allons pouvoir donner plus de ralisme au mouvement du stylo. Nous allons dans un premier temps dfinir la position dans laxe des y partir de laquelle le stylo commence sincliner, pour cela nous dfinissons une proprit constante car celle-ci ne changera pas lexcution :
// limite pour l'axe des y private static const LIMIT_Y:int = 140;

Puis nous intgrons bougeSouris :


{

la condition au sein de la mthode

private function bougeSouris ( pEvt:MouseEvent ):void

// rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // affectation de la position x = positionX; y = positionY; // si le stylo passe dans la zone suprieure alors nous inclinons le stylo if ( positionY < Stylo.LIMIT_Y ) { rotation = ( Stylo.LIMIT_Y - positionY );

26 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

} // force le rafrachissement pEvt.updateAfterEvent(); }

A ce stade, voici le code complet de notre classe Stylo :


package { import import import import flash.display.MovieClip; flash.events.MouseEvent; flash.events.Event; flash.ui.Mouse;

public class Stylo extends MovieClip { // limite pour l'axe des y private static const LIMIT_Y:int = 140; // position en cours de la souris private var positionX:Number; private var positionY:Number; public function Stylo () { // le stylo coute l'vnement Event.ADDED_TO_STAGE // diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); // le stylo coute l'vnement Event.REMOVED_FROM_STAGE // diffus lorsque celui-ci est supprim de la liste d'affichage addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { // cache le curseur Mouse.hide(); // coute de l'vnement MouseEvent.MOUSE_MOVE stage.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } private function desactivation ( pEvt:Event ):void { // affiche le curseur Mouse.show();

27 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// arrte l'coute de l'vnement MouseEvent.MOUSE_MOVE stage.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } private function bougeSouris ( pEvt:MouseEvent ):void { // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // affectation de la position x = positionX; y = positionY; le stylo // si le stylo passe dans la zone suprieure alors nous inclinons if ( positionY < Stylo.LIMIT_Y ) { rotation = ( Stylo.LIMIT_Y - positionY ); } // force le rafrachissement pEvt.updateAfterEvent(); } } }

Si nous testons lapplication, lorsque nous arrivons en zone suprieure le stylo sincline comme lillustre la figure 9-10 :

28 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-10. Inclinaison du stylo. Afin de rendre plus naturel le mouvement du stylo, nous ajoutons une formule dinertie :
private function bougeSouris ( pEvt:MouseEvent ):void { // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // affectation de la position x = positionX; y = positionY; // si le stylo passe dans la zone suprieure alors nous inclinons le stylo if ( positionY < Stylo.LIMIT_Y ) { rotation -= ( rotation - ( Stylo.LIMIT_Y - positionY ) ) *.2; // sinon, le stylo reprend son inclinaison d'origine } else rotation -= ( rotation - 0 ) *.2; // force le rafrachissement pEvt.updateAfterEvent(); }

29 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Il est temps dajouter prsent la notion de dessin, cette partie a dj t aborde lors du chapitre 7, nous allons donc rutiliser ce code dans notre application. Si nous pensons en termes de sparation des tches, il parat logique quun stylo se charge de son mouvement et du dessin, et non pas la cration de la toile o dessiner. Nous allons donc dfinir une mthode affecteToile qui aura pour mission dindiquer au stylo dans quel conteneur dessiner. Nous dfinissons dans la classe une proprit permettant de rfrencer le conteneur :
// stocke une rfrence au conteneur de dessin private var conteneur:DisplayObjectContainer;

Puis nous importons la classe

flash.display.DisplayObjectContainer :
import flash.display.DisplayObjectContainer;

Et ajoutons la mthode affecteToile :


// mthode permettant de spcifier le conteneur du dessin public function affecteToile ( pToile:DisplayObjectContainer ):void { conteneur = pToile; }

Tout type de conteneur peut tre pass, condition que celui-ci soit de type DisplayObjectContainer :
// cration du conteneur de tracs vectoriels var toile:Sprite = new Sprite(); // ajout du conteneur la liste d'affichage addChild ( toile ); // cration du symbole var monStylo:Stylo = new Stylo(); // affectation du conteneur de tracs monStylo.affecteToile ( toile ); // ajout du symbole la liste d'affichage addChild ( monStylo ); // positionnement en x et y monStylo.x = 250; monStylo.y = 200;

Ainsi, diffrents types dobjets graphiques peuvent servir de toile. Souvenez-vous grce lhritage un sous type peut tre pass partout o un super-type est attendu. 30 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

En plus de rutiliser les fonctionnalits de dessin que nous avions dvelopp durant le chapitre 7 intitul Interactivit nous allons aussi rutiliser la notion dhistorique combins aux raccourcis clavier : CTRL+Z et CTRL+Y. Nous dfinissons trois nouvelles proprits permettant de stocker les formes cres et supprimes, puis le trac en cours :
// tableaux rfrenant les formes traces et supprimes private var tableauTraces:Array = new Array(); private var tableauAncienTraces:Array = new Array(); // rfrence le trac en cours private var monNouveauTrace:Shape;

Attention, la classe flash.display.Shape doit tre importe :


import flash.display.Shape;

Au sein de la mthode activation, nous devons tout dabord couter le clic souris, afin de savoir quand est-ce que lutilisateur souhaite commencer dessiner :
private function activation ( pEvt:Event ):void { // cache le curseur Mouse.hide(); // coute des diffrents vnements stage.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); stage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); }

Nous ajoutons la mthode couteur clicSouris, qui se charge de crer les objets et dinitialiser le style du trac :
private function clicSouris ( pEvt:MouseEvent ):void { if ( conteneur == null ) throw new Error ( "Veuillez appeler au pralable la mthode affecteToile()" ); // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // un nouvel objet Shape est cr pour chaque trac monNouveauTrace = new Shape(); // nous ajoutons le conteneur de trac au conteneur principal conteneur.addChild ( monNouveauTrace ); // puis nous rfrenons le trac au sein du tableau // rfrenant les tracs affichs tableauTraces.push ( monNouveauTrace );

31 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// nous dfinissons un style de trac monNouveauTrace.graphics.lineStyle ( 1, 0x990000, 1 ); // la mine est dplace cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.moveTo ( positionX, positionY ); // si un nouveau trac intervient alors que nous sommes // repartis en arrire nous repartons de cet tat if ( tableauAncienTraces.length ) tableauAncienTraces = new Array; // coute du mouvement de la souris stage.addEventListener ( MouseEvent.MOUSE_MOVE, dessine ); }

Puis nous dfinissons la mthode dessine afin de grer le trac :


private function dessine ( pEvt:MouseEvent ):void { if ( monNouveauTrace != null ) { // la mine est dplae cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.lineTo ( positionX, positionY ); } }

Afin darrter de dessiner, nous coutons le relchement de la souris grce lvnement MouseEvent.MOUSE_UP :
private function activation ( pEvt:Event ):void { // cache le curseur Mouse.hide(); // coute des diffrents stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( } vnements MouseEvent.MOUSE_MOVE, bougeSouris ); MouseEvent.MOUSE_DOWN, clicSouris ); MouseEvent.MOUSE_UP, relacheSouris );

La mthode couteur relacheSouris est dclenche lors du relchement de la souris et interrompt lcoute de lvnement MouseEvent.MOUSE_MOVE :
private function relacheSouris ( pEvt:MouseEvent ):void { stage.removeEventListener ( MouseEvent.MOUSE_MOVE, dessine );

32 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Si nous testons notre application, tout fonctionne correctement, nous pouvons prsent dessiner, lorsque la souris est relche, le trac est stopp.

Figure 9-11. Application de dessin. Notre application est bientt termine, nous devons ajouter la notion dhistorique pour cela nous importons la classe KeyboardEvent :
import flash.events.KeyboardEvent;

Puis nous ajoutons lcoute du clavier au sein de la mthode activation :


private function activation ( pEvt:Event ):void { // cache le curseur Mouse.hide(); // coute de l'vnement stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( } MouseEvent.MOUSE_MOVE MouseEvent.MOUSE_MOVE, bougeSouris ); MouseEvent.MOUSE_DOWN, clicSouris ); MouseEvent.MOUSE_UP, relacheSouris ); KeyboardEvent.KEY_DOWN, ecouteClavier );

33 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

La mthode couteur ecouteClavier se charge de grer lhistorique, nous importons la classe Keyboard :
import flash.ui.Keyboard;

Et dfinissons deux proprits constantes stockant le code de chaque touche :


// code des touches Y et Z private static const codeToucheY:int = 89; private static const codeToucheZ:int = 90;

Puis nous ajoutons le code dj dvelopp durant le chapitre 7 pour la prcdente application de dessin :
private function ecouteClavier ( pEvt:KeyboardEvent ):void { // si la barre espace est enfonce if ( pEvt.keyCode == Keyboard.SPACE ) { // nombre d'objets Shape contenant des tracs var lng:int = tableauTraces.length; // suppression des tracs de la liste d'affichage while ( lng-- ) conteneur.removeChild ( tableauTraces[lng] ); // les tableaux d'historiques sont reinitialiss // les rfrences supprimes tableauTraces = new Array(); tableauAncienTraces = new Array(); monNouveauTrace = null; } if ( pEvt.ctrlKey ) { // si retour en arrire (CTRL+Z) if( pEvt.keyCode == Stylo.codeToucheZ && tableauTraces.length ) { // nous supprimons le dernier trac var aSupprimer:Shape = tableauTraces.pop(); // nous stockons chaque trac supprim // dans le tableau spcifique tableauAncienTraces.push ( aSupprimer ); // nous supprimons le trac de la liste d'affichage conteneur.removeChild( aSupprimer ); // si retour en avant (CTRL+Y) } else if ( pEvt.keyCode == Stylo.codeToucheY && tableauAncienTraces.length )

34 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

{ // nous rcuprons le dernier trac ajout var aAfficher:Shape = tableauAncienTraces.pop(); // nous le replaons dans le tableau de // tracs l'affichage tableauTraces.push ( aAfficher ); // puis nous l'affichons conteneur.addChild ( aAfficher ); } } }

Nous obtenons un symbole stylo intelligent dot de diffrentes fonctionnalits que nous avons ajoutes. Un des principaux avantages des sous-classes graphiques rside dans leur facilit de rutilisation. Nous pouvons tout moment lier un autre symbole notre sous-classe graphique, ce dernier hritera automatiquement des fonctionnalits de la classe Stylo et deviendra oprationnel. Avant cela, nous allons ajouter une autre fonctionnalit lie la molette souris. Il serait lgant de pouvoir choisir la couleur du trac travers lutilisation de la molette souris. Lorsque lutilisateur sen servira, la couleur du trac et du stylo sera modifie. Noublions pas quau sein de la sous-classe nous sommes sur le scnario du symbole. Nous modifions le symbole stylo en ajoutant plusieurs images cls :

35 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-12. Ajout des diffrentes couleurs. Lorsque lutilisateur fait usage de la molette, nous dplaons la tte de lecture du scnario du symbole. Nous ajoutons deux nouvelles proprits qui nous permettront de positionner la tte de lecture lorsque la souris sera glisse :
// position de la tte de lecture private var image:int; private var index:int;

Nous initialisons au sein du constructeur la proprit index qui sera plus tard incrmente, nous linitialisons donc 0 :
public function Stylo () { // le stylo coute l'vnement Event.ADDED_TO_STAGE // diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); // le stylo coute l'vnement Event.REMOVED_FROM_STAGE // diffus lorsque celui-ci est supprim de la liste d'affichage addEventListener ( Event.REMOVED_FROM_STAGE, desactivation );

36 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// initialisation des proprits utilises pour // le changement de couleurs image = 0; index = 1; }

Au sein de la mthode activation nous ajoutons un couteur de lvnement MouseEvent.MOUSE_WHEEL auprs de lobjet Stage :
private function activation ( pEvt:Event ):void { // cache le curseur Mouse.hide(); // coute des diffrents stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( } vnements MouseEvent.MOUSE_MOVE, bougeSouris ); MouseEvent.MOUSE_DOWN, clicSouris ); MouseEvent.MOUSE_UP, lacheSouris ); KeyboardEvent.KEY_DOWN, ecouteClavier ); MouseEvent.MOUSE_WHEEL, moletteSouris );

La mthode couteur moletteSouris se charge de dplacer la tte de lecture sous forme de boucle, laide de loprateur modulo %, la proprit index stocke lindex de la couleur slectionne :
// dplace la tte de lecture lors de lutilisation de la molette private function moletteSouris ( pEvt:MouseEvent ):void { gotoAndStop ( index = (++image%totalFrames)+1 ); }

Si nous testons lapplication, lorsque la molette de la souris est utilise, le stylo change de couleur. Grce au modulo, les couleurs dfilent en boucle. Il reste cependant changer la couleur du trac correspondant la couleur du stylo choisie par lutilisateur. Pour cela nous allons stocker toutes les couleurs disponibles dans un tableau au sein dune proprit statique. Pourquoi utiliser ici une proprit statique ? Car les couleurs sont globales toutes les occurrences du stylo qui pourraient tre cres, ayant un sens global nous crons donc un tableau au sein dune proprit couleurs statique :
// tableau contenant les couleurs disponibles private static var couleurs:Array = [ 0x5BBA48, 0xEA312F, 0x00B7F1, 0xFFF035, 0xD86EA3, 0xFBAE34 ];

37 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Il ne nous reste plus qu modifier au sein de la mthode clicSouris la couleur du trac, pour cela nous utilisons la proprit index qui reprsente lindex de la couleur au sein du tableau :
private function clicSouris ( pEvt:MouseEvent ):void { if ( conteneur == null ) throw new Error ( "Veuillez appeler au pralable la mthode affecteToile()" ); // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // un nouvel objet Shape est cr pour chaque trac monNouveauTrace = new Shape(); // nous ajoutons le conteneur de trac au conteneur principal conteneur.addChild ( monNouveauTrace ); // puis nous rfrenons le trac au sein du tableau // rfrenant les tracs affichs tableauTraces.push ( monNouveauTrace ); // nous dfinissons un style de trac monNouveauTrace.graphics.lineStyle ( 2, Stylo.couleurs[index-1], 1 ); // la mine est dplace cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.moveTo ( positionX, positionY ); // si un nouveau trac intervient alors que nous sommes // repartis en arrire nous repartons de cet tat if ( tableauAncienTraces.length ) tableauAncienTraces = new Array; // coute du mouvement de la souris stage.addEventListener ( MouseEvent.MOUSE_MOVE, dessine ); }

Lorsque la souris est enfonce, nous pointons grce la proprit index au sein du tableau couleurs afin de choisir la couleur correspondante. Nous ajoutons un petit dtail final rendant notre classe plus souple, il serait intressant de pouvoir en instanciant le stylo lui passer une vitesse permettant dinfluencer sa vitesse de rotation. Nous ajoutons une proprit friction de type Number :
// stocke la friction du stylo private var friction:Number;

Puis nous modifions le constructeur afin daccueillir la vitesse :


public function Stylo ( pFriction:Number=.1 ) {

38 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// le stylo coute l'vnement Event.ADDED_TO_STAGE // diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); // le stylo coute l'vnement Event.REMOVED_FROM_STAGE // diffus lorsque celui-ci est supprim de la liste d'affichage addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); // initialisation des proprits utilises pour // le changement de couleurs image = 0; index = 1; // affecte la friction friction = pFriction; }

Enfin, nous modifions la mthode bougeSouris afin dutiliser la valeur passe, stocke au sein de la proprit friction :
private function bougeSouris ( pEvt:MouseEvent ):void { // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // affectation de la position x = positionX; y = positionY; // si le stylo passe dans la zone suprieure // alors nous inclinons le stylo if ( positionY < Stylo.LIMIT_Y ) { friction; rotation -= ( rotation - ( Stylo.LIMIT_Y - positionY ) ) *

// sinon, le stylo reprend son inclinaison d'origine } else rotation -= ( rotation - 0 ) * friction; // force le rafrachissement pEvt.updateAfterEvent(); }

Souvenons-nous dun concept important de la programmation oriente objet : lencapsulation ! Dans notre classe Stylo les proprits sont toutes prives, car il ny aucune raison que celles-ci soient modifiables depuis lextrieur. Bien entendu, il existe des situations dans lesquelles lutilisation de proprits prives na pas de sens.

39 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Dans le cas dune classe gomtrique Point, il convient que les proprits x, y et z soient publiques et accessibles. Il convient de cacher les proprits qui ne sont pas utiles lutilisateur de la classe ou bien celles qui ont de fortes chances dvoluer dans le temps. Souvenez-vous, lintrt est de pouvoir changer limplmentation sans altrer linterface de programmation. En utilisant des proprits prives au sein de notre classe Stylo, nous garantissons quaucun code extrieur la classe ne peut accder et ainsi altrer le fonctionnement de lapplication. La friction qui est passe linitialisation dun stylo doit obligatoirement tre comprise entre 0 exclu et 1. Si ce nest pas le cas, linclinaison du stylo choue et le dveloppeur assiste au disfonctionnement de lapplication. Dans le code suivant le dveloppeur ne connat pas les valeurs possibles, et passe 10 comme vitesse dinclinaison :
// cration du symbole var monStylo:Stylo = new Stylo( 10 );

En testant lapplication, le dveloppeur se rend compte que le mcanisme dinclinaison du stylo ne fonctionne plus. Cela est d au fait que la valeur de friction exprime une force de frottement et doit tre comprise entre 0 et 1. Noublions pas que nous sommes en train de dvelopper un objet intelligent, autonome qui doit pouvoir voluer dans diffrentes situations et pouvoir indiquer au dveloppeur ce qui ne va pas. Cela ne vous rappelle rien ? Souvenez-vous dun point essentiel que nous avons trait lors du prcdent chapitre, le contrle daffectation. Nous allons ajouter un test au sein du constructeur afin de vrifier si la valeur passe est acceptable, si ce nest pas le cas nous passerons une valeur par dfaut qui assurera le bon fonctionnement du stylo, linverse si la valeur passe est incorrecte nous afficherons un message derreur. Nous rajoutons la condition au sein du constructeur :
public function Stylo ( pFriction:Number=.1 ) { // le stylo coute l'vnement Event.ADDED_TO_STAGE

40 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); // le stylo coute l'vnement Event.REMOVED_FROM_STAGE // diffus lorsque celui-ci est supprim de la liste d'affichage addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); // initialisation des proprits utilises pour // le changement de couleurs image = 0; index = 1; // affecte la friction if ( pFriction > 0 && pFriction <= 1 ) friction = pFriction; else trace("Erreur : Friction non correcte, la valeur doit tre comprise entre 0 et 1"); friction = .1; } } {

Grce ce test, nous contrlons laffectation afin dtre sur de la bonne excution de lapplication. Cette fois le dveloppeur a pass une valeur suprieure 1, laffectation est contrle, un message dinformation indique ce qui ne va pas :
/* affiche : Erreur : Friction non correcte la valeur doit tre comprise entre 0 et 1 */ var monStylo:Stylo = new Stylo( 50 );

De cette manire, le dveloppeur tiers possde toutes les informations pour sassurer du bon fonctionnement du stylo. Elgant nest ce pas ? Il faut cependant prvoir la possibilit de changer la vitesse du mouvement une fois lobjet cr, pour cela nous allons dfinir une mthode appele affecteVitesse qui soccupera daffecter la proprit friction. Afin de bien encapsuler notre classe nous allons contrler laffectation des proprits grce une mthode spcifique :
public function affecteVitesse ( pFriction:Number ):void { // affecte la friction if ( pFriction > 0 && pFriction <= 1 ) friction = pFriction; else trace("Erreur : Friction non correcte, la valeur doit tre comprise entre 0 et 1"); {

41 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

friction = .1; } }

Nous intgrons le code dfini prcdemment au sein du constructeur, afin de contrler laffectation la proprit friction. Nous pouvons donc remplacer le code du constructeur par un appel la mthode affecteVitesse :
public function Stylo ( pFriction:Number=.1 ) { // le stylo coute l'vnement Event.ADDED_TO_STAGE // diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); // le stylo coute l'vnement Event.REMOVED_FROM_STAGE // diffus lorsque celui-ci est supprim de la liste d'affichage addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); // initialisation des proprits utilises pour // le changement de couleurs image = 0; index = 1; affecteVitesse ( pFriction ); }

En ajoutant une mthode affecteVitesse, laffectation de la proprit friction est contrle. Voici le code final de la classe Stylo :
package { import import import import import import import import flash.display.MovieClip; flash.display.Shape; flash.display.DisplayObjectContainer; flash.events.MouseEvent; flash.events.KeyboardEvent; flash.events.Event; flash.ui.Mouse; flash.ui.Keyboard;

public class Stylo extends MovieClip { // limite pour l'axe des y private static const LIMIT_Y:int = 140; // position en cours de la souris private var positionX:Number; private var positionY:Number;

42 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// stocke une rfrence au conteneur de dessin private var conteneur:DisplayObjectContainer; // tableaux rfrenant les formes traces et supprimes private var tableauTraces:Array = new Array(); private var tableauAncienTraces:Array = new Array(); // rfrence le trac en cours private var monNouveauTrace:Shape; // code des touches Y et Z private static const codeToucheY:int = 89; private static const codeToucheZ:int = 90; // position de la tte de lecture private var image:int; private var index:int; // tableau contenant les couleurs disponibles private static var couleurs:Array = [ 0x5BBA48, 0xEA312F, 0x00B7F1, 0xFFF035, 0xD86EA3, 0xFBAE34 ]; // stocke la friction du stylo private var friction:Number; public function Stylo ( pFriction:Number=.1 ) { // le stylo coute l'vnement Event.ADDED_TO_STAGE // diffus lorsque celui-ci est ajout la liste d'affichage addEventListener ( Event.ADDED_TO_STAGE, activation ); // le stylo coute l'vnement Event.REMOVED_FROM_STAGE // diffus lorsque celui-ci est supprim de la liste d'affichage addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); // initialisation des proprits utilises pour // le changement de couleurs image = 0; index = 1; // affectation contrle de la vitesse affecteVitesse ( pFriction ); } private function activation ( pEvt:Event ):void { // cache le curseur Mouse.hide(); // coute des diffrents stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( stage.addEventListener ( vnements MouseEvent.MOUSE_MOVE, bougeSouris ); MouseEvent.MOUSE_DOWN, clicSouris ); MouseEvent.MOUSE_UP, relacheSouris ); KeyboardEvent.KEY_DOWN, ecouteClavier ); MouseEvent.MOUSE_WHEEL, moletteSouris );

43 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

} private function desactivation ( pEvt:Event ):void { // affiche le curseur Mouse.show(); // arrte lcoute des diffrents vnements stage.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); stage.removeEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); stage.removeEventListener ( MouseEvent.MOUSE_UP, relacheSouris ); stage.removeEventListener ( KeyboardEvent.KEY_DOWN, ecouteClavier stage.removeEventListener ( MouseEvent.MOUSE_WHEEL, moletteSouris } // dplace la tte de lecture au scroll souris private function moletteSouris ( pEvt:MouseEvent ):void { gotoAndStop ( index = (++image%totalFrames)+1 ); } private function ecouteClavier ( pEvt:KeyboardEvent ):void { // si la barre espace est enfonce if ( pEvt.keyCode == Keyboard.SPACE ) { // nombre d'objets Shape contenant des tracs var lng:int = tableauTraces.length; // suppression des tracs de la liste d'affichage while ( lng-- ) conteneur.removeChild ( tableauTraces[lng] // les tableaux d'historiques sont reinitialiss // les rfrences supprimes tableauTraces = new Array(); tableauAncienTraces = new Array(); monNouveauTrace = null; } if ( pEvt.ctrlKey ) { // si retour en arrire (CTRL+Z) if( pEvt.keyCode == Stylo.codeToucheZ && tableauTraces.length ) {

); );

);

44 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// nous supprimons le dernier trac var aSupprimer:Shape = tableauTraces.pop(); // nous stockons chaque trac supprim // dans le tableau spcifique tableauAncienTraces.push ( aSupprimer ); // nous supprimons le trac de la liste d'affichage conteneur.removeChild( aSupprimer ); // si retour en avant (CTRL+Y) } else if ( pEvt.keyCode == Stylo.codeToucheY && tableauAncienTraces.length ) { // nous rcuprons le dernier trac ajout var aAfficher:Shape = tableauAncienTraces.pop(); l'affichage // nous le replaons dans le tableau de tracs tableauTraces.push ( aAfficher ); // puis nous l'affichons conteneur.addChild ( aAfficher ); } } } private function clicSouris ( pEvt:MouseEvent ):void { if ( conteneur == null ) throw new Error ( "Veuillez appeler au pralable la mthode affecteToile()" ); // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // un nouvel objet Shape est cr pour chaque trac monNouveauTrace = new Shape(); // nous ajoutons le conteneur de trac au conteneur principal conteneur.addChild ( monNouveauTrace ); // puis nous rfrenons le trac au sein du tableau // rfrenant les tracs affichs tableauTraces.push ( monNouveauTrace ); // nous dfinissons un style de trac monNouveauTrace.graphics.lineStyle ( 2, Stylo.couleurs[index-1], // la mine est dplace cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.moveTo ( positionX, positionY );

1 );

45 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Array;

// si un nouveau trac intervient alors que nous sommes // repartis en arrire nous repartons de cet tat if ( tableauAncienTraces.length ) tableauAncienTraces = new // coute du mouvement de la souris stage.addEventListener ( MouseEvent.MOUSE_MOVE, dessine ); } private function bougeSouris ( pEvt:MouseEvent ):void { // rcupration des coordonnes de la souris en x et y positionX = pEvt.stageX; positionY = pEvt.stageY; // affectation de la position x = positionX; y = positionY; // si le stylo passe dans la zone suprieure // alors nous inclinons le stylo if ( positionY < Stylo.LIMIT_Y ) {

friction;

rotation -= ( rotation - ( Stylo.LIMIT_Y - positionY ) ) * // sinon, le stylo reprend son inclinaison d'origine } else rotation -= ( rotation - 0 ) * friction; // force le rafrachissement pEvt.updateAfterEvent();

} private function relacheSouris ( pEvt:MouseEvent ):void { stage.removeEventListener ( MouseEvent.MOUSE_MOVE, dessine ); } private function dessine ( pEvt:MouseEvent ):void { if ( monNouveauTrace != null ) { // la mine est dplace cette position // pour commencer dessiner partir de cette position monNouveauTrace.graphics.lineTo ( positionX, positionY ); } }

46 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// mthode permettant de spcifier le conteneur du dessin public function affecteToile ( pToile:DisplayObjectContainer ):void { conteneur = pToile; } public function affecteVitesse ( pFriction:Number ):void { // affecte la friction if ( pFriction > 0 && pFriction <= 1 ) friction = pFriction; else trace("Erreur : Friction non correcte, la valeur doit tre comprise entre 0 et 1"); friction = .1; } } } } {

A retenir

Etendre les classes graphiques natives de Flash permet de crer des objets interactifs puissants et rutilisables. Au sein dune sous-classe graphique, lutilisation du mot cl this fait directement rfrence au scnario du symbole.

Rutiliser le code
Notre application de dessin plat beaucoup, une agence vient de nous contacter afin de dcliner lapplication pour un nouveau client. Grce notre structure actuelle, la dclinaison graphique ne va ncessiter aucune modification du code. Dans un nouveau document Flash CS3 nous importons un nouveau graphisme relatif au stylo comme lillustre la figure 9-13 :

47 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-13. Nouveau graphisme. Au sein du panneau Proprits de liaison nous spcifions la classe Stylo dveloppe prcdemment. Voil, nous pouvons compiler, la figure 9-14 illustre le rsultat :

48 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-14. Rutilisation du code de la classe Stylo. La dclinaison de notre projet a pris quelques secondes seulement, tout symbole de type MovieClip peut tre li la classe Stylo, et bnficier de toutes ses fonctionnalits et comportements.

Classe dynamique
Comme nous lavons vu lors des prcdents chapitres, il existe en ActionScript 3 deux types de classes : dynamiques et non dynamiques. Dans le cas de sous-classes graphiques, quelque soit la nature de super-classe, la sous-classe graphique est par dfaut toujours non dynamique. Il est donc impossible ce stade dajouter une proprit ou une mthode lexcution une instance de la classe Stylo :
// cration du symbole var monStylo:Stylo = new Stylo( .1 ); // ajout d'une proprit l'excution monStylo.maProp = 12;

Le code prcdent gnre lerreur suivante la compilation :


1119: Accs la proprit maProp peut-tre non dfinie, via la rfrence de type static Stylo.

Ce comportement par dfaut est de bon augure, car il est gnralement dconseill davoir recours des classes dynamiques. En donnant la possibilit de modifier limplmentation lexcution, le dveloppeur 49 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

utilisant la classe doit parcourir le code dun projet afin de dcouvrir les comportements qui peuvent tre ajouts lexcution. En lisant la classe, ce dernier nest pas renseign de toutes les capacits et caractristiques de celle-ci. Dans de rares situations, il peut toutefois tre ncessaire de rendre la sous-classe graphique dynamique, pour cela nous ajoutons lattribut dynamic devant le mot cl class :
dynamic public class Stylo extends MovieClip

Une fois la classe modifie, le code suivant fonctionne :


// cration du symbole var monStylo:Stylo = new Stylo( .1 ); // ajout d'une proprit l'excution monStylo.maProp = 12; // affiche : 12 trace( monStylo.maProp);

Bien que lattribut dynamic existe, seule la classe MovieClip en profite en ActionScript 3, toutes les autres classes ne permettent pas lajout de proprits ou mthodes lexcution et sont dites non dynamiques. Il est fortement dconseill de sappuyer sur des classes dynamiques dans vos projets. En rendant une classe dynamique aucune vrification de type nest faite la compilation, nous pouvons donc mme si celleci nous renvoie undefined, accder des proprits prives :
// cration du symbole var monStylo:Stylo = new Stylo( .1 ); // aucune vrification de type la compilation // heureusement, la machine virtuelle (VM2) conserve les types // l'excution et empche son accs en renvoyant undefined // affiche : undefined trace( monStylo.nFriction );

La machine virtuelle 2 (VM2) conserve les types lexcution, ainsi lorsque nous accdons une proprit prive au sein dune classe dynamique, celle-ci renvoie undefined. Dans le cas dune mthode prive, une erreur lexcution de type TypeError est leve.

A retenir

50 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Aucune vrification de type nest faite la compilation sur une classe dynamique. Il est dconseill dutiliser des classes dynamiques dans des projets ActionScript. Dans le cas de sous-classes graphiques, quelque soit la nature de super-classe, la sous-classe graphique est par dfaut toujours non dynamique.

Lattribut dynamic permet de rendre une classe dynamique.

Un vrai constructeur
En ActionScript 1 et 2, il tait aussi possible dtendre la classe MovieClip. Le principe tait quasiment le mme, un symbole tait dfini dans la librairie, puis une classe tait lie au symbole. Attention, il est important de noter quen ActionScript 2 la sous-classe graphique que nous pouvions dfinir tait lie au symbole, cela signifie que seul lappel de la mthode attachMovie instanciait la classe. En ActionScript 3 grce au nouveau modle dinstanciation des objets graphiques, cest le symbole qui est li la classe. Cela signifie que linstanciation de la sous-classe entrane laffichage du symbole et non linverse. Le seul moyen dinstancier notre sous-classe graphique en ActionScript 2 tait dappeler la mthode attachMovie. ActionScript 2, contrairement ActionScript 3 souffrait dun lourd hritage, ce processus dinstanciation des objets graphiques tait archaque et ne collait pas au modle objet. En interne le lecteur dclenchait le constructeur de la sous-classe graphique. Il tait impossible de passer des paramtres dinitialisation, seul lobjet dinitialisation (initObject) permettait de renseigner des proprits avant mme que le constructeur ne soit dclench. En instanciant la sous-classe, le symbole ntait pas affich, le code suivant ne fonctionnait pas car le seul moyen dafficher le clip pour le lecteur tait la mthode attachMovie :
// la sous-classe tait instancie mais le symbole n'tait pas affich var monSymbole:SousClasseMC = new SousClasseMC();

De plus, la liaison entre le symbole et la classe se faisait uniquement travers le panneau Proprits de Liaison, ainsi il tait impossible de lier une classe un objet graphique cr par programmation.

51 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

ActionScript 3 corrige cela. Dans lexemple suivant nous allons crer une sous classe de flash.display.Sprite sans crer de symbole au sein de la bibliothque, tout sera ralis dynamiquement.

A retenir
En ActionScript 2, il tait impossible de passer des paramtres au constructeur dune sous classe graphique. Afin de palier ce problme, nous utilisions lobjet dinitialisation disponible au sein de la mthode attachMovie. ActionScript 3 rgle tout cela, grce au nouveau modle dinstanciation des objets graphiques.

En ActionScript 2, la classe tait lie au symbole. Seule la mthode attachMovie permettait dinstancier la sous-classe graphique.

En ActionScript 3, le symbole est li la classe. Linstanciation de la sous-classe par le mot cl new entrane la cration du symbole.

Crer des boutons dynamiques


Nous allons tendre la classe Sprite afin de crer des boutons dynamiques. Ces derniers nous permettront de crer un menu qui pourra tre modifi plus tard afin de donner vie dautres types de menus. Lors du chapitre 7 nous avions dvelopp un menu compos de boutons de type Sprite. Tous les comportements taient dfinis lextrieur de celui-ci, nous devions dabord crer le symbole correspondant, puis au sein de la boucle ajouter les comportements boutons, les diffrents effets de survol, puis ajouter le texte. Il tait donc impossible de recrer rapidement un bouton identique au sein dune autre application. En utilisant une approche oriente objet laide dune sous-classe graphique, nous allons pouvoir dfinir une classe Bouton qui pourra tre rutilise dans chaque application ncessitant un bouton fonctionnel. Nous allons prsent organiser nos classes en les plaant dans des paquetages spcifiques, au cours du chapitre prcdent nous avons trait la notion de paquetages sans vritablement lutiliser. Il est temps dutiliser cette notion, cela va nous permettre dorganiser nos classes proprement et viter dans certaines situations les conflits entre les classes. Nous crons un nouveau document Flash CS3, cot de celui-ci nous crons un rpertoire org contenant un rpertoire bytearray. Puis 52 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

au sein mme de ce rpertoire nous crons un autre rpertoire appel ui. Au sein du rpertoire ui nous crons une sous-classe de Sprite appele Bouton contenant le code suivant :
package org.bytearray.ui { import flash.display.Sprite; public class Bouton extends Sprite { public function Bouton () { trace ( this ); } } }

Nous remarquons que le paquetage reflte lemplacement de la classe au sein des rpertoires, si le chemin nest pas correct le compilateur ne trouve pas la classe et gnre une erreur. Pour instancier la classe Bouton dans notre document Flash nous devons obligatoirement limporter, car le compilateur recherche par dfaut les classes situes cot du fichier .fla mais ne parcours pas tous les rpertoires voisins afin de trouver la dfinition de classe. Nous lui indiquons o elle se trouve laide du mot cl import :
// import de la classe Bouton import org.bytearray.ui.Bouton; // affiche : [object Bouton] var monBouton:Bouton = new Bouton();

Notre bouton est bien cr mais pour linstant cela ne nous apporte pas beaucoup plus quune instanciation directe de la classe Sprite, nous allons tout de suite ajouter quelques fonctionnalits. Nous allons dessiner le bouton dynamiquement laide de lAPI de dessin. Grce lhritage, la classe Bouton possde toutes les capacits dun Sprite et hrite donc dune proprit graphics propre lAPI de dessin. Nous pourrions dessiner directement au sein de la classe Bouton mais nous prfrerons lutilisation dun objet Shape ddi cela. Si nous devons plus tard ajouter un effet de survol nous dformerons celui-ci 53 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

afin de ne pas tirer lenveloppe principale du bouton qui provoquerait un tirement global de tous les enfants du bouton. Au sein du constructeur de la classe Bouton nous dessinons le bouton :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; public class Bouton extends Sprite { // rfrence le fond du bouton private var fondBouton:Shape; public function Bouton () { // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // dessine le bouton fondBouton.graphics.beginFill ( Math.random()*0xFFFFFF, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); } } }

Nous choisissons une couleur alatoire, puis nous dessinons une forme rectangulaire, plus tard nous pourrons choisir la couleur du bouton lors de sa cration ou encore choisir sa dimension. Il ne nous reste plus qu lafficher :
// import de la classe Bouton import org.bytearray.ui.Bouton; // cration d'un conteneur pour le menu var conteneurMenu:Sprite = new Sprite(); // instanciation var monBouton:Bouton = new Bouton(); // ajout au sein du conteneur conteneurMenu.addChild ( monBouton ); // affichage des boutons addChild ( conteneurMenu );

54 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Nous obtenons le rsultat illustr en figure 9-15 :

Figure 9-15. Instance de la classe Bouton. Afin de rendre notre bouton cliquable, nous devons activer une proprit ? Vous souvenez-vous de laquelle ? La proprit buttonMode permet dactiver le comportement bouton auprs de nimporte quel DisplayObject :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; public class Bouton extends Sprite { // stocke le fond du bouton private var fondBouton:Shape; public function Bouton () { // cration du fond du bouton fondBouton = new Shape();

55 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// ajout la liste d'affichage addChild ( fondBouton ); // dessine le bouton fondBouton.graphics.beginFill ( Math.random()*0xFFFFFF, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; } } }

Le curseur main saffiche alors au survol du bouton :

Figure 9-16. Activation du mode bouton. Nous allons donner un peu de mouvement ce dernier, en utilisant la classe Tween dj aborde lors du chapitre 7. Nous importons la classe Tween puis nous crons un objet du mme type afin de grer le survol :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; // import des classes Tween lies au mouvement

56 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

import fl.transitions.Tween; import fl.transitions.easing.Bounce; public class Bouton extends Sprite { // stocke le fond du bouton private var fondBouton:Shape; // stocke l'objet Tween pour les diffrents tats du bouton private var interpolation:Tween; public function Bouton () { // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // dessine le bouton fondBouton.graphics.beginFill ( Math.random()*0xFFFFFF, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // cration de l'objet Tween interpolation = new Tween (fondBouton, "scaleX", Bounce.easeOut, 1, 1, 1, true ); } } }

Nous demandons lobjet Tween de soccuper de la proprit scaleX pour donner un effet dtirement lobjet Shape servant de fond notre bouton. Nous allons donner un effet de rebond exprim par la classe Bounce. Lorsque le bouton est survol nous dmarrons lanimation. Nous coutons lvnement MouseEvent.ROLL_OVER :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; // import des classes Tween lies au mouvement import fl.transitions.Tween; import fl.transitions.easing.Bounce; // import de la classe MouseEvent import flash.events.MouseEvent; public class Bouton extends Sprite

57 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

{ // stocke le fond du bouton private var fondBouton:Shape; // stocke l'objet Tween pour les diffrents tats du bouton private var interpolation:Tween; public function Bouton () { // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // dessine le bouton fondBouton.graphics.beginFill ( Math.random()*0xFFFFFF, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, 1, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); } // dclench lors du survol du bouton private function survolSouris ( pEvt:MouseEvent ):void { // dmarrage de l'animation interpolation.continueTo ( 2, 2 ); } } }

Lors du survol, le bouton stire avec un effet de rebond, la figure 917 illustre leffet :

58 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-17. Etirement du bouton. En ajoutant dautres boutons nous obtenons un premier menu :
// import de la classe Bouton import org.bytearray.ui.Bouton; // cration d'un conteneur pour le menu var conteneurMenu:Sprite = new Sprite(); var lng:int = 5; var monBouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // instanciation monBouton = new Bouton(); //positionnement monBouton.y = 50 * i; // ajout au sein du conteneur conteneurMenu.addChild ( monBouton ); } // affichage des boutons addChild ( conteneurMenu );

Le rsultat est illustr en figure 9-18 :

59 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-18. Cration du menu. La classe Bouton peut ainsi tre rutilise dans nimporte quel projet ncessitant un seul bouton, ou un menu. Il manque pour le moment une fonctionnalit permettant de refermer le bouton cliqu lorsquun autre est slectionn. Pour cela nous devons obligatoirement possder une rfrence envers tous les boutons du menu, et pouvoir dcider quels boutons refermer. Vous souvenez-vous de la classe Joueur cre au cours du chapitre 8? Nous avions dfini un tableau stockant les rfrences de chaque joueur cr, cela nous permettait tout moment de savoir combien de joueurs taient crs et de pouvoir y accder. Nous allons reproduire le mme mcanisme laide dun tableau statique. Le tableau tableauBoutons stocke chaque rfrence de boutons :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; // import des classes Tween lies au mouvement import fl.transitions.Tween; import fl.transitions.easing.Bounce; // import de la classe MouseEvent

60 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

import flash.events.MouseEvent; public class Bouton extends Sprite { // stocke le fond du bouton private var fondBouton:Shape; // stocke l'objet Tween pour les diffrents tats du bouton private var interpolation:Tween; // stocke les rfrences aux boutons private static var tableauBoutons:Array = new Array(); public function Bouton () { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // dessine le bouton fondBouton.graphics.beginFill ( Math.random()*0xFFFFFF, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, 1, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); } // dclench lors du survol du bouton private function survolSouris ( pEvt:MouseEvent ):void { // dmarrage de l'animation interpolation.continueTo ( 2, 2 ); } } }

A tout moment un bouton peut parcourir le tableau statique et accder ses frres et dcider de les refermer. Nous modifions la mthode survolSouris afin dappeler sur chaque bouton la mthode fermer : 61 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// dclench lors du survol du bouton private function survolSouris ( pEvt:MouseEvent ):void { // stocke la longueur du tableau var lng:int = Bouton.tableauBoutons.length; for (var i:int = 0; i<lng; i++ ) Bouton.tableauBoutons[i].fermer(); // dmarrage de l'animation interpolation.continueTo ( 2, 1 ); }

La mthode fermer est prive car celle ne sera appele quau sein de la classe Bouton :
// mthode permettant de refermer le bouton private function fermer ():void { // referme le bouton interpolation.continueTo ( 1, 1 );

Si nous testons lanimation nous remarquons que lors du survol les autres boutons ouverts se referment. Bien entendu dans la plupart des projets ActionScript nous nallons pas seulement crer des menus constitus de couleurs alatoires. Il serait intressant de pouvoir choisir la couleur de chaque bouton, pour cela nous ajoutons un paramtre afin de recevoir la couleur du bouton au sein du constructeur :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; // import des classes Tween lies au mouvement import fl.transitions.Tween; import fl.transitions.easing.Bounce; // import de la classe MouseEvent import flash.events.MouseEvent; public class Bouton extends Sprite { // stocke le fond du bouton private var fondBouton:Shape; // stocke l'objet Tween pour les diffrents tats du bouton private var interpolation:Tween; // stocke les rfrences aux boutons private static var tableauBoutons:Array = new Array(); // stocke la couleur en cours du bouton private var couleur:Number;

62 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

public function Bouton ( pCouleur:Number ) { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // stocke la couleur passe en paramtre couleur = pCouleur; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, 1, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); } // dclench lors du survol du bouton private function survolSouris ( pEvt:MouseEvent ):void { // stocke la longueur du tableau var lng:int = Bouton.tableauBoutons.length;

for (var i:int = 0; i<lng; i++ ) Bouton.tableauBoutons[i].fermer(); // dmarrage de l'animation interpolation.continueTo ( 2, 2 ); } // mthode permettant de refermer le bouton private function fermer ():void { // referme le bouton interpolation.continueTo ( 1, 1 );

} } }

63 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Dsormais la classe Bouton accepte un paramtre pour la couleur, le code suivant cre un menu constitu de boutons rouges seulement :

// import de la classe Bouton import org.bytearray.ui.Bouton; // cration d'un conteneur pour le menu var conteneurMenu:Sprite = new Sprite(); var lng:int = 5; var monBouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // instanciation // cration de boutons rouge monBouton = new Bouton( 0x990000 ); //positionnement monBouton.y = 50 * i; // ajout au sein du conteneur conteneurMenu.addChild ( monBouton ); } // affichage des boutons addChild ( conteneurMenu );

Cela na pas grand intrt, nous allons donc modifier le code actuel afin dassocier chaque bouton, une couleur prcise. Pour cela nous stockons les couleurs au sein dun tableau qui sera parcouru, le nombre de boutons du menu sera li la longueur du tableau :
// import de la classe Bouton import org.bytearray.ui.Bouton; // cration d'un conteneur pour le menu var conteneurMenu:Sprite = new Sprite(); // tableau de couleurs var couleurs:Array = [0x999900, 0x881122, 0x995471, 0x332100, 0x977821]; // nombre de couleurs var lng:int = couleurs.length; var monBouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // instanciation monBouton = new Bouton( couleurs[i] );

64 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

//positionnement monBouton.y = 50 * i; // ajout au sein du conteneur conteneurMenu.addChild ( monBouton ); } // affichage des boutons addChild ( conteneurMenu );

Nous obtenons le menu illustr en figure 9-19 :

Figure 9-19. Menu constitu de couleurs prdfinies. Chaque bouton accepte une couleur lors de sa cration, nous pourrions imaginer une application dans laquelle lutilisateur choisit la couleur des boutons de son menu au sein dune administration. Les couleurs sont alors rcupres dune base de donnes puis utilises pour crer ce menu. Nous verrons au cours du chapitre 19 intitul Flash Remoting comment mettre en place cette application. Notre menu nest pas encore complet il serait judicieux de pouvoir spcifier la vitesse douverture de chaque bouton. Souvenez-vous nous avons dj intgr ce contrle pour le mouvement du stylo de lapplication prcdente. Nos objets sont tous de la mme famille mais possdent des caractristiques diffrentes, comme la couleur ou bien la vitesse douverture.

65 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Pour cela nous allons dfinir une proprit prive vitesse qui se chargera de stocker la vitesse passe chaque bouton :
// stocke la vitesse d'ouverture de chaque bouton private var vitesse:Number;

Puis nous dfinissons une mthode affecteVitesse qui se charge daffecter la vitesse de manire contrle :
// gre l'affectation de la vitesse public function affecteVitesse ( pVitesse:Number ):void { // affecte la vitesse if ( pVitesse >= 1 && pVitesse <= 10 ) vitesse = pVitesse; else trace("Erreur : Vitesse non correcte, la valeur doit tre comprise entre 1 et 10"); vitesse = 1; } } {

Nous modifions le constructeur en ajoutant un paramtre appel pVitesse afin dappeler la mthode affecteVitesse avant la cration de lobjet Tween pour initialiser la proprit vitesse :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; // import des classes Tween lies au mouvement import fl.transitions.Tween; import fl.transitions.easing.Bounce; // import de la classe MouseEvent import flash.events.MouseEvent; public class Bouton extends Sprite { // stocke le fond du bouton private var fondBouton:Shape; // stocke l'objet Tween pour les diffrents tats du bouton private var interpolation:Tween; // stocke les rfrences aux boutons private static var tableauBoutons:Array = new Array(); // stocke la couleur en cours du bouton private var couleur:Number; // stocke la vitesse d'ouverture de chaque bouton private var vitesse:Number; public function Bouton ( pCouleur:Number, pVitesse:Number=1 )

66 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

{ // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // stocke la couleur passe en paramtre couleur = pCouleur; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // affectation de la vitesse contrle affecteVitesse ( pVitesse ); // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); } // dclench lors du survol du bouton private function survolSouris ( pEvt:MouseEvent ):void { // stocke la longueur du tableau var lng:int = Bouton.tableauBoutons.length; for (var i:int = 0; i<lng; i++ ) Bouton.tableauBoutons[i].fermer(); // dmarrage de l'animation interpolation.continueTo ( 2, vitesse ); } // mthode permettant de refermer le bouton private function fermer ():void { // referme le bouton interpolation.continueTo ( 1, vitesse ); } // gre l'affectation de la vitesse public function affecteVitesse ( pVitesse:Number ):void

67 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

{ // affecte la vitesse if ( pVitesse >= 1 && pVitesse <= 10 ) vitesse = pVitesse; else trace("Erreur : Vitesse non correcte, la valeur doit tre comprise entre 1 et 10"); vitesse = 1; } } } } {

Dans le code suivant, nous crons un menu compos de boutons dont nous pouvons choisir la vitesse douverture :
// import de la classe Bouton import org.bytearray.ui.Bouton; // cration d'un conteneur pour le menu var conteneurMenu:Sprite = new Sprite(); // tableau de couleurs var couleurs:Array = [0x999900, 0x881122, 0x995471, 0x332100, 0x977821]; // nombre de couleurs var lng:int = couleurs.length; var monBouton:Bouton; for (var i:int = 0; i< lng; i++ ) { // instanciation monBouton = new Bouton( couleurs[i], 1 ); //positionnement monBouton.y = 50 * i; // ajout au sein du conteneur conteneurMenu.addChild ( monBouton ); } // affichage du menu addChild ( conteneurMenu );

Au cas o la vitesse passe ne serait pas correcte, le menu continue de fonctionner et un message derreur est affich :
// affiche : Erreur : Vitesse non correcte, la valeur // doit tre comprise entre 1 et 10 var monBouton:Bouton = new Bouton( couleurs[i], 0 );

68 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Gnralement, les boutons dun menu contiennent une lgende, nous allons donc ajouter un champ texte chaque bouton. Pour cela nous importons les classes flash.text.TextField et flash.text.TextFieldAutoSize :
// import de la classe TextField et TextFieldAutoSize import flash.text.TextField; import flash.text.TextFieldAutoSize;

Nous crons une proprit legende afin de rfrencer la lgende :


// lgende du bouton private var legende:TextField;

Puis nous ajoutons le champ au bouton au sein du constructeur :


public function Bouton ( pCouleur:Number, pVitesse:Number=1 ) { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // cre le champ texte legende = new TextField(); // redimensionnement automatique du champ texte legende.autoSize = TextFieldAutoSize.LEFT; // rend le champ texte non slectionnable legende.selectable = false; // ajout la liste d'affichage addChild ( legende ); // stocke la couleur passe en paramtre couleur = pCouleur; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // affectation de la vitesse contrle affecteVitesse ( pVitesse ); // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris );

69 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Si nous testons notre menu, nous remarquons que le champ texte imbriqu dans le bouton reoit les entres souris et entre en conflit avec lenveloppe principale du bouton. Afin de dsactiver les objets enfants du bouton nous passons la valeur false la proprit mouseChildren du bouton :
// dsactivation des objets enfants mouseChildren = false;

Souvenez-vous, lors du chapitre 7, nous avons vu que la proprit mouseChildren permettait de dsactiver les vnements souris auprs des objets enfants. Nous ajoutons un paramtre au constructeur de la classe Bouton afin daccueillir le texte affich par la lgende :
public function Bouton ( pCouleur:Number, pVitesse:Number=1, pLegende:String="Lgende" ) { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // cre le champ texte legende = new TextField(); // redimensionnement automatique du champ texte legende.autoSize = TextFieldAutoSize.LEFT; // rend le champ texte non slectionnable legende.selectable = false; // ajout la liste d'affichage addChild ( legende ); // affecte la lgende legende.text = pLegende; // stocke la couleur passe en paramtre couleur = pCouleur; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // dsactivation des objets enfants

70 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

mouseChildren = false; // affectation de la vitesse contrle affecteVitesse ( pVitesse ); // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); }

Les donnes utilises afin de gnrer un menu proviennent gnralement dun flux XML ou de Flash Remoting et sont trs souvent formates sous forme de tableau associatif. Nous allons rorganiser nos donnes afin dutiliser un tableau associatif plutt que plusieurs tableaux spars :
// import de la classe Bouton import org.bytearray.ui.Bouton; // cration d'un conteneur pour le menu var conteneurMenu:Sprite = new Sprite(); // tableau associatif contenant les donnes var donnees:Array = new Array(); donnees.push donnees.push donnees.push donnees.push donnees.push ( ( ( ( ( { { { { { legende legende legende legende legende : : : : : "Accueil", vitesse : 1, couleur : 0x999900 } ); "Photos", vitesse : 1, couleur : 0x881122 } ); "Blog", vitesse : 1, couleur : 0x995471 } ); "Liens", vitesse : 1, couleur : 0x332100 } ); "Forum", vitesse : 1, couleur : 0x977821 } );

// nombre de rubriques var lng:int = donnees.length; var var var var monBouton:Bouton; legende:String; couleur:Number; vitesse:Number;

for (var i:int = 0; i< lng; i++ ) { // rcupration des infos legende = donnees[i].legende; couleur = donnees[i].couleur; vitesse = donnees[i].vitesse; // cration des boutons monBouton = new Bouton( couleur, vitesse, legende ); // positionnement monBouton.y = 50 * i; // ajout au sein du conteneur conteneurMenu.addChild ( monBouton );

71 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

} // affichage du menu addChild ( conteneurMenu );

En testant notre menu, nous obtenons le rsultat illustr en figure 920 :

Figure 9-20. Boutons avec lgendes. Le texte de lgende nest pas correctement format, pour y remdier nous utilisons la classe flash.text.TextFormat ainsi que la classe flash.text.Font. Nous reviendrons plus en profondeur sur le formatage du texte au cours du chapitre 16 intitul Le texte. Afin dintgrer une police au sein de la bibliothque nous cliquons sur licne prvue illustre en figure 9-21 :

Figure 9-21. Options de la bibliothque.

72 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Le panneau doptions de bibliothque souvre, nous slectionnons Nouvelle Police comme lillustre la figure 9-22 :

Figure 9-22. Insertion de police dans la bibliothque. Une fois slectionne, le panneau Proprits des symboles de police saffiche, ce dernier permet de slectionner la police qui sera embarque dans lanimation. Nous slectionnons pour notre exemple la police Trebuchet MS, puis nous donnons un nom de classe la police, nous conservons Police 1.

Figure 9-23. Proprits des symboles de police. 73 / 83


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Une fois valid, la police apparat dans la bibliothque, il ne nous reste plus qu lutiliser au sein de nos champs texte contenus dans chaque bouton. Pour cela il faut lier cette police comme pour un symbole classique. En faisant un clic droit sur celle-ci, nous slectionnons loption Liaisons le panneau Proprits de liaison souvre comme lillustre la figure 9-24 :

Figure 9-24. Panneau proprits de liaison. Nous choisissons MaPolice comme nom de classe, celle-ci va automatiquement tre cre par Flash et hritera de la classe flash.text.Font. Nous dfinissons une nouvelle proprit formatage afin de stocker lobjet TextFormat : Nous importons la classe flash.text.TextFormat : Puis nous modifions le constructeur de la classe Bouton afin daffecter le formatage et indiquer au champ texte dutiliser la police embarque :
) public function Bouton ( pCouleur:Number, pVitesse:Number, pLegende:String { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); import flash.text.TextFormat; // formatage des lgendes private var formatage:TextFormat;

74 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// cre le champ texte legende = new TextField(); // redimensionnement automatique du champ texte legende.autoSize = TextFieldAutoSize.LEFT; // ajout la liste d'affichage addChild ( legende ); // affecte la lgende legende.text = pLegende; // active l'utilisation de police embarque legende.embedFonts = true; // cre un objet de formatage formatage = new TextFormat(); // taille de la police formatage.size = 12; // instanciation de la police embarque var police:MaPolice = new MaPolice(); // affectation de la police au formatage formatage.font = police.fontName; // affectation du formatage au champ texte legende.setTextFormat ( formatage ); // rend le champ texte non selectionnable legende.selectable = false; // stocke la couleur passe en paramtre couleur = pCouleur; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, 40, 110 ); // activation du mode bouton buttonMode = true; // dsactivation des objets enfants mouseChildren = false; // affectation de la vitesse contrle affecteVitesse ( pVitesse ); // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); }

En testant le code prcdent, chaque bouton possde dsormais une lgende intgrant les contours de police :

75 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-25. Champs texte formats. Il ne nous reste plus qu intgrer une gestion de la largeur et hauteur de chaque bouton. Pour cela nous ajoutons au constructeur de la classe Bouton deux paramtres pLargeur et pHauteur :
public function Bouton ( pLargeur:Number, pHauteur:Number, pCouleur:Number, pVitesse:Number, pLegende:String ) { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // cre le champ texte legende = new TextField(); // redimensionnement automatique du champ texte legende.autoSize = TextFieldAutoSize.LEFT; // ajout la liste d'affichage addChild ( legende ); // affecte la lgende legende.text = pLegende; // active l'utilisation de police embarque legende.embedFonts = true;

76 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// cre un objet de formatage formatage = new TextFormat(); // taille de la police formatage.size = 12; // instanciation de la police embarque var police:MaPolice = new MaPolice(); // affectation de la police au formatage formatage.font = police.fontName; // affectation du formatage au champ texte legende.setTextFormat ( formatage ); // rend le champ texte non selectionnable legende.selectable = false; // stocke la couleur passe en paramtre couleur = pCouleur; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, pLargeur, pHauteur ); // activation du mode bouton buttonMode = true; // dsactivation des objets enfants mouseChildren = false; // affectation de la vitesse contrle affecteVitesse ( pVitesse ); // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); }

Puis nous passons les valeurs voulues lors de la cration de chaque bouton :
// cration des boutons monBouton = new Bouton( 60, 120, couleur, vitesse, legende );

La figure 9-26 illustre le rsultat final :

77 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Figure 9-26. Boutons aux dimensions dynamiques. Nous obtenons un menu simple mettre en place et facilement modifiable. Nous pourrions rajouter dautres fonctionnalits comme une adaptation automatique de la largeur du bouton par rapport la lgende. En ralit, nous pourrions ne jamais nous arrter ! Dans beaucoup de projets, les boutons dun menu sont gnralement lis des SWF. Lorsque le bouton est cliqu, le SWF correspondant est charg, cela permet un meilleur dcoupage du site et un chargement la demande. Nous allons modifier la classe Bouton afin de pouvoir stocker dans chaque bouton le nom du SWF correspondant. Nous ajoutons une proprit swf :
// swf associ private var swf:String;

Puis nous modifions le constructeur afin daccueillir le nom du SWF associ :


public function Bouton ( pLargeur:Number, pHauteur:Number, pSWF:String, pCouleur:Number, pVitesse:Number, pLegende:String ) { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape();

78 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

// ajout la liste d'affichage addChild ( fondBouton ); // cre le champ texte legende = new TextField(); // redimensionnement automatique du champ texte legende.autoSize = TextFieldAutoSize.LEFT; // ajout la liste d'affichage addChild ( legende ); // affecte la lgende legende.text = pLegende; // active l'utilisation de police embarque legende.embedFonts = true; // cre un objet de formatage formatage = new TextFormat(); // taille de la police formatage.size = 12; // instanciation de la police embarque var police:MaPolice = new MaPolice(); // affectation de la police au formatage formatage.font = police.fontName; // affectation du formatage au champ texte legende.setTextFormat ( formatage ); // rend le champ texte non selectionnable legende.selectable = false; // stocke la couleur passe en paramtre couleur = pCouleur; // stocke le nom du SWF associ swf = pSWF; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, pLargeur, pHauteur ); // activation du mode bouton buttonMode = true; // dsactivation des objets enfants mouseChildren = false; // affectation de la vitesse contrle affecteVitesse ( pVitesse ); // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris );

79 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

Puis nous instancions les boutons du menu :


// import de la classe Bouton import org.bytearray.ui.Bouton; // cration d'un conteneur pour le menu var conteneurMenu:Sprite = new Sprite(); // tableau associatif contenant les donnes var donnees:Array = new Array(); donnees.push ( { legende couleur : 0x999900 } ); donnees.push ( { legende : 0x881122 } ); donnees.push ( { legende 0x995471 } ); donnees.push ( { legende 0xCC21FF } ); donnees.push ( { legende 0x977821 } ); : "Accueil", vitesse : 1, swf : "accueil.swf", : "Photos", vitesse : 1, swf : "photos.swf", couleur : "Blog", vitesse : 1, swf : "blog.swf", couleur : : "Liens", vitesse : 1, swf : "liens.swf", couleur : : "Forum", vitesse : 1, swf : "forum.swf", couleur :

// nombre de rubriques var lng:int = donnees.length; var var var var var monBouton:Bouton; legende:String; couleur:Number; vitesse:Number; swf:String;

for (var i:int = 0; i< lng; i++ ) { // rcupration des infos legende = donnees[i].legende; couleur = donnees[i].couleur; vitesse = donnees[i].vitesse; swf = donnees[i].swf; // cration des boutons monBouton = new Bouton( 60, 120, swf, couleur, vitesse, legende ); // positionnement monBouton.y = 50 * i; // ajout au sein du conteneur conteneurMenu.addChild ( monBouton ); } // affichage du menu addChild ( conteneurMenu );

Chaque bouton est ainsi li un SWF. Lors du chapitre 7 nous avons cr un menu dynamique qui nous redirigeait une adresse spcifique en ouvrant une nouvelle fentre navigateur. Pour cela nous stockions

80 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

le lien au sein dune proprit du bouton, puis au moment du clic nous accdions celle-ci. Voici le code final de la classe Bouton :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; import flash.text.Font; import flash.text.TextFormat; // import des classes lies Tween au mouvement import fl.transitions.Tween; import fl.transitions.easing.Bounce; // import de la classe MouseEvent import flash.events.MouseEvent; // import de la classe TextField et TextFieldAutoSize import flash.text.TextField; import flash.text.TextFieldAutoSize; public class Bouton extends Sprite { // stocke le fond du bouton private var fondBouton:Shape; // stocke l'objet Tween pour les diffrents tats du bouton private var interpolation:Tween; // stocke les rfrences aux boutons private static var tableauBoutons:Array = new Array(); // stocke la couleur en cours du bouton private var couleur:Number; // stocke la vitesse d'ouverture de chaque bouton private var vitesse:Number; // lgende du bouton private var legende:TextField; // formatage des lgendes private var formatage:TextFormat; // swf associ private var swf:String; public function Bouton ( pLargeur:Number, pHauteur:Number, pSWF:String, pCouleur:Number, pVitesse:Number, pLegende:String ) { // ajoute chaque instance au tableau Bouton.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // cre le champ texte legende = new TextField(); // redimensionnement automatique du champ texte

81 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

legende.autoSize = TextFieldAutoSize.LEFT; // ajout la liste d'affichage addChild ( legende ); // affecte la lgende legende.text = pLegende; // active l'utilisation de police embarque legende.embedFonts = true; // cre un objet de formatage formatage = new TextFormat(); // taille de la police formatage.size = 12; // instanciation de la police embarque var police:MaPolice = new MaPolice(); // affectation de la police au formatage formatage.font = police.fontName; // affectation du formatage au champ texte legende.setTextFormat ( formatage ); // rend le champ texte non selectionnable legende.selectable = false; // stocke la couleur passe en paramtre couleur = pCouleur; // stocke le nom du SWF swf = pSWF; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, pLargeur, pHauteur ); // activation du mode bouton buttonMode = true; // dsactivation des objets enfants mouseChildren = false; // affectation de la vitesse contrle affecteVitesse ( pVitesse ); // cration de l'objet Tween interpolation = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); } // dclench lors du survol du bouton private function survolSouris ( pEvt:MouseEvent ):void { // stocke la longueur du tableau

82 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 9 Etendre les classes natives version 0.1.2

var lng:int = Bouton.tableauBoutons.length; for (var i:int = 0; i<lng; i++ ) Bouton.tableauBoutons[i].fermer(); // dmarrage de l'animation interpolation.continueTo ( 2, vitesse ); } // mthode permettant de refermer le bouton private function fermer ():void { // referme le bouton interpolation.continueTo ( 1, vitesse );

} // gre l'affectation de la vitesse public function affecteVitesse ( pVitesse:Number ):void { // affecte la vitesse if ( pVitesse >= 1 && pVitesse <= 10 ) vitesse = pVitesse; else trace("Erreur : Vitesse non correcte, la valeur doit tre comprise entre 1 et 10"); vitesse = 1; } } } } {

Cette approche fonctionne sans problme et convient dans beaucoup de situations, en revanche lors de lutilisation de classes personnalises comme dans cette application nous allons externaliser les informations ncessaires grce au modle vnementiel. De cette manire les objets parleront entre eux de manire faiblement couple. Nous allons pouser le mme modle que les objets natifs dActionScript 3, chaque bouton pourra diffuser un vnement spcifique nous renseignant sur sa couleur, sa vitesse ainsi que le SWF correspondant. Certains dentre vous ont peut tre dj devin vers quelle nouvelle notion nous nous dirigeons, en route pour le chapitre suivant intitul Diffusion dvnements personnaliss. 83 / 83
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

10
Diffusion dvnements personnaliss

LHISTOIRE ............................................................................................................ 1 LA CLASSE EVENTDISPATCHER ..................................................................... 2 MISE EN APPLICATION ............................................................................................. 4 CHOISIR UN NOM DVNEMENT ............................................................................. 9 ETENDRE EVENTDISPATCHER ...................................................................... 11 STOCKER EVENTDISPATCHER ..................................................................... 14 PASSER DES INFORMATIONS .................................................................................. 19 MENU ET VNEMENT PERSONNALIS .................................................................. 23

Lhistoire
Dans une application ActionScript, la communication inter objets se ralise majoritairement par la diffusion dvnements natifs ou personnaliss. Mais quest ce quun vnement personnalis ? Il sagit dun vnement qui nexiste pas au sein du lecteur Flash mais que nous ajoutons afin de grer les diffrentes interactions entre nos objets. Depuis trs longtemps ActionScript intgre diffrentes classes permettant de diffuser nos propres vnements. La premire fut AsBroadcaster intgre depuis le lecteur Flash 5, son implmentation native lavait rendu favorite des dveloppeurs ActionScript. Sa remplaante BroadcasterMX introduite plus tard par Flash MX et code en ActionScript fut moins bien accueillie. Aujourdhui ces classes nexistent plus en ActionScript 3 et sont remplaces par la classe native flash.events.EventDispatcher.

1 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

Au sein dune interface graphique nous pourrions imaginer un bouton diffusant en vnement indiquant son tat actif ou inactif. Une classe gnrant des fichiers PDF pourrait diffuser des vnements relatifs au processus de cration du fichier. Ainsi, la diffusion dvnements personnaliss constitue la suite logique la cration dobjets personnaliss.

La classe EventDispatcher
Comme nous le voyons depuis le dbut de louvrage, ActionScript 3 est un langage bas sur un modle vnementiel appel Document Object Model. Celui-ci repose sur la classe EventDispatcher dont toutes les classes natives de lAPI du lecteur Flash hritent. Dans le code suivant nous voyons la relation entre la classe EventDispatcher et deux classes graphiques :
var monSprite:Sprite = new Sprite(); // affiche : true trace( monSprite is EventDispatcher ); var monClip:MovieClip = new MovieClip(); // affiche : true trace( monClip is EventDispatcher );

Rappelez-vous, toutes les classes issues du paquetage flash sont des sous-classes dEventDispatcher et possdent donc ce type commun. Nous crons un Sprite puis nous coutons un vnement personnalis auprs de ce dernier :
// cration d'un Sprite var monSprite:Sprite = new Sprite(); // coute de l'vnement monEvent monSprite.addEventListener ( "monEvenement", ecouteur ); // fonction couteur function ecouteur ( pEvt:Event ):void { trace( pEvt ); }

La classe Sprite ne diffuse pas par dfaut dvnement nomm monEvenement, mais nous pouvons le diffuser simplement grce la mthode dispatchEvent dont voici la signature :
public function dispatchEvent(event:Event):Boolean

2 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

En ActionScript 2 la mthode dispatchEvent diffusait un objet vnementiel non typ. Nous utilisions gnralement un objet littral auquel nous ajoutions diffrentes proprits manuellement. En ActionScript 3, afin de diffuser un vnement nous devons crer un objet vnementiel, reprsent par une instance de la classe flash.events.Event :
// cration de l'objet vnementiel var objetEvenementiel:Event = new Event (type, bubbles, cancelable);

Le constructeur de la classe Event accepte trois paramtres :


bubbles : indique si lvnement participe la phase de remonte. cancelable : indique si lvnement peut tre annul. type : le nom de lvnement diffuser.

Dans la plupart des situations nous nutilisons que le premier paramtre type de la classe Event. Une fois lobjet vnementiel cr, nous le passons la mthode dispatchEvent :
// cration d'un sprite var monSprite:Sprite = new Sprite(); // coute de l'vnement monEvent monSprite.addEventListener ( "monEvenement", ecouteur ); // fonction couteur function ecouteur (pEvt:Event):void { // affiche [Event type="monEvenement" bubbles=false cancelable=false eventPhase=2] trace( pEvt ); } // cration de l'objet vnementiel var objetEvenementiel:Event = new Event (monEvenement, bubbles, cancelable); // nous diffusons l'vnement monEvenement monSprite.dispatchEvent (objetEvenementiel);

Gnralement, nous ne crons pas lobjet vnementiel sparment, nous linstancions directement en paramtre de la mthode dispatchEvent :
// cration d'un Sprite var monSprite:Sprite = new Sprite(); // coute de l'vnement monEvent monSprite.addEventListener ("monEvenement", ecouteur );

3 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

// fonction couteur function ecouteur ( pEvt:Event ):void { // affiche [Event type="monEvenement" bubbles=false cancelable=false eventPhase=2] trace( pEvt ); } // nous diffusons l'vnement monEvenement monSprite.dispatchEvent ( new Event (monEvenement) );

Cet exemple nous montre la facilit avec laquelle nous pouvons diffuser un vnement en ActionScript 3.

A retenir
Le modle vnementiel ActionScript 3 repose sur la classe EventDispatcher. Toutes les classes issues du paquetage flash peuvent diffuser des vnements natifs ou personnaliss.

Afin de diffuser un vnement, nous utilisons la mthode dispatchEvent. La mthode dispatchEvent accepte comme paramtre une instance de la classe Event.

Mise en application
Nous allons dvelopper une classe permettant un symbole de se dplacer dans diffrentes directions. Lorsque celui-ci arrive destination nous souhaitons diffuser un vnement appropri. A ct dun nouveau document Flash CS3, nous sauvons une classe nomme Balle.as contenant le code suivant :
package { import flash.display.Sprite; // la classe Balle tend la classe Sprite public class Balle extends Sprite { public function Balle () { trace( this );

4 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

} } }

Nous lions notre symbole de forme circulaire celle-ci par le panneau Proprits de liaison. Puis nous instancions le symbole :
// cration du symbole // affiche : [object Balle] var maBalle:Balle = new Balle(); // ajout la liste d'affichage addChild ( maBalle );

A chaque clic souris sur la scne, la balle doit se diriger vers le point cliqu. Nous devons donc couter lvnement MouseEvent.CLICK de manire globale auprs de lobjet Stage. Nous avons vu au cours du prcdent chapitre comment accder de manire scurise lobjet Stage. Nous intgrons le mme mcanisme dans la classe Balle :
package { import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; // la classe Balle tend la classe Sprite public class Balle extends Sprite { public function Balle () { // coute de l'vnement Event.ADDED_TO_STAGE addEventListener ( Event.ADDED_TO_STAGE, ajoutAffichage );

} private function ajoutAffichage( pEvt:Event ):void { // coute de l'vnement MouseEvent.CLICK stage.addEventListener ( MouseEvent.CLICK, clicSouris ); } private function clicSouris ( pEvt:MouseEvent ):void {

5 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

// affiche : [MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=81 localY=127 stageX=81 stageY=127 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); } } }

A chaque clic sur la scne, lvnement MouseEvent.CLICK est diffus, la fonction couteur clicSouris est alors dclenche. Nous allons intgrer prsent la notion de mouvement. Nous dfinissons deux proprits sourisX et sourisY au sein de la classe. Celles-ci vont nous permettre de stocker la position de la souris :
// stocke les coordonnes de la souris private var sourisX:Number; private var sourisY:Number;

Puis nous modifions la mthode clicSouris afin daffecter ces proprits :


package { import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; // la classe Balle tend la classe Sprite public class Balle extends Sprite { // stocke les coordonnes de la souris private var sourisX:Number; private var sourisY:Number; public function Balle () { // coute de l'vnement Event.ADDED_TO_STAGE addEventListener ( Event.ADDED_TO_STAGE, ajoutAffichage );

} private function ajoutAffichage( pEvt:Event ):void { // coute de l'vnement MouseEvent.CLICK stage.addEventListener ( MouseEvent.CLICK, clicSouris ); } private function clicSouris ( pEvt:MouseEvent ):void

6 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

{ // affecte les coordonnes aux proprits sourisX = pEvt.stageX; sourisY = pEvt.stageY; } } }

Enfin, nous dclenchons le mouvement en coutant lvnement Event.ENTER_FRAME hrit de la classe Sprite :
package { import flash.display.Sprite; import flash.events.MouseEvent; import flash.events.Event; // la classe Balle tend la classe Sprite public class Balle extends Sprite { // stocke les coordonnes de la souris private var sourisX:Number; private var sourisY:Number; public function Balle () { // coute de l'vnement Event.ADDED_TO_STAGE addEventListener ( Event.ADDED_TO_STAGE, ajoutAffichage );

} private function ajoutAffichage( pEvt:Event ):void { // coute de l'vnement MouseEvent.CLICK stage.addEventListener ( MouseEvent.CLICK, clicSouris ); } private function clicSouris ( pEvt:MouseEvent ):void { // affecte les coordonnes aux proprits sourisX = pEvt.stageX; sourisY = pEvt.stageY; addEventListener ( Event.ENTER_FRAME, mouvement ); }

7 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

private function mouvement ( pEvt:Event ):void { // value la destination x et y var destinationX:Number = ( sourisX - width/2); var destinationY:Number = ( sourisY - height/2); // dplace la balle avec un effet de ralentissement (inertie) x -= (x - destinationX)*.1; y -= (y - destinationY)*.1; } } }

Si nous testons notre animation, la balle se dplace lendroit cliqu avec un effet de ralenti. La figure 10.1 illustre le comportement.

Figure 10.1 Dplacement position clic souris. Lorsquun mouvement entre en jeu, nous souhaitons gnralement savoir quand est ce quil se termine. La classe Tween diffuse par dfaut tous les vnements ncessaires la synchronisation dune animation. Mais comment faire dans notre cas pour diffuser notre propre vnement ? La classe Sprite hrite de la classe EventDispatcher et peut donc diffuser nimporte quel vnement. Au sein de la mthode 8 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

mouvement nous testons si la diffrence entre la position en cours et

la destination est infrieure 1. Si cest le cas, cela signifie que nous sommes arrivs destination.
private function mouvement ( pEvt:Event ):void { // value la destination x et y var destinationX:Number = ( sourisX - width/2); var destinationY:Number = ( sourisY - height/2);

// dplace la balle avec un effet de ralentissement (inertie) x -= (x - destinationX)*.1; y -= (y - destinationY)*.1; if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y - destinationY ) < 1 ) { removeEventListener ( Event.ENTER_FRAME, mouvement ); trace ("arriv destination !"); } }

La mthode Math.abs nous permet de rendre la distance absolue, car une distance entre deux points est toujours positive. Nous supprimons lcoute de lvnement Event.ENTER_FRAME lorsque la balle arrive destination afin doptimiser les ressources, puis nous affichons un message indiquant que la balle est arrive. En testant notre animation, nous remarquons que le message indiquant larrive est bien dclench, mais pour linstant aucun vnement nest diffus.

Choisir un nom dvnement


Lorsquun objet diffuse un vnement nous devons nous assurer que son nom soit simple et intuitif pour les personnes utilisant la classe. Dans notre exemple nous allons diffuser un vnement mouvementTermine. Nous modifions la mthode mouvement afin que celle-ci le diffuse :
private function mouvement ( pEvt:Event ):void { // value la destination x et y var destinationX:Number = ( sourisX - width/2); var destinationY:Number = ( sourisY - height/2);

9 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

// dplace la balle avec un effet de ralentissement (inertie) x -= (x - destinationX)*.1; y -= (y - destinationY)*.1; if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y - destinationY ) < 1 ) { removeEventListener ( Event.ENTER_FRAME, mouvement ); // diffusion de l'vnement motionComplete dispatchEvent ( new Event ("mouvementTermine") ); } }

Notre

mouvementTermine. Il ne nous reste plus qu lcouter :


// cration du symbole // affiche : [object Balle] var maBalle:Balle = new Balle(); // ajout la liste d'affichage addChild ( maBalle );

symbole

balle

diffuse

dsormais

un

vnement

// coute de l'vnement personnalis motionComplete maBalle.addEventListener ( "mouvementTermine", arrivee ); // fonction couteur function arrivee ( pEvt:Event ):void { trace("mouvement termin !"); }

La fonction couteur arrivee est dclenche lorsque la balle arrive destination. En cas de rutilisation de la classe Balle nous savons que celle-ci diffuse lvnement mouvementTermine. Libre nous de dcider quoi faire lorsque lvnement est diffus, la rutilisation de la classe Balle est donc facilite. Notre code fonctionne trs bien, mais il nest pas totalement optimis. Voyez-vous ce qui pose problme ? Souvenez-vous, lors du chapitre 3 intitul Le modle vnementiel, nous avons vu que nous ne qualifions jamais le nom des vnements directement. Cela rend notre code rigide et non standard. Nous prfrons lutilisation de constantes de classe. Nous dfinissons donc une proprit constante MOUVEMENT_TERMINE au sein de la classe Balle contenant le nom de lvnement diffus : 10 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

// stocke le nom de l'vnement diffus public static const MOUVEMENT_TERMINE:String = "mouvementTermine";

Puis nous ciblons la proprit afin dcouter lvnement :


// coute de l'vnement personnalis Balle.MOUVEMENT_TERMINE maBalle.addEventListener ( Balle.MOUVEMENT_TERMINE, arrivee );

Nous modifions la mthode mouvement afin de cibler la constante Balle.MOUVEMENT_TERMINE :


private function mouvement ( pEvt:Event ):void { // value la destination x et y var destinationX:Number = ( sourisX - width/2); var destinationY:Number = ( sourisY - height/2); // dplace la balle avec un effet de ralentissement (inertie) x -= (x - destinationX)*.1; y -= (y - destinationY)*.1; if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y - destinationY ) < 1 ) { removeEventListener ( Event.ENTER_FRAME, mouvement ); // diffusion de l'vnement motionComplete dispatchEvent ( new Event ( Balle.MOUVEMENT_TERMINE ) ); } }

Nous venons de traiter travers cet exemple le cas le plus courant lors de la diffusion dvnements personnaliss. Voyons maintenant dautres cas.

A retenir
Un vnement doit porter un nom simple et intuitif. Afin de stocker le nom dun vnement nous utilisons toujours une proprit constante de classe.

Etendre EventDispatcher
Rappelez-vous que toutes les classes rsidant dans le paquetage flash hritent de la classe EventDispatcher. Dans vos dveloppements, certaines classes nhriteront pas de classes natives et nauront pas la possibilit de diffuser des vnements par dfaut.

11 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

Prenons le cas dune classe nomme XMLLoader devant charger des donnes XML. Nous souhaitons indiquer la fin du chargement des donnes en diffusant un vnement appropri. Le code suivant illustre le contenu de la classe :
package { import flash.events.Event; public class XMLLoader { public function XMLLoader () { // code non indiqu } public function charge ( pXML:String ):void { // code non indiqu } public function chargementTermine ( pEvt:Event ):void { dispatchEvent ( new Event ( XMLLoader.COMPLETE ) ); } } }

Si nous tentons de compiler cette classe, un message derreur nous avertira quaucune mthode dispatchEvent nexiste au sein de la classe. Afin dobtenir les capacits de diffusion la classe XMLLoader hrite de la classe EventDispatcher :
package { import flash.events.Event; import flash.events.EventDispatcher; public class XMLLoader extends EventDispatcher { public static const COMPLETE:String = "complete";

12 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

public function XMLLoader () { // code non indiqu } public function charge ( pXML:String ):void { // code non indiqu } public function chargementTermine ( pEvt:Event ):void { dispatchEvent ( new Event ( XMLLoader.COMPLETE ) ); } } }

En sous classant EventDispatcher, la classe XMLLoader peut dsormais diffuser des vnements. Afin dcouter lvnement XMLLoader.COMPLETE, nous crivons le code suivant :
// cration d'une instance de XMLLoader var chargeurXML:XMLLoader = new XMLLoader(); // chargement des donnes chargeurXML.charge ("news.xml"); // coute de l'vnement XMLLoader.COMPLETE chargeurXML.addEventListener ( XMLLoader.COMPLETE, chargementTermine ); // fonction couteur function chargementTermine ( pEvt:Event ):void { trace( "donnes charges"); }

Lorsque nous appelons la mthode charge, les donnes XML sont charges. Une fois le chargement termin nous diffusons lvnement XMLLoader.COMPLETE. Une partie du code de la classe XMLLoader nest pas montr car nous navons pas encore trait la notion de chargement externe. Nous

13 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

reviendrons sur cette classe au cours du chapitre 14 intitul Chargement et envoi de donnes afin de la complter. Bien que cette technique soit efficace, elle ne rvle pas tre la plus optimise. Comme nous lavons vu lors du chapitre 8 intitul Programmation oriente objet, ActionScript nintgre pas dhritage multiple. Ainsi, en hritant de la classe EventDispatcher la classe XMLLoader ne peut hriter dune autre classe. Nous cassons la chane dhritage. Pire encore, si la classe XMLLoader devait tendre une autre classe, nous ne pourrions tendre en mme temps EventDispatcher. Dans un scnario comme celui-ci nous allons utiliser une notion essentielle de la programmation oriente objet aborde au cours du chapitre 8. Peut tre avez vous dj une ide ?

Stocker EventDispatcher
Afin de bien comprendre cette nouvelle notion, nous allons nous attarder sur la classe Administrateur dveloppe lors du chapitre 8. Voici le code de la classe Administrateur :
package { // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); } // mthode permettant l'administrateur de se prsenter override public function sePresenter ( ):void { // dclenche la mthode surcharge super.sePresenter(); trace("Je suis modrateur"); } // mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void

14 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

{ trace ("Kick " + pJoueur ); } // mthode permettant de jouer un son public function jouerSon ( ):void { trace("Joue un son"); } } }

En dcouvrant la notion dvnements personnaliss nous dcidons de diffuser un vnement lorsquun joueur est supprim de la partie. Il faudrait donc que la mthode kickJoueur puisse appeler la mthode dispatchEvent, ce qui est impossible pour le moment. La classe Administrateur ne peut par dfaut diffuser des vnements, nous tentons donc dtendre la classe EventDispatcher. Nous sommes alors confronts un problme, car la classe Administrateur tend dj la classe Joueur. Comment allons nous faire, sommes nous rellement bloqus ? Souvenez vous, nous avons vu au cours du chapitre 8 une alternative lhritage. Cette technique dcrivait une relation de type possde un au lieu dune relation de type est un . En rsum, au lieu dtendre une classe pour hriter de ses capacits nous allons crer une instance de celle-ci au sein de la classe et dlguer les fonctionnalits. Ainsi au lieu de sous classer EventDispatcher nous allons stocker une instance de la classe EventDispatcher et dlguer la gestion des vnements celle-ci :
package { import flash.events.EventDispatcher; // l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur { // stocke l'instance d'EventDispatcher private var diffuseur:EventDispatcher;

15 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); // cration d'une instance d'EventDispatcher diffuseur = new EventDispatcher(); } // mthode permettant l'administrateur de se prsenter override public function sePresenter ( ):void { // dclenche la mthode surcharge super.sePresenter(); trace("Je suis modrateur"); } // mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { // code grant la dconnexion du joueur concern trace ("Kick " + pJoueur ); } // mthode permettant de jouer un son public function jouerSon ( ):void { trace("Joue un son"); } } }

Bien entendu, la classe Administrateur ne possde pas pour le moment les mthodes dispatchEvent, addEventListener, et removeEventListener. Cest nous de les implmenter, pour cela nous implmentons linterface flash.events.IEventDispatcher :
package { import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.events.Event;

16 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

// l'hritage est traduit par le mot cl extends public class Administrateur extends Joueur implements IEventDispatcher { // stocke l'instance d'EventDispatcher private var diffuseur:EventDispatcher; public function Administrateur ( pPrenom:String, pNom:String, pAge:int, pVille:String ) { super ( pPrenom, pNom, pAge, pVille ); // cration d'une instance d'EventDispatcher diffuseur = new EventDispatcher(); } // mthode permettant l'administrateur de se prsenter override public function sePresenter ( ):void { // dclenche la mthode surcharge super.sePresenter(); trace("Je suis modrateur"); } // mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { // code grant la dconnexion du joueur concern trace ("Kick " + pJoueur ); } // mthode permettant de jouer un son public function jouerSon ( ):void { trace("Joue un son"); } public function addEventListener( type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false ):void { diffuseur.addEventListener( type, listener, useCapture, priority, useWeakReference );

17 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

} public function dispatchEvent( event:Event ):Boolean { return diffuseur.dispatchEvent( event ); } public function hasEventListener( type:String ):Boolean { return diffuseur.hasEventListener( type ); } public function removeEventListener( type:String, listener:Function, useCapture:Boolean=false ):void { diffuseur.removeEventListener( type, listener, useCapture ); } public function willTrigger( type:String ):Boolean { return diffuseur.willTrigger( type ); } } }

Afin de rendre notre classe Administrateur diffuseur dvnements nous implmentons linterface IEventDispatcher. Chacune des mthodes dfinissant le type EventDispatcher doivent donc tre dfinies au sein de la classe Administrateur. Nous dfinissons une constante de classe KICK_JOUEUR, stockant le nom de lvnement :
public static const KICK_JOUEUR:String = "deconnecteJoueur";

Puis nous modifions la mthode kickJoueur afin de diffuser un vnement Administrateur.KICK_JOUEUR :


// mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { // code grant la dconnexion du joueur concern // diffuse l'vnement Administrateur.KICK_JOUEUR

18 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

dispatchEvent ( new Event ( Administrateur.KICK_JOUEUR ) ); }

Dans le code suivant, nous crons un modrateur et un joueur. Le joueur est supprim de la partie, lvnement Administrateur.KICK_JOUEUR est bien diffus :
var monModo:Administrateur "Los Angeles"); = new Administrateur("Michael", "Jackson", 48,

// coute l'vnement Administrateur.KICK_JOUEUR monModo.addEventListener (Administrateur.KICK_JOUEUR, deconnexionJoueur ); var premierJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit"); // un joueur est supprim de la partie monModo.kickJoueur ( premierJoueur ); // fonction couteur function deconnexionJoueur ( pEvt:Event ):void { trace( "Un joueur a quitt la partie !" ); }

Grce la composition, nous avons pu rendre la classe Administrateur diffuseur dvnements. Lhritage nest donc pas la seule alternative permettant de rendre une classe diffuseur dvnements.

A retenir
Lorsque nous ne pouvons pas tendre la classe EventDispatcher nous stockons une instance de celle-ci et dlguons les fonctionnalits. Linterface IeventDispatcher permet une implmentation obligatoire des diffrentes mthodes ncessaires la diffusion dvnements.

Passer des informations


La plupart des vnements diffuss contiennent diffrentes informations relatives ltat de lobjet diffusant lvnement. Nous avons vu lors du chapitre 6 intitul Intractivit que les classes MouseEvent ou KeyboardEvent possdaient des proprits renseignant sur ltat de lobjet diffusant lvnement. Dans lexercice prcdent, la classe Administrateur diffuse un vnement Administrateur.KICK_JOUEUR mais celui-ci ne contient aucune information. Il serait intressant que celui-ci nous renseigne sur le joueur supprim. De la mme manire la classe 19 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

XMLLoader pourrait diffuser un vnement XMLoader.COMPLETE

contenant le flux XML charg afin quil soit facilement utilisable par les couteurs.

Afin de passer des informations lors de la diffusion dun vnement nous devons tendre la classe Event afin de crer un objet vnementiel spcifique lvnement diffus. En ralit, nous nous plions au modle dfinit par ActionScript 3. Lorsque nous coutons un vnement li la souris nous nous dirigeons instinctivement vers la classe flash.events.MouseEvent. De la mme manire pour couter des vnements lis au clavier nous utilisons la classe flash.events.KeyboardEvent. De manire gnrale, il convient de crer une classe vnementielle pour chaque classe devant diffuser des vnements contenant des informations particulires. Nous allons donc tendre la classe Event et crer une classe nomme AdministrateurEvent :
package { import flash.events.Event; public class AdministrateurEvent extends Event { public static const KICK_JOUEUR:String = "deconnecteJoueur"; public function AdministrateurEvent ( type:String, bubbles:Boolean=false, cancelable:Boolean=false ) { // initialisation du constructeur de la classe Event super( type, bubbles, cancelable ); } // la mthode clone doit tre surcharge public override function clone ():Event { return new AdministrateurEvent ( type, bubbles, cancelable ) } // la mthode toString doit tre surcharge public override function toString ():String {

20 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

return '[AdministrateurEvent type="'+ type +'" bubbles=' + bubbles + ' cancelable=' + cancelable + ']'; } } }

Puis nous utilisons une instance de cette classe afin de diffuser lvnement :
// mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { // code grant la dconnexion du joueur concern // diffuse l'venement AdministrateurEvent.KICK_JOUEUR dispatchEvent ( new AdministrateurEvent ( AdministrateurEvent.KICK_JOUEUR ) ); }

Il est important de noter que jusqu prsent nous navions diffus des vnements quavec la classe Event. Lorsque nous dfinissons une classe vnementielle spcifique nous stockons la constante de classe dans celle-ci. Ainsi au lieu de cibler le nom de lvnement sur la classe diffusant lvnement :
// coute l'vnement Administrateur.KICK_JOUEUR monModo.addEventListener ( Administrateur.KICK_JOUEUR, joueurQuitte );

Nous prfrerons stocker le nom de lvnement au sein dune constante de la classe vnementielle :
// coute l'vnement AdministrateurEvent.KICK_JOUEUR monModo.addEventListener ( AdministrateurEvent.KICK_JOUEUR, joueurQuitte );

A ce stade, aucune information ne transite par lobjet vnementiel AdministrateurEvent. Afin de stocker des donnes supplmentaires nous dfinissons les proprits voulues au sein de la classe puis nous affectons leur valeur selon les paramtres passs durant linstanciation de lobjet vnementiel :
package { import flash.events.Event; public class AdministrateurEvent extends Event { public static const KICK_JOUEUR:String = "deconnecteJoueur"; public var joueur:Joueur;

21 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

public function AdministrateurEvent ( type:String, bubbles:Boolean=false, cancelable:Boolean=false, pJoueur:Joueur=null ) { // initialisation du constructeur de la classe Event super( type, bubbles, cancelable ); // stocke le joueur supprim joueur = pJoueur; } // la mthode clone doit tre surcharge public override function clone ():Event { return new AdministrateurEvent ( type, bubbles, cancelable ) } // la mthode toString doit tre surcharge public override function toString ():String { return "[AdministrateurEvent type : " + type +", bubbles + ", cancelable : " + cancelable + "]"; } } } bubbles : " +

Puis nous modifions la mthode kickJoueur de la classe Administrateur afin de passer en paramtre le joueur supprim :
// mthode permettant de supprimer un joueur de la partie public function kickJoueur ( pJoueur:Joueur ):void { // code grant la dconnexion du joueur concern // diffuse l'vnement Administrateur.KICK_JOUEUR dispatchEvent ( new AdministrateurEvent ( AdministrateurEvent. KICK_JOUEUR, false, false, pJoueur ) ); }

Lorsque lvnement est diffus, la fonction couteur accde la proprit joueur afin de savoir quel joueur a quitt la partie :
var monModo:Administrateur "Los Angeles"); = new Administrateur("Michael", "Jackson", 48,

// coute l'vnement Administrateur.KICK_JOUEUR monModo.addEventListener ( AdministrateurEvent.KICK_JOUEUR, joueurQuitte ); var premierJoueur:Joueur = new Joueur ("Bobby", "Womack", 66, "Detroit");

22 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

// un joueur est supprim de la partie monModo.kickJoueur ( premierJoueur ); // fonction couteur function joueurQuitte ( pEvt:AdministrateurEvent ):void { // affiche : [AdministrateurEvent type="deconnecteJoueur" bubbles=false cancelable=false] trace( pEvt ); // affiche Bobby a quitt la partie ! trace( pEvt.joueur.prenom + " a quitt la partie !"); }

En testant le code prcdent, fonction couteur joueurQuitte est notifi de lvnement AdministrateurEvent.KICK_JOUEUR et reoit en paramtre un objet vnementiel de type AdministrateurEvent. La proprit joueur retourne le joueur supprim de la partie, nous navons plus qu accder aux proprits voulues.

A retenir
Afin de passer des paramtres lors de la diffusion dun vnement, nous devons obligatoirement tendre la classe Event. Les classes et sous-classes dEvent reprsentent les objets vnementiels diffuss.

Menu et vnement personnalis


En fin de chapitre prcdent nous avons dvelopp un menu entirement dynamique. Nous ne lavions pas totalement finalis car il nous manquait la notion dvnements personnaliss. Souvenez-vous, nous avions besoin de diffuser un vnement qui contiendrait le nom du SWF li au bouton cliqu. Au sein dun rpertoire evenements lui-mme plac au sein du rpertoire org nous crons une classe ButtonEvent :
package org.bytearray.evenements { import flash.events.Event; public class ButtonEvent extends Event {

23 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

public static const CLICK:String = "buttonClick"; public var lien:String; public function ButtonEvent ( type:String, bubbles:Boolean=false, cancelable:Boolean=false, pLien:String=null ) { // initialisation du constructeur de la classe Event super( type, bubbles, cancelable ); // stocke le lien li au bouton cliqu lien = pLien; } // la mthode clone doit tre surcharge public override function clone ():Event { return new ButtonEvent ( type, bubbles, cancelable, lien ) } // la mthode toString doit tre surcharge public override function toString ():String { return '[ButtonEvent type="'+ type +'" bubbles=' + bubbles + ' eventPhase='+ eventPhase + ' cancelable=' + cancelable + ']'; } } }

Dans le constructeur nous ajoutons la ligne suivante afin dcouter lvnement MouseEvent.CLICK :
// coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.CLICK, clicSouris );

Nous dfinissons la mthode couteur clicSouris qui diffuse lvnement ButtonEvent.CLICK en passant le SWF correspondant :
private function clicSouris ( pEvt:MouseEvent ):void { // diffusion de l'vnement ButtonEvent.CLICK dispatchEvent ( new ButtonEvent ( ButtonEvent.CLICK, true, false, swf

) ); }

24 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

Puis nous coutons lvnement ButtonEvent.CLICK auprs de chaque bouton, nous utilisons la phase de capture afin doptimiser le code :
// import de la classe Bouton import org.bytearray.ui.Button; import org.bytearray.evenements.ButtonEvent; // tableau associatif contenant les donnes var donnees:Array = new Array(); donnees.push ( { legende : "Accueil", vitesse : 1, swf : "accueil.swf", couleur : 0x999900 } ); donnees.push ( { legende : "Photos", vitesse : 1, swf : "photos.swf", couleur : 0x881122 } ); donnees.push ( { legende : "Blog", vitesse : 1, swf : "blog.swf", couleur : 0x995471 } ); donnees.push ( { legende : "Liens", vitesse : 1, swf : "liens.swf", couleur : 0xCC21FF } ); donnees.push ( { legende : "Forum", vitesse : 1, swf : "forum.swf", couleur : 0x977821 } ); // nombre de rubriques var lng:int = donnees.length; // conteneur du menu var conteneurMenu:Sprite = new Sprite(); addChild ( conteneurMenu ); for (var i:int = 0; i< lng; i++ ) { // rcupration des infos var legende:String = donnees[i].legende; var couleur:Number = donnees[i].couleur; var vitesse:Number = donnees[i].vitesse; var swf:String = donnees[i].swf; // cration des boutons var monBouton:Button = new Button( 60, 120, swf, couleur, vitesse, legende ); // positionnement monBouton.y = 50 * i; // ajout la liste d'affichage conteneurMenu.addChild ( monBouton ); } // coute de l'vnement ButtonEvent.CLICK pour la phase de capture conteneurMenu.addEventListener ( ButtonEvent.CLICK, clicBouton, true ); function clicBouton ( pEvt:ButtonEvent ):void { // affiche : /*accueil.swf photos.swf

25 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

blog.swf liens.swf */ trace( pEvt.lien ); }

Lorsque lvnement ButtonEvent.CLICK est diffus, la fonction clicBouton est dclenche. La proprit lien de lobjet vnementiel de type ButtonEvent permet de charger le SWF correspondant. Celle-ci pourrait contenir dans une autre application une URL atteindre lorsque lutilisateur clique sur un bouton. Il serait tout fait envisageable de dfinir de nouvelles proprits au sein de la classe ButtonEvent telles legende, couleur ou vitesse ou autres afin de passer les caractristiques de chaque bouton. Voici le code complet de la classe Button :
package org.bytearray.ui { import flash.display.Shape; import flash.display.Sprite; import flash.text.Font; import flash.text.TextFormat; import org.events.ButtonEvent; // import des classes lies Tween au mouvement import fl.transitions.Tween; import fl.transitions.easing.Bounce; // import de la classe MouseEvent import flash.events.MouseEvent; // import de la classe TextField et TextFieldAutoSize import flash.text.TextField; import flash.text.TextFieldAutoSize; public class Button extends Sprite { // stocke le fond du bouton private var fondBouton:Shape; // stocke l'objet Tween pour les diffrents tat du bouton private var etatTween:Tween; // stocke les rfrences aux boutons private static var tableauBoutons:Array = new Array(); // stocke la couleur en cours du bouton private var couleur:Number; // stocke la vitesse d'ouverture de chaque bouton private var vitesse:Number; // lgende du bouton private var legende:TextField; // formatage des lgendes private var formatage:TextFormat; // swf associ private var swf:String;

26 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

public function Button ( pWidth:Number, pHeight:Number, pSWF:String, pCouleur:Number, pVitesse:Number, pLegende:String ) { // ajoute chaque instance au tableau Button.tableauBoutons.push ( this ); // cration du fond du bouton fondBouton = new Shape(); // ajout la liste d'affichage addChild ( fondBouton ); // cre le champ texte legende = new TextField(); // redimensionnement automatique du champ texte legende.autoSize = TextFieldAutoSize.LEFT; // ajout la liste d'affichage addChild ( legende ); // affecte la lgende legende.text = pLegende; // active l'utilisation de police embarque legende.embedFonts = true; // cre un objet de formatage formatage = new TextFormat(); // taille de la police formatage.size = 12; // instanciation de la police embarque var police:MaPolice = new MaPolice(); // affectation de la police au formatage formatage.font = police.fontName; // affectation du formatage au champ texte legende.setTextFormat ( formatage ); // rend le champ texte non selectionnable legende.selectable = false; // stocke la couleur passe en paramtre couleur = pCouleur; // stocke le nom du SWF swf = pSWF; // dessine le bouton fondBouton.graphics.beginFill ( couleur, 1 ); fondBouton.graphics.drawRect ( 0, 0, pWidth, pHeight ); // activation du mode bouton buttonMode = true; // dsactivation des objets enfants mouseChildren = false;

27 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

// affecte la vitesse passe en paramtre setVitesse ( pVitesse ); // cration de l'objet Tween etatTween = new Tween ( fondBouton, "scaleX", Bounce.easeOut, 1, 1, vitesse, true ); // coute de l'vnement MouseEvent.ROLL_OVER addEventListener ( MouseEvent.ROLL_OVER, survolSouris ); // coute de l'vnement MouseEvent.CLICK addEventListener ( MouseEvent.CLICK, clicSouris ); } private function clicSouris ( pEvt:MouseEvent ):void { // diffusion de l'vnement ButtonEvent.CLICK dispatchEvent ( new ButtonEvent ( ButtonEvent.CLICK, true, false,

swf ) ); }

// dclench lors du clic sur le bouton private function survolSouris ( pEvt:MouseEvent ):void { // stocke la longueur du tableau var lng:int = Button.tableauBoutons.length;

for (var i:int = 0; i<lng; i++ ) Button.tableauBoutons[i].fermer(); // dmarrage de l'animation etatTween.continueTo ( 2, vitesse ); } // mthode permettant de refermer le bouton private function fermer ():void { // referme le bouton etatTween.continueTo ( 1, vitesse );

} // gre l'affectation de la vitesse public function setVitesse ( pVitesse:Number ):void { // affecte la vitesse if ( pVitesse >= 1 && pVitesse <= 10 ) vitesse = pVitesse; else {

28 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 10 Diffusion dvnements personnaliss version 0.1.1

trace("Erreur : Vitesse non correcte, la valeur doit tre comprise entre 1 et 10"); vitesse = 1; } } } }

La diffusion dvnements personnaliss est un point essentiel dans tout dveloppement orient objet ActionScript. En diffusant nos propres vnements nous rendons nos objets compatibles avec le modle vnementiel ActionScript 3 et facilement rutilisables. En livrant une classe un dveloppeur tiers, celui-ci regardera en premier lieu les capacits offertes par celle-ci puis sattardera sur les diffrents vnements diffuss pour voir comment dialoguer facilement avec celle-ci.

A retenir
Nous pouvons dfinir autant de proprits que nous le souhaitons au sein de lobjet vnementiel diffus.

Nous allons nous intresser maintenant la notion de classe du document. Cette nouveaut apporte par Flash CS3 va nous permettre de rendre nos dveloppements ActionScript 3 plus aboutis. En avant pour le chapitre 11 intitul Classe du document.

29 / 29
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

11
Classe du document

INTERETS ............................................................................................................... 1 LA CLASSE MAINTIMELINE ............................................................................. 2 CLASSE DU DOCUMENT ..................................................................................... 3 LIMITATIONS DE LA CLASSE SPRITE ........................................................................ 6 ACTIONS DIMAGES ................................................................................................. 8 DECLARATION AUTOMATIQUE DES OCCURRENCES.............................. 9 DCLARATION MANUELLE DES OCCURRENCES ..................................................... 11 AJOUTER DES FONCTIONNALITES .............................................................. 12 INITIALISATION DE LAPPLICATION .......................................................... 15 ACCS GLOBAL LOBJET STAGE ......................................................................... 18 AUTOMATISER LACCS GLOBAL LOBJET STAGE .............................................. 21

Intrts
Depuis lintroduction dActionScript 2, les dveloppeurs avaient pour habitude de crer une classe servant de point dentre leur application. Celle-ci instanciait dautres objets ayant gnralement besoin daccder au scnario principal. Il fallait donc passer une rfrence au scnario principal. Il ntait donc pas rare de trouver sur une image du scnario de lapplication le code suivant :
var monApplication:Application = new Application ( this );

Lapplication pouvait aussi tre initialise laide dune simple mthode statique :
Application.initialise ( this );

1 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Dautres dveloppeurs utilisaient une autre technique, consistant changer le contexte dexcution de la classe principale avec le code suivant :
this.__proto__= Application.prototype; Application['apply']( this , null );

En utilisant le mot-cl this au sein de linstance de la classe Application nous faisions alors rfrence au scnario principal. ActionScript 3 intgre une nouvelle fonctionnalit appele Classe du document permettant dviter davoir recours ces diffrentes astuces.

La classe MainTimeline
Comme nous lavons vu lors du chapitre 4 intitul La liste daffichage le lecteur Flash est constitu dun objet Stage situ au sommet de la liste daffichage. Lorsquun SWF est lu dans le lecteur, celui-ci ajoute le scnario principal du SWF charg en tant quenfant de lobjet Stage :

Figure 11-1. Classe du document MainTimeline. Par dfaut le scnario dun SWF est reprsent par la classe MainTimeline. Nous pouvons nous en rendre compte trs facilement. Dans un nouveau document Flash CS3, testez le code suivant sur le scnario principal :
// affiche : [object MainTimeline] trace( this );

Nous allons voir que nous ne sommes pas limits cette classe. 2 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

A retenir
Par dfaut le scnario principal est reprsent par la classe MainTimeline.

Classe du document
Flash CS3 intgre une nouvelle fonctionnalit permettant dutiliser une instance de sous classe graphique comme scnario principal. Attention, celle-ci doit hriter obligatoirement dune classe graphique telle flash.display.Sprite ou flash.display.MovieClip. Il est techniquement possible dutiliser une sous classe de flash.display.Shape mais il serait impossible dajouter un objet graphique sur le scnario principal, la classe Shape ntant pas une sous classe de flash.display.DisplayObjectContainer. Si nous utilisons comme classe du document une sous-classe graphique nomme Application, Flash ajoute aussitt une instance de celle-ci comme scnario principal. La figure 11-2 illustre lide :

Figure 11-2. Classe du document Application. Si nous ne spcifions aucune classe, le lecteur cre une instance de MainTimeline.

3 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Nous allons crer un nouveau document Flash CS3 puis crer a cot un rpertoire org dans lequel nous crons un rpertoire document. Au sein du rpertoire document nous dfinissons une classe Application dont voici le code :
package org.bytearray.document { import flash.display.Sprite; public class Application extends Sprite { public function Application () { // affiche : [object Application] trace( this ); } } }

Grce la notion de classe du document, le mot-cl this fait ici rfrence linstance de la classe Application, donc au scnario principal. Afin de lier cette classe du document notre document Flash nous utilisons le champ Classe du document du panneau Inspecteur de proprits illustr en figure 11-3 :

Figure 11-3. Champ Classe du document. Nous spcifions le paquetage complet de la classe Application. Il est intressant de noter que linstanciation de la classe Application est transparente. Lorsque lanimation est charge, le lecteur Flash cre automatiquement une instance de la classe Application et lajoute en tant quenfant de lobjet Stage :
package org.bytearray.document {

4 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

import flash.display.Sprite; public class Application extends Sprite { public function Application () { // affiche : [object Stage] trace( parent ); // affiche : [object Stage] trace( stage ); // affiche : [object Application] trace( this ); } } }

Si nous coutons lvnement Event.ADDED_TO_STAGE nous remarquons que celui-ci est bien diffus :
package org.bytearray.document { import flash.display.Sprite; import flash.events.Event; public class Application extends Sprite { public function Application () { // coute de l'vnement Event.ADDED_TO_STAGE // est diffus automatiquement car le lecteur // ajoute la liste d'affichage une instance // de la classe Application addEventListener ( Event.ADDED_TO_STAGE, ajoutAffichage ); } private function ajoutAffichage ( pEvt:Event ):void { // affiche : [Event type="addedToStage" bubbles=true cancelable=false eventPhase=2] trace( pEvt ); }

5 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

} }

Il faut donc considrer la classe du document comme le point dentre de notre projet. Nous avons dans notre exemple utilis une sous-classe de Sprite comme classe du document. Nous allons voir dans quelle mesure son utilisation est limite dans ce contexte.

A retenir

Classe du document de linspecteur de proprits.

Pour affecter une classe du document, nous utilisons le champ

Linstanciation de classe du document est transparente, le lecteur sen charge automatiquement. Linstance de la classe du document devient le scnario principal.

Limitations de la classe Sprite


Rappelez-vous, lors du chapitre 4 intitul La liste daffichage nous avons dcouvert que la classe Sprite ne disposait pas de scnario. Ainsi, lorsque la classe du document est une sous-classe de Sprite il est impossible de faire appel aux diffrentes mthodes de manipulation du scnario telles gotoAndPlay, gotoAndStop ou autres. Le code suivant ne peut tre compil :
package org.bytearray.document { import flash.display.Sprite; public class Application extends Sprite { public function Application () { gotoAndStop ( "intro" ); } } }

Lerreur suivante est gnre la compilation :


1180: Appel une mthode qui ne semble pas dfinie, gotoAndStop.

6 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Nous utiliserons donc une sous-classe de Sprite comme classe du document lorsque nous naurons pas besoin de scnario. Nous pourrions penser quil sagit de la seule limitation de la classe Sprite, mais il est aussi impossible dajouter du code sur les images du scnario. Les lignes suivantes sont poses sur limage 1 de notre animation :
var monClip:MovieClip = new MovieClip();

Lerreur suivante est gnre :


1180: Appel une mthode qui ne semble pas dfinie, addFrameScript.

Une simple ligne de code commente empche la compilation :


//var monClip:MovieClip = new MovieClip();

La puissance de la classe du document rside dans linteraction entre le code des images et les fonctionnalits apportes par la classe du document. Lutilisation dune sous-classe de Sprite comme classe du document est donc gnralement dconseille. En tendant la classe MovieClip, nous pouvons ajouter des actions dimages et manipuler le scnario. Nous modifions la classe Application afin dobtenir le code suivant :
package org.bytearray.document { import flash.display.MovieClip; public class Application extends MovieClip { public function Application () {

} } }

Dans le cas dune application ncessitant un minimum danimations et de code sur les images nous prfrerons tendre la classe MovieClip.

7 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Actions dimages
Toute action dimage sexcute dans le contexte de linstance de la classe du document. Prenons un exemple simple, nous ajoutons une mthode afficheMenu au sein de la classe Application :
package org.bytearray.document { import flash.display.MovieClip; public class Application extends MovieClip { public function Application () { } // mthode de cration du menu public function afficheMenu ( ):void { trace("cration du menu"); } } }

La mthode afficheMenu devient donc une mthode du scnario principal, sur nimporte quelle image nous pouvons lexcuter en plaant le code suivant :
// stop le scnario principal stop(); // appelle la mthode afficheMenu de la classe du document // affiche : cration du menu afficheMenu();

Nous stoppons le scnario principal puis appelons la mthode afficheMenu dfinie au sein de la classe du document. Cette capacit pouvoir dclencher diffrentes fonctionnalits de la classe du document depuis les images rend le dveloppement plus souple. Lorsquun simple effet ou un ensemble de mcanismes complexes doivent tre dclenchs nous lappelons directement depuis le scnario.

A retenir
8 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Lutilisation dune sous classe de Sprite comme classe du document est gnralement dconseille. Nous prfrons MovieClip. gnralement utiliser une sous-classe de

La puissance rside dans linteraction entre les actions dimages et les fonctionnalits de la classe du document.

Dclaration automatique des occurrences


Afin daccder aux objets graphiques depuis la classe du document nous utilisons deux techniques. Comme nous lavons vu au cours du chapitre 9 intitul Etendre les classes natives, loption Dclarer automatiquement les occurrences de scne est active par dfaut dans Flash CS3. Dans lexemple suivant nous crons un symbole clip auquel nous associons une classe Personnage, puis nous posons une occurrence sur la scne, nomme monPersonnage :

Figure 11-4. Occurrence monPersonnage. A la compilation, une proprit monPersonnage est automatiquement ajoute et rfrence loccurrence :
package org.bytearray.document { import flash.display.MovieClip; public class Application extends MovieClip

9 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

{ public function Application () { // affiche : [object Personnage] trace( monPersonnage ); } } }

On pourrait galement recourir la mthode getChildByName :


package org.bytearray.document { import flash.display.MovieClip; public class Application extends MovieClip { public function Application () { // suppression du symbole monPersonnage removeChild ( getChildByName ( "monPersonnage" ) ); } } }

Le code suivant supprime loccurrence monPersonnage pose sur le scnario principal :


package org.bytearray.document { import flash.display.MovieClip; public class Application extends MovieClip { public function Application () { // suppression du symbole monPersonnage removeChild ( monPersonnage ); }

10 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

} }

Lorsque loption Dclarer automatiquement les occurrences de scne est active, le compilateur ajoute automatiquement au sein de la classe conteneur des proprits pointant vers les occurrences cres depuis lenvironnement auteur. Cette dclaration automatique des occurrences empche le dveloppeur de voir quels sont les objets utiliss au sein de la classe et ne facilite pas le dboguage de lapplication. Cest pour cette raison que nous prfrons gnralement dsactiver la dclaration automatique dans un projet gr par des dveloppeurs seulement. A linverse, dans un contexte dinteractions dveloppeurs-designers le graphiste peut tre amen poser un nouvel objet sur la scne et lui donner un nom doccurrence afin denrichir graphiquement lapplication. Si loption Dclarer automatiquement les occurrences de scne est dsactive, le graphiste, ne pourra plus compiler le projet sans que le dveloppeur najoute une proprit au sein de la classe du document correspondant loccurrence ajoute. Cela risque donc de gner le flux de travail au sein dune quipe.

Dclaration manuelle des occurrences


En dsactivant la dclaration automatique des occurrences, nous devons dfinir manuellement au sein de la classe des proprits du mme nom que les occurrences :
package org.bytearray.document { import flash.display.MovieClip; public class Application extends MovieClip { // proprit lie l'occurrence public var monPersonnage:MovieClip public function Application () { // suppression du symbole monPersonnage removeChild ( monPersonnage ); }

11 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

} }

La proprit dfinie manuellement doit obligatoirement tre publique. Si nous la rendons prive laide de lattribut private le compilateur gnre une erreur :
ReferenceError: Error #1056: Impossible de crer la proprit monPersonnage sur org.bytearray.document.Application.

Lorsque le compilateur tente daffecter la proprit monPersonnage afin de la faire pointer vers loccurrence, celle-ci est prive et nest donc pas accessible depuis lextrieur de la classe. Ainsi, les proprits lies aux occurrences doivent toujours tre publique. Si nous dfinissons la proprit et que loption Dclarer les occurrences de scne est active, une erreur la compilation saffiche :
1151: Conflit dans la dfinition monPersonnage dans l'espace de nom internal.

Le compilateur tente de dfinir une proprit pointant vers loccurrence pose sur la scne, mais celui ci rencontre une proprit du mme nom dj dfinie. Un conflit de proprits est gnr.

A retenir
De manire gnrale, il est prfrable de dsactiver la dclaration automatique des occurrences de scne. Dans un contexte dinteractions designers-dveloppeurs, il est prfrable dactiver la dclaration automatique des occurrences. Les proprits lies aux occurrences doivent toujours tre publique.

Ajouter des fonctionnalits


Il faut bien comprendre que lorsquune classe du document est dfinie, son instance reprsente le scnario principal. Nous avons dcouvert lors du chapitre 4 intitul La liste daffichage diffrents comportements lis laccs aux objets graphiques. Souvenez vous en ActionScript 2 nous pouvions crire le code suivant :
gotoAndStop (20); monPersonnage._alpha = 100;

Mme si loccurrence monPersonnage ntait prsente qu limage 20, nous pouvions depuis nimporte quelle image y accder. Cela nest plus vrai en ActionScript 3.

12 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Afin daccder correctement lobjet monPersonnage nous pouvons utiliser lvnement Event.RENDER. Cet vnement est dclench lorsque le lecteur a fini de rendre les donnes vectorielles. Afin quil soit diffus nous devons appeler la mthode invalidate de la classe Stage. Ainsi, nous pourrions accder lobjet monPersonnage avec le code suivant :
// coute de l'vnement Event.RENDER addEventListener ( Event.RENDER, miseAJour ); function miseAJour ( pEvt:Event ):void { monPersonnage.alpha = 1; // supprime l'coute removeEventListener( Event.RENDER, miseAJour ); } // dplace le tte de lecture gotoAndStop (20); // force le rafrachissement stage.invalidate();

La fonction miseAJour est dclenche lorsque le lecteur a termin dafficher les objets graphiques prsents limage 20. Nous pouvons donc accder sans problme loccurrence monPersonnage. Grce la classe du document, nous allons mettre en place une fonctionnalit rendant tout ce traitement transparent. Au sein de la classe Application nous dfinissons une mthode myGotoAndStop :
package org.bytearray.document { import flash.display.MovieClip; import flash.events.Event; public class Application extends MovieClip { // proprits lies l'occurrence public var monPersonnage:MovieClip // proprit permettant lexcution de la fonction de rappel private var rappel:Function; public function Application ()

13 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

{ } // mthode de dplacement de la tte de lecture personnalis public function myGotoAndStop ( pImage:int, pFonction:Function ):void { // coute de l'vnement Event.RENDER addEventListener ( Event.RENDER, miseAJour ); // dplacement de la tte de lecture gotoAndStop ( pImage ); // retourne un objet permettant rappel = pFonction; // force la diffusion de l'vnement Event.RENDER stage.invalidate(); } private function miseAJour ( pEvt:Event ):void { // nous tentons d'appeler la fonction de rappel try { rappel(); // si cela choue, nous affichons un message d'erreur } catch ( pErreur:Error ) { trace("Erreur : La mthode de rappel n'a pas t dfinie"); Event.RENDER // dans tout les cas, nous supprimons l'coute de l'vnement } finally { removeEventListener ( Event.RENDER, miseAJour ); } } } }

Ainsi lorsque nous souhaitons retrouver le comportement de la mthode gotoAndStop des anciennes versions dActionScript nous crivons le code suivant :
// dplace la tte de lecture en image 10 // lorsque tout les objets sont disponibles // la fonction actifAccessible est dclenche myGotoAndStop ( 10, actifAccessible );

14 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

function actifAccessible ():void { // affiche : [object Personnage] trace(monPersonnage); // affecte la rotation de l'occurrence monPersonnage.rotation = 90; } stop();

La fonction actifAccessible est dclenche automatiquement lorsque les occurrences de limage 20 sont disponibles. Grce au bloc try, catch, finally nous grons les erreurs lexcution. Au cas o lutilisateur oublierait de dfinir la fonction associe :
// dplace la tte de lecture en image 10 // lorsque tout les objets sont disponibles // la fonction actifAccessible est dclenche // affiche : Erreur : La mthode de rappel n'a pas t dfinie myGotoAndStop ( 10, actifAccessible ); // fonction non dfinie var actifAccessible:Function; stop();

Lerreur lexcution est gre et nous affichons le message indiquant loublie de dfinition. Grce la classe du document nous pouvons ajouter des fonctionnalits au scnario principal ou modifier certains comportements. Nous tendons les capacits du scnario en dfinissant de nouvelles mthodes au sein de la classe du document.

A retenir
Il est prfrable de dsactiver la dclaration automatique des occurrences. Les proprits lies aux occurrences doivent toujours tre publique.

Initialisation de lapplication
Nous allons supprimer loccurrence nomme monPersonnage sur la scne, puis initialiser lapplication en instanciant par programmation linstance de Personnage.

15 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Afin dinitialiser notre application, nous utilisons simplement le constructeur de la classe du document. Dans le code suivant nous instancions puis affichons le symbole Personnage :
package org.bytearray.document { import flash.display.MovieClip; import flash.events.Event; public class Application extends MovieClip { // proprit permettant lexcution de la fonction de rappel private var rappel:Function; public function Application () { // cration du symbole Personnage var autrePersonnage:Personnage = new Personnage(); // ajout la liste d'affichage addChild ( autrePersonnage ); // l'occurrence est centre autrePersonnage.x = (stage.stageWidth - autrePersonnage.width)/2; autrePersonnage.y = (stage.stageHeight autrePersonnage.height)/2; } // mthode de dplacement de la tte de lecture personnalis public function myGotoAndStop ( pImage:int, pFonction:Function ):void { // coute de l'vnement Event.RENDER addEventListener ( Event.RENDER, miseAJour ); // dplacement de la tte de lecture gotoAndStop ( pImage ); // retourne un objet permettant rappel = pFonction; // force la diffusion de l'vnement Event.RENDER stage.invalidate(); } private function miseAJour ( pEvt:Event ):void { // nous tentons d'appeler la fonction de rappel try {

16 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

rappel(); // si cela choue, nous affichons un message d'erreur } catch ( pErreur:Error ) { trace("Erreur : La mthode de rappel n'a pas t dfinie"); // dans tout les cas, nous supprimons // l'coute de l'vnement Event.RENDER } finally { removeEventListener ( Event.RENDER, miseAJour ); } } } }

La figure 11-6 illustre le rsultat :

Figure 11-6. Symbole Personnage affich. Ne remarquez vous pas quelque chose de particulier ? Nous normalement couter lvnement Event.ADDED_TO_STAGE pour pouvoir accder lobjet Stage de manire scurise. Dans le cas de la classe du document, le code est 17 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

devrions

Chapitre 11 Classe du document version 0.1.2

excut une fois linstance de la classe du document ajoute la liste daffichage, ce qui est automatique et assur par le lecteur. Lobjet Stage est donc directement accessible par la proprit stage depuis le constructeur.

Accs global lobjet Stage


Il serait intressant dutiliser la classe du document comme point daccs global lobjet Stage. Pour cela, nous dfinissons une proprit statique GLOBAL_STAGE au sein de la classe Application :
// point d'accs l'objet Stage public static var GLOBAL_STAGE:Stage;

Puis nous modifions le constructeur :


public function Application () { // affecte une rfrence l'objet Stage Application.GLOBAL_STAGE = stage; }

En ciblant la proprit Application.GLOBAL_STAGE, nimporte quel objet obtiendra une rfrence lobjet Stage. Afin dillustrer cette fonctionnalit, nous allons dfinir au sein dun rpertoire ui une classe Fenetre :
package org.bytearray.ui { import flash.display.Shape; public class Fenetre extends Shape { public function Fenetre ( pLargeur:Number, pHauteur:Number, pRayon:Number, pCouleur:Number ) { graphics.beginFill ( pCouleur ); graphics.drawRoundRect ( 0, 0, pLargeur, pHauteur, pRayon ); } } }

Cette classe cre une forme rectangulaire illustrant une simple fentre. 18 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

En pointant vers la proprit Application.GLOBAL_STAGE nous obtenons un point daccs global lobjet Stage depuis nimporte quelle instance de la classe Fenetre :
package org.bytearray.ui { import flash.display.Shape; import org.bytearray.document.Application; public class Fenetre extends Shape { public function Fenetre ( pLargeur:Number, pHauteur:Number, pRayon:Number, pCouleur:Number ) { graphics.beginFill ( pCouleur ); graphics.drawRoundRect ( 0, 0, pLargeur, pHauteur, pRayon ); // nous centrons la fentre // en utilisant la proprit statique Application.GLOBAL_STAGE nous bnficions d'un point d'accs global l'objet Stage x = (Application.GLOBAL_STAGE.stageWidth - width)/2; y = (Application.GLOBAL_STAGE.stageHeight - height)/2; } } }

Puis nous affichons la fentre :


package org.bytearray.document { import flash.display.MovieClip; import flash.events.Event; import org.bytearray.ui.Fenetre; public class Application extends MovieClip { // point d'accs l'objet Stage public static var GLOBAL_STAGE:Stage; // proprit permettant lexcution de la fonction de rappel private var rappel:Function; public function Application () { addEventListener ( Event.ADDED_TO_STAGE, activation ); }

19 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

private function activation ( pEvt:Event ):void { // affecte une rfrence l'objet Stage Application.GLOBAL_STAGE = stage; // cration de la fentre var maFenetre:Fenetre = new Fenetre( 250, 250, 8, 0x88CCAA ); // ajout la liste d'affichage addChild ( maFenetre ); } // mthode de dplacement de la tte de lecture personnalis public function myGotoAndStop ( pImage:int, pFonction:Function ):void { // coute de l'vnement Event.RENDER addEventListener ( Event.RENDER, miseAJour ); // dplacement de la tte de lecture gotoAndStop ( pImage ); // retourne un objet permettant rappel = pFonction; // force la diffusion de l'vnement Event.RENDER stage.invalidate(); } private function miseAJour ( pEvt:Event ):void { // nous tentons d'appeler la fonction de rappel try { rappel(); // si cela choue, nous affichons un message d'erreur } catch ( pErreur:Error ) { trace("Erreur : La mthode de rappel n'a pas t dfinie"); Event.RENDER // dans tout les cas, nous supprimons l'coute de l'vnement } finally { removeEventListener ( Event.RENDER, miseAJour ); } } } }

20 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

La figure 11-7 illustre le rsultat :

Figure 11-7. Instance de la classe Fenetre affiche. Grce cette technique nous navons pas besoin dcouter lvnement Event.ADDED_TO_STAGE afin de pouvoir cibler lobjet Stage. Mais comme nous lavons vu prcdemment, il est toujours recommand dcouter en interne lvnement Event.ADDED_TO_STAGE afin daccder lobjet Stage. Cette technique permet donc aux objets non graphiques daccder lobjet Stage de manire simplifie. En revanche, si nous devons rutiliser la classe Fenetre dans un autre projet, nous serons oblig de dfinir une classe du document nomme Application ainsi quune proprit statique GLOBAL_STAGE rfrenant lobjet Stage. Afin dautomatiser ce processus, nous allons utiliser lhritage.

Automatiser laccs global lobjet Stage


Pour pouvoir bnficier dun accs global lobjet Stage automatiquement dans tous nos projets, nous avons plusieurs possibilits. La premire, comme nous venons de voir linstant consiste dfinir dans la classe du document de chaque projet, une proprit statique 21 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

accessible de nimporte classe. Permettant ainsi un accs simplifi lobjet Stage. Il serait dommage de devoir recopier ce code dans classe du document, nous prfrerons donc utiliser la solution suivante. Nous savons que la classe Application contient des fonctionnalits pouvant tre ncessaires chaque projet :
Un accs global lobjet Stage Une mthode myGotoAndStop

Afin dhriter de ces fonctionnalits, nous allons utiliser pour chaque projet une classe du document hritant de la classe Application. Il nest pas ncessaire de dupliquer celle-ci dans le rpertoire de classes de chaque projet nous allons lisoler en la plaant dans un rpertoire spcifique global tous nos projets. A la racine de notre disque dur nous crons un rpertoire nomm classes_as3. Puis nous crons rpertoire org contenant un rpertoire abstrait. Au sein de ce dernier nous stockons la classe ApplicationDefaut dont voici le code complet final :
package org.bytearray.abstrait { import flash.display.MovieClip; import flash.events.Event; import flash.display.Stage; public class ApplicationDefaut extends MovieClip { // point d'accs l'objet Stage public static var GLOBAL_STAGE:Stage; // proprit permettant lexcution de la fonction de rappel private var rappel:Function; public function ApplicationDefaut () { // affecte une rfrence l'objet Stage ApplicationDefaut.GLOBAL_STAGE = stage; } // mthode de dplacement de la tte de lecture personnalis public function myGotoAndStop ( pImage:int, pFonction:Function ):void { // coute de l'vnement Event.RENDER

22 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

addEventListener ( Event.RENDER, miseAJour ); // dplacement de la tte de lecture gotoAndStop ( pImage ); // retourne un objet permettant rappel = pFonction; // force la diffusion de l'vnement Event.RENDER stage.invalidate(); } private function miseAJour ( pEvt:Event ):void { // nous tentons d'appeler la fonction de rappel try { rappel(); // si cela choue, nous affichons un message d'erreur } catch ( pErreur:Error ) { trace("Erreur : La mthode de rappel n'a pas t dfinie"); Event.RENDER // dans tout les cas, nous supprimons l'coute de l'vnement } finally { removeEventListener ( Event.RENDER, miseAJour ); } } } }

Afin de spcifier un chemin daccs de classes global tous les projets au sein de Flash CS3, nous cliquons sur Modifier puis Prfrences puis dans la liste nous slectionnons ActionScript.

Figure 11-8. Paramtres dActionScript 3. Une fois cliqu sur le bouton Paramtres dActionScript 3 le panneau indiquant le chemin daccs aux classes saffiche. Comme lillustre la figure 11-9. 23 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Figure 11-9. Chemin daccs aux classes. En cliquant sur licne + nous ajoutons un champ afin de spcifier le chemin daccs au rpertoire contenant les classes globales. Dans notre exemple nous ajoutons une entre contenant le chemin C:\classes_as3. Deux autres lignes sont dj prsentes, la premire indique que le compilateur doit regarder cot du document en cours. Puis la deuxime indique le chemin daccs aux classes natives de Flash. En cliquant sur OK, les classes stockes au sein du rpertoire classes_as3 seront disponibles depuis nimporte quel projet. Nous allons modifier notre application en affectant une classe du document hritant de la classe ApplicationDefaut. Au sein du rpertoire document nous crons une classe Document :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { trace( this ); } }

24 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Puis nous laffectons comme classe du document en cours, comme lindique la figure 11-10 :

Figure 11-10. Affectation de la classe Document. A la compilation [object


Document]

saffiche dans le panneau Sortie.

La classe ApplicationDefaut devient ainsi une classe ne devant tre quhrite. Une sorte de classe abstraite, mme si comme nous lavons vu lors du chapitre 8 intitul Programmation oriente objet, ActionScript 3 ne permet pas la dfinition de vraie classe abstraite. Tous les objets prsents ou non au sein de la liste daffichage devant faire rfrence lobjet Stage devront cibler la proprit
ApplicationDefaut.GLOBAL_STAGE.

Nous modifions donc la classe Fenetre :


package org.bytearray.ui { import flash.display.Shape; import org.bytearray.abstrait.ApplicationDefaut; public class Fenetre extends Shape { public function Fenetre ( pLargeur:Number, pHauteur:Number, pRayon:Number, pCouleur:Number ) { graphics.beginFill ( pCouleur ); graphics.drawRoundRect ( 0, 0, pLargeur, pHauteur, pRayon ); // nous centrons la fentre // en utilisant la proprit statique Application.GLOBAL_STAGE // nous bnficions d'un point d'accs global l'objet Stage x = (ApplicationDefaut.GLOBAL_STAGE.stageWidth - width)/2; y = (ApplicationDefaut.GLOBAL_STAGE.stageHeight - height)/2; } } }

25 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Puis nous modifions la classe Document afin dinstancier la classe Fenetre :


package org.bytearray.document { import flash.events.Event; import org.bytearray.abstrait.ApplicationDefaut; import org.bytearray.ui.Fenetre; public class Document extends ApplicationDefaut { public function Document () { // cration de la fentre var maFenetre:Fenetre = new Fenetre( 250, 250, 8, 0x88CCAA ); // ajout la liste d'affichage addChild ( maFenetre ); } } }

La figure 11-11 illustre le rsultat :

26 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 11 Classe du document version 0.1.2

Figure 11-11. Instance de la classe Fenetre. Souvenez-vous, la sous-classe nhrite pas des proprits statiques de la classe parente. Ainsi, la classe Document nhrite pas de la proprit GLOBAL_STAGE de la classe ApplicationDefaut.

A retenir
La fentre Paramtres dActionScript 3 permet de dfinir un chemin daccs global aux classes.

Nous allons nous intresser au cours du prochain chapitre la gestion des bitmap par programmation en ActionScript 3. Les classes flash.display.Bitmap et flash.display.BitmapData nauront plus de secrets pour vous !

27 / 27
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

12
Programmation Bitmap

BITMAP ET VECTORIELS .................................................................................. 2 COULEURS ............................................................................................................. 3 MANIPULER LES COULEURS .................................................................................... 5 LA CLASSE BITMAPDATA ................................................................................. 7 CODAGE DE COULEURS..................................................................................... 8 GERER LES RESSOURCES AVEC LE PROFILER ......................................... 9 LA CLASSE BITMAP ........................................................................................... 12 RUTILISER LES DONNES BITMAPS ...................................................................... 15 LIBERER LES RESSOURCES ............................................................................ 18 CALCULER LE POIDS DUNE IMAGE EN MEMOIRE ................................................... 26 LIMITATIONS MMOIRE ......................................................................................... 27 IMAGES EN BIBLIOTHEQUE ........................................................................... 28 PEINDRE DES PIXELS ........................................................................................ 30 LIRE DES PIXELS .................................................................................................... 34 ACCROCHAGE AUX PIXELS .................................................................................... 37 LE LISSAGE .......................................................................................................... 38 MISE EN CACHE DES BITMAP A LEXECUTION ....................................... 39 EFFETS PERVERS ................................................................................................... 43 FILTRER UN LMENT VECTORIEL............................................................ 46 FILTRER UNE IMAGE BITMAP ................................................................................. 59 ANIMER UN FILTRE................................................................................................ 61 RENDU BITMAP DOBJET VECTORIELS ..................................................... 63 OPTIMISER LES PERFORMANCES ................................................................ 78 1 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Bitmap et vectoriels
Avant dentamer la dcouverte des fonctionnalits offertes par le lecteur Flash en matire de programmation bitmap, il convient de sattarder sur le concept dimage bitmap et vectorielle. Une image bitmap peut tre considre comme une grille constitue de pixels de couleur spcifique. En zoomant sur une image bitmap nous pouvons apercevoir chaque pixel la constituant.

Figure 12-1. Image bitmap agrandie. A linverse, une image vectorielle nest pas compose de pixels, mais de tracs issus de coordonnes mathmatiques. Le lecteur Flash les interprte et dessine la forme correspondante. La figure 12-2 illustre un exemple de trac vectoriel :

2 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-2. Image vectorielle agrandie. En cas dagrandissement le trac est recalcul, empchant toute pixellisation de limage quelque soit la rsolution ou dimension. En matire de performances, laffichage vectoriel requiert peu de mmoire mais peut ncessiter en cas de tracs complexes une forte sollicitation du processeur.

Couleurs
Lespace de couleur utilis dans Flash est lARVB, chaque couleur repose sur quatre composantes :
Lalpha Le rouge Le vert Le bleu

Les trois composantes de couleurs donnent une combinaison de 16777215 couleurs possibles. Lespace colorimtrique RVB sapproche en ralit du nombre de couleurs maximum que lil de lhomme peut distinguer, ainsi le terme de couleurs vraies est couramment utilis.

3 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Chaque composante est code sur 8 bits soit 1 octet et varie de 0 255. En binaire, une couleur ARVB peut tre reprsente de la manire suivante :
Alpha | Rouge | Vert | Bleu 11111111 | 11111111 | 00000000 | 00000000

Bien entendu, pour des questions de pratique nous travaillons gnralement avec une base 16, plus couramment appele reprsentation hexadcimale :
Alpha FF | | Rouge FF | | Vert 00 | | Bleu 00

Nous ajoutons le prfixe 0x devant une valeur hexadcimale afin de prciser au lecteur Flash quil sagit dune couleur encode en base 16 :
// stocke une couleur hexadcimale var couleur:Number = 0xFFFF0000; // affiche : 4294901760 trace( couleur );

Pour gnrer une couleur alatoire, nous pouvons valuer un nombre alatoire compris entre 0 et la couleur la plus haute, soit 0xFFFFFF :
// gnre une couleur alatoire var couleurAleatoire:Number = Math.floor ( Math.random()*0xFFFFFF ); // affiche : 9019179 trace( couleurAleatoire );

Lorsque nous affichons la couleur value, celle-ci est rendue par dfaut sous une forme dcimale. Si nous souhaitons obtenir une autre reprsentation de la couleur nous pouvons utiliser la mthode toString de la classe Object. Celle-ci permet de convertir un nombre dans une base spcifique :
// gnre une couleur alatoire var couleurAleatoire:Number = Math.floor ( Math.random()*0xFFFFFF ); // affiche la couleur au format hxadcimal (base 16) // affiche : d419f6 trace( couleurAleatoire.toString(16) ); // affiche la couleur au format octal (base 8) // affiche : 52267144 trace( couleurAleatoire.toString(8) ); // affiche la couleur au format binaire (base 2) // affiche : 101010010110111001100100 trace( couleurAleatoire.toString(2) );

4 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Une couleur RVB est reprsente par une valeur hexadcimale six chiffres. Deux chiffres tant ncessaires pour chaque composante :
var rouge:Number = 0xFF0000; var vert:Number = 0x00FF00; var bleu:Number = 0x0000FF;

Pour reprsenter une couleur ARVB, nous ajoutons le composant alpha en dbut de couleur :
var rougeTransparent:Number = 0x00FF0000; var rougeSemiTransparent:Number = 0x88FF0000; var rougeOpaque:Number = 0xFFFF0000;

Afin de faciliter la comprhension des couleurs dans Flash, nous allons nous attarder prsent sur leur manipulation.

Manipuler les couleurs


La manipulation de couleurs est facilite grce aux oprateurs de manipulation binaire. Au cours de ce chapitre, nous allons travailler avec les diffrentes composantes de couleurs. Nous allons crer une classe BitmapOutils globale tous nos projets, contenant diffrentes mthodes de manipulation. Rappelez-vous, au cours du chapitre 11 intitul Classe du document, nous avions cr un rpertoire global de classes nomm classes_as3. Au sein du rpertoire org du rpertoire nous crons un rpertoire nomm outils. Puis nous dfinissons une classe BitmapOutils contenant une premire mthode hexArgb :
package org.bytearray.outils { import flash.display.BitmapData; public class BitmapOutils { public static function hexArgb ( pCouleur:Number ):Object { var composants:Object = new Object(); // extraction de chaque composante composants.alpha = (pCouleur >>> 24) & 0xFF; composants.rouge = (pCouleur >>> 16) & 0xFF; composants.vert = (pCouleur >>> 8) & 0xFF; composants.bleu = pCouleur & 0xFF; return composants;

5 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

} } }

Grce la mthode hexArgb, lextraction des composantes est simplifie :


import org.bytearray.outils.BitmapOutils; // gnre une couleur alatoire var couleurAleatoire:Number = Math.floor ( Math.random()*0xFFFFFFFF ); // affiche : 767D62D5 trace( couleurAleatoire.toString(16).toUpperCase() ); // extraction des composants var composants:Object = BitmapOutils.hexArgb ( couleurAleatoire ); var var var var transparence:Number = composants.alpha; rouge:Number = composants.rouge; vert:Number = composants.vert; bleu:Number = composants.bleu;

// affiche : 76 trace( transparence.toString(16).toUpperCase() ); // affiche : 7D trace( rouge.toString(16).toUpperCase() ); // affiche : 62 trace( vert.toString(16).toUpperCase() ); // affiche : D5 trace( bleu.toString(16).toUpperCase() );

Nous pouvons aussi ajouter une mthode argbHex permettant dassembler une couleur hexadcimale partir de quatre composantes :
package org.bytearray.outils { import flash.display.BitmapData; public class BitmapOutils { public static function hexArgb ( pCouleur:Number ):Object { var composants:Object = new Object(); composants.alpha = (pCouleur >>> 24) & 0xFF; composants.rouge = (pCouleur >>> 16) & 0xFF; composants.vert = (pCouleur >>> 8) & 0xFF; composants.bleu = pCouleur & 0xFF;

6 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

return composants; } public static function argbHex ( pAlpha:int, pRouge:int, pVert:int, pBleu:int ):uint { return pAlpha << 24 | pRouge << 16 | pVert << 8 | pBleu; } } }

Une fois la mthode argbHex dfinie, nous pouvons gnrer une couleur alatoire partir de quatre composantes :
import org.bytearray.outils.BitmapOutils; var var var var transparence:Number = Math.floor ( Math.random()*256 ); rouge:Number = Math.floor ( Math.random()*256 ); vert:Number = Math.floor ( Math.random()*256 ); bleu:Number = Math.floor ( Math.random()*256 );

// assemble la couleur var couleur:Number = BitmapOutils.argbHex ( transparence, rouge, vert, bleu ); // affiche : 3F31D4B2 trace( couleur.toString(16).toUpperCase() );

Notre classe BitmapOutils sera trs vite enrichie, nous y ajouterons bientt de nouvelles fonctionnalits. Libre vous dajouter par la suite diffrentes mthodes facilitant la manipulation des couleurs.

A retenir
Une couleur est constitue de 4 composants cods sur 8 bits, soit un octet. Chaque composant varie entre 0 et 255. Pour changer la base dun nombre, nous utilisons la mthode toString de la classe Object. La manipulation des couleurs est facilite grce aux oprateurs de manipulation binaire.

La classe BitmapData
La classe BitmapData fut introduite avec le lecteur Flash 8 et permet la cration dimages bitmaps par programmation. En ActionScript 3, son utilisation est tendue car toutes les donnes bitmaps sont reprsentes par la classe flash.display.BitmapData.
7 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Lutilisation dimages bitmaps par programmation permet de travailler sur les pixels et de crer toutes sortes deffets complexes qui ne peuvent tre raliss laide de vecteurs. Afin de crer dynamiquement une image bitmap, nous devons tout dabord crer les pixels la composant. Pour cela nous utilisons la classe flash.display.BitmapData dont voici la signature du constructeur :
public fonction BitmapData(width:int, height:int, transparent:Boolean = true, fillColor:uint = 0xFFFFFFFF)

Celui-ci accepte quatre paramtres :


width : largueur de limage. height : hauteur de limage.

transparent : un boolen indiquant la transparence de limage. Transparent par dfaut fillColor : la couleur du bitmap en 32 ou 24 bits. Couleur blanche par dfaut.

Pour crer une image transparente de 1000 * 1000 pixels de couleur beige nous crivons le code suivant :
// cration d'une image de 1000 * 1000 pixels, transparente de couleur beige var monImage:BitmapData = new BitmapData (1000, 1000, true, 0x00F0D062);

Aussitt limage cre, les donnes bitmaps sont stockes en mmoire.

Codage de couleurs
Lorsquune image est cre, le lecteur Flash stocke la couleur de chaque pixel en mmoire. Chacun dentre eux est cod sur 32 bits, 4 octets sont donc ncessaires la description dun pixel. Afin dillustrer ce comportement, nous crons une premire image bitmap semi transparente de 1000 * 1000 pixels :
// cration d'une image transparente var monImage:BitmapData = new BitmapData ( 1000, 1000, true, 0xAAF0D062 ); // rcupre la couleur d'un pixel var couleurPixel:Number = monImage.getPixel32 ( 0, 0 ); // extraction du canal alpha var transparence:Number = BitmapOutils.hexArgb ( couleurPixel ).alpha; // affiche : 170 trace( transparence );

En isolant le canal alpha, nous voyons que son intensit vaut 170. Dans un souci doptimisation nous pourrions dcider de crer une image non transparente, pensant que celle-ci serait code sur 24 bits :
8 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

// cration d'une image non transparente var monImage:BitmapData = new BitmapData ( 1000, 1000, false, 0xF0D062 ); // rcupre la couleur d'un pixel var couleurPixel:Number = monImage.getPixel32 ( 0, 0 ); // extraction du canal alpha var transparence:Number = BitmapOutils.hexArgb ( couleurPixel ).alpha; // affiche : 255 trace( transparence );

Dans le cas dune image non transparente, lintensit du canal alpha est automatiquement dfinie 255. Si nous tentons de passer une couleur 32 bits, les 8 premiers bits sont ignors :
// cration d'une image non transparente var monImage:BitmapData = new BitmapData ( 1000, 1000, false, 0xBBF0D062 ); // rcupre la couleur d'un pixel var couleurPixel:Number = monImage.getPixel32 ( 0, 0 ); // rcupre le canal alpha var transparence:Number = BitmapOutils.hexArgb ( couleurPixel ).alpha; // affiche : 255 trace( transparence );

Au sein du lecteur Flash, quelque soit la transparence de limage, les couleurs sont toujours codes sur 32 bits. La cration dune image opaque nentrane donc aucune optimisation mmoire mais facilite en revanche laffichage.

A retenir
Pour crer une image par programmation nous utilisons la classe BitmapData. Chaque couleur de pixel composant une instance de BitmapData est code sur 32 bits. 1 pixel pse 4 octets en mmoire. La cration dimage opaque noptimise pas la mmoire mais facilite le rendu de limage.

Grer les ressources avec le profiler


Afin doptimiser un projet ActionScript 3, il est impratif de matriser la gestion des ressources. Lutilisation de la classe BitmapData ncessite une attention toute particulire quant loccupation mmoire engendre par son utilisation. Pour tester le comportement du lecteur Flash nous utiliserons le profiler de Flex Builder 3, qui est aujourdhui loutil le plus adapt en matire de gestion et optimisation des ressources.
9 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Le profiler de Flex Builder 3 est un module permettant de connatre en temps rel loccupation mmoire de chaque objet ainsi que loccupation mmoire totale dune application ActionScript 3. Il nexiste malheureusement pas doutil similaire dans Flash CS3. Lorsque vous souhaitez tester les ressources dun projet ActionScript 3 dvelopp avec Flash CS3, vous avez la possibilit de charger celleci au sein dune application Flex, afin de bnficier du profiler. Nous allons analyser la mmoire utilise par le lecteur Flash en crant une premire image bitmap transparente :
// cration dune image transparente var monImage:BitmapData = new BitmapData ( 1000, 1000, true, 0x00F0D062 );

La figure 12-4 illustre la fentre Utilisation Mmoire du profiler :

Figure 12-4. Cration de donnes bitmaps transparente. Nous voyons la courbe augmenter sensiblement lors de la cration de linstance de BitmapData. Celle-ci occupe environ 3906 Ko en mmoire vive. Afin danalyser ce rsultat, faisons un tour dhorizon des diffrentes lgendes du panneau Utilisation mmoire :
Peak Memory (Seuil maximum atteint) : plus haute occupation mmoire atteinte depuis le lancement de lanimation. Current Memory (Mmoire actuelle) : occupation mmoire courante. Time (Temps) : nombre de secondes coules depuis le lancement de lanimation.

Le profiler nous indique que limage cre, occupe en mmoire environ 3,9 Mo. Nous pouvons facilement vrifier cette valeur avec le calcul suivant :

10 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

1000 * 1000 = 1000000 pixels

Chaque pixel est cod sur 32 bits (4 octets) :


1000000 pixels * 4 octets = 4000000 octets

Afin dobtenir lquivalent en Ko, nous divisons par 1024 :


4000000 / 1024 = 3906,25 Ko

Nous retrouvons le poids indiqu par le profiler. Nous verrons trs bientt comment faciliter ce calcul au sein dune application ActionScript. Comme nous lavons vu prcdemment, le lecteur Flash code, quelque soit la transparence de limage, les couleurs sur 32 bits. Si nous crons une image non transparente, loccupation mmoire reste la mme :
// cration d'une image non transparente var monImage:BitmapData = new BitmapData ( 1000, 1000, false, 0xF0D062 );

La figure 12-4 illustre le rsultat :

Figure 12-4. Cration de donnes bitmaps non transparentes. Le profiler est un outil essentiel au dboguage dapplications ActionScript 3. Certains outils tiers existent mais noffrent pas une telle granularit dans les informations apportes.

A retenir

11 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Le Profiler est un outil intgr Flex Builder 3 facilitant la gestion des ressources dun projet ActionScript 3. Il nexiste pas doutil intgr quivalent dans Flash CS3.

La classe Bitmap
Comme son nom lindique, la classe BitmapData reprsente les donnes bitmaps mais celle-ci ne peut tre affiche directement. Afin de rendre une image nous devons associer linstance de BitmapData la classe flash.display.Bitmap. Il est important de considrer la classe Bitmap comme simple conteneur, celle-ci sert prsenter les donnes bitmaps. Dans les prcdentes versions dActionScript la classe MovieClip tait utilise pour afficher limage :
// cration d'un clip conteneur var monClipConteneur:MovieClip = this.createEmptyMovieClip ("conteneur", 0); // cration des donnes bitmaps var donneesBitmap:BitmapData = new BitmapData (300, 300, false, 0xFF00FF); // affichage de l'image monClipConteneur.attachBitmap (donneesBitmap, 0);

En ActionScript 3, nous utilisons la classe Bitmap dont voici le constructeur :


Bitmap(bitmapData:BitmapData = null, pixelSnapping:String = "auto", smoothing:Boolean = false)

Celui-ci accepte trois paramtres :


pixelSnapping : accrochage aux pixels. bitmapData : les donnes bitmaps afficher. Smoothing : un boolen indiquant si limage doit tre lisse ou non.

Nous passons en premier paramtre linstance de BitmapData afficher, les deux autres paramtres seront traits plus loin :
// cration d'une image de 250 * 250 pixels, non transparente de couleur beige var monImage:BitmapData = new BitmapData (250, 250, false, 0xF0D062); // cration d'un conteneur pour l'image bitmap var monConteneurImage:Bitmap = new Bitmap ( monImage ); // ajout du conteneur addChild ( monConteneurImage );

En testant le code prcdent, nous obtenons le rsultat illustr en figure 12-5 :

12 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-5. Affichage dune instance de BitmapData. Si nous souhaitons modifier la prsentation des donnes bitmaps, nous utilisons les diffrentes proprits de la classe Bitmap. A linverse, si nous devons travailler sur les pixels composant limage, nous utiliserons les mthodes de la classe BitmapData. De par lhritage, toutes les proprits de la classe DisplayObject sont donc disponibles sur la classe Bitmap :
// cration d'une image de 250 * 250 pixels, non transparente de couleur beige var monImage:BitmapData = new BitmapData (250, 250, false, 0xF0D062); // cration d'un conteneur pour l'image bitmap var monConteneurImage:Bitmap = new Bitmap ( monImage ); // ajout du conteneur addChild ( monConteneurImage ); // positionnement et redimensionnement monConteneurImage.x = 270; monConteneurImage.y = 120; monConteneurImage.scaleX = .5; monConteneurImage.scaleY = .5; monConteneurImage.rotation = 45;

Le code prcdent dplace limage, rduit limage et lui fait subir une rotation de 45 degrs. Le rsultat est illustr en figure 12-6 :

13 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-6. Dplacement et rotation dune image bitmap. Il tait impossible daccder aux donnes bitmaps associes un MovieClip dans les prcdentes versions dActionScript. En ActionScript 3, nous utilisons la proprit bitmapData de la classe Bitmap :
// cration d'une image de 250 * 250 pixels, non transparente de couleur beige var monImage:BitmapData = new BitmapData (250, 250, false, 0xF0D062); // cration d'un conteneur pour l'image bitmap var monConteneurImage:Bitmap = new Bitmap ( monImage ); // ajout du conteneur addChild ( monConteneurImage ); // positionnement et redimensionnement monConteneurImage.x = 270; monConteneurImage.y = 120; monConteneurImage.scaleX = .5; monConteneurImage.scaleY = .5; monConteneurImage.rotation = 45; var donneesBitmap:BitmapData = monConteneurImage.bitmapData; // affiche : 250 trace(donneesBitmap.width ); // affiche : 250 trace(donneesBitmap.height );

Nous remarquons que les dimensions de linstance de BitmapData ne sont pas altres par le redimensionnement de lobjet Bitmap.
14 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Souvenez-vous, la classe Bitmap prsente simplement les donnes bitmaps dfinies par la classe BitmapData.

Rutiliser les donnes bitmaps


Dans certaines applications, les donnes bitmaps peuvent tre rutilises lorsque les pixels sont identiques mais prsents diffremment. Imaginons que nous souhaitons construire un damier comme celui illustr en figure 12-7 :

Figure 12-7. Damier constitu dinstances de BitmapData. Nous pourrions tre tents dcrire le code suivant :
for ( var i:int = 0; i< 300; i++ ) { // cration d'un carr de 20 * 20 pixels, non transparent de couleur beige var monImage:BitmapData = new BitmapData (20, 20, false, 0xF0D062); // cration d'un conteneur pour l'image bitmap var monConteneurImage:Bitmap = new Bitmap ( monImage ); // ajout du conteneur la liste daffichage addChild ( monConteneurImage ); // positionnement des conteneurs dimages monConteneurImage.x = ( monConteneurImage.width + 8 ) * Math.round (i % 20);

15 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

monConteneurImage.y = ( monConteneurImage.height + 8 ) * Math.floor (i / 20); }

Pour chaque itration nous crons une instance de BitmapData, puis un objet Bitmap afin dafficher chaque image. Si nous regardons loccupation mmoire. Lorsque le damier est cr, loccupation mmoire est de 571 Ko.

Figure 12-8. Occupation mmoire sans optimisation du damier. Le code prcdent nest pas optimis car nous crons chaque itration une instance de BitmapData de 20 pixels pesant 1,56 Ko. En instanciant 300 fois ces donnes bitmaps, nous obtenons un poids total cumul pour les images denviron 470 Ko. Souvenez-vous, la classe Bitmap permet dafficher des donnes bitmaps, il est donc tout fait possible dafficher plusieurs images partir dune mme instance de BitmapData. Nous pourrions obtenir le mme damier en divisant le poids de presque 6 fois :
// cration dune seule instance de BitmapData en dehors de la boucle var monImage:BitmapData = new BitmapData (20, 20, false, 0xF0D062); for ( var i:int = 0; i< 300; i++ ) { // cration d'un conteneur pour les donnes bitmaps var monConteneurImage:Bitmap = new Bitmap ( monImage ); // ajout du conteneur addChild ( monConteneurImage ); // positionnement des conteneurs de bitmap

16 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

monConteneurImage.x = ( monConteneurImage.width + 8 ) * Math.round (i % 20); monConteneurImage.y = ( monConteneurImage.height + 8 ) * Math.floor (i / 20); }

Avec le code prcdent loccupation mmoire a chut, nous passons environ 88 Ko doccupation mmoire.

Figure 12-9. Occupation mmoire avec optimisation du damier. Il est donc fortement recommand de rutiliser les donnes bitmaps lorsque nous souhaitons afficher plusieurs fois les mmes donnes bitmaps, mme sous une forme diffrente. Nous pourrions modifier la prsentation des donnes bitmaps grce aux diffrentes proprits de la classe Bitmap. Dans le code suivant nous modifier la taille et la rotation de chaque lment du damier :
// cration dune seule instance de BitmapData en dehors de la boucle var monImage:BitmapData = new BitmapData (20, 20, false, 0xF0D062); for ( var i:int = 0; i< 300; i++ ) { // cration d'un conteneur pour les donnes bitmaps var monConteneurImage:Bitmap = new Bitmap ( monImage ); // ajout du conteneur addChild ( monConteneurImage ); // positionnement des conteneurs de bitmap monConteneurImage.x = ( monConteneurImage.width + 8 ) * Math.round (i % 20); monConteneurImage.y = ( monConteneurImage.height + 8 ) * Math.floor (i / 20); // taille, rotation et transparence alatoires

17 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

monConteneurImage.scaleX = monConteneurImage.scaleY = Math.random(); monConteneurImage.rotation = Math.floor ( Math.random()*360 ); monConteneurImage.alpha = Math.random(); }

La figure 12-10 illustre le rsultat :

Figure 12-10. Damier constitu dune seule instance de BitmapData. Ce dcor est constitu des mmes donnes bitmaps, mais prsentes sous diffrentes formes.

A retenir
La classe Bitmap permet de prsenter les donnes bitmaps dfinies par la classe BitmapData. Il est important de rutiliser les donnes bitmaps lorsque cela est possible.

Librer les ressources


Comme nous lavons vu lors du chapitre 2 intitul Langage et API du lecteur Flash lorsquun objet nest pas rfrenc, celui-ci est aussitt considr comme ligible la suppression par le ramasse-miettes.

18 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Dans le code suivant nous crons une instance de BitmapData partir de 5 secondes, aucune rfrence nest conserve :
// cration d'un minuteur var minuteur:Timer = new Timer ( 5000, 0 ); // coute de l'vnement TimerEvent.TIMER minuteur.addEventListener( TimerEvent.TIMER, creation ); // dmarrage du minuteur minuteur.start(); function creation ( pEvt:TimerEvent ):void { // cration d'une image non transparente de 350 * 350 pixels var monBitmap:BitmapData = new BitmapData ( 350, 350, false, 0x990000 ); }

Une fois la fonction creation excute, la variable locale monBitmap expire, les donnes bitmaps sont aussitt supprimes de la mmoire. En testant le code prcdent nous ne remarquons aucune augmentation de loccupation mmoire :

Figure 12-11. Occupation mmoire de lapplication.


BitmapData cre, les donnes bitmaps sont alors rfrences et ne

Si nous ajoutons la liste daffichage chaque instance de

sont plus ligibles la suppression :

// cration d'un minuteur var minuteur:Timer = new Timer ( 5000, 0 ); // coute de l'vnement TimerEvent.TIMER minuteur.addEventListener( TimerEvent.TIMER, creation ); // dmarrage du minuteur minuteur.start();

19 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

function creation ( pEvt:TimerEvent ):void { // cration d'une image non transparente de 350 * 350 pixels var monBitmap:BitmapData = new BitmapData ( 350, 350, false, 0x990000 ); // cration de lenveloppe Bitmap var monConteneurBitmap:Bitmap = new Bitmap ( monBitmap ); // ajout la liste daffichage addChild ( monConteneurBitmap ); }

La figure 12-12 montre laugmentation de loccupation mmoire :

Figure 12-12. Augmentation de loccupation mmoire de lapplication. Toutes les 5 secondes, une image bitmap est cre puis ajoute la liste daffichage. A partir dune minute et dix secondes nous obtenons une occupation mmoire denviron 6,7 Mo. Chaque instance ncessitant environ 478,5 Ko. Lorsque nous navons plus besoin dune image, il est fortement recommand de la dsactiver afin de librer la mmoire. Pour cela, nous disposons de plusieurs solutions. La premire, consiste dsactiver limage et librer la mmoire en utilisant la mthode dispose de la classe BitmapData. Dans le code suivant, un minuteur cre des instances de BitmapData toutes les 5 secondes puis les ajoutent la liste daffichage. A partir de 30 secondes nous stoppons la cration des images et appelons la mthode dispose sur chaque instance de BitmapData :
// cration d'un minuteur var minuteur:Timer = new Timer ( 5000, 0 );

20 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

// coute de l'vnement TimerEvent.TIMER minuteur.addEventListener( TimerEvent.TIMER, creation ); // dmarrage du minuteur minuteur.start(); function creation ( pEvt:TimerEvent ):void { // cration d'une image non transparente de 350 * 350 pixels var monBitmap:BitmapData = new BitmapData ( 350, 350, false, 0x990000 ); // cration de lenveloppe Bitmap var monConteneurBitmap:Bitmap = new Bitmap ( monBitmap ); // ajout la liste daffichage addChild ( monConteneurBitmap ); } var minuteurNettoyage:Timer = new Timer ( 30000, 1 ); minuteurNettoyage.addEventListener( TimerEvent.TIMER, nettoyage ); minuteurNettoyage.start(); // dsactivation des donnes bitmaps partir de 30 secondes function nettoyage ( pEvt:TimerEvent ):void { minuteur.stop(); var lng:int = numChildren; var monImage:Bitmap; while ( lng-- ) { monImage = Bitmap ( getChildAt ( lng ) ); // dsactive les donnes bitmaps monImage.bitmapData.dispose(); } }

En analysant les informations fournies par le profiler, nous voyons que la mmoire nest pas libre :

21 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-13. Occupation mmoire de lapplication. Il sagit en ralit dun bogue du profiler, qui naffiche pas correctement la libration de la mmoire lors de lutilisation de la mthode dispose. A laide dautres outils, nous remarquons que la mmoire est libre immdiatement. Attention, bien que lappel la mthode dispose supprime visuellement les images et libre la mmoire celles-ci sont toujours prsentes au sein de la liste daffichage et donc rfrences. Afin de totalement dsactiver une instance de BitmapData il faut veiller supprimer linstance de la liste daffichage, puis appeler la mthode dispose. Nous modifions le code prcdent en supprimant chaque instance de laffichage puis en appelant la mthode dispose :
// cration d'un minuteur var minuteur:Timer = new Timer ( 5000, 0 ); // coute de l'vnement TimerEvent.TIMER minuteur.addEventListener( TimerEvent.TIMER, execution ); // dmarrage du minuteur minuteur.start(); // cration et ajout l 'affichage d'une instance de BitmapData // toutes les 5 secondes function execution ( pEvt:TimerEvent ):void { // cration d'une image non transparente de 350 * 350 pixels var monBitmap:BitmapData = new BitmapData ( 350, 350, false, 0x990000 ); // cration de lenveloppe Bitmap var monConteneurBitmap:Bitmap = new Bitmap ( monBitmap ); // ajout la liste daffichage addChild ( monConteneurBitmap );

22 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

} var minuteurNettoyage:Timer = new Timer ( 30000, 1 ); minuteurNettoyage.addEventListener( TimerEvent.TIMER, nettoyage ); minuteurNettoyage.start(); // libration des ressources au bout de 30 secondes function nettoyage ( pEvt:TimerEvent ):void { minuteur.stop(); var lng:int = numChildren; var monImage:Bitmap; while ( lng-- ) { monImage = Bitmap ( removeChildAt ( lng ) ); // dsactive les donnes bitmaps monImage.bitmapData.dispose(); } }

Lapproche suivante sappuie sur le ramasse-miettes en supprimant les rfrences pointant vers les instances de BitmapData. Cette technique a pour inconvnient de ne pas supprimer immdiatement les donnes bitmaps en mmoire. Elles le seront uniquement si le ramasse-miettes procde un nettoyage. Souvenez-vous que celui-ci peut ne jamais intervenir. Dans certaines applications, les donnes bitmaps peuvent tre utilises sans tre affiches. Dans le cas dune application dencodage et de compression dimages, un tableau de rfrences est gnralement cr afin daccder rapidement chaque instance :
// cration d'un minuteur var minuteur:Timer = new Timer ( 5000, 5 ); // coute de l'vnement TimerEvent.TIMER minuteur.addEventListener( TimerEvent.TIMER, execution ); // dmarrage du minuteur minuteur.start(); // conteneur de rfrences var tableauImages:Array = new Array(); // cration d'une instance de BitmapData // toutes les 5 secondes

23 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

function execution ( pEvt:TimerEvent ):void { // cration d'une image non transparente de 350 * 350 pixels var monBitmap:BitmapData = new BitmapData ( 350, 350, false, Math.random()*0xFFFFFF ); // cration de lenveloppe Bitmap var monConteneurBitmap:Bitmap = new Bitmap ( monBitmap ); // stockage des rfrences tableauImages.push ( monConteneurBitmap ); } var minuteurNettoyage:Timer = new Timer ( 30000, 1 ); minuteurNettoyage.addEventListener( TimerEvent.TIMER, nettoyage ); minuteurNettoyage.start(); // libration des ressources au bout de 30 secondes function nettoyage ( pEvt:TimerEvent ):void { minuteur.stop(); var lng:int = tableauImages.length; while ( lng-- ) { // supprime chaque rfrence tableauImages [ lng ] = null; } }

Les seules rfrences aux instances de BitmapData ne rsident pas au sein de la liste daffichage mais au sein du tableau tableauImages. Pour librer les ressources, nous passons chaque rfrence null. Lorsque le ramasse-miettes intervient, les ressources sont libres :

24 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-14. Chute de loccupation mmoire de lapplication. En utilisant cette technique il nest pas ncessaire dappeler la mthode dispose. Afin doptimiser nos tests nous pouvons grce au profiler dclencher le ramasse-miettes et voir si les ressources sont bien libres lors de son passage. Il nest pas possible officiellement de dclencher le ramasse-miettes par programmation. Une fois une image dsactive, celle-ci ne peut plus tre utilise. Dans le code suivant, nous tentons de cloner une image bitmap dsactive :
// cration d'une image de 250 * 250 pixels // non transparente de couleur beige var monImage:BitmapData = new BitmapData (250, 250, false, 0xF0D062); // dsactivation des donnes bitmaps monImage.dispose(); // tentative de clonage des donnes bitmaps // affiche : ArgumentError: Error #2015: BitmapData non valide. monImage.clone();

Lappel

de la mthode clone lve une erreur de type ArgumentError. Une fois dsactive, il est impossible de ractiver limage.

A retenir

25 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La mthode dispose permet de dsactiver une image et de librer instantanment la mmoire utilise. Cette technique est recommande. En supprimant simplement les rfrences pointant vers limage nous ne sommes pas garantis que la mmoire soit libre. Nous sommes tributaires du ramasse-miettes.

Calculer le poids dune image en mmoire


Afin de faciliter la manipulation de donnes bitmaps par programmation nous allons ajouter une mthode nomme poids au sein de la classe BitmapOutils :
package org.bytearray.outils { import flash.display.BitmapData; public class BitmapOutils { public static function hexArgb ( pCouleur:Number ):Object { var composants:Object = new Object(); composants.alpha = (pCouleur >>> 24) & 0xFF; composants.rouge = (pCouleur >>> 16) & 0xFF; composants.vert = (pCouleur >>> 8) & 0xFF; composants.bleu = pCouleur & 0xFF; return composants; } public static function argbHex ( pAlpha:int, pRouge:int, pVert:int, pBleu:int ):uint { return ( pAlpha << 24 | pRouge << 16 | pVert << 8 | pBleu ); } public static function poids ( pBitmapData:BitmapData ):Number { return (pBitmapData.width * pBitmapData.height) * 4; } } }

26 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La mthode poids nous renvoie le poids de limage en mmoire en octets :


import org.bytearray.outils.BitmapOutils; // cration d'une instance de BitmapData var monBitmap:BitmapData = new BitmapData ( 1000, 1000, false, Math.random()*0xFFFFFF ); // calcul du poids en mmoire var poids:Number = BitmapOutils.poids ( monBitmap ) / 1024; // affiche : 3906.25 trace( poids );

La classe BitmapOutils pourra ainsi tre rutilise tout moment dans diffrents projets.

Limitations mmoire
Pour des raisons de performances, la taille maximale dune instance de BitmapData cre par programmation est limite 2880 * 2880 pixels. Une image dune telle dimension ncessite prs de 32 Mo de mmoire vive, ce qui reprsente une occupation mmoire non ngligeable :
import org.bytearray.outils.BitmapOutils; // cration d'une image de 2880 * 2880 pixels // non transparente de couleur beige var monImage:BitmapData = new BitmapData (2880, 2880, false, 0xF0D062); var poidsImage:Number = BitmapOutils.poids ( monImage ); // affiche : 32400 trace( poidsImage / 1024 );

Si nous tentons tout de mme de crer une image dune taille suprieure, le lecteur Flash lve une erreur lexcution :
// lve l'erreur l'excution suivante : // Error #2015: BitmapData non valide. var monImage:BitmapData = new BitmapData (3000, 3000, false, 0xF0D062);

Dans le cas dune application de dessin, nous pourrions indiquer lutilisateur que limage cre est trop grande en grant lexception :
try { var monImage:BitmapData = new BitmapData (3000, 3000, false, 0xF0D062); } catch ( pError:Error ) { // affiche : ArgumentError: Error #2015: BitmapData non valide. trace( pError );

27 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

trace("Image trop grande !"); }

Dans le cas dune application ncessitant des images de dimensions suprieures, plusieurs instances de BitmapData peuvent tre utilises afin de contourner cette limitation.

A retenir
La taille maximale dune instance de BitmapData cre par programmation est de 2880*2880 pixels.

Images en bibliothque
Lorsquune image est prsente au sein de la bibliothque. Celle-ci est assimile une instance de BitmapData. Dans un nouveau document Flash CS3, nous importons une image en bibliothque, puis nous dfinissons une classe associe nomme Logo. Nous instancions limage et laffichons :
// instanciation des donnes bitmaps var monLogo:Logo = new Logo(0,0); // creation dune envelope Bitmap var monConteneur:Bitmap = new Bitmap ( monLogo ); // ajout l'affichage addChild ( monConteneur ); // positionnement de l'image monConteneur.x = 100; monConteneur.y = 100;

La figure 12-15 montre le rsultat :

28 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-15. Affichage dune image de bibliothque. De manire gnrale, il nest pas forment ncessaire dutiliser le type spcifique pour stocker linstance de BitmapData. Nous pouvons aussi stocker linstance de Logo au sein dune variable de type BitmapData :
// instanciation des donnes bitmaps var monLogo:BitmapData = new Logo(0,0);

Si limage en bibliothque est un PNG transparent, linstance de BitmapData cre est transparente :
// instanciation du logo var monLogo:BitmapData = new Logo(0,0); // affiche : true trace( monLogo.transparent );

Nous avons jusqu prsent cr des images bitmaps de couleur unies, nous allons nous attarder maintenant la modification des donnes bitmaps, en travaillant sur les pixels. Il est important de noter quune image en bibliothque ne possde pas de limitations de taille, contrairement aux instances de BitmapData cres par programmation.

29 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Peindre des pixels


Trois mthodes sont disponibles pour peindre les pixels dune image, voici le dtail de chacune dentre elles :
BitmapData.setPixel : peint un pixel au format RVB. BitmapData.setPixel32 : peint un pixel au format ARVB.

BitmapData.setPixels : peint des pixels daprs un tableau doctets source dfinie par la classe flash.utils.ByteArray.

La mthode setPixel permet de colorer un pixel une position donne :


public function setPixel(x:int, y:int, color:uint):void

Les paramtres x et y dfinissent la position du pixel peindre. La couleur doit tre spcifie au format RVB. Dans le code suivant, nous nous colorons alatoirement au sein dune boucle certains pixels de limage :
var monImage:BitmapData = new BitmapData ( 200, 200, false, 0xFFFFFF ); var conteneurImage:Bitmap = new Bitmap ( monImage ); addChild ( conteneurImage ); for ( var i:int = 0; i<20000; i++ ) { // positions alatoires // arrondi automatique, d au type int var positionX:int = Math.random()*251; var positionY:int = Math.random()*251; // peint un pixel monImage.setPixel ( positionX, positionY, 0x990000 ); }

Le rsultat est illustr en figure 12-16 :

30 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-16. Dessin par setPixel. Si nous passons une couleur au format ARVB, le canal alpha est ignor :
var monImage:BitmapData = new BitmapData ( 200, 200, false, 0xFFFFFF ); var conteneurImage:Bitmap = new Bitmap ( monImage ) addChild ( conteneurImage ); for ( var i:int = 0; i<20000; i++ ) { // positions alatoires // arrondi automatique, d au type int var positionX:int = Math.random()*251; var positionY:int = Math.random()*251; // peint un pixel, le canal alpha est ignor monImage.setPixel ( positionX, positionY, 0x33990000 ); }

En ralit lorsque nous appelons la mthode setPixel nous travaillons avec une couleur code sur 24 bits, le canal alpha tant automatiquement dfini. Afin de pouvoir peindre des pixels en prcisant la transparence, nous devons utiliser la mthode setPixel32 dont voici la signature :

31 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

public function setPixel32(x:int, y:int, color:uint):void

Dans lexemple suivant nos modifions la transparence de limage en utilisant une couleur semi opaque :
var monImage:BitmapData = new BitmapData ( 200, 200, true, 0xFFFFFFFF ); var conteneurImage:Bitmap = new Bitmap ( monImage ) addChild ( conteneurImage ); for ( var i:int = 0; i<20000; i++ ) { // positions alatoires // arrondi automatique, d au type int var positionX:int = Math.random()*251; var positionY:int = Math.random()*251; // peint les pixels avec un transparence de 40% monImage.setPixel32 ( positionX, positionY, 0x66990000 ); }

La figure 12-17 illustre le rsultat :

Figure 12-17. Dessin par setPixel32. Contrairement aux mthodes setPixel et setPixel32 permettant de peindre un seul pixel par appel, la mthode setPixels permet de peindre une zone de pixels dfinie par une instance de la classe flash.geom.Rectangle.
32 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Voici la signature de la mthode setPixels :


public function setPixels(rect:Rectangle, inputByteArray:ByteArray):void

Le premier paramtre accueille une instance de la classe Rectangle dfinissant la zone peindre, puis en deuxime paramtre un tableau doctets contenant la couleur de chaque pixel. Nous reviendrons sur la manipulation de donnes binaire au cours du chapitre 19 intitul ByteArray. Dans le code suivant, nous crons une image bitmap non transparente :
// cration d'une image bitmap non transparente var monImage:BitmapData = new BitmapData ( 200, 200, false, 0x99AAAA ); var conteneurImage:Bitmap = new Bitmap ( monImage ) addChild ( conteneurImage );

Puis un tableau binaire contenant la couleur de chaque pixel :


// tableau de pixels var pixels:ByteArray = new ByteArray(); for ( var i:int = 0; i< 50; i++ ) { for ( var j:int = 0; j< 50; j++ { )

// store la couleur de chaque pixel 32bits pixels.writeUnsignedInt(0x990000); } }

Nous rinitialisons lindex de lecture du flux :


pixels.position = 0;

Puis, les pixels sont peints au sein de limage bitmap :


monImage.setPixels( new Rectangle (0, 0, 50, 50), pixels );

La figure 12-18 illustre le rsultat :

33 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-18. Dessin dune zone par setPixels. Bien que cette mthode puisse paratre pratique, elle ne savre pas tre la plus efficace. Nous sommes obligs de remplir un tableau doctets contenant la couleur de chaque pixel puis dappeler la mthode setPixels. Dans la plupart des cas nous prfrerons utiliser la mthode setPixel qui savre plus rapide.

A retenir
La mthode setPixel permet de peindre un pixel au format RVB. La mthode setPixel32 permet de peindre un pixel au format ARVB. La mthode setPixels permet de peindre un ensemble de pixels, partir dun tableau doctets source.

Lire des pixels


Pour accder la couleur de chaque pixel nous disposons de trois autres mthodes dont voici le dtail :
getPixel : renvoie la couleur du pixel au format RVB selon un point spcifique.

34 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

getPixel32 : renvoie la couleur du pixel au format ARVB selon un point spcifique. getPixels : renvoie un tableau doctets contenant les couleurs de pixels dfini selon une zone rectangulaire (flash.geom.Rectangle)

Afin de lire les pixels nous pouvons utiliser les mthodes correspondantes getPixel et getPixel32. La mthode getPixel renvoie la couleur du pixel au format RVB et possde la signature suivante :
public function getPixel(x:int, y:int):uint

Nous allons crer une image de couleur verte et rcuprer la couleur dun pixel :
var monImage:BitmapData = new BitmapData ( 200, 200, false, 0xFFFF00 ); var conteneurImage:Bitmap = new Bitmap ( monImage ); addChild ( conteneurImage ); var couleurPixel:Number = monImage.getPixel( 0, 0 ); // affiche : FFFF00 trace( couleurPixel.toString(16).toUpperCase() );

Si nous tentons de rcuprer la couleur dun pixel composant une image transparente, le canal alpha est ignor :
var monImage:BitmapData = new BitmapData ( 200, 200, true, 0x99FF0088 ); var conteneurImage:Bitmap = new Bitmap ( monImage ); addChild ( conteneurImage ); var couleurPixel:Number = monImage.getPixel ( 0, 0 ); // affiche : FF0088 // le canal alpha est ignor trace( couleurPixel.toString(16).toUpperCase() );

La mthode getPixel32 permet, de rcuprer la couleur dun pixel au format ARVB :


var monImage:BitmapData = new BitmapData ( 200, 200, true, 0x99FF0088 ); var conteneurImage:Bitmap = new Bitmap ( monImage ); addChild ( conteneurImage ); var couleurPixel:Number = monImage.getPixel32 ( 0, 0 ); // affiche : 99FF0088 trace( couleurPixel.toString(16).toUpperCase() );

35 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Alors que les mthodes getPixel et getPixel32 existent depuis le lecteur Flash 8, la mthode getPixels a t introduite par ActionScript 3. Celle-ci lavantage de renvoyer un tableau binaire contenant les pixels de la zone spcifie. En ActionScript 1 et 2, nous tions obligs de parcourir manuellement limage bitmap afin dobtenir lensemble des pixels. Dans le code suivant nous examinons une image bitmap et stockons chaque pixel dans un tableau :
// instanciation du logo var monLogo:Logo = new Logo(0,0); // affichage var monConteneur:Bitmap = new Bitmap ( monLogo ); // ajout l'affichage addChild ( monConteneur ); // positionnement de l'image monConteneur.x = 100; monConteneur.y = 100; // rcupration largeur et hauteur var largeur:Number = monConteneur.width; var hauteur:Number = monConteneur.height; // tableau contenant les pixels var tableauPixels:Array = new Array(); for ( var i:int = 0; i< largeur; i++ ) { for ( var j:int = 0; j< hauteur; j++ ) { // rcupre la couleur de chaque pixel var couleur:Number = monLogo.getPixel ( i, j ); // stocke chaque couleur au sein d'un tableau tableauPixels.push ( couleur ); } } // affiche : 17424 trace( tableauPixels.length );

En affichant la longueur du tableau, nous voyons que 17424 pixels sont stocks au sein du tableau. Cette opration fonctionnait sans problme sur des images bitmapss de petite taille. Pour des images de dimensions leves, lutilisation de boucles imbriques ntait plus possible.
36 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

En ActionScript 3, nous utilisons la mthode getPixels :


// instanciation du logo var monLogo:Logo = new Logo(0,0); // affiche : 132, 132 (132*132 = 17424) trace( monLogo.width, monLogo.height ); var tableauPixels:ByteArray = monLogo.getPixels ( monLogo.rect ); // affiche : 69696 trace( tableauPixels.length );

Celle-ci retourne un tableau doctets contenant les pixels de la zone spcifie. Au sein dun tableau doctets, un pixel occupe 4 index. Si nous divisons 69696 par 4 nous obtenons 17424 pixels. Lutilisation de la mthode getPixels savre beaucoup plus rapide que la mthode getPixel, car la totalit des pixels dune image peut tre retourne instantanment.

A retenir
La mthode getPixel retourne la couleur dun pixel au format RVB. La mthode getPixel32 retourne la couleur dun pixel au format ARVB. La mthode getPixels retourne un tableau doctets contenant un ensemble de pixels.

Accrochage aux pixels


Lorsquune image bitmap est affiche nous pouvons garantir laccrochage aux pixels grce au paramtre pixelSnapping. Trois constantes sont utilises afin de dterminer laccrochage dune image :
PixelSnapping.ALWAYS : limage est toujours accroche au pixel le plus proche. PixelSnapping.AUTO : limage bitmap est accroche au pixel le plus proche si elle est dessine sans rotation ni inclinaison et que son facteur de redimensionnement est compris entre 99,9 % et 100,1 %. PixelSnapping.NEVER : laccrochage aux pixels est dsactiv.
Bitmap(bitmapData:BitmapData = null, pixelSnapping:String = "auto", smoothing:Boolean = false)

Par dfaut la valeur du paramtre est auto :

En cas de rotation ou transformation de limage affiche, celle-ci pourrait voir ses coordonnes glisser sur des coordonnes flottantes,
37 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

donnant un aspect flout aux contours de limage. Laccrochage au pixels garantie un rendu net de limage affiche.

Le lissage
Grce au lissage, la classe Bitmap permet damliorer le rendu dune image lorsque celle-ci est redimensionne. Nous avons la possibilit dactiver le lissage grce au dernier paramtre du constructeur de la classe Bitmap :
// instanciation du logo var monLogo:Logo = new Logo(0,0); // affichage de l'image en accrochant toujours les pixels et en dsactivant le lissage var monConteneur:Bitmap = new Bitmap ( monLogo, PixelSnapping.ALWAYS, false ); // ajout l'affichage addChild ( monConteneur ); // positionnement de l'image monConteneur.x = 100; monConteneur.y = 100; // redimensionnement monConteneur.scaleX = monConteneur.scaleY = 1.2; // affichage de l'image en accrochant toujours les pixels et en activant le lissage var monConteneurBis:Bitmap = new Bitmap ( monLogo, PixelSnapping.ALWAYS, true ); // ajout l'affichage addChild ( monConteneurBis ); // positionnement de l'image monConteneurBis.x = 300; monConteneurBis.y = 100; // redimensionnement monConteneurBis.scaleX = monConteneurBis.scaleY = 1.2;

La figure 12-19 illustre la diffrence entre une image non lisse et lisse :

38 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-19. Images bitmaps non lisse et lisse. En adoucissant linterpolation des pixels, limage conserve un rendu liss lorsque celle-ci doit tre redimensionne.

Mise en cache des bitmap lexcution


La mise en cache des bitmap lexcution est une fonctionnalit accessible par programmation ou depuis lenvironnement auteur, permettant dacclrer grandement la vitesse de rendu dun lment vectoriel. Celle-ci est exclusivement rserve aux objets de type DisplayObject. Pour comprendre cette fonctionnalit, nous allons nous attarder quelques instants sur le systme de rendu du lecteur Flash. Le terme de mise en cache illustre le mcanisme interne du lecteur visant crer temporairement en mmoire une version bitmap de lobjet vectoriel. En activant la mise en cache des bitmap sur un DisplayObject de 300 * 300 pixels, une image bitmap 32 bits de mme taille est cre en mmoire puis affiche en remplacement de lobjet vectoriel. Cette technique permet au lecteur dafficher simplement limage stocke en mmoire et de ne plus rendre les donnes vectorielles, entranant ainsi une augmentation significative de la vitesse daffichage. Pour mettre en cache un objet graphique, il suffit dactiver la proprit cacheAsBitmap de la classe DisplayObject :
DisplayObject.cacheAsBitmap = true;

Pour dsactiver la mise en cache, nous passons la valeur boolenne false la proprit cacheAsBitmap :
DisplayObject.cacheAsBitmap = false;

Lorsque la mise en cache est dsactive, le bitmap associ est automatiquement supprim de la mmoire. Afin de mettre en avant lintrt de la mise en cache des bitmap lexcution nous allons mettre cette fonctionnalit en pratique. Dans un nouveau document Flash CS3 nous crons un nouveau symbole clip reprsentant une pomme. La figure 12-20 illustre le symbole utilis :

39 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-20. Symbole Pomme. Grce au panneau Liaison nous associons une classe nomme Pomme que nous dfinissons a ct du document Flash en cours. Celle-ci contient le code suivant :
package { import flash.display.MovieClip; import flash.events.Event; public class Pomme extends MovieClip { private var destinationX:Number; private var destinationY:Number; public function Pomme () { addEventListener ( Event.ADDED_TO_STAGE, ajoutAffichage ); addEventListener ( Event.REMOVED_FROM_STAGE, supprimeAffichage ); } public function ajoutAffichage ( pEvt:Event ):void { init();

40 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

addEventListener ( Event.ENTER_FRAME, mouvement ); } private function supprimeAffichage ( ):void { removeEventListener ( Event.ENTER_FRAME, mouvement ); } private function init ( ):void { destinationX = Math.random()*(stage.stageWidth-width); destinationY = Math.random()*(stage.stageHeight-height); } private function mouvement ( pEvt:Event ):void { x -= ( x - destinationX ) *.5; y -= ( y - destinationY ) *.5; if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y destinationY ) < 1 ) init(); } } }

Puis nous affichons diffrentes instances du symbole Pomme :


var conteneur:Sprite = new Sprite(); addChild ( conteneur ); for ( var i:int = 0; i< 100; i++ ) { var maPomme:Pomme = new Pomme(); conteneur.addChild ( maPomme ); }

Chaque instance se dplace alatoirement sur la scne comme lillustre la figure 12-21 :

41 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-21. Dplacement des pommes. Nous remarquons que lanimation nest pas trs fluide. Nous allons optimiser le rendu en activant la mise en cache des bitmaps lexcution :
var conteneur:Sprite = new Sprite(); addChild ( conteneur ); for ( var i:int = 0; i< 100; i++ ) { var maPomme:Pomme = new Pomme(); conteneur.addChild ( maPomme ); } stage.addEventListener ( MouseEvent.CLICK, miseEnCache ); function miseEnCache ( pEvt:MouseEvent ):void { var lng:int = conteneur.numChildren; for ( var i:int = 0; i< lng; i++) { var pomme:Pomme = Pomme ( conteneur.getChildAt ( i ) ); pomme.cacheAsBitmap = ! Boolean ( pomme.cacheAsBitmap );

42 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

} }

Lorsque nous cliquons sur la scne, nous activons ou dsactivons la mise en cache des bitmap sur chaque pomme, le rendu est grandement acclr. Lutilisation de cette fonctionnalit entrane les mmes considrations que la cration dimages bitmaps avec la classe BitmapData. Chaque pomme mesure 47,5 * 43 pixels, cela correspond pour chaque pomme une image bitmap de 7,97 Ko en mmoire. Nous affichons dans le code prcdent 100 pommes, lactivation de la mise en cache des bitmap consomme donc pour cette animation 797 Ko en mmoire vive. Ainsi, il convient de veiller aux dimensions de lobjet mis en cache. Un lment vectoriel de plus de 2880 pixels ne peut tre mis en cache car la cration dune telle image bitmap en mmoire est impossible pour les raisons voques en dbut de chapitre. Cette fonctionnalit de mise en cache des bitmap peut paratre comme la situation un grand nombre de problmes lis aux performances, mais il faut prendre en considration certains effets pervers souvent mconnus pouvant inverser la donne.

A retenir
Afin dactiver la mise en cache des bitmap lexcution, nous passons la valeur true la proprit cacheAsBitmap. Afin de dsactiver la mise en cache des bitmap lexcution, nous passons la valeur false la proprit cacheAsBitmap. Lintrt de la mise en cache des bitmap, consiste afficher une reprsentation bitmap de lobjet vectoriel.

La mise en cache des bitmap augmente sensiblement la vitesse de rendu mais requiert plus de mmoire. Lorsque la mise en cache des bitmaps est dsactive, limage bitmap associe est supprime de la mmoire.

Effets pervers
La mise en cache des bitmap lexcution est une fonctionnalit qui doit tre utilise avec rflexion. Si celle-ci nest pas maitrise, nous obtenons linverse du rsultat escompt. Lorsquun DisplayObject subit une transformation autre quune simple translation en x et y, la mise en cache des bitmap est viter. Chaque tirement, rotation, changement dopacit, ou dplacement de
43 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

la tte de lecture ncessite une mise jour de limage bitmap cre en mmoire. Afin de mettre en vidence cet effet pervers, nous ajoutons une image cl limage 10 du symbole Pomme comme lillustre la figure 12-22 :

Figure 12-22. Agrandissement du symbole Pomme. Sur cette image cl, nous agrandissons la taille de la pomme. Si nous testons nouveau notre animation et activons la mise en cache des bitmap. Nous remarquons qu chaque passage de la tte de lecture sur limage 10, le lecteur dtecte un changement de taille et met jour limage bitmap en cache. Ce processus ralentit grandement laffichage et annule lintrt de la fonctionnalit. De la mme manire, si nous procdons simplement une rotation de chaque instance, le lecteur met jour le bitmap pour chaque nouvelle image. Nous modifions la mthode mouvement au sein de la classe Pomme :
private function mouvement ( pEvt:Event ):void { rotation += 5; x -= ( x - destinationX ) *.5; y -= ( y - destinationY ) *.5; if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y - destinationY ) < 1 ) init(); }

44 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Pour chaque nouvelle image parcourue, le lecteur Flash met jour le bitmap associ. Il convient donc dutiliser la mise en cache des bitmap uniquement lorsque lobjet subit une translation en x et y. Nous allons nous intresser maintenant un autre effet pervers. La figure 12-23 illustre un symbole clip contenant plusieurs instances du symbole Pomme :

Figure 12-23. Clip contenant diffrentes instances du symbole Pomme. Afin de gagner du temps, nous pourrions tre tents dactiver la mise en cache sur le clip conteneur. Cela entrane la cration dune image bitmap transparente en mmoire de la taille du conteneur comme lillustre la figure 12-24 :

45 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-24. Dimensions du clip conteneur. La mise en cache du clip conteneur cre un bitmap de 229,95 Ko en mmoire. Toute la surface transparente est stocke en mmoire inutilement. Il serait plus judicieux dactiver la mise en cache sur chaque instance du symbole Pomme, ce qui ncessiterait 55,79 Ko en mmoire. Il est aussi possible dactiver la mise en cache des bitmap lexcution au sein de lenvironnement auteur en slectionnant lobjet graphique mettre en cache puis en cochant la case correspondante au sein de linspecteur de proprits :

Figure 12-25. Mise en cache des bitmaps lexcution depuis lenvironnement auteur. Il convient dutiliser cette fonctionnalit avec attention, de plus lutilisation de filtres est directement lie la mise en cache des bitmaps lexcution. Nous allons nous y intresser prsent afin de mieux comprendre le fonctionnement des filtres.

A retenir
La mise en cache des bitmaps lexcution doit tre active uniquement sur des objets subissant une translation en x et y. Le lecteur met jour le bitmap associ pour tout autre modification. Si cette fonctionnalit nest pas matrise, nous obtenons un ralentissement de la vitesse de rendu et une forte occupation mmoire.

Filtrer un lment vectoriel


Les filtres sont intimement lis la notion de bitmap. Lorsque nous utilisons un filtre sur un objet vectoriel, le lecteur Flash cre en mmoire deux images bitmaps afin de produire le rsultat filtr. La figure 12-26 illustre le mcanisme interne :

46 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-26. Mcanisme de cration de filtres. Le premier bitmap est utilis pour reprsenter lobjet non filtr, en ralit le lecteur utilise la mise en cache des bitmaps lexcution afin de gnrer un premier bitmap sur lequel travailler pour appliquer le filtre. Le deuxime bitmap sert accueillir le rendu filtr. Lutilisation de filtres requiert donc deux fois plus de mmoire que la mise en cache des bitmap lexcution et entrane les mmes prcautions dutilisation. Afin daffecter un filtre un DisplayObject, nous affectons un tableau de filtres la proprit filters. Lutilisation dun tableau permet de cumuler plusieurs filtres appliqus un objet graphique et de modifier la superposition de chaque filtre. Dans un nouveau document Flash CS3, nous posons une instance du symbole Pomme sur la scne et lui donnons comme nom doccurrence pomme :

47 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-27. Occurrence du symbole Pomme. Puis nous ajoutons dynamiquement un des filtres situs dans le paquetage flash.filters. Voici en dtail les diffrents filtres disponibles :
flash.filters.BevelFilter : applique un effet de biseau. flash.filters.GradientBevelFilter : applique un effet de biseau dgrad. flash.filters.BlurFilter : applique un effet de flou. flash.filters.GlowFilter : applique rayonnement. un effet de

flash.filters.GradientGlowFilter : applique un effet de rayonnement dgrad. flash.filters.ColorMatrixFilter : transformation de couleurs chaque pixel. applique une

flash.filters.ConvolutionFilter : applique un filtre de convolution de matrice. flash.filters.DisplacementMapFiler : applique un effet de dplacement sur chaque pixel. flash.filters.DropShadowFilter : applique dombre porte. un effet

Nous allons utiliser la classe BlurFilter pour affecter un filtre de flou, dont voici le constructeur :
public fonction BlurFilter(blurX:Number = 4.0, blurY:Number = 4.0, quality:int = 1)

Les deux premiers paramtres concernent la dilatation des pixels pour chaque axe. Le dernier paramtre permet de spcifier la qualit du
48 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

rsultat final, il sagit en ralit du nombre de passage du filtre. Une valeur comprise entre 1 et 3 est gnralement utilise. Quelque soit la qualit du filtre ou dilatation des pixels, le poids des images bitmaps cres en mmoire reste le mme. A linverse, la vitesse daffichage est fortement lie la dilatation des pixels ainsi que la qualit. Pour des raisons de performances il est fortement recommand de toujours utiliser des multiples de 2 pour les quantits de flous et de ne jamais dpasser une qualit de 15. Dans le code suivant nous ajoutons un filtre de flou :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter (10, 10, 1); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours;

La figure 12-28 illustre le rsultat :

Figure 12-28. Filtre de flou.

49 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Trois qualits de filtres sont disponibles et accessible depuis des constantes de la classe BitmapFilterQuality :
BitmapFilterQuality.LOW : qualit infrieure. BitmapFilterQuality.MEDIUM : qualit moyenne.

BitmapFilterQuality.HIGH : qualit suprieure, sapproche du flou gaussien.

Il est donc possible de spcifier manuellement la qualit du filtre, mais pour des raisons de portabilit nous prfrons toujours utiliser des constantes de classe :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours;

Une fois le filtre appliqu nous remarquons que la mise en cache des bitmaps est active automatiquement :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // affiche : true trace( pomme.cacheAsBitmap );

Linstance du symbole Pomme mesure 122 * 110 pixels. Dans notre exemple, le filtre de flou pse en mmoire 104,84 Ko. Afin de faciliter le calcul du poids dun objet en mmoire contenant diffrents filtres nous pouvons ajouter au sein de notre classe BitmapOutils une nouvelle mthode appele poidsFiltres :
package org.outils { import flash.display.BitmapData; import flash.display.DisplayObject;

50 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

public class BitmapOutils { public static function hexArgb ( pCouleur:Number ):Object { var composants:Object = new Object(); composants.alpha = (pCouleur >>> 24) & 0xFF; composants.rouge = (pCouleur >>> 16) & 0xFF; composants.vert = (pCouleur >>> 8) & 0xFF; composants.bleu = pCouleur & 0xFF; return composants; } public static function argbHex ( pAlpha:int, pRouge:int, pVert:int, pBleu:int ):uint { return ( pAlpha << 24 | pRouge << 16 | pVert << 8 | pBleu ); } public static function poids ( pBitmapData:BitmapData ):Number { return (pBitmapData.width * pBitmapData.height) * 4; } ):Number public static function poidsFiltres ( pDisplayObject:DisplayObject { return (((pDisplayObject.width * pDisplayObject.height) * 4) * pDisplayObject.filters.length) * 2; } } }

Cette mthode nous permet de connatre le poids total dun objet filtr en mmoire :
import org.outils.BitmapOutils; // cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou );

51 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

// affectation du filtre pomme.filters = filtresEnCours; // affiche : 112.08052734375 trace( BitmapOutils.poidsFiltres ( pomme ) / 1024 );

Linstance du symbole Pomme pse en mmoire environ 112 Ko. Si nous souhaitons ajouter de nouveaux filtres il nest pas possible dajouter directement un filtre au tableau filters :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee );

Nous devons obligatoirement affecter nouveau la proprit filters un tableau contenant la totalit des filtres :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee ); // affectation des filtres pomme.filters = filtresEnCours;

Le code prcdent gnre le rsultat suivant :

52 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-29. Filtre de flou et ombre porte. Si nous valuons nouveau le poids de linstance du symbole Pomme en mmoire, nous remarquons que son poids en mmoire a doubl et ncessite dsormais 224 Ko en mmoire :
import org.outils.BitmapOutils; // cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee ); // affectation des filtres pomme.filters = filtresEnCours; // affiche : 224.1610546875 trace( BitmapOutils.poidsFiltres ( pomme ) / 1024 );

53 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Il nexiste aucune mthode native permettant dinverser la position des filtres au sein du tableau filters. Si nous souhaitons supprimer un filtre, nous devons travailler avec les mthodes de classe Array puis affecter nouveau le tableau de filtres modifi. Dans le code suivant nous supprimons le filtre de flou :
import org.outils.BitmapOutils; // cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee ); // suppression du filtre de flou filtresEnCours.splice ( 0, 1 ); // affectation des filtres pomme.filters = filtresEnCours; // affiche : 112.08052734375 trace( BitmapOutils.poidsFiltres ( pomme ) / 1024 );

La figure 12-30 illustre le rsultat :

54 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-30. Ombre porte. A laide de la mthode splice nous avons supprim le filtre de flou positionn lindex 0. Dans certaines situations, si nous ne connaissons pas la position dun filtre au sein du tableau interne, nous pouvons utiliser la mthode indexOf de la classe Array :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee ); // rcupre la position du filtre var position:int = filtresEnCours.indexOf( filtreFlou ); // si le filtre est trouv if ( position != -1 ) {

55 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

// le filtre est supprim filtresEnCours.splice ( position, 1 ); } else trace ( "Filtre non prsent !" ); // affectation des filtres pomme.filters = filtresEnCours;

Grce la mthode indexOf, nous navons pas besoin de savoir la position du filtre dans le tableau interne. Lindex retourn nous permet de supprimer le filtre correspondant. Il nexiste pas de mthode dispose pour librer la mmoire utilise par un filtre. Si nous souhaitons librer les ressources nous devons supprimer les rfrences pointant vers le filtre. Dans le code suivant, nous rendons le filtre de flou ligible la suppression par le ramassemiettes :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee ); // rcupre la position du filtre var position:int = filtresEnCours.indexOf( filtreFlou ); // si le filtre est trouv if ( position != -1 ) { // le filtre est supprim filtresEnCours.splice ( position, 1 ); // puis dsactiv filtreFlou = null;

} else trace ( "Filtre non prsent !" ); // affectation des filtres pomme.filters = filtresEnCours;

Afin

supprimer la totalit des filtres affects un DisplayObject, nous affectons la proprit filters un tableau vide :
// cration du filtre de flou

de

56 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee ); // affectation des filtres pomme.filters = filtresEnCours; // crase le tableau de filtres existants filtresEnCours = new Array(); // dsactivation des filtres filtreFlou = null; ombrePortee = null; // mise jour de l'affichage pomme.filters = filtresEnCours;

La figure 12-31 illustre le rsultat final :

Figure 12-31. Suppression des filtres.


57 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Une fois que les filtres ne sont plus lis au DisplayObject, la mise en cache des bitmaps lexcution est dsactive automatiquement :
// cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // tableau de filtres var filtresEnCours:Array = new Array(); // ajout du filtre de flou filtresEnCours.push ( filtreFlou ); // affectation du filtre pomme.filters = filtresEnCours; // cration d'une ombre porte var ombrePortee:DropShadowFilter = new DropShadowFilter (); // ajout du filtre d'ombre porte filtresEnCours.push ( ombrePortee ); // affectation des filtres pomme.filters = filtresEnCours; // crase le tableau de filtres existants filtresEnCours = new Array(); // dsactivation des filtres filtreFlou = null; ombrePortee = null; // mise jour de l'affichage pomme.filters = filtresEnCours; // affiche : false trace( pomme.cacheAsBitmap );

Attention, la dsactivation des filtres en mmoire par suppression des rfrences repose sur lintervention du ramasse-miettes.

A retenir

58 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Lutilisation de filtres sur un DisplayObject active automatiquement la mise en cache des bitmaps lexcution. En plus du premier bitmap cr lors de la mise en cache des bitmaps lexcution, un deuxime est cr afin de produire le rendu filtr. Les mmes prcautions dutilisation lies lactivation de la mise en cache des bitmaps doivent tre appliques lors de lutilisation de filtres appliqus des lments vectoriels. La dsactivation des filtres sappuie sur lintervention du ramassemiettes.

Filtrer une image bitmap


Il est aussi possible dappliquer diffrents filtres une image bitmap. Pour cela nous utilisons la mthode applyFilter de la classe BitmapData dont voici la signature :
public function applyFilter(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, filter:BitmapFilter):void

Celle ci accepte quatre paramtres :


sourceBitmapData : les donnes bitmaps filtrer. destPoint : point de dpart utilis par le rectangle. Filter : le filtre appliquer. sourceRect : la zone sur laquelle le filtre est appliqu.

Dans le code suivant, nous instancions une image provenant de la bibliothque. Un filtre de flou est partiellement appliqu :
// instanciation du logo var donneesBitmap:Logo = new Logo ( 0, 0 ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); monImage.x = (stage.stageWidth - monImage.width) / 2; monImage.y = (stage.stageHeight - monImage.height) / 2 addChild ( monImage ); // cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // dfinition d'une surface var zone:Rectangle = new Rectangle ( 0, 0, 80, 60 ); // affectation du filtre de flou sur une surface limite donneesBitmap.applyFilter( donneesBitmap, zone, new Point(0,0), filtreFlou );

La figure 12-32 illustre le rsultat :

59 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Figure 12-32. Filtre partiel. Afin daffecter le filtre sur la surface totale de limage, nous passons lobjet Rectangle accessible par la proprit rect de la classe BitmapData :
// instanciation du logo var donneesBitmap:Logo = new Logo ( 0, 0 ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); monImage.x = (stage.stageWidth - monImage.width) / 2; monImage.y = (stage.stageHeight - monImage.height) / 2 addChild ( monImage ); // cration du filtre de flou var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); // dfinition d'une surface var zone:Rectangle = donneesBitmap.rect; // affectation du filtre de flou sur une surface limite donneesBitmap.applyFilter( donneesBitmap, zone, new Point(0,0), filtreFlou );

Ainsi, le filtre est appliqu sur la totalit de limage :

60 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-33. Image entirement filtre. Lorsquun filtre est appliqu une image bitmap laide de la mthode applyFilter, le lecteur ne cre aucun bitmap supplmentaire en mmoire afin de produire le rendu filtr. Les pixels de limage bitmap sont directement travaills sur linstance de BitmapData. Lorsquun filtre est associ un DisplayObject, il est possible de faire marche arrire et de supprimer le filtre. A linverse, lorsquun filtre est appliqu une image bitmap, les pixels sont dfinitivement modifis. Il est impossible de faire marche arrire.

A retenir
Lutilisation de filtres sur une instance de BitmapData ne cre aucune image bitmap supplmentaire en mmoire.

Animer un filtre
Afin de donner du mouvement un filtre nous devons modifier les valeurs correspondantes puis appliquer constamment le filtre afin de mettre jour laffichage. Dans le cas de filtres appliqus un DisplayObject nous pouvons crire le code suivant :
stage.addEventListener ( Event.ENTER_FRAME, animFiltre ); var tableauFiltres:Array = new Array();

61 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); tableauFiltres.push ( filtreFlou ); var i:Number = 0; function animFiltre ( pEvt:Event ):void { // valeur oscillant entre 1 et 21 var oscillation:Number = Math.floor ( Math.sin ( i += .2 ) * 10 + 11 ); filtreFlou.blurX = filtreFlou.blurY = oscillation pomme.filters = tableauFiltres; }

Le code prcdent fait osciller la quantit de flou entre 1 et 21 donnant un effet dondulation. Pour reproduire le mme effet sur une instance de BitmapData, nous utilisons la mthode applyFilter :
var donneesBitmap:Logo = new Logo ( 0, 0 ); var copieDonneesBitmap:BitmapData = donneesBitmap.clone(); var monImage:Bitmap = new Bitmap ( donneesBitmap ); monImage.x = (stage.stageWidth - monImage.width) / 2; monImage.y = (stage.stageHeight - monImage.height) / 2 addChild ( monImage ); stage.addEventListener ( Event.ENTER_FRAME, animFiltre ); var tableauFiltres:Array = new Array(); var filtreFlou:BlurFilter = new BlurFilter ( 10, 10, BitmapFilterQuality.HIGH ); tableauFiltres.push ( filtreFlou ); var i:Number = 0; function animFiltre ( pEvt:Event ):void { // valeur oscillant entre 1 et 20 var oscillation:Number = Math.floor ( Math.sin ( i += .2 ) * 10 + 11 ); filtreFlou.blurX = filtreFlou.blurY = oscillation; donneesBitmap.copyPixels ( copieDonneesBitmap, copieDonneesBitmap.rect, new Point ( 0, 0 ) ); donneesBitmap.applyFilter ( donneesBitmap, donneesBitmap.rect, new Point ( 0, 0 ), filtreFlou ); }

62 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

Trs souvent, les filtres sont utiliss sur des DisplayObject dj anims. Dans le cas dune agence Web, le graphiste anime gnralement des objets et affecte diffrents filtres. Lanimation du filtre est alors gre automatiquement par le lecteur Flash. Pour chaque tape de lanimation, le lecteur Flash met jour les deux bitmaps en mmoire afin de produire le rendu filtr. Ce processus savre complexe et peut ralentir grandement la vitesse daffichage. Il est donc conseill de ne pas utiliser un trop grand nombre de filtres sous peine de voir les performances de lanimation chuter.

Rendu bitmap dobjet vectoriels


La mthode draw de la classe BitmapData savre tre une mthode trs intressante. Celle-ci permet de rendre sous forme bitmap nimporte quel DisplayObject. Cette fonctionnalit ouvre un grand nombre de possibilits que nous explorerons au cours des prochains chapitres. Voici la signature de la mthode draw :
public function draw(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:String = null, clipRect:Rectangle = null, smoothing:Boolean = false):void

Chacun des paramtres permet de travailler sur limage bitmap :


source : objet source dessiner. matrix : matrice de transformation

colorTransform : objet de transformation de couleur permettant de teinter limage bitmap gnre. blendMode : mode de fondu appliquer limage bitmap gnre. clipRect : surface dfinissant la zone dessiner. smoothing : boolen dfinissant le lissage de limage bitmap gnre.

Dans un nouveau document Flash CS3, nous plaons une instance du symbole Pomme sur la scne et lui donnons comme nom doccurrence pomme. Linstance a t agrandie denviron 300% :

63 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-34. Instance du symbole Pomme. Nous allons rendre sous forme bitmap une instance du symbole Pomme pose sur la scne. Pour cela nous crons une image bitmap afin daccueillir les pixels dessins :
// cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height, false, 0xCCCCCC ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // positionnement de la copie bitmap monImage.x += 310; monImage.y += pomme.y addChild ( monImage );

Nous rcuprons les dimensions de lobjet rendre sous forme bitmap afin de crer un bitmap de destination aux dimensions identiques. Souvenez-vous que la cration de bitmap entrane une forte occupation mmoire, chaque pixel doit tre optimis. La figure 12-35 illustre le rsultat :

64 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-35. Image bitmap. Puis nous passons la mthode draw linstance du symbole rendre sous forme bitmap :
// cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height, false, 0xCCCCCC ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // positionnement de la copie bitmap monImage.x += 310; monImage.y += pomme.y addChild ( monImage ); // rend sous forme bitmap les donnes vectorielles donneesBitmap.draw ( pomme );

Le rsultat suivant est gnr :

65 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-36. Rendu bitmap. Nous remarquons que le rendu bitmap ne possde pas les mmes dimensions que linstance. La mthode draw dessine par dfaut lobjet selon son tat en bibliothque. De la mme manire si nous faisons subir une rotation ou un tirement linstance du symbole, le rendu bitmap ne reflte pas ces modifications :

66 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-37. Rendu sans transformations. Si nous souhaitons prendre en considration les transformations actuelles de lobjet dessiner, nous devons utiliser une matrice de transformation. Dans notre exemple, nous allons passer la matrice de transformation de linstance. Celle-ci est accessible depuis la proprit matrix de la classe Transform, elle-mme accessible depuis la proprit transform de tout DisplayObject :
// cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height, false, 0xCCCCCC ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // positionnement de la copie bitmap monImage.x += 310; monImage.y += pomme.y addChild ( monImage ); // transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // rend sous forme bitmap les donnes vectorielles en conservant les transformations donneesBitmap.draw ( pomme, transformationPomme );

Ainsi, le rendu bitmap prend en considration les transformations de linstance. La figure 12-37 illustre le rsultat :
67 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-37. Rendu avec transformations. Les pixels ont donc t dessins avec un dcalage correspondant la position de linstance par rapport la scne. Dans notre cas, nous souhaitons conserver les mmes dimensions. Afin de dcaler les pixels du bitmap, nous utilisons les proprits tx et ty de la classe Matrix. Celles-ci nous permettent de modifier la translation des pixels dans limage bitmap gnre :
// cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height, false, 0xCCCCCC ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // positionnement de la copie bitmap monImage.x += 310; monImage.y += pomme.y addChild ( monImage ); // transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // repositionne les pixels transformationPomme.tx = 0; transformationPomme.ty = 0; // rend sous forme bitmap les donnes vectorielles en conservant les transformations donneesBitmap.draw ( pomme, transformationPomme );

68 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-38 illustre le rsultat :

La figure 12-38. Translation. Nous pouvons voir que limage bitmap est tronque sur le cot gauche et sur le cot suprieur de limage. Cela vient du fait que les vecteurs composant le symbole Pomme passent en dessous de 0 pour laxe x et y. Attention, lorsque la mthode draw est utilise, le point denregistrement de lobjet rasteris devient le point haut gauche du BitmapData gnr. Afin dviter cette coupure nous pouvons dplacer simplement les pixels en procdent une simple translation de quelques pixels :
// dcalage en pixels var decalage:int = 5; // cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width+decalage, pomme.height+decalage, false, 0xCCCCCC ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // positionnement de la copie bitmap monImage.x += 310; monImage.y += pomme.y - decalage; addChild ( monImage );

69 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

// transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // repositionne les pixels transformationPomme.tx = decalage; transformationPomme.ty = decalage; // rend sous forme bitmap les donnes vectorielles en conservant les transformations donneesBitmap.draw ( pomme, transformationPomme );

Puis nous adaptons la taille du bitmap de destination et supprimons la couleur de fond :


// dcalage en pixels var decalage:int = 5; // cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width+decalage, pomme.height+decalage ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // positionnement de la copie bitmap monImage.x += 310; monImage.y += pomme.y - decalage; addChild ( monImage ); // transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // repositionne les pixels transformationPomme.tx = decalage; transformationPomme.ty = decalage; // rend sous forme bitmap les donnes vectorielles en conservant les transformations donneesBitmap.draw ( pomme, transformationPomme );

La figure 12-39 illustre le rsultat :

70 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-39. Rendu bitmap de linstance. Comme vous pouvez limaginer, lvaluation manuelle dune valeur de translation ne savre pas la solution la plus souple. Dans la pratique, il serait idal de pouvoir dterminer dynamiquement la translation ncessaire limage bitmap sans risquer que celle-ci soit coupe lors de la capture. Afin dvaluer les dbordements de lobjet vectoriel, nous pouvons utiliser la mthode getBounds de la classe DisplayObject. Dans le code suivant nous dterminons le dbordement du symbole Pomme laide de la mthode getBounds :
var debordement:Rectangle = pomme.getBounds ( pomme ); // affiche : (x=-1, y=-0.5, w=48.75, h=44.5) trace( debordement );

La mthode getBounds renvoie un objet Rectangle dont les proprits x et y nous renvoient les dbordements pour les axes respectifs. Grce celles-ci nous pouvons tablir une translation dynamique, plus souple quun dcalage manuel :
// cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height ); var monImage:Bitmap = new Bitmap ( donneesBitmap );

71 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

// positionnement de la copie bitmap monImage.x += 310; monImage.y += pomme.y; addChild ( monImage ); // transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // repositionne les pixels transformationPomme.tx = 0; transformationPomme.ty = 0; var debordement:Rectangle = pomme.getBounds ( pomme ); // dcalage rpercut sur la matrice de transformation transformationPomme.translate ( -debordement.x * pomme.scaleX, -debordement.y * pomme.scaleY ); // rend sous forme bitmap les donnes vectorielles en conservant les transformations // grce la translation dynamique ralise, l'image bitmap n'est pas coupe donneesBitmap.draw ( pomme, transformationPomme );

Nous obtenons ainsi de manire dynamique le mme rsultat que prcdemment :

La figure 12-40. Rendu bitmap de linstance avec valuation dynamique du dbordement. Comme nous lavons vu prcdemment, le symbole Pomme possde son point denregistrement en haut gauche. Grce la dtection du
72 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

dbordement du symbole, nous pouvons ainsi grer la capture de symboles ayant leur point denregistrement au centre. Si nous dplaons le point denregistrement du symbole Pomme en son centre, la capture fonctionne parfaitement sans aucune modification du code except le positionnement de limage bitmap :
// positionnement de la copie bitmap monImage.x += 310; monImage.y = pomme.y - (pomme.height * .5);

Nous allons prsent rcuprer la couleur de chaque pixel composant notre image bitmap. Pour cela nous devons cliquer sur la pomme bitmap et accder la couleur du pixel cliqu. La classe Bitmap nest pas une sous-classe de la classe InteractiveObject, ainsi si nous souhaitons interagir avec une image bitmap nous devons au pralable la placer dans un conteneur de type InteractiveObject. Pour cela, nous utilisons la classe flash.display.Sprite. Nous plaons limage bitmap au sein dun conteneur :
// cration d'un conteneur pour l'image bitmap var conteneurImage:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurImage ); // cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // ajout de l'image au conteneur conteneurImage.addChild ( monImage ); // positionnement de la copie bitmap conteneurImage.x += 310; conteneurImage.y += pomme.y - (pomme.height * .5); // transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // repositionne les pixels transformationPomme.tx = 0; transformationPomme.ty = 0; var debordement:Rectangle = pomme.getBounds ( pomme ); // dcalage rpercut sur la matrice de transformation transformationPomme.translate ( -debordement.x * pomme.scaleX, -debordement.y * pomme.scaleY ); // rend sous forme bitmap les donnes vectorielles en conservant les transformations // grce la translation dynamique ralise, l'image bitmap n'est pas coupe

73 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

donneesBitmap.draw

( pomme, transformationPomme );

Puis nous coutons diffrents vnements lis linteractivit sur le conteneur afin de rcuprer la couleur du pixel survol :
// cration d'un conteneur pour l'image bitmap var conteneurImage:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurImage ); // cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // ajout de l'image au conteneur conteneurImage.addChild ( monImage ); // positionnement de la copie bitmap conteneurImage.x += 310; conteneurImage.y += pomme.y - (pomme.height * .5); // transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // repositionne les pixels transformationPomme.tx = 0; transformationPomme.ty = 0; var debordement:Rectangle = pomme.getBounds ( pomme ); // dcalage rpercut sur la matrice de transformation transformationPomme.translate ( -debordement.x * pomme.scaleX, -debordement.y * pomme.scaleY ); // rend sous forme bitmap les donnes vectorielles en conservant les transformations // grce la translation dynamique ralise, l'image bitmap n'est pas coupe donneesBitmap.draw ( pomme, transformationPomme ); // coute des vnements MouseEvent.MOUSE_DOWN et MouseEvent.MOUSE_UP // auprs du conteneur et de l'objet Stage (cela permet de capter le relchement en dehors du conteneur) conteneurImage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); stage.addEventListener ( MouseEvent.MOUSE_UP, relacheSouris ); function clicSouris ( pEvt:MouseEvent ):void { bougeSouris ( pEvt ); pEvt.currentTarget.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } function relacheSouris ( pEvt:MouseEvent ):void { conteneurImage.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris );

74 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

} function bougeSouris ( pEvt:MouseEvent ):void { // accde la couleur du pixel survol var couleurPixel:Number = donneesBitmap.getPixel32 ( pEvt.localX, pEvt.localY ); // affiche : ffd8631f trace( couleurPixel.toString ( 16 ) ); }

Enfin, nous ajoutons une image bitmap de taille rduite et une lgende afin dafficher la couleur survole :
// cration d'un conteneur pour l'image bitmap var conteneurImage:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( conteneurImage ); // cration des donnes bitmaps aux dimensions de l'image source var donneesBitmap:BitmapData = new BitmapData ( pomme.width, pomme.height ); var monImage:Bitmap = new Bitmap ( donneesBitmap ); // ajout de l'image au conteneur conteneurImage.addChild ( monImage ); // positionnement de la copie bitmap conteneurImage.x += 310; conteneurImage.y += pomme.y - (pomme.height * .5); // transformations actuelles de la pomme var transformationPomme:Matrix = pomme.transform.matrix; // repositionne les pixels transformationPomme.tx = 0; transformationPomme.ty = 0; var debordement:Rectangle = pomme.getBounds ( pomme ); // dcalage rpercut sur la matrice de transformation transformationPomme.translate ( -debordement.x * pomme.scaleX, -debordement.y * pomme.scaleY ); // rend sous forme bitmap les donnes vectorielles en conservant les transformations // grce la translation dynamique ralise, l'image bitmap n'est pas coupe donneesBitmap.draw ( pomme, transformationPomme ); // coute des vnements MouseEvent.MOUSE_DOWN et MouseEvent.MOUSE_UP // auprs du conteneur et de l'objet Stage (cela permet de capter le relchement en dehors du conteneur) conteneurImage.addEventListener ( MouseEvent.MOUSE_DOWN, clicSouris ); stage.addEventListener ( MouseEvent.MOUSE_UP, relacheSouris ); function clicSouris ( pEvt:MouseEvent ):void

75 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

{ bougeSouris ( pEvt ); pEvt.currentTarget.addEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } function relacheSouris ( pEvt:MouseEvent ):void { conteneurImage.removeEventListener ( MouseEvent.MOUSE_MOVE, bougeSouris ); } function bougeSouris ( pEvt:MouseEvent ):void { // accde la couleur du pixel survol var couleurPixel:Number = donneesBitmap.getPixel32 ( pEvt.localX, pEvt.localY ); // force le rafraichissement pEvt.updateAfterEvent(); // remplissage du bitmap d'aperu apercuCouleur.fillRect ( apercuCouleur.rect, couleurPixel ); } // cration d'une image bitmap d'aperu de selection de couleur var apercuCouleur:BitmapData = new BitmapData ( 120, 60, false, 0 ); var imageApercu:Bitmap = new Bitmap ( apercuCouleur ); // positionnement imageApercu.x = 210; imageApercu.y = 320; // ajout la liste d'affichage addChild ( imageApercu ); // cration d'une lgende var legende:TextField = new TextField(); legende.x = 210; legende.y = 300; // redimensionnement automatique legende.autoSize = TextFieldAutoSize.LEFT; // formatage du texte var formatage:TextFormat = new TextFormat(); formatage.font = "Arial"; formatage.size = 11; // affectation du formatage legende.defaultTextFormat = formatage

76 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

legende.text = "Couleur slectionne :"; addChild ( legende );

A la slection dune zone sur limage bitmap nous obtenons un aperu comme lillustre la figure 12-41 :

La figure 12-41. Slection dune couleur. La mthode getPixel32 nous permet de rcuprer la couleur de chaque pixel survol. Puis nous colorons limage bitmap apercuCouleur afin dobtenir un aperu. La capture dlments vectoriels sous forme bitmap offre de nouvelles possibilits comme la compression dimages au format JPEG ou lencodage au format PNG, GIF ou autres. Rendez-vous au chapitre 20 intitul ByteArray pour plus de dtails concernant lencodage dimages sous diffrents formats.

A retenir

77 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La mthode draw permet de rendre sous forme bitmap un lment vectoriel. Afin dappliquer des transformations au sein du bitmap rendu, nous utilisons une matrice de transformation.

Optimiser les performances


Nous allons utiliser la classe BitmapData afin doptimiser les performances dune animation traditionnelle. En crant une classe AnimationBitmap nous allons capturer chaque tat de lanimation vectorielle sous forme dimage bitmap, puis simuler lanimation en les affichant successivement. Cela va nous permettre damliorer les performances de rendu et rduire loccupation processeur. Dans un nouveau document Flash CS3 nous utilisons un personnage anim, la figure 12-42 illustre lanimation :

La figure 12-42. Animation du personnage. Par le panneau Liaison nous lions le symbole anim une classe Garcon puis nous linstancions avec le code suivant :
78 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

// instancie l'animation var monPersonnage:Garcon = new Garcon();

A cot du document flash en cours nous crons un rpertoire bitmap puis nous plaons lintrieur une classe nomme AnimationBitmap. Celle-ci contient le code suivant :
package bitmap { import import import import import import flash.display.Bitmap; flash.display.BitmapData; flash.display.MovieClip; flash.events.TimerEvent; flash.geom.Matrix; flash.utils.Timer;

public class AnimationBitmap extends Bitmap { // tableau stockant chaque tape de l'animation sous forme bitmap private var tableauBitmaps:Array; // sauve une rfrence vers l'objet vectoriel animer private var animationCible:MovieClip; // cration d'un minuteur pour grr l'animation private var minuteur:Timer; // variable d'incrmentation private var i:int; public function AnimationBitmap ( pCible:MovieClip, pVitesse:int=100 ) { i = 0; animationCible = pCible; tableauBitmaps = new Array(); minuteur = new Timer ( pVitesse, 0 ); minuteur.addEventListener ( TimerEvent.TIMER, anime ); genereEtapes (); } private function genereEtapes ():void { // rcupre le nombre dimage totale var lng:int = animationCible.totalFrames; var etapeAnimation:BitmapData; for ( var i:int = 0; i< lng; i++ ) { // parcours l'animation animationCible.gotoAndStop(i); // cre un bitmap pour chaque tape

79 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

etapeAnimation = new BitmapData ( animationCible.width + 30, animationCible.height, true, 0 ); // rend l'image sous forme bitmap etapeAnimation.draw ( animationCible ); tableauBitmaps.push ( etapeAnimation ); } minuteur.start(); } private function anime ( pEvt:TimerEvent ):void { // met jour l'affichage, l'animation se joue en boucle bitmapData = tableauBitmaps[i++%tableauBitmaps.length]; } } }

Puis nous passons au constructeur de la classe AnimationBitmap lobjet vectoriel animer sous forme bitmap puis sa vitesse de lecture :
// instancie l'animation var monPersonnage:Garcon = new Garcon(); // cre l'animation var animPersonnage:AnimationBitmap = new AnimationBitmap ( monPersonnage, 30 ); // positionnement animPersonnage.x = 20; animPersonnage.y = 100; // ajout la liste d'affichage addChild ( animPersonnage );

Si nous zoomons sur lanimation gnre, nous pouvons apercevoir quil sagit dune succession dimage bitmaps, le rendu est pixelis :

80 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-43. Animation bitmap. Il est possible de choisir la vitesse de chaque animation. Dans le code suivant nous crons trois personnages. Chacun dentre eux possde sa propre vitesse :
// instancie l'animation var monPersonnage:Garcon = new Garcon(); // cre l'animation var animPersonnage:AnimationBitmap = new AnimationBitmap ( monPersonnage, 30 ); // positionnement animPersonnage.x = 20; animPersonnage.y = 100; // ajout la liste d'affichage addChild ( animPersonnage ); var deuxiemeAnimPersonnage:AnimationBitmap = new AnimationBitmap ( monPersonnage, 90 ); deuxiemeAnimPersonnage.x = 190; deuxiemeAnimPersonnage.y += 100; addChild ( deuxiemeAnimPersonnage ); var troisiemeAnimPersonnage:AnimationBitmap = new AnimationBitmap ( monPersonnage, 150 ); troisiemeAnimPersonnage.x = 370; troisiemeAnimPersonnage.y = 100; addChild ( troisiemeAnimPersonnage );

81 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

La figure 12-44 illustre les trois animations produites :

La figure 12-43. Animation bitmap. En utilisant des images bitmaps pour simuler lanimation nous avons amlior la fluidit de lanimation et rduit loccupation processeur. Une dernire astuce va nous permettre doptimiser la vitesse de rendu des animations. Lorsque nous gnrons les diffrentes tapes de lanimation sous forme bitmap nous passons la scne en qualit optimale. Puis, une fois les images bitmaps rendues nous passons la scne en qualit rduite. Les bitmaps affichs restent ainsi lisses tout en ayant pass lanimation en qualit rduite :
// passe la qualit du rendu en optimale stage.quality = StageQuality.HIGH; // instancie l'animation var monPersonnage:Garcon = new Garcon(); // cre l'animation var animPersonnage:AnimationBitmap = new AnimationBitmap ( monPersonnage, 30 ); // positionnement animPersonnage.x = 20; animPersonnage.y = 100; // ajout la liste d'affichage addChild ( animPersonnage );

82 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 12 Programmation Bitmap version 0.1.2

var deuxiemeAnimPersonnage:AnimationBitmap = new AnimationBitmap ( monPersonnage, 90 ); deuxiemeAnimPersonnage.x = 190; deuxiemeAnimPersonnage.y += 100; addChild ( deuxiemeAnimPersonnage ); var troisiemeAnimPersonnage:AnimationBitmap = new AnimationBitmap ( monPersonnage, 150 ); troisiemeAnimPersonnage.x = 370; troisiemeAnimPersonnage.y = 100; addChild ( troisiemeAnimPersonnage ); // une fois l'animation bitmap gnre, nous repassons en qualit rduite stage.quality = StageQuality.LOW;

Nous bnficions ainsi des performances dune animation lue en qualit rduite sans dgrader la qualit de lanimation. Nous verrons lors du chapitre 16 intitul Gestion du texte dautres techniques doptimisations du rendu. Peut tre avez-vous pens activer la mise en cache des bitmaps sur chaque personnage afin doptimiser le rendu ? Souvenez-vous, lorsque la tte de lecture dun objet graphique est en mouvement et que la mise en cache des bitmaps est active, pour chaque nouvelle image le lecteur met jour le bitmap cr en mmoire et ralentit les performances de lanimation. Lintrt majeur de la classe AnimationBitmap repose sur la cration dimages bitmaps indpendantes qui ne sont pas mises jour quelles que soient les transformations appliques lanimation gnre.

A retenir
Lutilisation de filtres sur une instance de BitmapData ne cre aucune image bitmap supplmentaire en mmoire.

Au cours du prochain chapitre nous dcouvrirons comment charger des donnes non graphiques dans nos applications.

83 / 83 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

13
Chargement du contenu

LA CLASSE LOADER ............................................................................................ 1 CHARGER UN LMENT EXTERNE............................................................................ 2 LA CLASSE LOADERINFO .................................................................................. 5 INTERAGIR AVEC LE CONTENU ................................................................... 18 CREER UNE GALERIE ....................................................................................... 26 LA COMPOSITION .................................................................................................. 29 REDIMENSIONNEMENT AUTOMATIQUE .................................................................. 34 GESTION DU LISSAGE ............................................................................................ 44 PRECHARGER LE CONTENU .......................................................................... 48 INTERROMPRE LE CHARGEMENT ............................................................... 57 COMMUNIQUER ENTRE DEUX ANIMATIONS ........................................... 58 MODELE DE SECURITE DU LECTEUR FLASH ........................................... 61 PROGRAMMATION CROISE ................................................................................... 62 UTILISER UN FICHIER DE RGULATION .................................................................. 63 CONTEXTE DE CHARGEMENT ...................................................................... 65 CONTOURNER LES RESTRICTIONS DE SECURITE.................................. 66 BIBLIOTHEQUE PARTAGEE ........................................................................... 72 DESACTIVER UNE ANIMATION CHARGEE ................................................ 79 COMMUNICATION AVM1 ET AVM2 .............................................................. 81

La classe Loader
ActionScript 3 limine les nombreuses fonctions et mthodes existantes dans les prcdentes versions dActionScript telles loadMovie ou loadMovieNum et tend le concept de la classe MovieClipLoader introduite avec Flash MX 2004. 1 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Afin de charger diffrents types de contenus tels des images ou des animations nous utilisons la classe flash.display.Loader. Celleci hrite de la classe DisplayObjectContainer et peut donc tre ajoute la liste daffichage. Dautres telles flash.net.URLLoader ou flash.net.URLStream peuvent tre utilises afin de charger tout type de contenu, mais leur utilisation savre plus complexe. Nous reviendrons en dtail sur ces classes au cours du chapitre 14 intitul Charger et envoyer des donnes. Nous allons commencer notre apprentissage en chargeant diffrents types de contenu, puis nous continuerons notre exploration de la classe Loader en crant une galerie photo. classes

A retenir
La classe Loader permet le chargement de contenu graphique seulement. Celle-ci hrite de la classe DisplayObjectContainer et peut donc tre ajoute la liste daffichage. Il faut considrer la classe Loader comme un objet daffichage.

Charger un lment externe


La classe Loader permet de charger des images au format PNG, JPEG, JPEG progressif et GIF non anim. Des animations SWF peuvent aussi tre charges, nous verrons comment communiquer avec celles-ci en fin de chapitre. Afin de charger un de ces fichiers, nous instancions tout dabord la classe Loader :
var chargeur:Loader = new Loader();

Puis nous appelons la mthode load dont voici la signature :


public function load(request:URLRequest, context:LoaderContext = null):void

Celle-ci requiert deux paramtres dont voici le dtail :


request : un objet URLRequest contenant ladresse de llment charger. context : un objet LoaderContext dfinissant le contexte de llment charg.

La classe URLRequest est utilise pour toute connexion externe HTTP lie aux classes URLLoader, URLStream, Loader et FileReference. 2 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Dans le code suivant, nous enveloppons lURL atteindre dans un objet URLRequest :
// cration du chargeur var chargeur:Loader = new Loader(); // url atteindre var maRequete:URLRequest = new URLRequest ("photo.jpg"); // chargement du contenu chargeur.load ( maRequete );

Lutilisation dun objet URLRequest diffre des prcdentes versions dActionScript o une simple chane de caractres tait passe aux diffrentes fonctions lies au chargement externe. Certaines classes ActionScript 3 telles Socket, URLStream, NetConnection et NetStream font nanmoins exception et ne ncessitent pas dobjet URLRequest. Nous reviendrons en dtail sur la classe URLRequest au cours du chapitre 14 intitul Charger et envoyer des donnes. Nous ne nous intressons pas pour le moment au paramtre context qui nest pas utilis par dfaut. Nous verrons plus loin dans ce chapitre lintrt de ce dernier. Afin de rendre graphiquement le contenu charg nous devons ajouter lobjet Loader la liste daffichage :
// cration du chargeur var chargeur:Loader = new Loader(); // url atteindre var maRequete:URLRequest = new URLRequest ("photo.jpg"); // chargement du contenu chargeur.load( maRequete ); // ajout la liste d'affichage addChild ( chargeur );

Limage est alors affiche :

3 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-1. Image charge dynamiquement. Il est important de signaler quune instance de la classe Loader ne peut contenir quun seul enfant. Ainsi, lorsque la mthode load est excute, le contenu prcdent est automatiquement remplac. Ainsi, les images bitmap sont automatiquement supprimes de la mmoire, lorsque celles-ci sont dcharges. Ce nest pas le cas des animations dont nous devons grer la dsactivation complte. Nous reviendrons sur cette contrainte en fin de chapitre. Comme depuis toujours, le chargement des donnes externes en ActionScript est asynchrone, cela signifie que linstruction load nest pas bloquante. Le code continue dtre excut une fois le chargement dmarr. Nous serons avertis de la fin du chargement des donnes plus tard dans le temps grce un vnement appropri.

A retenir

4 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Afin de voir le contenu charg, lobjet Loader doit tre ajout la liste daffichage.

La mthode load permet de charger le contenu externe.

La classe LoaderInfo
Afin de pouvoir accder au contenu charg, ou dtre averti des diffrentes phases du chargement nous utilisons les vnements diffuss par la classe flash.display.LoaderInfo. Celle-ci contient des informations relatives au contenu charg de type bitmap ou SWF. Il existe deux moyens daccder un objet LoaderInfo :
Par la proprit contentLoaderInfo de lobjet Loader. Par la proprit loaderInfo de nimporte quel DisplayObject.

Lorsque nous accdons la proprit contentLoaderInfo de la classe Loader nous rcuprons une rfrence lobjet LoaderInfo grant le chargement de llment. Si nous tentons dcouter les diffrents vnements lis au chargement directement auprs de lobjet Loader, aucun vnement nest diffus :
// cration du chargeur var chargeur:Loader = new Loader(); // tentative d'coute de l'vnement Event.COMPLETE chargeur.addEventListener ( Event.COMPLETE, contenuCharge ); // url atteindre var maRequete:URLRequest = new URLRequest ("photo.jpg"); // chargement du contenu chargeur.load( maRequete ); // ajout la liste d'affichage addChild ( chargeur ); function contenuCharge ( pEvt:Event ):void { trace( pEvt ); }

Ainsi, lcoute des diffrents vnements propres au chargement, se fait auprs de lobjet LoaderInfo accessible par la proprit contentLoaderInfo de lobjet Loader :
// cration du chargeur

5 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

var chargeur:Loader = new Loader(); // coute de l'vnement Event.COMPLETE chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, contenuCharge ); // url atteindre var maRequete:URLRequest = new URLRequest ("photo.jpg"); // chargement du contenu chargeur.load( maRequete ); // ajout la liste d'affichage addChild ( chargeur ); function contenuCharge ( pEvt:Event ):void { // affiche : [Event type="complete" bubbles=false cancelable=false eventPhase=2] trace( pEvt ); // affiche : [object LoaderInfo] trace( pEvt.target ); }

De nombreux vnements sont diffuss par la classe LoaderInfo, voici le dtail de chacun dentre eux :
Event.OPEN : diffus lorsque le lecteur commence charger le contenu. ProgressEvent.PROGRESS : diffus lorsque le chargement est en cours. Celui-ci renseigne sur le nombre doctets chargs et totaux. Sa frquence de diffusion est lie la vitesse de tlchargement de llment. Event.INIT : diffus lorsquil est possible daccder au code dun SWF charg. Cet vnement nest pas utilis dans le cas de chargement dimage bitmap. Event.COMPLETE : diffus lorsque le chargement est termin. Event.UNLOAD : diffus lorsque la mthode unload est appele ou quun nouvel lment va tre charg pour remplacer le prcdent. IOErrorEvent.IO_ERROR : diffus lorsque le chargement choue. HTTPStatusEvent.HTTP_STATUS : indique le code dtat de la requte HTTP.

Afin de grer avec finesse le chargement nous pouvons couter chacun des vnements :
// cration du chargeur var chargeur:Loader = new Loader(); // rfrence l'objet LoaderInfo var cli:LoaderInfo = chargeur.contentLoaderInfo;

6 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

// coute des vnements lis au chargement cli.addEventListener ( Event.OPEN, debutChargement ); cli.addEventListener ( Event.INIT, initialisation ); cli.addEventListener ( ProgressEvent.PROGRESS, chargement ); cli.addEventListener ( Event.COMPLETE, chargementTermine ); cli.addEventListener ( IOErrorEvent.IO_ERROR, echecChargement ); cli.addEventListener ( HTTPStatusEvent.HTTP_STATUS, echecHTTP ); cli.addEventListener ( Event.UNLOAD, suppressionContenu ); // url atteindre var maRequete:URLRequest = new URLRequest ("photo.jpg"); // chargement du contenu chargeur.load( maRequete ); // ajout la liste d'affichage addChild ( chargeur ); function debutChargement ( pEvt:Event ):void { // affiche : [Event type="open" bubbles=false cancelable=false eventPhase=2] trace( pEvt ); } function initialisation ( pEvt:Event ):void { // affiche : [Event type="init" bubbles=false cancelable=false eventPhase=2] trace( pEvt ); } function chargement ( pEvt:ProgressEvent ):void { // affiche : [ProgressEvent type="progress" bubbles=false cancelable=false eventPhase=2 bytesLoaded=0 bytesTotal=5696] trace( pEvt ); } function chargementTermine ( pEvt:Event ):void { // affiche : [Event type="complete" bubbles=false cancelable=false eventPhase=2] trace( pEvt ); } function echecChargement ( pEvt:IOErrorEvent ):void {

7 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

trace( pEvt ); } function echecHTTP ( pEvt:HTTPStatusEvent ):void { trace( pEvt ); } function suppressionContenu ( pEvt:Event ):void { // affiche : [Event type="unload" bubbles=false cancelable=false eventPhase=2] trace( pEvt ); }

Si nous tentons daccder au contenu charg avant lvnement Event.COMPLETE laccs la proprit content lve une erreur lexcution. Dans le code suivant nous tentons daccder au contenu durant lvnement ProgressEvent.PROGRESS :
function chargement ( pEvt:ProgressEvent ):void { trace( pEvt.target.content ); }

Lerreur suivante est leve lexcution :


Error: Error #2099: L'objet en cours de chargement n'est pas suffisamment charg pour fournir ces informations.

Une fois le contenu totalement charg, nous y accdons grce la proprit content de la classe LoaderInfo :
function chargementTermine ( pEvt:Event ):void { // affiche : [object Bitmap] trace( pEvt.target.content ); }

Mais aussi par la proprit content de lobjet Loader :


// affiche : [object Bitmap] trace( chargeur.content ) ;

A laide des diffrentes proprits de la classe LoaderInfo nous obtenons diffrents types dinformations : 8 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // accs aux informations lies au contenu charg var contenu:Bitmap = Bitmap ( objetLoaderInfo.content ); var largeur:Number = objetLoaderInfo.width; var hauteur:Number = objetLoaderInfo.height; var urlContenu:String = objetLoaderInfo.url; var type:String = objetLoaderInfo.contentType; // affiche : [object Bitmap] trace ( contenu ); // affiche : 167 65 trace ( largeur, hauteur ); // affiche : file:///K|/Work/Bytearray.org/O%27Reilly/Pratique%20d%27ActionScript/Chapitre %2013/flas/photo.jpg trace ( urlContenu ); // affiche : image/jpeg trace ( type ); }

Lors du chapitre 12 intitul Programmation Bitmap, nous avons vu que toutes les donnes bitmap taient assimiles des objets BitmapData. Dans le cas dune image charge dynamiquement, les donnes bitmap sont enveloppes au pralable par un objet flash.display.Bitmap afin de pouvoir tre rendues. Dans le code suivant, nous rcuprons les dimensions de limage charge ainsi que les donnes bitmap la composant :
function chargementTermine ( pEvt:Event ):void { // rfrence le contenu var contenu:DisplayObject = pEvt.target.content; // si le contenu charg est une image if ( contenu is Bitmap ) { // transtype en tant qu'image var image:Bitmap = Bitmap ( contenu ); // affiche : 167 65 trace( image.width, image.height ); //affiche : [object BitmapData]

9 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

trace( image.bitmapData ); } }

Nous pouvons effacer une partie de limage charge, en peignant directement dessus laide de la mthode fillRect de la classe BitmapData :
function chargementTermine ( pEvt:Event ):void { // rfrence le contenu var contenu:DisplayObject = pEvt.target.content; // si le contenu charg est une image if ( contenu is Bitmap ) { // transtype en tant qu'image var image:Bitmap = Bitmap ( contenu ); var donneesBitmap:BitmapData = image.bitmapData; donneesBitmap.fillRect( new Rectangle ( 15, 15, 135, 30 ), 0xC8BCA4 ); } }

La figure 13-2 illustre le rsultat :

10 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-2. Modification des donnes bitmap. La partie centrale de limage charge est peinte de pixels beiges. Souvenez-vous que les vnements sont diffuss depuis lobjet LoaderInfo. Ainsi la proprit target ne rfrence pas lobjet Loader mais lobjet LoaderInfo associ llment charg. Afin daccder lobjet Loader associ lobjet LoaderInfo, nous utilisons sa proprit loader :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // affiche : true trace( objetLoaderInfo.loader == chargeurImage ); }

De la mme manire, la proprit contentLoaderInfo de lobjet Loader pointe vers le mme objet LoaderInfo que celui rfrenc par la proprit loaderInfo de llment charg :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); var objetLoader:Loader = pEvt.target.loader; // affiche : true trace( objetLoader.contentLoaderInfo == objetLoaderInfo.content.loaderInfo ); }

Lobjet LoaderInfo est donc partag entre deux environnements : Lanimation chargeant le contenu et le contenu lui-mme. Il est important de noter que bien quil sagisse dune sous-classe de DisplayObjectContainer, la classe Loader ne possde pas toutes les capacits propres ce type. Certaines proprits telles numChildren peuvent tre utilises :
function chargementTermine ( pEvt:Event ):void { var objetLoader:Loader = pEvt.target.loader; // affiche : true

11 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

trace( objetLoader.numChildren ); }

Il est en revanche impossible dappeler les diffrentes mthodes de manipulation dobjets enfants sur celle-ci. Dans le code suivant nous tentons dappeler la mthode removeChildAt sur lobjet chargeurImage :
function chargementTermine ( pEvt:Event ):void { var objetLoader:Loader = pEvt.target.loader; // tentative de suppression du premier enfant de l'objet Loader objetLoader.removeChildAt(0); }

Lerreur lexcution suivante est leve :


// affiche : Error: Error #2069: La classe Loader ne met pas en oeuvre cette mthode.

Afin de supprimer limage charge, nous devons appeler la mthode unload de la classe Loader. Dans le code suivant, nous supprimons limage charge aussitt le chargement termin :
function chargementTermine ( pEvt:Event ):void { // rfrence le contenu var contenu:DisplayObject = pEvt.target.content; // si le contenu charg est une image if ( contenu is Bitmap ) { // transtype en tant qu'image var image:Bitmap = Bitmap ( contenu ); var donneesBitmap:BitmapData = image.bitmapData; donneesBitmap.fillRect( new Rectangle ( 15, 15, 135, 30 ), 0x009900 ); pEvt.target.loader.unload(); } }

Aussitt le contenu supprim, lvnement Event.UNLOAD est diffus.

12 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

En cas derreur de chargement, nous devons grer et indiquer lutilisateur quune erreur sest produite. Pour cela nous devons obligatoirement couter lvnement IOErrorEvent.IO_ERROR. Ce dernier est diffus lorsque le contenu na pu tre charg, pour des raisons daccs ou de compatibilit. Si nous tentons de charger un contenu non gr et que lvnement IOErrorEvent.IO_ERROR nest pas cout, une erreur lexcution est leve. Attention, si le bouton retour du navigateur est cliqu pendant le chargement de contenu externe par le lecteur Flash. Lvnement IOErrorEvent.IO_ERROR est diffus, alors que le contenu est pourtant compatible ou disponible. Il convient donc de toujours couter cet vnement. Dans le code suivant, un fichier non compatible est charg, lvnement IOErrorEvent.IO_ERROR est diffus :
// cration du chargeur var chargeur:Loader = new Loader(); // rfrence l'objet LoaderInfo var cli:LoaderInfo = chargeur.contentLoaderInfo; // coute des vnements lis au chargement cli.addEventListener ( Event.OPEN, debutChargement ); cli.addEventListener ( Event.INIT, initialisation ); cli.addEventListener ( ProgressEvent.PROGRESS, chargement ); cli.addEventListener ( Event.COMPLETE, chargementTermine ); cli.addEventListener ( IOErrorEvent.IO_ERROR, echecChargement ); cli.addEventListener ( HTTPStatusEvent.HTTP_STATUS, echecHTTP ); cli.addEventListener ( Event.UNLOAD, suppressionContenu ); // tentative de chargement d'un document PDF // entrane la diffusion de l'vnement IOErrorEvent.IO_ERROR var maRequete:URLRequest = new URLRequest ("document.pdf"); // chargement du contenu chargeur.load( maRequete ); // ajout la liste d'affichage addChild ( chargeur ); function debutChargement ( pEvt:Event ):void { trace( pEvt ); } function initialisation ( pEvt:Event ):void {

13 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

trace( pEvt ); } function chargement ( pEvt:ProgressEvent ):void { trace( pEvt ); } function chargementTermine ( pEvt:Event ):void { trace( pEvt ); } function suppressionContenu ( pEvt:Event ):void { trace( pEvt ); } function echecChargement ( pEvt:Event ):void { // affiche : [IOErrorEvent type="ioError" bubbles=false cancelable=false eventPhase=2 text="Error #2124: Le type du fichier charg est inconnu. URL: file:///D|/Work/Bytearray.org/O%27Reilly/Pratique%20d%27ActionScript/Chapitre %2013/flas/document.pdf"] trace ( pEvt ); }

Lorsquune animation est charge par un objet Loader, lobjet LoaderInfo active de nouvelles proprits lies au SWF. Voici le dtail de chacune dentre elles :
LoaderInfo.applicationDomain : retourne une rfrence lobjet ApplicationDomain contenant les dfinitions de classe du SWF charg. LoaderInfo.actionScriptVersion : indique la version dActionScript du SWF charg. LoaderInfo.parameters : objet contenant les FlashVars associs au SWF charg. LoaderInfo.swfVersion : indique la version du SWF charg. LoaderInfo.frameRate : Indique la cadence du SWF charg.

14 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Toute tentative daccs, lune de ces proprits lorsquune image est charge, se traduit par un chec. Dans le code suivant, une image est charge, nous tentons alors daccder la proprit frameRate :
function chargementTermine ( pEvt:Event ):void { // tentative d'accs la cadence de l'lment charg trace( pEvt.target.frameRate ); }

Lerreur lexcution suivante est leve :


Error: Error #2098: L'objet en cours de chargement n'est pas un fichier .swf, vous ne pouvez donc pas en extraire des proprits SWF.

Afin dutiliser ces proprits nous chargeons une animation :


// url atteindre var maRequete:URLRequest = new URLRequest ("animation.swf");

Puis nous accdons au sein de la fonction chargementTermine aux diffrentes informations lies au SWF :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // affiche : 30 trace( objetLoaderInfo.frameRate ); // affiche : [object ApplicationDomain] trace( objetLoaderInfo.applicationDomain );

// affiche : 3 trace( objetLoaderInfo.actionScriptVersion ); // affiche : [object Object] trace( objetLoaderInfo.parameters ); // affiche : 9 trace( objetLoaderInfo.swfVersion ); }

Attention, il convient de toujours utiliser les proprits width et height de lobjet LoaderInfo afin de rcuprer la largeur et hauteur du contenu charg. Dans le cas de chargement danimations, la proprit content rfrence le scnario principal du SWF. Ainsi, lutilisation des proprits width et height renvoient la taille du contenu de lanimation, et non les dimensions du SWF :
function chargementTermine ( pEvt:Event ):void

15 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

{ var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); var contenu:DisplayObject = objetLoaderInfo.content; // accde aux dimensions du contenu de l'animation // affiche : 187.5 187.5 trace( contenu.width, contenu.height ); // accde aux dimensions du SWF // affiche : 550 400 trace( objetLoaderInfo.width, objetLoaderInfo.height ); }

Nous pouvons tester lors de la fin du chargement le type de contenu charg laide du mot cl is afin de dterminer si le contenu charg est une animation ou une image En testant le type de la proprit content nous pouvons dterminer le type de contenu charg :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); var contenu:DisplayObject = objetLoaderInfo.content; if ( contenu is Bitmap ) { trace("Une image a t charge !"); } else { trace("Une animation a t charge !"); } }

Nous venons de traiter lutilisation de la classe LoaderInfo travers la proprit contentLoaderInfo de lobjet Loader. A linverse lorsque nous accdons la proprit loaderInfo dun DisplayObject nous accdons aux informations du SWF contenant le DisplayObject. A linstar des proprits stage et root, la proprit loaderInfo renvoie null, tant que lobjet graphique nest pas prsent au sein de la liste daffichage : 16 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

// cration d'un objet graphique var monSprite:Sprite = new Sprite(); // affiche : null trace( monSprite.loaderInfo ); // ajout la liste d'affichage addChild ( monSprite ); // affiche : [object LoaderInfo] trace( monSprite.loaderInfo ); // affiche : true trace( loaderInfo == monSprite.loaderInfo );

Afin daccder aux diffrentes proprits de la classe LoaderInfo. Nous devons attendre la fin du chargement du SWF en cours. Si nous tentons daccder aux proprits de la classe LoaderInfo avant lvnement Event.INIT :
trace( monSprite.loaderInfo.width );

Lerreur suivante est leve lexcution :


Error: Error #2099: L'objet en cours de chargement n'est pas suffisamment charg pour fournir ces informations.

A laide de lvnement Event.INIT ou Event.COMPLETE nous pouvons accder correctement aux informations du SWF en cours :
// cration d'un objet graphique var monSprite:Sprite = new Sprite(); // ajout la liste d'affichage addChild ( monSprite ); // coute de l'vnement Event.COMPLETE auprs de // l'objet LoaderInfo du SWF en cours monSprite.loaderInfo.addEventListener ( Event.COMPLETE, chargementTermine ); function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // affiche : [object LoaderInfo] trace( objetLoaderInfo ); // affiche : 550 400 trace( objetLoaderInfo.width, objetLoaderInfo.height ); // affiche : application/x-shockwave-flash trace( objetLoaderInfo.contentType ); // affiche : 12 trace( objetLoaderInfo.frameRate ); // affiche : 577 577 trace ( objetLoaderInfo.bytesLoaded, objetLoaderInfo.bytesTotal );

17 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Nous pouvons aussi rfrencer lobjet LoaderInfo par la proprit loaderInfo du scnario principal :
// coute de l'vnement Event.COMPLETE auprs de // l'objet LoaderInfo du SWF en cours loaderInfo.addEventListener ( Event.COMPLETE, chargementTermine );

De cette manire, un SWF peut grer son prchargement de manire autonome. Ainsi, chaque SWF possde un objet LoaderInfo associ regroupant les informations relatives celui-ci. Nous retrouvons avec le code prcdent, lquivalent de lvnement onLoad existant en ActionScript 1 et 2.

A retenir
La classe LoaderInfo contient des informations relatives un lment charg de type bitmap ou SWF. La proprit contentLoaderInfo de lobjet Loader rfrence lobjet LoaderInfo associ llment charg.

Tous les objets de type DisplayObject possdent une proprit loaderInfo associ au SWF en cours. Lorsquune image est charge, seules certaines proprits de la classe LoaderInfo sont utilisables.

Lorsquun SWF est charg, la totalit des proprits de la classe LoaderInfo sont utilisables.

Interagir avec le contenu


Il est gnralement courant, de devoir interagir avec le contenu charg. La classe Loader est un DisplayObjectContainer et hrite donc de la classe InteractiveObject facilitant linteractivit souris et clavier. Il est cependant impossible dactiver la proprit buttonMode auprs de lobjet Loader, linteractivit est donc rduite. Une premire technique consiste dplacer le contenu de lobjet Loader laide de la mthode addChild. Dans le code suivant, nous accdons au contenu charg puis nous le dplaons au sein dun autre conteneur :
function chargementTermine ( pEvt:Event ):void { // rfrence le contenu var contenu:DisplayObject = pEvt.target.content;

18 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

// affiche : [object Loader] trace( contenu.parent ); // rattribution de conteneur, l'image quitte l'objet Loader addChild ( contenu ); // affiche : [object MainTimeline] trace( contenu.parent ); }

Nous rfrenons le contenu laide de la proprit content de lobjet LoaderInfo. Il convient de rester le plus gnrique possible, nous utilisons donc le type commun DisplayObject propre tout type de contenu charg, puis nous dplaons le contenu charg laide de la mthode addChild. Souvenez-vous, lorsquun DisplayObject dj prsent dans la liste daffichage est pass la mthode addChild, lobjet sur lequel la mthode est appele devient le nouveau conteneur. Cest le concept de rattribution de parent couvert au sein du chapitre 4 intitul Liste daffichage. Afin de dupliquer limage charge, nous navons pas recharger limage au sein dun autre objet Loader. Nous copions les donnes bitmap puis les associons un nouvel objet Bitmap :
function chargementTermine ( pEvt:Event ):void { // rfrence le contenu var contenu:DisplayObject = pEvt.target.content; // si le contenu charg est une image if ( contenu is Bitmap ) { // transtype en tant qu'image var image:Bitmap = Bitmap ( contenu ); var donneesBitmap:BitmapData = image.bitmapData; // cration d'une copie des donnes bitmaps var copieDonneesBitmap:BitmapData = donneesBitmap.clone(); var copieImage:Bitmap = new Bitmap ( copieDonneesBitmap ); copieImage.x = image.width + 5; addChild ( image ); addChild ( copieImage );

19 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

} }

La figure 13-3 illustre le rsultat :

Figure 13-3. Copie de limage charge. Afin de rendre les images cliquables, nous les ajoutons au sein dobjets interactifs tels Sprite :
function chargementTermine ( pEvt:Event ):void { // rfrence le contenu var contenu:DisplayObject = pEvt.target.content; // si le contenu charg est une image if ( contenu is Bitmap ) { // transtype en tant qu'image var image:Bitmap = Bitmap ( contenu ); var premierConteneur:Sprite = new Sprite(); premierConteneur.buttonMode = true; premierConteneur.addChild( image ); var donneesBitmap:BitmapData = image.bitmapData;

20 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

// cration d'une copie des donnes bitmaps var copieDonneesBitmap:BitmapData = donneesBitmap.clone(); var copieImage:Bitmap = new Bitmap ( copieDonneesBitmap ); var secondConteneur:Sprite = new Sprite(); secondConteneur.buttonMode = true; secondConteneur.addChild( copieImage ); secondConteneur.x = premierConteneur.width + 5; addChild ( premierConteneur ); addChild ( secondConteneur ); premierConteneur.addEventListener( MouseEvent.CLICK, clicImage ); secondConteneur.addEventListener( MouseEvent.CLICK, clicImage ); } } function clicImage ( pEvt:MouseEvent ):void { // affiche : [MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=48 localY=36 stageX=220 stageY=36 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); }

Les deux images bitmaps sont places dans un conteneur Sprite spcifique, facilitant linteractivit. Afin dactiver le comportement bouton, nous utilisons la proprit buttonMode. Souvenez-vous, afin de rendre plus souple notre code, nous capturons lvnement MouseEvent.CLICK auprs du parent des vnements ractifs. Nous prfrons donc le code suivant :
// cration d'un conteneur pour les images var conteneurImages:Sprite = new Sprite(); addChild ( conteneurImages ); // capture de l'vnement MouseEvent.MOUSE_CLICK auprs du conteneur conteneurImages.addEventListener( MouseEvent.CLICK, clicImages, true ); function clicImages ( pEvt:MouseEvent ):void { // [MouseEvent type="click" bubbles=true cancelable=false eventPhase=1 localX=87 localY=4 stageX=259 stageY=4 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); }

21 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

function chargementTermine ( pEvt:Event ):void { // rfrence le contenu var contenu:DisplayObject = pEvt.target.content; // si le contenu charg est une image if ( contenu is Bitmap ) { // transtype en tant qu'image var image:Bitmap = Bitmap ( contenu ); var premierConteneur:Sprite = new Sprite(); premierConteneur.buttonMode = true; premierConteneur.addChild( image ); var donneesBitmap:BitmapData = image.bitmapData; // cration d'une copie des donnes bitmaps var copieDonneesBitmap:BitmapData = donneesBitmap.clone(); var copieImage:Bitmap = new Bitmap ( copieDonneesBitmap ); var secondConteneur:Sprite = new Sprite(); secondConteneur.buttonMode = true; secondConteneur.addChild( copieImage ); secondConteneur.x = premierConteneur.width + 5; conteneurImages.addChild ( premierConteneur ); conteneurImages.addChild ( secondConteneur ); } }

Grce la capture de lvnement MouseEvent.CLICK, lorsque de nouvelles images seront places ou supprimes du conteneur conteneurImages, aucune nouvelle souscription ou dsinscription dcouteurs ne sera ncessaire. A chaque clic sur les images nous la supprimons de la liste daffichage, pour cela nous modifions la fonction clicImages :
function clicImages ( pEvt:MouseEvent ):void { pEvt.currentTarget.removeChild ( DisplayObject ( pEvt.target ) ); }

22 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Lorsque limage est clique, celle-ci est supprime de la liste daffichage. Une rfrence vers limage dorigine demeure cependant au sein de lobjet Loader, il nous est donc impossible de librer correctement les ressources. De plus, en dplaant le contenu de lobjet Loader nous modifions son fonctionnement interne. Ainsi, lors du prochain appel de la mthode load, celui-ci tentera de supprimer son contenu afin daccueillir le nouveau. Celui-ci ayant t dplac, lobjet Loader tente alors de supprimer un objet qui nest plus son enfant, ce qui provoque alors une erreur lexcution. Ce comportement est un bug du lecteur Flash 9.0.115 qui sera corrig au sein du prochain lecteur Flash 10. Il est donc fortement dconseill de dplacer le contenu de lobjet Loader sous peine de perturber le mcanisme interne de celui-ci. De manire gnrale nous prfrons crer un objet Loader pour chaque contenu charg. Nous pouvons donc tablir le bilan suivant :
// bonne pratique addChild ( chargeur ); // mauvaise pratique addChild ( chargeur.content );

Dans le code suivant, nous chargeons plusieurs images au sein de diffrents objets Loader :
var conteneurImages:Sprite = new Sprite(); addChild ( conteneurImages ); // capture de l'vnement MouseEvent.MOUSE_CLICK auprs du conteneur conteneurImages.addEventListener( MouseEvent.CLICK, clicImages, true ); var chargeurImage:Loader; var requete:URLRequest = new URLRequest(); var tableauImages:Array = new Array ("photo1.jpg", "photo2.jpg", "photo3.jpg", "photo4.jpg", "photo5.jpg", "photo3.jpg"); var lng:int = tableauImages.length; var colonnes:int = 4; for ( var i:int = 0; i< lng; i++ ) { // cration du chargeur chargeurImage = new Loader();

23 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

requete.url = tableauImages[i]; chargeurImage.load ( requete ); chargeurImage.x = Math.round (i % colonnes) * 120; chargeurImage.y = Math.floor (i / colonnes) * 80 conteneurImages.addChild ( chargeurImage ); } function clicImages ( pEvt:MouseEvent ):void { // [MouseEvent type="click" bubbles=true cancelable=false eventPhase=1 localX=10 localY=49 stageX=250 stageY=49 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); }

Afin de rendre les images cliquables, nous les imbriquons dans des conteneurs de type Sprite :
var conteneurImages:Sprite = new Sprite(); addChild ( conteneurImages ); // capture de l'vnement MouseEvent.MOUSE_CLICK auprs du conteneur conteneurImages.addEventListener( MouseEvent.CLICK, clicImages, true ); var chargeurImage:Loader; var conteneurLoader:Sprite; var requete:URLRequest = new URLRequest(); var tableauImages:Array = new Array ("photo1.jpg", "photo2.jpg", "photo3.jpg", "photo4.jpg", "photo5.jpg", "photo3.jpg"); var lng:int = tableauImages.length; var colonnes:int = 4; for (var i:int = 0; i< lng; i++ ) { // cration du chargeur chargeurImage = new Loader(); conteneurLoader = new Sprite(); conteneurLoader.addChild( chargeurImage ); requete.url = tableauImages[i]; chargeurImage.load ( requete ); conteneurLoader.x = Math.round (i % colonnes) * 120;

24 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

conteneurLoader.y = Math.floor (i / colonnes) * 80 conteneurImages.addChild ( conteneurLoader ); } function clicImages ( pEvt:MouseEvent ):void { // [MouseEvent type="click" bubbles=true cancelable=false eventPhase=1 localX=10 localY=49 stageX=250 stageY=49 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0] trace( pEvt ); }

Lactivation du mode bouton est rendue possible grce lactivation de la proprit buttonMode sur les instances de Sprite contenant les objets Loader :
conteneurLoader.buttonMode = true;

Puis nous modifions la fonction clicImages :


function clicImages ( pEvt:MouseEvent ):void { DisplayObject( pEvt.target ).alpha = .5; }

La figure 13-4 illustre le rsultat :

25 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-4. Mini galerie. A chaque image clique, celle-ci passe en opacit rduite afin de mmoriser visuellement laction utilisateur.

A retenir
Le contenu charg est accessible par la proprit content de lobjet LoaderInfo. Bien qutant une sous-classe de DisplayObjectContainer, la classe Loader ne dispose pas de toutes les capacits propres ce type. Il est techniquement possible de dplacer le contenu charg et de rattribuer son parent, mais il est fortement recommand de conserver le contenu au sein de lobjet Loader. Afin dafficher le curseur main au survol dun objet Loader, nous devons au pralable limbriquer au sein dun conteneur de type Sprite.

Crer une galerie


Nous allons mettre en pratique un ensemble de notions abordes depuis le dbut de louvrage ainsi que celles abordes en dbut de ce chapitre. Nous allons crer une galerie photo et ainsi voir comment la concevoir de manire optimise. Ouvrez un nouveau document Flash CS3, puis dfinissez une classe de document au sein dun paquetage org.bytearray.document :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { trace( this ); } } }

26 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Cette classe tend la classe ApplicationDefaut cre au cours du chapitre 11 intitul Classe du document, nous rcuprons ainsi toutes les fonctionnalits dfinies par celle-ci. Une fois cre, nous dfinissons celle-ci comme classe du document grce au panneau appropri de linspecteur de proprits :

Figure 13-5. Classe du document. Nous dfinissons la classe ChargeurMedia au sein du paquetage org.bytearray.chargement. Cette classe va nous permettre de grer facilement le chargement dimages. Nous pourrions utiliser simplement la classe Loader mais nous avons besoin daugmenter les ses fonctionnalits. La classe ChargeurMedia se chargera de toute la logique de chargement et de redimensionnement des mdias chargs. Ainsi pour chaque projet ncessitant un chargement de contenu notre classe ChargeurMedia pourra tre rutilise. Voici le code de base de la classe ChargeurMedia :
package org.bytearray.chargement { import import import import import import flash.display.Loader; flash.display.Sprite; flash.display.LoaderInfo; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent;

public class ChargeurMedia extends Sprite { private var chargeur:Loader; private var cli:LoaderInfo; public function ChargeurMedia () { chargeur = new Loader(); addChild ( chargeur );

27 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

cli = chargeur.contentLoaderInfo; cli.addEventListener cli.addEventListener cli.addEventListener cli.addEventListener cli.addEventListener } private function demarreChargement ( pEvt:Event ):void { trace ("dmarre chargement"); } private function initChargement ( pEvt:Event ):void { trace ("initialisation du chargement"); } private function chargement ( pEvt:Event ):void { trace ("chargement en cours"); } private function chargementTermine ( pEvt:Event ):void { trace ("chargement termin"); } private function echecChargement ( pEvt:Event ):void { trace ("erreur de chargement"); } } } ( ( ( ( ( Event.INIT, initChargement ); Event.OPEN, demarreChargement ); ProgressEvent.PROGRESS, chargement ); Event.COMPLETE, chargementTermine ); IOErrorEvent.IO_ERROR, echecChargement );

Une fois la classe ChargeurMedia dfinie, nous pouvons linstancier au sein de la classe Document :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut;

28 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import org.bytearray.chargement.ChargeurMedia; public class Document extends ApplicationDefaut { // visionneuse private var visionneuse:ChargeurMedia; public function Document () { // cration de l'objet Loader visionneuse = new ChargeurMedia (); addChild ( visionneuse ); } } }

Certains dentre vous seront peut-tre tonns de voir que nous nhritons pas de la classe Loader. Pourtant, toutes les raisons paraissent ici runies pour utiliser lhritage. Notre classe ChargeurMedia doit augmenter les capacits de la classe Loader, lhritage semble donc tre la meilleure solution. Nous allons prfrer ici une deuxime approche dj utilise au cours des chapitres prcdents. La classe ChargeurMedia ne sera pas un objet Loader mais possdera un objet Loader. Vous souvenez-vous de cette opposition ?

La composition
Nous allons prfrer ici lutilisation de la composition lhritage. Nous avons vu au cours du chapitre 8 intitul Programmation oriente objet les avantages quoffre la composition en matire dvolutivit et de modification du code. Lorsque lapplication est excute, le constructeur de la classe Document instancie un objet ChargeurMedia. Trs gnralement, les donnes charger proviennent dune base de donnes ou dun fichier XML. Nous allons raliser une premire version laide dun tableau contenant les URL des images charger. Nous crons ensuite un tableau contenant ladresse de chaque image :
package org.bytearray.document {

29 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import org.bytearray.abstrait.ApplicationDefaut; import org.bytearray.chargement.ChargeurMedia; public class Document extends ApplicationDefaut { // visionneuse private var visionneuse:ChargeurMedia; // donnes private var tableauImages:Array; public function Document () { // tableau contenant les url des images tableauImages = new Array ( "imgs/photo1.jpg", "imgs/photo2.jpg", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia (); addChild ( visionneuse ); } } }

Un rpertoire imgs est cr contenant diffrentes images portant le nom de celles dfinies dans le tableau tableauImages. Pour le moment, la classe ChargeurMedia nest pas utilisable, nous devons ajouter une mthode permettant de charger le media spcifi. Nous importons les classes URLRequest et LoaderContext dans la dfinition de la classe :
import import import import import import import flash.display.Loader; flash.display.Sprite; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.URLRequest; flash.system.LoaderContext;

Puis, nous ajoutons une mthode load. Celle-ci appelle en interne la mthode load de lobjet Loader :
public function load ( pURL:URLRequest, pContext:LoaderContext=null ):void { chargeur.load ( pURL, pContext ); }

30 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

En dlgant les fonctionnalits une classe interne. La classe ChargeurMedia procde une dlgation. Ce mcanisme est lun des plus puissants de la composition. Grce cela, la classe ChargeurMedia peut lexcution dcider dutiliser un autre type dobjet dans lequel charger les donnes. Chose impossible si la classe ChargeurMedia avait hrite de la classe Loader. Cest pour cette raison que la composition est considre comme une approche dynamique, sopposant au caractre statique de lhritage li la compilation. Sur la scne nous posons deux boutons boutonSuivant et boutonPrecedent, auxquels nous donnons deux noms doccurrences respectifs. La figure 13-6 illustre linterface :

Figure 13-6. Boutons de navigation. Nous dfinissons chaque bouton au sein de la classe Document :
package org.bytearray.document {

31 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import import import import

flash.display.SimpleButton; flash.events.MouseEvent; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.chargement.ChargeurMedia;

public class Document extends ApplicationDefaut { // interface public var boutonSuivant:SimpleButton; public var boutonPrecedent:SimpleButton; // visionneuse private var visionneuse:ChargeurMedia; // donnes private var tableauImages:Array; public function Document () { // tableau contenant les url des images tableauImages = new Array ( "imgs/photo1.jpg", "imgs/photo2.jpg", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia (); addChild ( visionneuse ); boutonPrecedent.addEventListener ( MouseEvent.CLICK, clicPrecedent ); boutonSuivant.addEventListener ( MouseEvent.CLICK, clicSuivant ); } private function clicPrecedent ( pEvt:MouseEvent=null ):void { trace("Image prcdente"); } private function clicSuivant ( pEvt:MouseEvent=null ):void { trace("Image suivante"); } } }

Puis nous associons la logique spcifique chaque bouton :


package org.bytearray.document

32 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

{ import import import import import flash.display.SimpleButton; flash.events.MouseEvent; flash.net.URLRequest; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.chargement.ChargeurMedia;

public class Document extends ApplicationDefaut { // interface public var boutonSuivant:SimpleButton; public var boutonPrecedent:SimpleButton; // visionneuse private var visionneuse:ChargeurMedia; // donnes private var tableauImages:Array; private var position:int; private var requete:URLRequest; public function Document () { position = -1; requete = new URLRequest(); // tableau contenant les url des images tableauImages = new Array ( "imgs/photo1.jpg", "imgs/photo2.jpg", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia (); addChild ( visionneuse ); boutonPrecedent.addEventListener ( MouseEvent.CLICK, clicPrecedent ); boutonSuivant.addEventListener ( MouseEvent.CLICK, clicSuivant ); } private function clicPrecedent ( pEvt:MouseEvent=null ):void { position = Math.max ( 0, --position ); requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } private function clicSuivant ( pEvt:MouseEvent=null ):void {

33 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

position = ++position % tableauImages.length; requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } } }

A chaque clic, nous dclenchons la fonction clicPrecedent ou clicSuivant. Nous remarquons que lorsque nous cliquons sur le bouton boutonSuivant nous tournons en boucle, linverse le boutonPrecedent bute lindex 0. En crant une proprit position, nous incrmentons ou dcrmentons celle-ci, afin de naviguer au sein du tableau tableauImages. Ne vous est-il jamais arriv de charger des images de diffrentes dimensions, et de souhaiter que quelles que soient leurs tailles, cellesci saffiche correctement dans un espace donn ?

Redimensionnement automatique
Lune des premires fonctionnalits que nous allons ajouter la classe Loader concerne lajustement automatique de limage selon une dimension spcifie. Les images charges au sein de la galerie peuvent parfois dborder ou ne pas tre ajustes automatiquement. Cela est gnralement prvu par la partie serveur mais afin de scuriser notre application, nous allons intgrer ce mcanisme ct client. Cela nous vitera de mauvaises surprises au cas o limage naurait pas t correctement redimensionne au pralable. Lorsque lvnement Event.COMPLETE est diffus, nous ajustons automatiquement la taille de lobjet ChargeurMedia aux dimensions voulues tout en conservant les proportions afin de ne pas dformer le contenu charg :
package org.bytearray.chargement { import import import import import flash.display.Loader; flash.display.Sprite; flash.display.LoaderInfo; flash.events.Event; flash.events.ProgressEvent;

34 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import import import import

flash.events.IOErrorEvent; flash.net.URLRequest; flash.system.LoaderContext; flash.display.DisplayObject;

public class ChargeurMedia extends Sprite { private private private private var var var var chargeur:Loader; cli:LoaderInfo; largeur:Number; hauteur:Number;

public function ChargeurMedia ( pLargeur:Number, pHauteur:Number ) { largeur = pLargeur; hauteur = pHauteur; chargeur = new Loader(); addChild ( chargeur ) ; cli = chargeur.contentLoaderInfo; cli.addEventListener cli.addEventListener cli.addEventListener cli.addEventListener cli.addEventListener } ):void public function load ( pURL:URLRequest, pContext:LoaderContext=null { chargeur.load ( pURL, pContext ); } private function demarreChargement ( pEvt:Event ):void { trace ("dmarre chargement"); } private function initChargement ( pEvt:Event ):void { trace ("initialisation chargement"); } private function chargement ( pEvt:Event ):void ( ( ( ( ( Event.INIT, initChargement ); Event.OPEN, demarreChargement ); ProgressEvent.PROGRESS, chargement ); Event.COMPLETE, chargementTermine ); IOErrorEvent.IO_ERROR, echecChargement );

35 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

{ trace ("chargement en cours"); } private function chargementTermine ( pEvt:Event ):void { var contenuCharge:DisplayObject = pEvt.target.content; var ratio:Number = Math.min ( hauteur / contenuCharge.height ); scaleX = scaleY = 1; if ( ratio < 1 ) scaleX = scaleY = ratio; } private function echecChargement ( pEvt:Event ):void { trace ("erreur de chargement"); } } } largeur / contenuCharge.width,

Puis nous instancions lobjet ChargeurMedia en spcifiant les dimensions respecter :


// cration de la visionneuse visionneuse = new ChargeurMedia ( 350, 450 );

Si nous testons la galerie nous remarquons que les images sont bien affiches et correctement redimensionnes si cela est ncessaire. Pour le moment, elles ne sont pas centres :

36 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-7. Galerie. Nous devons placer le code correspondant au centrage de limage en dehors de la classe ChargeurMedia. Intgrer une telle logique notre classe ChargeurMedia la rendrait rigide. Noubliez pas un point essentiel de la programmation oriente objet ! Nous devons isoler ce qui risque dtre modifi dun projet un autre, nous plaons donc le positionnement des images en dehors de la classe ChargeurMedia. Afin de pouvoir utiliser la classe ChargeurMedia comme la classe Loader, nous devons prsent diffuser tous les vnements ncessaires son bon fonctionnement. Afin de permettre cette diffusion nous devons dabord les couter en interne, puis les rediriger par la suite.

Nous modifions pour cela chaque fonction couteur, puis nous procdons la redirection de chaque vnement :
package org.bytearray.chargement { import import import import import flash.display.Loader; flash.display.Sprite; flash.display.DisplayObject; flash.display.LoaderInfo; flash.events.Event;

37 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import import import import

flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.URLRequest; flash.system.LoaderContext;

public class ChargeurMedia extends Sprite { private private private private var var var var chargeur:Loader; cli:LoaderInfo; largeur:Number; hauteur:Number;

public function ChargeurMedia ( pLargeur:Number, pHauteur:Number ) { largeur = pLargeur; hauteur = pHauteur; chargeur = new Loader(); addChild ( chargeur ) ; cli = chargeur.contentLoaderInfo; cli.addEventListener cli.addEventListener cli.addEventListener cli.addEventListener cli.addEventListener } ):void public function load ( pURL:URLRequest, pContext:LoaderContext=null { chargeur.load ( pURL, pContext ); } private function demarreChargement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function initChargement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function chargement ( pEvt:Event ):void ( ( ( ( ( Event.INIT, initChargement ); Event.OPEN, demarreChargement ); ProgressEvent.PROGRESS, chargement ); Event.COMPLETE, chargementTermine ); IOErrorEvent.IO_ERROR, echecChargement );

38 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

{ dispatchEvent ( pEvt ); } private function chargementTermine ( pEvt:Event ):void { var contenuCharge:DisplayObject = pEvt.target.content; var ratio:Number = Math.min ( hauteur / contenuCharge.height ); scaleX = scaleY = 1; if ( ratio < 1 ) scaleX = scaleY = ratio; dispatchEvent ( pEvt ); } private function echecChargement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } } } largeur / contenuCharge.width,

De cette manire, la classe ChargeurMedia, diffuse les mmes vnements que la classe Loader. Cela reste totalement transparent pour lutilisateur de la classe ChargeurMedia et sinscrit comme suite logique de la composition et de la dlgation. Lorsquils sont redirigs, les objets vnementiels voient leur proprit target rfrencer lobjet diffuseur ayant relay lvnement. La proprit target fait donc dsormais rfrence lobjet ChargeurMedia et non plus lobjet LoaderInfo. En coutant lvnement Event.COMPLETE auprs de la classe ChargeurMedia, nous sommes avertis de la fin du chargement du contenu :
package org.bytearray.document { import flash.display.SimpleButton; import flash.events.Event; import flash.events.MouseEvent;

39 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import org.bytearray.abstrait.ApplicationDefaut; import org.bytearray.chargement.ChargeurMedia; public class Document extends ApplicationDefaut { // interface public var boutonSuivant:SimpleButton; public var boutonPrecedent:SimpleButton; // visionneuse private var visionneuse:ChargeurMedia; public function Document () { // tableau contenant les url des images var tableauImages:Array = new Array ( "imgs/photo1.jpg", "imgs/anim1.swf", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia ( 350, 450 ); addChild ( visionneuse ); // coute de l'vnement Event.COMPLETE auprs de la visionneuse visionneuse.addEventListener ( Event.COMPLETE, chargementTermine

);

boutonPrecedent.addEventListener ( MouseEvent.CLICK, clicPrecedent ); boutonSuivant.addEventListener ( MouseEvent.CLICK, clicSuivant ); } private function chargementTermine ( pEvt:Event ):void { // centre la visionneuse visionneuse.x = ( stage.stageWidth - visionneuse.width ) / 2; visionneuse.y = ( stage.stageHeight - visionneuse.height ) / 2; } private function clicPrecedent ( pEvt:MouseEvent=null ):void { position = Math.max ( 0, --position ); requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } private function clicSuivant ( pEvt:MouseEvent=null ):void {

40 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

position = ++position % tableauImages.length; requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } } }

Afin de garantir un affichage align sur les pixels, il est recommand de toujours positionner une image sur des coordonnes non flottantes. modifions la fonction couteur chargementTermine en arrondissant les coordonnes de limage centre :
private function chargementTermine ( pEvt:Event ):void { // centre la visionneuse visionneuse.x = int ( ( stage.stageWidth - visionneuse.width ) / 2); visionneuse.y = int ( ( stage.stageHeight - visionneuse.height ) / 2); }

Pour

cela,

nous

Si nous testons la galerie, les images sont correctement charges et centres, comme lillustre la figure 13-8 :

41 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-8. Galerie centre. Afin de charger la premire image, nous pouvons dclencher manuellement la fonction couteur clicSuivant :
public function Document () { // tableau contenant les url des images var tableauImages:Array = new Array ( "imgs/photo1.jpg", "imgs/photo2.jpg", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia ( tableauImages, 350, 450 ); addChild ( visionneuse ); // coute de l'vnement Event.COMPLETE auprs de la visionneuse visionneuse.addEventListener ( Event.COMPLETE, chargementTermine ); boutonPrecedent.addEventListener ( MouseEvent.CLICK, clicPrecedent ); boutonSuivant.addEventListener ( MouseEvent.CLICK, clicSuivant ); clicSuivant(); }

Lorsque lapplication est lance, la premire image est charge automatiquement. Nous venons de dclencher ici, manuellement une fonction couteur lie lvnement MouseEvent.CLICK. Le code de la classe ChargeurMedia peut tre optimis en utilisant une seule fonction couteur pour rediriger diffrents types dvnements. Dans le code suivant la mthode redirigeEvenement redirige plusieurs vnements :
package org.bytearray.chargement { import import import import import import import import import import flash.display.Bitmap; flash.display.Loader; flash.display.Sprite; flash.display.DisplayObject; flash.display.LoaderInfo; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.URLRequest; flash.system.LoaderContext;

public class ChargeurMedia extends Sprite { private var chargeur:Loader;

42 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

private var cli:LoaderInfo; private var largeur:Number; private var hauteur:Number; public function ChargeurMedia ( pLargeur:Number, pHauteur:Number ) { largeur = pLargeur; hauteur = pHauteur; chargeur = new Loader(); addChild ( chargeur ) ; cli = chargeur.contentLoaderInfo; cli.addEventListener ( Event.INIT, redirigeEvenement ); cli.addEventListener ( Event.OPEN, redirigeEvenement ); cli.addEventListener ( ProgressEvent.PROGRESS, redirigeEvenement cli.addEventListener ( Event.COMPLETE, chargementTermine ); cli.addEventListener ( IOErrorEvent.IO_ERROR, redirigeEvenement } ):void public function load ( pURL:URLRequest, pContext:LoaderContext=null { chargeur.load ( pURL, pContext ); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function chargementTermine ( pEvt:Event ):void { var contenuCharge:DisplayObject = pEvt.target.content; var ratio:Number = Math.min ( largeur / contenuCharge.width, hauteur / contenuCharge.height ); scaleX = scaleY = 1; if ( ratio < 1 ) scaleX = scaleY = ratio; dispatchEvent ( pEvt ); }

); );

43 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

} }

Bien entendu, cette technique nest viable que dans le cas o aucune logique ne doit tre ajoute en interne pour chaque vnement diffus. Cest pour cette raison que lvnement Event.COMPLETE est toujours cout en interne par la mthode couteur chargementTermine, car nous devons ajouter une logique de redimensionnement spcifique.

Gestion du lissage
Nous allons ajouter une seconde fonctionnalit permettant dafficher les images charges de manire lisse. Ainsi, lorsque limage charge est redimensionne, grce au lissage celle-ci nest pas crnele. Pour cela, nous activons la proprit smoothing si le contenu charg est de type Bitmap :
package org.bytearray.chargement { import import import import import import import import import import flash.display.Bitmap; flash.display.Loader; flash.display.Sprite; flash.display.DisplayObject; flash.display.LoaderInfo; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.URLRequest; flash.system.LoaderContext;

public class ChargeurMedia extends Sprite { private private private private private var var var var var chargeur:Loader cli:LoaderInfo; largeur:Number; hauteur:Number; lissage:Boolean;

public function ChargeurMedia ( pLargeur:Number, pHauteur:Number, pLissage:Boolean=true ) { largeur = pLargeur; hauteur = pHauteur; lissage = pLissage; chargeur = new Loader();

44 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

addChild ( chargeur ) ; cli = chargeur.contentLoaderInfo; cli.addEventListener ( Event.INIT, redirigeEvenement ); cli.addEventListener ( Event.OPEN, redirigeEvenement ); cli.addEventListener ( ProgressEvent.PROGRESS, redirigeEvenement cli.addEventListener ( Event.COMPLETE, chargementTermine ); cli.addEventListener ( IOErrorEvent.IO_ERROR, redirigeEvenement } ):void public function load ( pURL:URLRequest, pContext:LoaderContext=null { chargeur.load ( pURL, pContext ); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function chargementTermine ( pEvt:Event ):void { var contenuCharge:DisplayObject = pEvt.target.content; = lissage; if ( contenuCharge is Bitmap ) Bitmap ( contenuCharge ).smoothing

); );

var ratio:Number = Math.min ( largeur / contenuCharge.width, hauteur / contenuCharge.height ); scaleX = scaleY = 1; if ( ratio < 1 ) scaleX = scaleY = ratio; dispatchEvent ( pEvt ); } } }

Puis nous modifions linstanciation de lobjet ChargeurMedia :


// cration de l'objet Loader visionneuse = new ChargeurMedia ( tableauImages, 350, 450, true );

45 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Les images sont dsormais lisses automatiquement. Si nous souhaitons dsactiver le lissage, nous le prcisons lors de linstanciation de lobjet ChargeurMedia :
// cration de l'objet Loader visionneuse = new ChargeurMedia ( tableauImages, 350, 450, false );

Souvenez-vous, nous devons toujours intgrer un mcanisme de desactivation des objets. Ainsi la classe ChargeurMedia ne droge pas la rgle et intgre un mcanisme de dsactivation. Aussitt supprime de la liste daffichage, linstance de ChargeurMedia est dsactive grce lvnement Event.REMOVED_FROM_STAGE.
Event.ADDED_TO_STAGE afin Nous utilisons lvnement dinitialiser lobjet ChargeurMedia lorsque celui-ci est affich :
package org.bytearray.chargement { import import import import import import import import import import flash.display.Bitmap; flash.display.Loader; flash.display.Sprite; flash.display.DisplayObject; flash.display.LoaderInfo; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.URLRequest; flash.system.LoaderContext;

public class ChargeurMedia extends Sprite { private private private private private var var var var var chargeur:Loader; cli:LoaderInfo; largeur:Number; hauteur:Number; lissage:Boolean;

public function ChargeurMedia ( pLargeur:Number, pHauteur:Number, pLissage:Boolean=true ) { largeur = pLargeur; hauteur = pHauteur; lissage = pLissage; chargeur = new Loader(); addChild ( chargeur ); cli = chargeur.contentLoaderInfo;

46 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { cli.addEventListener ( Event.INIT, redirigeEvenement ); cli.addEventListener ( Event.OPEN, redirigeEvenement ); cli.addEventListener ( ProgressEvent.PROGRESS, redirigeEvenement cli.addEventListener ( Event.COMPLETE, chargementTermine ); cli.addEventListener ( IOErrorEvent.IO_ERROR, redirigeEvenement } private function desactivation ( pEvt:Event ):void { cli.removeEventListener cli.removeEventListener cli.removeEventListener redirigeEvenement ); cli.removeEventListener cli.removeEventListener redirigeEvenement ); } ):void public function load ( pURL:URLRequest, pContext:LoaderContext=null { chargeur.load ( pURL, pContext ); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function chargementTermine ( pEvt:Event ):void { var contenuCharge:DisplayObject = pEvt.target.content; = lissage; if ( contenuCharge is Bitmap ) Bitmap ( contenuCharge ).smoothing ( Event.INIT, redirigeEvenement ); ( Event.OPEN, redirigeEvenement ); ( ProgressEvent.PROGRESS, ( Event.COMPLETE, chargementTermine ); ( IOErrorEvent.IO_ERROR,

); );

var ratio:Number = Math.min ( largeur / contenuCharge.width, hauteur / contenuCharge.height );

47 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

scaleX = scaleY = 1; if ( ratio < 1 ) scaleX = scaleY = ratio; dispatchEvent ( pEvt ); } } }

La classe ChargeurMedia fonctionne sans problme. Nous verrons au cours du chapitre 14 intitul Chargement de donnes comment remplacer les donnes par un flux XML charg dynamiquement. Nous verrons comment rutiliser la classe ChargeurMedia tout en la faisant communiquer avec une classe permettant le chargement de donnes XML externe.

Prcharger le contenu
Il est impratif de prcharger tout contenu dynamique. Un utilisateur interrompt trs rapidement sa navigation si aucun lment nindique visuellement ltat du chargement des donnes. Dans le cas de notre galerie photo, nous devons prcharger chaque image afin dinformer lutilisateur de ltat du chargement. Nous allons utiliser une barre de prchargement classique puis utiliser chacun des vnements afin de synchroniser son affichage. Nous allons tout dabord crer un symbole contenant la barre de prchargement. La figure 13-9 illustre le symbole en bibliothque :

48 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-9. Symbole prechargeur. Puis nous dfinissons une classe Prechargeur auquel le symbole est li :

Figure 13-10. Classe Prechargeur associe. Une fois le symbole associ, nous linstancions ds linitialisation de lapplication. Vous remarquerez que celui-ci nest pas affich pour linstant :
package org.bytearray.document { import flash.display.SimpleButton; import flash.display.Sprite;

49 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import import import import import import

flash.events.MouseEvent; flash.events.Event; flash.events.ProgressEvent; flash.net.URLRequest; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.chargement.ChargeurMedia;

public class Document extends ApplicationDefaut { // interface public var boutonSuivant:SimpleButton; public var boutonPrecedent:SimpleButton; public var prechargeur:Prechargeur; // visionneuse private var visionneuse:ChargeurMedia; // donnes private var tableauImages:Array; private var position:int; private var requete:URLRequest; public function Document () { position = -1; requete = new URLRequest(); // tableau contenant les url des images tableauImages = new Array ( "imgs/photo1.jpg", "imgs/photo2.jpg", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia ( 445, 335 ); addChild ( visionneuse ); prechargeur = new Prechargeur(); prechargeur.x = Math.round ( (stage.stageWidth prechargeur.width) / 2); prechargeur.y = Math.round ( (stage.stageHeight prechargeur.height) / 2); ); visionneuse.addEventListener ( Event.COMPLETE, chargementTermine

boutonPrecedent.addEventListener ( MouseEvent.CLICK, clicPrecedent ); boutonSuivant.addEventListener ( MouseEvent.CLICK, clicSuivant ); clicSuivant(); } private function chargementTermine ( pEvt:Event ):void {

50 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

2); / 2); }

// centre la visionneuse visionneuse.x = int ( ( stage.stageWidth - visionneuse.width ) / visionneuse.y = int ( ( stage.stageHeight - visionneuse.height )

private function clicPrecedent ( pEvt:MouseEvent=null ):void { position = Math.max ( 0, --position ); requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } private function clicSuivant ( pEvt:MouseEvent=null ):void { position = ++position % tableauImages.length; requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } } }

Puis nous coutons chacun des vnements ncessaires au prchargement :


package org.bytearray.document { import import import import import import import import flash.display.SimpleButton; flash.display.Sprite; flash.events.MouseEvent; flash.events.Event; flash.events.ProgressEvent; flash.net.URLRequest; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.chargement.ChargeurMedia;

public class Document extends ApplicationDefaut { // interface public var boutonSuivant:SimpleButton; public var boutonPrecedent:SimpleButton; public var prechargeur:Prechargeur;

51 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

// visionneuse private var visionneuse:ChargeurMedia; // donnes private var tableauImages:Array; private var position:int; private var requete:URLRequest; public function Document () { position = -1; requete = new URLRequest(); // tableau contenant les url des images tableauImages = new Array ( "imgs/photo1.jpg", "imgs/photo2.jpg", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia ( 445, 335 ); addChild ( visionneuse ); prechargeur = new Prechargeur(); prechargeur.x = Math.round ( (stage.stageWidth prechargeur.width) / 2); prechargeur.y = Math.round ( (stage.stageHeight prechargeur.height) / 2); visionneuse.addEventListener ( Event.OPEN, chargementDemarre ); visionneuse.addEventListener ( ProgressEvent.PROGRESS, chargementEnCours ); visionneuse.addEventListener ( Event.COMPLETE, chargementTermine ); boutonPrecedent.addEventListener ( MouseEvent.CLICK, clicPrecedent ); boutonSuivant.addEventListener ( MouseEvent.CLICK, clicSuivant ); clicSuivant ( new MouseEvent ( MouseEvent.CLICK ) ); } private function chargementDemarre ( pEvt:Event ):void { } private function chargementEnCours ( pEvt:ProgressEvent ):void { } private function chargementTermine ( pEvt:Event ):void

52 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

{ // centre la visionneuse visionneuse.x = int ( ( stage.stageWidth - visionneuse.width ) / visionneuse.y = int ( ( stage.stageHeight - visionneuse.height ) } private function clicPrecedent ( pEvt:MouseEvent=null ):void { position = Math.max ( 0, --position ); requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } private function clicSuivant ( pEvt:MouseEvent=null ):void { position = ++position % tableauImages.length; requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } } }

2); / 2);

Lorsque le chargement dmarre, nous ajoutons la barre de prchargement laffichage si celle-ci nest pas dj affiche :
private function chargementDemarre ( pEvt:Event ):void { if ( !contains ( prechargeur ) ) addChild ( prechargeur ); }

Puis nous adaptons sa taille selon le pourcentage charg :


private function chargementEnCours ( pEvt:ProgressEvent ):void { prechargeur.scaleX = pEvt.bytesLoaded / pEvt.bytesTotal; }

Enfin, nous supprimons la barre de prchargement lorsque les donnes sont charges : 53 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

private function chargementTermine ( pEvt:Event ):void { // centre la visionneuse visionneuse.x = int ( ( stage.stageWidth - visionneuse.width ) / 2); visionneuse.y = int ( ( stage.stageHeight - visionneuse.height ) / 2); if ( contains ( prechargeur ) ) removeChild ( prechargeur ); }

En testant notre galerie, chaque image est prcharge, comme lillustre la figure 13-11 :

Figure 13-11. Prchargement des images. Libre vous dintgrer diffrents types dlments indiquant le niveau de chargement des donnes. Nous aurions pu utiliser une barre de prchargement accompagne dun champ texte affichant un pourcentage de 0 100, ou bien une animation se jouant selon le volume de donnes charges. Afin dajouter un peu desthtisme notre galerie, nous ajoutons un effet de fondu permettant aux photos dtre affiches progressivement :
package org.bytearray.document { import flash.display.SimpleButton;

54 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

import import import import import import import import import

flash.display.Sprite; flash.events.MouseEvent; flash.events.Event; flash.events.ProgressEvent; flash.net.URLRequest; fl.transitions.Tween; fl.transitions.easing.Strong; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.chargement.ChargeurMedia;

public class Document extends ApplicationDefaut { // interface public var boutonSuivant:SimpleButton; public var boutonPrecedent:SimpleButton; public var prechargeur:Prechargeur; // visionneuse private var visionneuse:ChargeurMedia; // donnes private var tableauImages:Array; private var position:int; private var requete:URLRequest; // fondu private var fondu:Tween; public function Document () { position = -1; requete = new URLRequest(); // tableau contenant les url des images tableauImages = new Array ( "imgs/photo1.jpg", "imgs/photo2.jpg", "imgs/photo3.jpg", "imgs/photo4.jpg", "imgs/photo5.jpg" ); // cration de l'objet Loader visionneuse = new ChargeurMedia ( 445, 335 ); addChild ( visionneuse ); 0, true ); fondu = new Tween ( visionneuse, "alpha", Strong.easeOut, 1, 1, prechargeur = new Prechargeur(); 2); 2); prechargeur.x = int ( (stage.stageWidth - prechargeur.width) / prechargeur.y = int ( (stage.stageHeight - prechargeur.height) /

visionneuse.addEventListener ( Event.OPEN, chargementDemarre ); visionneuse.addEventListener ( ProgressEvent.PROGRESS, chargementEnCours ); visionneuse.addEventListener ( Event.COMPLETE, chargementTermine );

55 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

boutonPrecedent.addEventListener ( MouseEvent.CLICK, clicPrecedent ); boutonSuivant.addEventListener ( MouseEvent.CLICK, clicSuivant ); clicSuivant ( new MouseEvent ( MouseEvent.CLICK ) ); } private function chargementDemarre ( pEvt:Event ):void { if ( !contains ( prechargeur ) ) addChild ( prechargeur ); } private function chargementEnCours ( pEvt:ProgressEvent ):void { prechargeur.scaleX = pEvt.bytesLoaded / pEvt.bytesTotal; } private function chargementTermine ( pEvt:Event ):void { // centre la visionneuse visionneuse.x = Math.round ( ( stage.stageWidth visionneuse.width ) / 2); visionneuse.y = Math.round ( ( stage.stageHeight visionneuse.height ) / 2); if ( contains ( prechargeur ) ) removeChild ( prechargeur ); fondu.continueTo ( 1, 1 ); } private function clicPrecedent ( pEvt:MouseEvent=null ):void { fondu.continueTo ( 0, 1 ); position = Math.max ( 0, --position ); requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } private function clicSuivant ( pEvt:MouseEvent=null ):void { fondu.continueTo ( 0, 1 ); position = ++position % tableauImages.length;

56 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

requete.url = tableauImages [ position ]; visionneuse.load ( requete ); } } }

En testant notre galerie, nous remarquons un effet de fondu entre chaque image. Nous pouvons rendre le type de transition dynamique en passant en paramtre un type de comportement spcifique. A vous dimaginer la suite, pour cela voici un indice : En privilgiant une approche par composition, une classe comportementale pourrait tre dfinie puis passe en paramtre la classe ChargeurMedia afin de terminer un type de transition.

A retenir
Le contenu doit toujours tre prcharg afin doffrir une exprience utilisateur optimale.

Interrompre le chargement
Il tait impossible avec les prcdentes versions dActionScript dinterrompre le chargement dun lment externe. Le seul moyen tait de fermer le lecteur Flash. ActionScript 3 intgre dornavant une mthode close sur la classe Loader permettant de stopper instantanment le chargement dun lment. Afin de rendre notre classe ChargeurMedia souple, nous pouvons ajouter une nouvelle mthode close :
public function close ( ):void { chargeur.close(); }

Celle-ci dlgue cette fonctionnalit la classe Loader interne. Lorsque la mthode load est appele, tout chargement prcdemment initi est interrompu. Nous navons pas abord jusqu prsent la notion de communication entre lobjet Loader et le contenu. Dans la partie suivante nous allons nous attarder sur la communication entre deux animations.

57 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

A retenir
La mthode close de lobjet Loader permet dinterrompre le chargement en cours.

Communiquer entre deux animations


Dans un nouveau document Flash CS3 nous plaons sur la scne une instance de symbole anim. Lanimation est reprsente par une instance du symbole Garcon utilis lors du chapitre prcdent. Nous lui donnons animation comme nom doccurrence :

Figure 13-12. Instance du symbole Garcon. Une fois lanimation exporte, nous la chargeons laide de lobjet Loader depuis un autre SWF :
var chargeur:Loader = new Loader(); chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, termine ); chargeur.load ( new URLRequest ("chap-13-anim.swf") ); addChild ( chargeur ); function termine ( pEvt:Event ):void { // rfrence le scnario de l'animation charge var scenario:DisplayObject = pEvt.target.content; // affiche : [object MainTimeline] trace( scenario );

58 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Une fois lanimation charge, nous pouvons stopper lanimation en utilisant la syntaxe pointe :
function termine ( pEvt:Event ):void { // rfrence le scnario de l'animation charge var scenario:DisplayObject = pEvt.target.content; // si le scnario est un MovieClip nous accdons // l'animation et la stoppons if ( scenario is MovieClip ) MovieClip ( scenario ).animation.stop(); }

Si laccs au contenu de lanimation charge savre aise, la communication inverse savre plus complexe. Afin daccder au scnario du SWF initiant le chargement, nous utilisons la proprit root du scnario principal du SWF charg, puis la proprit parent de ce mme scnario :
// affiche : [object Loader] trace( root.parent );

Nous accdons ainsi lobjet Loader chargeant actuellement notre animation. En ciblant la proprit root de ce mme objet nous accdons au scnario principal du SWF chargeur :
// affiche : [object MainTimeline] trace( root.parent.root );

Afin de cibler une animation pose sur ce mme scnario, nous pouvons crire le code suivant :
// rfrence le scnario principal du SWF parent var scenarioPrincipal:DisplayObject = root.parent.root; // si celui-ci est un MovieClip if ( scenarioPrincipal is MovieClip ) { // alors nous transtypons et accdons au clip animation MovieClip ( scenarioPrincipal ).animation.stop(); }

Notons que si lobjet Loader initiant le chargement nest pas prsent au sein de la liste daffichage, sa proprit root renverra null. Dans lexemple prcdent, nous navons rencontr aucune difficult accder au contenu lanimation charge car les deux animations voluent depuis le mme domaine.

59 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Une autre technique plus lgante consiste diffuser un vnement personnalis depuis le SWF initiant le chargement. Le SWF charg est lcoute de ce dernier et reoit les informations de manire souple et lgante. Pour raliser cet change, nous utilisons lobjet EventDispatcher disponible par la proprit sharedEvents de la classe LoaderInfo. Au sein du SWF initiant le chargement, nous diffusons un vnement personnalis contenant les informations transmettre :
var chargeur:Loader = new Loader(); chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, termine ); chargeur.load ( new URLRequest ("chap-13-anim.swf") ); addChild ( chargeur ); function termine ( pEvt:Event ):void { // nous diffusons un vnement EvenementInfos.INFOS au SWF charg pEvt.target.sharedEvents.dispatchEvent ( new EvenementInfos ( EvenementInfos.INFOS, "contenu !" ) ); }

Au sein du SWF charg nous coutons ce mme vnement :


loaderInfo.sharedEvents.addEventListener ( EvenementInfos.INFOS, ecouteur ); function ecouteur ( pEvt:EvenementInfos ):void { // affiche : contenu ! trace( pEvt.infos ); }

Cette approche facilite grandement la communication entre deux animations et doit tre considre en priorit car elle ne souffre daucune restriction de scurit mme en contexte interdomaine. Dans un contexte de chargement de contenu externe, nous risquons dtre confronts aux restrictions de scurit du lecteur Flash. Dans la partie suivante nous allons faire le point sur ces limitations afin de mieux comprendre comment les apprhender et rsoudre certains problmes.

60 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Modle de scurit du lecteur Flash


Depuis le lecteur Flash 6, des restrictions de scurit ont t ajoutes au sein du lecteur Flash lors du chargement ou denvoi de donnes afin de protger les auteurs de contenu divers. Le modle de scurit du lecteur Flash appel Security Sandbox en Anglais distingue deux acteurs :
Le crateur du contenu Le chargeur de contenu

Ce modle sapplique tout type de contenu charg au sein du lecteur. Il faut comprendre que par principe, lorsquun contenu graphique est charg depuis un autre domaine, celui-ci est en lecture seule au sein du lecteur. Ce dernier refuse de scripter ou de modifier tout contenu graphique provenant dun autre domaine. On dit alors que les fichiers voluent dans un contexte interdomaine. Par le terme scripter nous entendons laccs par ActionScript des donnes contenues dans une autre animation. Adobe utilise le terme de programmation croise pour exprimer ce mcanisme. Par le terme de modification, nous entendons par exemple lactivation de la proprit smoothing sur une image bitmap charge, ou encore la capture dune vido sous forme bitmap laide de la mthode draw de la classe BitmapData. Afin de bien comprendre le modle de scurit, nous allons tudier diffrentes situations. Commenons par le cas de figure suivant : Nous dveloppons un portail de jeux Flash cens charger des jeux provenant de diffrents sites. Si le lecteur Flash nintgrait pas de modle de scurit, il serait possible dajouter du code aux diffrents jeux chargs et de pirater ces derniers. Si toutefois, nous devons accder au code dun jeu charg, nous devons ajouter au sein de celui-ci une ligne de code autorisant le domaine spcifique scripter lanimation. En dautres termes, le contenu charg doit autoriser le chargeur le scripter. Dans le cas de chargement dimages provenant dautres domaines, celles-ci nont pas la possibilit dautoriser le chargeur par ActionScript. Nous utilisons dans cas des fichiers XML contenant la liste des domaines autoriss. Ces fichiers sont appels fichiers de rgulation.

A retenir
61 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Par dfaut, le lecteur Flash empche de scripter ou modifier tout contenu graphique provenant dun domaine diffrent. En revanche, la lecture seule danimation ou images est toujours autorise quelque soit le contexte. Le chargement de donnes type XML ou autres est toujours interdit dans un contexte interdomaine.

Programmation croise
Nous entendons par le terme de programmation croise, laccs par ActionScript au code contenu par un SWF. Nous avons vu au cours de la prcdente partie comment faire communiquer deux animations situes sur un mme domaine. Nous savons que par dfaut, la communication est toujours autorise entre deux SWF rsidant sur le mme domaine. A linverse, lorsque deux animations souhaitent communiquer mais ne rsident par sur le mme domaine, lanimation devant tre scripte doit autoriser lanimation ayant amorc le chargement. La figure 13-13 illustre la situation :

Figure 13-13. Animations en contexte interdomaine. Dans le schma illustr par la figure 13-15 nous voyons deux animations en contexte interdomaine. Lanimation SWF A souhaite scripter lanimation SWF B. Pour cela, cette dernire doit appeler la mthode allowDomain de la classe flash.system.Security dont voici la signature : 62 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

public static function allowDomain(... domains):void

Nous pouvons passer en paramtre les domaines autoriss scripter le SWF en cours. Ainsi nous placerons au sein de lanimation SWF B le code suivant :
Security.allowDomain("monserveur.fr");

Il nest pas ncessaire dajouter http devant le domaine, nous ne spcifions gnralement que le nom et le domaine. Il est aussi possible de spcifier une adresse IP.

A retenir
Dans un contexte interdomaine, laccs par ActionScript au code contenu par un SWF est appel programmation croise.

Utiliser un fichier de rgulation


Comme expliqu prcdemment, dans le cas de chargement dimages bitmap provenant dun serveur distant, il est impossible d'ajouter au sein de celles-ci un appel la mthode allowDomain. Afin de pallier ce problme, nous pouvons crer des fichiers de rgulation. Ces derniers doivent tre placs sur le serveur hbergeant les images et sont en ralit de simples fichiers XML sauvs sous le nom de crossdomain.xml. La figure 13-14 illustre lide :

Figure 13-14. Fichier de rgulation.

63 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Lorsque le lecteur Flash charge une image, celui-ci tentait dans ses prcdentes versions de charger automatiquement le fichier de rgulation depuis la racine du serveur. Depuis le lecteur 9, il est ncessaire dindiquer sil est ncessaire de charger le fichier de rgulation avant de commencer le chargement de llment. Lutilisation de fichiers de rgulation est fondamentale pour les sites hbergeant des images utilises au sein dapplications Flash. Voici une liste de sites utilisant un fichier de rgulation :
http://www.facebook.com/crossdomain.xml http://www.adobe.com/crossdomain.xml http://www.youtube.com/crossdomain.xml http://static.flickr.com/crossdomain.xml

En analysant le contenu dun fichier de rgulation nous dcouvrons un simple fichier XML contenant la liste des domaines autoriss modifier les images. Ces derniers sont appels plus couramment domaines de confiance. Voici le contenu du fichier de rgulation situ sur le serveur YouTube :
<cross-domain-policy> <allow-access-from domain="*.youtube.com"/> <allow-access-from domain="*.google.com"/> </cross-domain-policy>

Ce fichier de rgulation indique que seuls les SWF hbergs dans des sous-domaines de youtube.com ou google.com peuvent scripter les images hberges sur youtube.com. En analysant le fichier de rgulation de flickr, nous remarquons que ces derniers sont beaucoup plus permissifs :
<cross-domain-policy> <allow-access-from domain="*"/> </cross-domain-policy>

Nous dcouvrons que tous les domaines peuvent modifier les images hberges sur flickr.com.

A retenir

64 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Dans le cas de chargement dimages distantes, des fichiers de rgulation peuvent tre crs afin dautoriser les domaines de confiance. Un fichier de rgulation est un simple fichier XML. Celui-ci doit tre nomm par dfaut crossdomain.xml.

Contexte de chargement
Lorsque les mthodes load et loadBytes de la classe Loader sont appeles, nous pouvons passer en deuxime paramtre un contexte de chargement exprim par la classe flash.system.LoaderContext. La classe LoaderContext permet dindiquer le domaine dapplication et de scurit dans lequel sera plac le contenu. Le constructeur de la classe LoaderContext accepte trois paramtres dont voici le dtail :
checkPolicyFile : indique si un fichier de rgulation doit tre charg avant de commencer charger le contenu. applicationDomain : sert prciser le domaine dapplication utiliser une fois le contenu charg. La notion de domaine dapplication est traite dans la partie intitule Bibliothque partage. securityDomain : reprsente le modle de scurit. Il est seulement utilis lors du chargement de fichiers SWF. Son rle est trait dans la partie intitule Bibliothque partage.

Ainsi, afin de pouvoir modifier une image hberge depuis un domaine distant nous demandons au lecteur Flash de charger un fichier de rgulation :
var chargeur:Loader = new Loader(); var requete:URLRequest = new URLRequest ("http://www.serveurdistant.com/images/wiiflash.jpg"); var contexte:LoaderContext = new LoaderContext ( true ); chargeur.load ( requete, contexte );

Automatiquement, le lecteur Flash tente de charger un fichier de rgulation stock la racine du serveur serveurdistant. Si un tel fichier nomm crossdomain.xml est prsent et autorise notre domaine alors nous pouvons modifier limage charge. Si aucun fichier de rgulation nest trouv, le lecteur Flash empche toute modification de limage charge. Pour des questions de pratique, il vous est peut-tre impossible de placer un fichier de rgulation la racine du domaine distant. Si le 65 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

fichier de rgulation est plac un emplacement diffrent de la racine du domaine distant. Nous spcifions son chemin laide de la mthode loadPolicyFile de la classe Security. Dans le code suivant, nous spcifions au lecteur de ne pas chercher le fichier de rgulation la racine du domaine mais au sein du rpertoire images :
var chargeur:Loader = new Loader(); Security.loadPolicyFile("http://serveurdistant.com/images/regulation.xml"); var requete:URLRequest = new URLRequest ("http://www.serveurdistant.com/images/wiiflash.jpg"); var contexte:LoaderContext = new LoaderContext ( true ); chargeur.load ( requete, contexte );

Notons que grce la mthode loadPolicyFile nous pouvons aussi spcifier le nom du fichier de rgulation, ici regulation.xml. Attention, lemplacement du fichier de rgulation a une importance. A la racine, celui-ci autorise laccs tous les fichiers du site. Sil se trouve plus loin dans larborescence, il nautorisera que les fichiers de ce dossier ainsi que ceux des dossiers enfants.

Contourner les restrictions de scurit


Nous avons vu dans la partie intitule Programmation croise quil tait possible dautoriser la manipulation dimages hberges sur des domaines distants par la cration de fichiers de rgulation. Malheureusement, dans certains cas, lajout de tels fichiers est impossible. Certains sites prvoient quelquefois leur installation, mais ils demeurent minoritaires. Il peut donc tre intressant de savoir contourner les restrictions de scurit du lecteur Flash. Cest ce que nous allons appendre ds maintenant. Imaginons le cas suivant : Vous venez dapprendre que vous devez dvelopper une application Flash base sur des images provenant de diffrentes sources. En dautres termes, chaque image devra tre charge tout en tant hberge sur nimporte quel domaine. Pour linstant, cela ne pose aucun problme car le simple chargement dimages en situation interdomaine est autoris. En cours de dveloppement vous vous rendez compte quun lissage est ncessaire et quil serait intressant de pouvoir lactiver auprs des images charges. 66 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Un premier rflexe vous incite activer la proprit smoothing sur lobjet Bitmap charg :
// cration de l'objet Loader var chargeur:Loader = new Loader(); // coute de la fin du chargement chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, termine ); // image google map var requete:URLRequest = new URLRequest ( "http://kh0.google.fr/kh?n=404&v=22&t=trtqttqqrrqrqssts" ); // chargement de l'image chargeur.load ( requete ); // ajout la liste d'affichage addChild ( chargeur ); function termine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // accs l'image bitmap var image:Bitmap = Bitmap ( objetLoaderInfo.content ); // activation du lissage image.smoothing = true; }

En testant lapplication en local, le lissage fonctionne, la figure 13-15 illustre le rsultat :

67 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-15. Image lisse provenant de Google Map. Malheureusement, une fois lapplication publie sur votre serveur tout accs au contenu charg lve une erreur lexcution de type SecurityError :
SecurityError: Error #2122: Violation de la scurit Sandbox : LoaderInfo.content : http://www.bytearray.org/pratique-as3/chap-13-proxy.swf ne peut pas accder http://kh0.google.fr/kh?n=404&v=22&t=trtqttqqrrqrqssts. Un fichier de rgulation est ncessaire, mais l'indicateur checkPolicyFile n'a pas t dfini lors du chargement de ce support.

Ceci est d au fait que le serveur Google ne possde aucun fichier de rgulation nous autorisant manipuler ses images. Lutilisation de la proprit checkPolicyFile est donc dans ce cas prcis inutile. En dautres termes, le contenu charg est en lecture seule. Nous allons donc utiliser une astuce consistant faire croire au lecteur Flash que nous chargeons un lment provenant du mme domaine. Pour cela, nous utilisons un relais, plus couramment appel serveur mandataire. La figure 13-16 illustre le concept :

68 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-16. Chargement dimages par serveur mandataire. Lastuce consiste charger limage depuis le serveur mandataire, qui est ensuite charg par le lecteur Flash. Ce dernier pense charger un lment provenant du mme domaine, sans penser que le serveur mandataire contient limage provenant du domaine distant. La cration du serveur mandataire se limite deux lignes de code PHP. Nous utilisons dans notre exemple le langage serveur PHP qui savre tre un des langages les plus efficace pour travailler avec Flash. Au sein dun fichier intitul proxy.php nous ajoutons le script suivant :
<?php $chemin = $_POST["chemin"]; readfile($chemin); ?>

Nous passons par le tableau $_POST le chemin dimage. Puis la fonction PHP readfile renvoie le flux dimage directement au lecteur Flash. Puis nous modifions le code, afin de charger limage par le serveur mandataire :
// cration de l'objet Loader var chargeur:Loader = new Loader(); // coute de la fin du chargement chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, termine);

69 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

// image google map var requete:URLRequest = new URLRequest ( "proxy.php" ); // cration d'un objet URLVariables permettant // de passer des variables au serveur mandataire var variables:URLVariables = new URLVariables(); // cration de la variable chemin variables.chemin = "http://kh0.google.fr/kh?n=404&v=22&t=trtqttqqrrqrqssts"; // les variables doivent tre passes par la requete HTTP requete.data = variables; // les variables sont envoyes au sein du tableau POST requete.method = URLRequestMethod.POST; // chargement de l'image chargeur.load ( requete ); // ajout la liste d'affichage addChild ( chargeur ); function termine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // accs l'image bitmap charge var image:Bitmap = Bitmap ( objetLoaderInfo.content ); }

Nous utilisons la classe URLVariables afin de passer ladresse de limage charger au serveur mandataire. Nous reviendrons en dtail sur cette classe au cours du chapitre 14 intitul Chargement et envoi de donnes. Une fois publie, si nous lanons lapplication, celle-ci ne lve plus derreur lexcution lorsque nous accdons la proprit content. Nous pouvons ainsi activer le lissage en passant la valeur boolenne true la proprit smoothing de lobjet Bitmap charg :
function termine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // accs l'image bitmap charge var image:Bitmap = Bitmap ( objetLoaderInfo.content ); // activation du lissage image.smoothing = true; }

La figure 13-17 illustre la diffrence entre limage lisse et non lisse : 70 / 84


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-17. Image non lisse et lisse. De la mme manire, il peut tre ncessaire de rendre sous forme bitmap un objet Loader contenant une image provenant dun domaine distant :
function termine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // cration d'une instance de BitmapData var donneesBitmap:BitmapData = new BitmapData ( objetLoaderInfo.width, objetLoaderInfo.height ); // l'objet Loader est rendu sous forme bitmap donneesBitmap.draw ( pEvt.target.loader ); var image:Bitmap = new Bitmap ( donneesBitmap ); image.x = objetLoaderInfo.width + 5; addChild ( image ); }

Ce qui gnre le rsultat illustr en figure 13-18 :

71 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-18. Image duplique. Sans serveur mandataire, lappel de la mthode draw aurait lev une erreur de scurit lexcution. Cette technique de serveur mandataire est une solution efficace qui possde malheureusement un inconvnient. Au lieu dtre directement charge depuis le client, limage est dabord charge par le serveur puis charge par le client. La charge serveur peut donc tre plus importante et surveiller sur un grand projet destin un trafic important.

A retenir
Lutilisation dun serveur mandataire permet de charger et modifier tout type de contenu provenant dun diffrent domaine. Cest une solution simple et efficace mais qui peut entraner une charge serveur plus importante.

Bibliothque partage
Dans le cas de chargement danimations, il peut tre parfois utile dextraire une classe utilise au sein dun SWF afin de lutiliser au sein de lanimation procdant au chargement. Cela est rendu possible grce au mcanisme de bibliothque partage lexcution apport par la classe flash.system.ApplicationDomain. Dans un nouveau document Flash nous crons un symbole clip comme illustr en figure 13-19 :

72 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-19. Symbole clip. Puis nous lions le symbole une classe Ninja grce au panneau de Proprits de liaisons :

Figure 13-20. Panneau de proprits de liaison. Une fois dfinie, nous exportons simplement lanimation sous le nom de bibliotheque.swf. Nous allons maintenant charger ce fichier SWF et accder dynamiquement la classe Ninja. Dans un nouveau document Flash, nous crons un objet Loader puis nous chargeons lanimation bibliotheque.swf :
// cration de l'objet Loader var chargeur:Loader = new Loader(); // coute de la fin du chargement

73 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementTermine ); // chargement de l'animation contenant la classe Ninja chargeur.load ( new URLRequest ("bibliotheque.swf") ); function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // affiche : [object LoaderInfo] trace( objetLoaderInfo ); }

Une fois lanimation charge, nous pouvons accder toutes les classes dfinies au sein de celle-ci grce la mthode getDefinition de la classe. Celle-ci peut tre appele ds lors que lvnement Event.INIT est diffus. Nous najoutons pas volontairement lobjet Loader a liste daffichage car nous souhaitons simplement extraire une classe partage. Souvenez-vous, nous avons vu prcdemment que la classe LoaderInfo possde une proprit applicationDomain utilise lors du chargement de SWF. Celle-ci rfrence un objet appel Domaine dapplication :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // rfrence le domaine d'application du SWF charg var domaineApplication:ApplicationDomain = objetLoaderInfo.applicationDomain; // affiche : [object ApplicationDomain] trace( domaineApplication ); }

Le domaine dapplication est un objet dans lequel sont placs toutes les dfinitions de classe dun SWF. Ainsi, le domaine dapplication de lanimation bibliotheque.swf contient une classe Ninja. La classe ApplicationDomain possde deux mthodes dont voici le dtail :
hasDefinition : Indique si la dfinition de classe existe. getDefinition : Extrait une dfinition de classe spcifique.

Deux proprits peuvent aussi tre utilises : 74 / 84


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

currentDomain : Rfrence le domaine dapplication du SWF en cours. parentDomain : Rfrence le domaine dapplication parent.

Nous allons extraire la classe Ninja du domaine dapplication du SWF charg puis linstancier et afficher le symbole au sein de lanimation procdant au chargement :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // rfrence le domaine d'application du SWF charg var domaineApplication:ApplicationDomain = objetLoaderInfo.applicationDomain; // extrait la dfinition de classe Ninja var importNinja:Class = Class ( domaineApplication.getDefinition( "Ninja" ) ); // cration d'une instance de Ninja var instanceNinja:DisplayObject = new importNinja(); // ajout la liste d'affichage addChild ( instanceNinja ); }

La figure 13-21 illustre le rsultat :

75 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-21. Affichage du symbole Ninja. Si nous tentons dextraire une classe inexistante :
// tente dextraire une dfinition classe nomme Nina var importNinja:Class = Class ( domaineApplication.getDefinition( "Nina" ) );

Une erreur de type ReferenceError est leve :


ReferenceError: Error #1065: La variable Nina n'est pas dfinie.

A laide dun bloc, try catch nous pouvons grer lerreur et ainsi ragir :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // rfrence le domaine d'application du SWF charg var domaineApplication:ApplicationDomain = objetLoaderInfo.applicationDomain; try { // tentative d' extraction de la dfinition de classe var importNinja:Class = Class ( domaineApplication.getDefinition( "Nina" ) ); // cration d'une instance de Ninja var instanceNinja:DisplayObject = new importNinja(); // ajout la liste d'affichage addChild ( instanceNinja ); } catch ( pError:Error ) { trace("La dfinition de classe spcifie n'est pas disponible"); } }

Si lutilisation dun bloc try catch ne vous convient pas, lappel de la mthode hasDefinition offre un rsultat quivalent :
// cration de l'objet Loader var chargeur:Loader = new Loader(); // coute de la fin du chargement chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementTermine ); // chargement de l'animation contenant la classe Ninja chargeur.load ( new URLRequest ("librairie.swf") );

76 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

var definitionClasse:String = "Ninja"; function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // rfrence le domaine d'application du SWF charg var domaineApplication:ApplicationDomain = objetLoaderInfo.applicationDomain; // vrifie si la dfinition de classe Ninja est disponible if ( domaineApplication.hasDefinition( definitionClasse ) ) { // tentative d' extraction de la dfinition de classe var importNinja:Class = Class ( domaineApplication.getDefinition( definitionClasse ) ); // cration d'une instance de Ninja var instanceNinja:DisplayObject = new importNinja(); // ajout la liste d'affichage addChild ( instanceNinja ); } else trace ("La dfinition de classe " + disponible"); } definitionClasse + " n'est pas

Lextraction de classes est rendue possible car lanimation contenant les classes extraire provient du mme domaine que lanimation procdant au chargement. Bien entendu, le modle de scurit du lecteur Flash empche par dfaut lextraction de classes entre deux SWF voluant dans un contexte interdomaine. Dans ce cas, nous devons explicitement demander au lecteur Flash de placer le SWF charg dans le mme domaine de scurit afin de pouvoir extraire les classes. Une premire approche consiste appeler la mthode allowDomain de la classe Security depuis le SWF dont les classes sont extraites.
Security.allowDomain("monserveurDeConfiance.fr");

La seconde requiert le placement dun fichier de rgulation sur le domaine du SWF charger, puis de passer un objet LoaderContext la mthode load de lobjet Loader en spcifiant le domaine de scurit en cours par la proprit securityDomain :
// cration de l'objet Loader var chargeur:Loader = new Loader(); // coute de la fin du chargement chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementTermine );

77 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

// cration d'un objet de contexte var contexte:LoaderContext = new LoaderContext(); // nous demandons de placer le SWF charg dans le mme domaine // de scurit afin de pouvoir extraire ses classes contexte.securityDomain = SecurityDomain.currentDomain; // chargement de l'animation contenant la classe Ninja // en spcifiant le contexte chargeur.load ( new URLRequest ("http://serveurdistant.com/swf/bibliotheque.swf"), contexte ); function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // rfrence le domaine d'application du SWF charg var domaineApplication:ApplicationDomain = objetLoaderInfo.applicationDomain; // extrait la dfinition de classe Ninja var importNinja:Class = Class ( domaineApplication.getDefinition( "Ninja" ) ); // cration d'une instance de Ninja var instanceNinja:DisplayObject = new importNinja(); // ajout la liste d'affichage addChild ( instanceNinja ); }

Si pour des questions de pratique, vous navez pas la possibilit dappeler la mthode allowDomain de la classe Security ou de placer un fichier de rgulation sur le domaine distant, lutilisation dun fichier serveur mandataire est ici aussi envisageable. Grce ce concept dimport dynamique de classes, nous pouvons imaginer toutes sortes dapplications tirant profit dune telle fonctionnalit. Une application Flash pourrait importer, ds son initialisation un SWF contenant les dfinitions de classe ncessaires. Celles-ci seraient dynamiquement instancies puis utilises dans lapplication. Lapplication reposerait donc entirement sur ces classes importes dynamiquement. Afin de mettre jour lapplication, nous pourrions simplement rgnrer le SWF contenant les dfinitions de classe.
.dll

Lapplication pourrait tre mise jour de la mme manire que des dans dautres langages comme C++ ou C#.

A retenir
78 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

La mthode getDefinition de la classe ApplicationDomain permet dextraire une dfinition de classe contenue dans un SWF. Cette extraction est soumise au modle de scurit du lecteur Flash. Afin de pouvoir extraire une classe dun SWF distant, celui-ci doit autoriser lanimation ayant amorc le chargement par la mthode allowDomain de la classe Security ou la cration dun fichier de rgulation.

Dsactiver une animation charge


Trs souvent, un site Flash est constitu dune application principale chargeant diffrents modules, spars en plusieurs SWF. Chacun dentre eux est ensuite charg afin de naviguer dans le site. Le fonctionnement de la classe Loader nous rserve encore quelques surprises. En ralit, la mthode unload vide le contenu charg mais ne le dsactive pas. Cela diffre du traditionnel loadMovie utilis en ActionScript 1 et 2, qui remplaait le contenu prcdemment charg en dsactivant tous les objets contenus. En ActionScript 3, lorsque la mthode unload est excute, le contenu charg est simplement supprim de la liste daffichage. La seule rfrence lanimation est celle que possde lobjet Loader. Si nous supprimons son contenu il nexiste alors plus aucune rfrence vers lanimation. Celle-ci va donc demeurer et vivre en mmoire jusqu ce que le ramasse miettes intervienne et la supprime dfinitivement. Ainsi, au chargement dune nouvelle rubrique, le son de la prcdente continuerait de jouer. De la mme manire, tous les vnements souscrits continueraient dtre diffuss. Il faut donc prvoir obligatoirement un mcanisme de dsactivation comme nous lavons fait jusqu prsent pour les objets daffichage. Afin de correctement dsactiver une animation, nous utilisons lvnement Event.UNLOAD diffus par lobjet LoaderInfo associ au SWF en cours. Le code suivant doit donc tre plac au sein du SWF dsactiver :
// coute la suppression de l'animation loaderInfo.addEventListener ( Event.UNLOAD, desactivation ); function desactivation ( pEvt:Event ):void { // logique de dsactivation de l'animation // dsactivation des vnements, du son, objets videos etc

79 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Lorsque la mthode unload est excute, ou quune nouvelle animation est charge, lvnement Event.UNLOAD est diffus au sein de lanimation charge. Nous intgrons au sein de la fonction couteur desactivation la logique ncessaire afin de dsactiver totalement lanimation en cours. Dans le code suivant, nous stoppons le son en cours de lecture :
// cration d'un objet Sound var monSon:Sound = new Sound(); // chargement du son monSon.load ( new URLRequest ("son.mp3") ); // cration d'un objet SoundChannel par l'appel de la mthode Sound.play() var canalAudio:SoundChannel = monSon.play(); // coute la suppression de l'animation loaderInfo.addEventListener ( Event.UNLOAD, desactivation ); function desactivation ( pEvt:Event ):void { // logique de dsactivation de l'animation // dsactivation des vnements, du son, objets videos etc canalAudio.stop(); }

Lanimation charge tant supprime de la liste daffichage, lcoute de lvnement Event.REMOVED_FROM_STAGE est aussi envisageable :
// cration d'un objet Sound var monSon:Sound = new Sound (); // chargement du son monSon.load ( new URLRequest ("son.mp3") ); // cration d'un objet SoundChannel par l'appel de la mthode Sound.play() var canalAudio:SoundChannel = monSon.play(); // coute la suppression de l'animation addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); function desactivation ( pEvt:Event ):void { // logique de dsactivation de l'animation // dsactivation des vnements, du son, objets videos etc canalAudio.stop(); }

80 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Ce comportement peut poser de graves problmes lorsque vous ntes pas lauteur du contenu charg. Vous tes donc dans lincapacit dintgrer un mcanisme de dsactivation. Il nexiste aujourdhui aucune solution viable permettant de dsactiver automatiquement un contenu tiers.

A retenir
Lorsque la mthode unload est appele, le contenu est supprim de la liste daffichage mais nest pas dsactiv. Il est impratif de prvoir un mcanisme de dsactivation au sein des animations charges. Il nest pas possible de dsactiver automatiquement un contenu tiers.

Communication AVM1 et AVM2


La machine virtuelle ActionScript 3 (AVM2) offre la possibilit de lire des animations dveloppes en ActionScript 1 et 2 (AVM1). Celles-ci sont alors considres comme des objets de type flash.display.AVM1Movie. Dans le cas dun portail de jeux vido dvelopp en ActionScript 3, la majorit des jeux chargs seront dancienne gnration, dvelopps en ActionScript 1 ou 2. Malheureusement, lchange entre les deux animations nest pas simplifi. Si nous tentons daccder au contenu de ces derniers, le lecteur lve une erreur indiquant que laccs est impossible. Il faut considrer un objet AVM1Movie comme un objet hermtique ne pouvant tre pntr. Afin de mettre en vidence ce comportement, nous allons crer une animation ActionScript 1 ou 2 et y intgrer une simple animation. Lanimation est reprsente par une instance du symbole Garcon utilis lors du chapitre prcdent. Nous lui donnons animation comme nom doccurrence.

81 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

Figure 13-22. Instance du symbole Garcon. Nous allons depuis lanimation ActionScript 3, communiquer avec le contenu lanimation dancienne gnration pour stopper lanimation du clip animation. Dans le code suivant, nous chargeons lanimation dancienne gnration :
var chargeur:Loader = new Loader(); chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.load ( new URLRequest ("anim-vm1.swf" )); addChild ( chargeur ); function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target ); // affiche : [object AVM1Movie] trace( objetLoaderInfo.content ); }

Nous remarquons que la proprit content de lobjet LoaderInfo nous renvoie un objet de type AVM1Movie. Si nous tentons de pntrer lintrieur de lanimation :
function chargementTermine ( pEvt:Event ):void { var objetLoaderInfo:LoaderInfo = LoaderInfo ( pEvt.target );

82 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

var contenu:DisplayObject = objetLoaderInfo.content; if ( contenu is AVM1Movie ) { var animationVM1:AVM1Movie = AVM1Movie ( contenu ); // tentative d'accs l'occurrence animation trace( animationVM1.animation ); } }

Lerreur la compilation suivante est gnre :


1119: Accs la proprit animation peut-tre non dfinie, via la rfrence de type static flash.display:AVM1Movie.

Afin daccder au contenu de lanimation charge, nous devons passer par un moyen dtourn. Deux classes vont nous permettre de communiquer :
flash.net.LocalConnection : la classe LocalConnection permet dchanger des donnes entre diffrents SWF distincts. flash.external.ExternalInterface : la classe ExternalInterface permet la communication entre le code ActionScript et la page contenant le lecteur Flash.

Nous allons utiliser pour cet exemple la classe LocalConnection qui savre tre la solution la plus souple, en ne ncessitant pas de code JavaScript contrairement la classe ExternalInterface. Nous par crer une instance de la classe LocalConnection dans lanimation dans laquelle nous souhaitons accder :
// cration de lobjet rcepteur var recepteur:LocalConnection = new LocalConnection(); // connexion au canal utilis par lmetteur recepteur.connect ("canalCommunication"); // dfinition de la mthode appele par lmetteur recepteur.stopAnimation = function ( ) { animation.stop(); }

commenons

Puis au sein de lanimation souhaitant initier la communication, nous ajoutons une nouvelle instance de la classe LocalConnection afin dmettre les messages : 83 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 13 Chargement de contenu version 0.1.2

var chargeur:Loader = new Loader(); chargeur.load ( new URLRequest ("anim-vm1.swf" )); addChild ( chargeur ); // cration de lobjet metteur var emetteur:LocalConnection = new LocalConnection(); boutonStop.addEventListener ( MouseEvent.CLICK, clicBouton ); function clicBouton ( pEvt:MouseEvent ):void { // mission dun message pour excuter la mthode stopAnimation par le canal canalCommunication emetteur.send ("canalCommunication ", "stopAnimation"); }

Lorsque nous cliquons sur le bouton boutonStop, un message est envoy lanimation charge par lappel de la mthode send de lobjet LocalConnection.

A retenir
La communication entre deux animations AVM1 et AVM2 est possible par lintermdiaire de la classe LocalConnection ou ExternalInterface.

Nous savons dsormais comment charger du contenu graphique au sein du lecteur Flash. Passons maintenant au chargement et lenvoi de donnes en ActionScript 3.

84 / 84
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

14
Chargement et envoi de donnes

LA CLASSE URLLOADER ................................................................................... 1 CHARGER DU TEXTE ................................................................................................ 3 LENCODAGE URL................................................................................................ 13 CHARGER DES VARIABLES .................................................................................... 14 CHARGER DES DONNES XML .............................................................................. 20 CHARGEMENT DE DONNEES ET SECURITE .............................................. 30 CHARGER DES DONNES BINAIRES ............................................................ 32 CONCEPT DENVOI DE DONNEES ................................................................. 37 ENVOYER DES VARIABLES..................................................................................... 38 LA METHODE GET OU POST ................................................................................ 45 ENVOYER DES VARIABLES DISCRTEMENT............................................................ 48 RENVOYER DES DONNEES DEPUIS LE SERVEUR...................................................... 50 ALLER PLUS LOIN .................................................................................................. 56 ENVOYER UN FLUX BINAIRE .................................................................................. 57 TELECHARGER UN FICHIER .......................................................................... 58 PUBLIER UN FICHIER ............................................................................................. 68 PUBLIER PLUSIEURS FICHIERS ............................................................................... 73 CREATION DE LA CLASSE ENVOIMULTIPLE ........................................................... 80 RETOURNER DES DONNEES UNE FOIS LENVOI TERMINE ........................................ 89 ALLER PLUS LOIN.............................................................................................. 93

La classe URLLoader
Afin de concevoir une application dynamique nous avons besoin de pouvoir envoyer ou charger des donnes externes au sein du lecteur Flash. Le chargement dynamique de donnes offre une grande souplesse de dveloppement en permettant la mise jour complte du contenu dune application sans recompiler celle-ci.

1 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Parmi les formats les plus couramment utiliss nous pouvons citer le texte ainsi que le XML mais aussi un format brut comme le binaire. Nous reviendrons en dtail sur la manipulation de donnes binaire au cours du chapitre 20 intitul ByteArray. ActionScript 3 offre la possibilit de charger et denvoyer ces diffrents types de donnes grce la classe flash.net.URLLoader qui remplace lobjet LoadVars ainsi que les diffrentes fonctions loadVariables, loadVariablesNum utilises dans les prcdentes versions dActionScript. Nous retrouvons ici lintrt dActionScript 3 consistant centraliser les fonctionnalits du lecteur. Il est important de noter que contrairement la classe Loader, la classe URLLoader diffuse directement les vnements et ne dispose pas dobjet LoaderInfo interne. Ainsi lcoute des diffrents vnements se fait directement auprs de lobjet URLLoader. En revanche, les vnements diffuss sont quasiment les mmes que la classe LoaderInfo. Voici le dtail de chacun dentre eux :
Event.OPEN : diffus lorsque le lecteur commence charger les donnes. ProgressEvent.PROGRESS : diffus lorsque le chargement est en cours. Celui-ci renseigne sur le nombre doctets chargs et totaux. Event.COMPLETE : diffus lorsque le chargement est termin. HTTPStatusEvent.HTTP_STATUS : indique le code dtat de la requte HTTP. IOErrorEvent.IO_ERROR : diffus lorsque le chargement choue. SecurityErrorEvent.SECURITY_ERROR : diffus lorsque le lecteur tente de charger des donnes depuis un domaine non autoris.

Tout en grant les diffrentes erreurs pouvant survenir lors du chargement de donnes, nous allons commencer notre apprentissage en chargeant de simples donnes au format texte, puis XML. Puis nous traiterons en dtail lenvoi et rception de variables mais aussi de fichiers grace aux classes flash.net.FileReference et flash.net.FileReferenceList.

A retenir

2 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Les fonctions et mthodes loadVariables, loadVariablesNum et la classe LoadVars sont remplaces par la classe URLLoader.

La classe URLLoader permet de charger des donnes au format texte, XML et binaire.

Charger du texte
Afin de charger des donnes nous crons une instance de la classe URLLoader, puis nous utilisons la mthode load dont voici la signature :
public function load(request:URLRequest):void

Comme nous lavons vu lors du prcdent chapitre intitul chargement de contenu, toute url doit tre spcifie au sein dun objet flash.net.URLRequest. A laide dun diteur tel le bloc notes, nous crons un fichier texte nomm donnees.txt ayant le contenu suivant :
Voici le contenu du fichier texte !

Dans un nouveau document Flash CS3, nous crons une instance de la classe URLLoader puis nous chargeons le fichier texte :
// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees.txt") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementTermine ( pEvt:Event ):void { trace("donnes charges"); } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); }

3 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); }

A linstar de la classe flash.display.LoaderInfo la classe URLLoader diffuse deux vnements, une fois les donnes charges. Lvnement HTTPStatsEvent.HTTP_STATUS puis lvnement Event.COMPLETE. Souvenez-vous quen local la proprit status de lobjet vnementiel HTTPStatusEvent vaut toujours 0, mme si le chargement choue. Dans le code suivant, nous chargeons le mme fichier texte depuis un serveur, la proprit status de lobjet vnementiel renvoie 200 :
// chargement des donnes // cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // chargement des donnes chargeurDonnees.load ( new URLRequest ("http://www.monserveur.org/donnees.txt") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementTermine ( pEvt:Event ):void { trace("donnes charges"); } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 200 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement");

4 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Si le lecteur ne parvient pas charger le fichier distant, lvnement IoErrorEvent.IO_ERROR est diffus ainsi que lvnement HTTPStatusEvent.HTTP_STATUS. Dans ce cas la proprit status contient le code derreur HTTP permettant de connatre la raison de lechec. Pour un tableau rcapitulatif des diffrents codes derreurs possibles reportez-vous au chapitre 13 intitul Chargement de contenu. Afin daccder aux donnes charges nous utilisons la proprit data de lobjet URLLoader :
// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees.txt") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var contenu:String = pEvt.target.data; // affiche : Voici le contenu du fichier texte ! trace( contenu ); } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement");

5 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

La proprit dataFormat de lobjet URLLoader permet de dfinir quel format de donnes nous chargeons. Celle-ci a la valeur text par dfaut :
// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // affiche : text trace( chargeurDonnees.dataFormat );

Il est recommand pour des questions de lisibilit de code et de travail en quipe de toujours spcifier le type de donnes que nous chargeons mme si il sagit de donnes texte. Pour cela nous utilisons trois proprits constantes de la classe flash.net.URLLoaderDataFormat. Voici le dtail de chacune dentre elles :
URLLoaderDataFormat.BINARY : permet de charger des donnes au format binaire. URLLoaderDataFormat.TEXT : permet de charger du texte brut. URLLoaderDataFormat.VARIABLES : permet de charger des donnes url encodes.

Ainsi, mme si nous souhaitons charger du texte, nous prfrons lindiquer pour des questions de lisibilit :
// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // nous souhaitons charger des donnes texte chargeurDonnees.dataFormat = URLLoaderDataFormat.TEXT; // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees.txt") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var contenu:String = pEvt.target.data; // affiche : Voici le contenu du fichier texte ! trace( contenu );

6 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

// affiche : text trace( pEvt.target.dataFormat ); } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); }

Dans les prcdentes versions dActionScript la classe LoadVars tait utilise afin de charger des donnes externes. Celle-ci possdait un vnement onData nous permettant de rcuprer les donnes charges brutes, sans passer par une interprtation des donnes charges. Lquivalent nexiste plus en ActionScript 3. Si nous souhaitons charger des donnes brutes, nous utilisons le format URLLoaderDataFormat.BINARY. Dans certains cas, le chargement de fichier texte peut tre utile, en particulier lors de chargement de fichiers comme le CSV. Le CSV est un format simple de reprsentation de donnes sous forme de valeurs spares par des virgules. Il est couramment utilis en format dexport de logiciels comme Microsoft Excel ou Microsoft Outlook. Dans lexemple suivant nous chargeons un fichier CSV export depuis Microsoft Excel contenant des statistiques. Voici un aperu du contenu du fichier :
100 133.46 144.02 148 94.04 87.17 92.27 96.83 98.81

7 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

113.8 113.2

Dans le code suivant nous chargeons le fichier CSV en tant que donnes texte, puis nous transformons la chane en un tableau laide de la mthode split de la classe String :
// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // nous souhaitons charger des donnes texte chargeurDonnees.dataFormat = URLLoaderDataFormat.TEXT; // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees.csv") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var contenu:String = pEvt.target.data; // transformation de la chane en un tableau en sparant les donnes chaque saut de ligne var tableauDonnees:Array = contenu.split("\n"); // affiche : 100 trace( tableauDonnees[0] ); // affiche : 94 trace( tableauDonnees.length ); } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); }

8 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Nous calculons lamplitude du graphique en extrayant les valeurs minimum et maximum :


// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // nous souhaitons charger des donnes texte chargeurDonnees.dataFormat = URLLoaderDataFormat.TEXT; // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees.csv") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); var hauteurMaximum:Number = 180; function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var contenu:String = pEvt.target.data; // transformation de la chane en un tableau en sparant les donnes chaque saut de ligne var tableauDonnees:Array = contenu.split("\n"); // extraction des valeurs minimum et maximum var valeurMinimum:Number = calculeMinimum ( tableauDonnees ); var valeurMaximum:Number = calculeMaximum ( tableauDonnees ); // calcul de l'amplitude du graphique var ratio:Number = hauteurMaximum / ( valeurMaximum - valeurMinimum ); // affiche : 1.2699308593198815 trace( ratio ); } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); }

9 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

function calculeMaximum ( pTableau:Array ):Number { var lng:int = pTableau.length; var valeurMaximum:Number = Number.MIN_VALUE; while ( lng-- ) valeurMaximum = Math.max ( valeurMaximum, pTableau[lng] ); return valeurMaximum; } function calculeMinimum ( pTableau:Array ):Number { var lng:int = pTableau.length; var valeurMinimum:Number = Number.MAX_VALUE; while ( lng-- ) valeurMinimum = Math.min ( valeurMinimum, pTableau[lng] ); return valeurMinimum; }

Puis nous dessinons le graphique laide de la fonction dessineGaphique :


// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // nous souhaitons charger des donnes texte chargeurDonnees.dataFormat = URLLoaderDataFormat.TEXT; // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees.csv") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); var hauteurMaximum:Number = 180; function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var contenu:String = pEvt.target.data; // affiche : Voici le contenu du fichier texte ! var tableauDonnees:Array = contenu.split("\n"); // extraction des valeurs minimum et maximum var valeurMinimum:Number = calculeMinimum ( tableauDonnees );

10 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

var valeurMaximum:Number = calculeMaximum ( tableauDonnees ); // calcul de l'amplitude du graphique var ratio:Number = hauteurMaximum / ( valeurMaximum - valeurMinimum ); // dessine le graphique dessineGraphique ( ratio, valeurMaximum, tableauDonnees ); } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); } function calculeMaximum ( pTableau:Array ):Number { var lng:int = pTableau.length; var valeurMaximum:Number = Number.MIN_VALUE; while ( lng-- ) valeurMaximum = Math.max ( valeurMaximum, pTableau[lng] ); return valeurMaximum; } function calculeMinimum ( pTableau:Array ):Number { var lng:int = pTableau.length; var valeurMinimum:Number = Number.MAX_VALUE; while ( lng-- ) valeurMinimum = Math.min ( valeurMinimum, pTableau[lng] ); return valeurMinimum; } var courbe:Shape = new Shape(); courbe.graphics.lineStyle ( 1, 0x990000, 1 ); var conteneurGraphique:Sprite = new Sprite(); conteneurGraphique.addChild( courbe ); conteneurGraphique.opaqueBackground = 0xFCEEBC;

11 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

addChild ( conteneurGraphique ); function dessineGraphique ):void { for ( var i:int = 0; i< pDonnees.length; i++ ) { if ( i == 0 ) courbe.graphics.moveTo ( i, (pMaximum-pDonnees[0]) * pRatio ); ); } // centre le graphique conteneurGraphique.x = ( stage.stageWidth - conteneurGraphique.width ) / 2; conteneurGraphique.y = ( stage.stageHeight - conteneurGraphique.height ) / 2; } else courbe.graphics.lineTo ( i * 4, (pMaximum-pDonnees[i]) * pRatio ( pRatio:Number, pMaximum:Number, pDonnees:Array

La figure 14-1 illustre le graphique gnr depuis les donnes extraites du fichier CSV :

Figure 14-1. Courbe de statistique. Si nous modifions la valeur de la variable hauteurMaximum : 12 / 98


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

var hauteurMaximum:Number = 80;

Nous voyons que le graphique est dessin en conservant les proportions, comme lillustre la figure 14-2 :

Figure 14-2. Courbe de statistique rduite. Bien entendu, dautres formats de donnes peuvent tre utiliss. Nous allons nous intresser au chargement de variables encodes au format URL.

A retenir
Afin de charger des donnes au format texte, nous passons la valeur URLLoaderDataFormat.TEXT la proprit dataFormat. Afin daccder aux donnes charges, nous utilisons la proprit data de lobjet URLLoader. Lobjet URLLoader ne possde pas dobjet LoaderInfo interne.

Lencodage URL
L'encodage URL est le standard de transmition d'informations par formulaire. Il permet de rendre compatible diffrents paramtres avec le protocole HTTP. Lencodage respecte les rgles suivantes :
Les champs sont spars par des esperluettes (caractre &)

13 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Un champ comporte un nom de champ, suivi de = puis de la valeur. Les espaces sont remplacs par des +. Les caractres non alphanumriques sont remplacs par %XX o XX reprsente le code ASCII en hexadcimal du caractre.

Lencodage URL va tre utilis au cours des prochaines parties afin de charger ou denvoyer des variables.

Charger des variables


Le chargement de donnes texte brut convient lorsque nous chargeons des donnes non structures. Pour des donnes plus dtailles nous pouvons utiliser le format dencodage URL dtaill prcdemment. La chane suivante est une chane encode URL :
titre=Voici un titre&contenu=Voici le contenu !

Nous allons placer la chane prcdente au sein dun fichier texte nomm donnees_url.txt. Au sein dun nouveau document Flash CS3, nous chargeons le fichier texte laide de la mthode load :
// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // nous souhaitons charger des donnes texte chargeurDonnees.dataFormat = URLLoaderDataFormat.TEXT; // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees_url.txt") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var contenu:String = pEvt.target.data; // affiche : titre=Voici un titre&contenu=Voici le contenu ! trace( contenu ); } function codeHTTP ( pEvt:HTTPStatusEvent ):void {

14 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

// affiche : 0 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); }

Les donnes sont ainsi charges en tant que donnes texte brut. Il nous est difficile daccder pour le moment la valeur de chaque variable titre ou contenu. Afin dinterprter et dextraire des donnes de la chane encode, deux possibilits soffrent nous : La premire consiste dcoder les donnes texte laide de la classe flash.net.URLVariables. En passant la chane encode URL au constructeur de celle-ci nous la dcodons afin dextraire la valeur de chaque variable :
function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var contenu:String = pEvt.target.data; // dcodage de la chane encode url sous forme d'objet var variables:URLVariables = new URLVariables ( contenu ); // itration sur chaque variable dcode /* affiche : titre --> Voici un titre contenu --> Voici le contenu ! */ for ( var p in variables ) trace( p, "--> " + variables[p] ); }

Une fois lobjet URLVariables cr, nous accdons chaque variable par la syntaxe pointe. Nous retrouvons ici le mme comportement que la classe LoadVars des prcdentes versions dActionScript. Les variables charges deviennent des proprits de lobjet URLVariables. Dans le code suivant, nous accdons manuellement aux deux variables titre et contenu devenues proprits : 15 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var donnees:String = pEvt.target.data; // dcodage de la chane url encode sous forme d'objet var variables:URLVariables = new URLVariables ( donnees ); var titre:String = variables.titre; // affiche : Voici un titre trace( titre ); var contenu:String = variables.contenu; // affiche : Voici le contenu ! trace( contenu ); }

Au sein de notre document, nous plaons deux champs texte dynamique auxquels nous associons deux noms doccurrence. Puis nous remplissons dynamiquement ces derniers avec le contenu charg correspondant :
function chargementTermine ( pEvt:Event ):void { // accs aux donnes charges var donnees:String = pEvt.target.data; // dcodage de la chane url encode sous forme d'objet var variables:URLVariables = new URLVariables ( donnees ); var titre:String = variables.titre; var contenu:String = variables.contenu; // affectation des donnes charges au champs texte champTitre.text = titre; champContenu.text = contenu; }

La figure 14-3 illustre le rsultat :

16 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-3. Contenu texte charg dynamiquement. Une seconde approche plus rapide consiste spcifier au pralable la proprit dataFormat la valeur URLLoaderDataFormat.VARIABLES. Ainsi, les donnes charges sont automatiquement dcodes par un objet URLVariables :
// cration de l'objet URLLoader var chargeurDonnees:URLLoader = new URLLoader(); // nous souhaitons charger des donnes url encodes chargeurDonnees.dataFormat = URLLoaderDataFormat.VARIABLES; // chargement des donnes chargeurDonnees.load ( new URLRequest ("donnees_url.txt") ); // coute de l'vnement Event.COMPLETE chargeurDonnees.addEventListener( Event.COMPLETE, chargementTermine ); // coute de l'vnement HTTPStatusEvent.HTTP_STATUS chargeurDonnees.addEventListener( HTTPStatusEvent.HTTP_STATUS, codeHTTP ); // coute de l'vnement IOErrorEvent.IO_ERROR chargeurDonnees.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementTermine ( pEvt:Event ):void { // accs lobjet URLVariables var variables:URLVariables = pEvt.target.data; var titre:String = variables.titre;

17 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

var contenu:String = variables.contenu; // affectation des donnes charges au champs texte champTitre.text = titre; champContenu.text = contenu; } function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); }

Lorsque nous demandons explicitement de charger les donnes sous la forme de variables encodes URL, la proprit data de lobjet URLLoader retourne un objet URLVariables cr automatiquement par le lecteur Flash. La classe URLVariables dfinie une mthode decode appele en interne lors de linterprtation de la chane encode. Celle-ci peut aussi tre appele manuellement lorsque lobjet URLVariables est dj cr et que nous souhaitons dcoder une nouvelle chane. Au cas o les variables charges ne seraient pas formates correctement, lappel de celle-ci lve une erreur lexcution. Dans le cas de la chane suivante :
Voici un titre&contenu=Voici le contenu !

Nous avons omis volontairement la premire variable titre associe au contenu Voici un titre. Si nous testons nouveau le code prcdent, lerreur suivante est leve lors de lvnement Event.COMPLETE :
Error: Error #2101: La chane transmise URLVariables.decode() doit tre une requte au format de code URL contenant des paires nom/valeur.

Si le texte charg contient le caractre & et que nous ne souhaitons pas linterprter comme sparateur mais comme caractre composant une chane, nous pouvons indiquer son code caractre ASCII au format hxadcimal laide du symbole %. Ainsi, pour ajouter comme titre la chane de caractre suivante : 18 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Bobby & The Groove Orchestra

Nous remplaons le caractre & par son code caractre ASCII de la manire suivante :
titre=Bob %26 The Groove Orchestra&contenu=Sortie de lalbum en Aot 2008 !

La figure 14-4 illustre le rsultat :

Figure 14-4. Caractre spcial encod en hexadcimal. Malheureusement, pour des questions de lisibilit et dorganisation, le format encod URL savre rapidement limit. Nous prfrerons lutilisation du format XML qui savre tre un format standard et universel adapt la reprsentation de donnes complexes.

A retenir

19 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Afin de dcoder une chane encode URL nous utilisons un objet URLVariables.

Afin de charger des variables encodes URL, nous passons la valeur URLLoaderDataFormat.VARIABLES la proprit dataFormat. Afin de dcoder une chaine de caractres encode URL nous pouvons la passer au constructeur de la classe URLVariables o la mthode decode.

Charger des donnes XML


Si nous devions reprsenter une petite partie de la discographie de Stevie Wonder au format encod URL nous obtiendrons la chane suivante :
&album_1=Talking Book&artiste_1=Stevie Wonder&label_1=Motown&annee_1=1972&album_2=Songs in the key of life&artiste_2=Stevie Wonder&label_2=Motown&annee_2=1976

Les mmes donnes au format XML seraient formates de la manire suivante :


<DISCOGRAPHIE> <ALBUM> <TITRE> Talking Book </TITRE> <ANNEE DATE="1972"/> <ARTISTE NOM="Wonder" PRENOM="Stevie"/> <LABEL NOM="Motown"/> </ALBUM > <ALBUM> <TITRE> Songs in the key of life </TITRE> <ANNEE DATE="1976"/> <ARTISTE NOM="Wonder" PRENOM="Stevie"/> <LABEL NOM="Motown"/> </ALBUM > </DISCOGRAPHIE>

La reprsentation XML est plus naturelle et plus adapte dans le cas de donnes structures. Nous avons manipul le format XML au cours du chapitre 2 intitul Langage et API du lecteur Flash. Nous allons dcouvrir comment charger dynamiquement un fichier XML afin de construire une interface graphique. En ActionScript 3, la classe XML ne fait plus partie de lAPI du lecteur Flash, mais appartient au langage ActionScript 3 reposant sur ECMAScript, cest donc une classe haut niveau.

20 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

La classe XML ne gre donc plus le chargement de donnes comme ctait le cas dans les prcdentes versions dActionScript. Afin de charger des donnes XML, nous chargeons simplement une chane de caractres sous la forme de texte brut, puis nous la transformons en objet XML. Dans un nouveau document Flash CS3, nous associons la classe de document suivante :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { } } }

A ct du document Flash en cours nous sauvons un fichier XML sous le nom donnees.xml contenant les donnes suivantes :
<MENU> <BOUTON <BOUTON <BOUTON <BOUTON <BOUTON </MENU> legende="Accueil" couleur="0x887400" vitesse="1" swf="accueil.swf"/> legende="Photos" couleur="0x005587" vitesse="1" url="photos.swf"/> legende="Blog" couleur="0x125874" vitesse="1" url="blog.swf"/> legende="Liens" couleur="0x59CCAA" vitesse="1" url="liens.swf"/> legende="Forum" couleur="0xEE44AA" vitesse="1" url="forum.swf"/>

Puis nous le chargeons :


package org.bytearray.document { import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.net.URLRequest; flash.events.Event; flash.events.HTTPStatusEvent; flash.events.IOErrorEvent;

public class Document extends ApplicationDefaut {

21 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

private var chargeur:URLLoader; public function Document () { chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.TEXT; chargeur.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.addEventListener ( HTTPStatusEvent.HTTP_STATUS, codeHTTP

);

chargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeur.load ( new URLRequest ("donnees.xml") ); } private function chargementTermine ( pEvt:Event ) :void { var donnneesXML:XML = new XML ( pEvt.target.data ); /* affiche : <MENU> <BOUTON legende="Accueil" couleur="0x887400" vitesse="1" url="accueil.swf"/> <BOUTON legende="Photos" couleur="0x005587" vitesse="1" url="photos.swf"/> <BOUTON legende="Blog" couleur="0x125874" vitesse="1" url="blog.swf"/> <BOUTON legende="Liens" couleur="0x59CCAA" vitesse="1" url="liens.swf"/> <BOUTON legende="Forum" couleur="0xEE44AA" vitesse="1" url="forum.swf"/> </MENU> */ trace( donnneesXML ); } private function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } private function erreurChargement ( pEvent:IOErrorEvent ):void { trace("erreur de chargement"); }

22 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

} }

Lorsque la mthode couteur chargementTermine se dclenche nous accdons aux donnes charges puis nous transformons la chane de caractres en objet XML. Nous allons reprendre le menu construit lors du chapitre 10 intitul Diffusion dvnements personnaliss afin de charger dynamiquement les donnes du menu depuis un fichier XML. Afin de crer notre menu, nous reprenons la classe Bouton utilise lors du chapitre 10 puis nous limportons ainsi que la classe Sprite :
package org.bytearray.document { import import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; org.bytearray.ui.Bouton; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.net.URLRequest; flash.events.Event; flash.events.HTTPStatusEvent; flash.events.IOErrorEvent; flash.display.Sprite;

public class Document extends ApplicationDefaut { private var chargeur:URLLoader; private var conteneurMenu:Sprite; public function Document () { conteneurMenu = new Sprite(); addChild ( conteneurMenu ); chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.TEXT; chargeur.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.addEventListener ( HTTPStatusEvent.HTTP_STATUS, codeHTTP

);

chargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeur.load ( new URLRequest ("donnees.xml") ); } private function chargementTermine ( pEvt:Event ) :void

23 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ var donnneesXML:XML = new XML ( pEvt.target.data ); creerMenu ( donnneesXML ); } private function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } private function erreurChargement ( pEvent:IOErrorEvent ):void { trace("erreur de chargement"); } private function creerMenu ( pXML:XML ):void { var i:int = 0; var monBouton:Bouton; for each ( var enfant:XML in pXML.* ) { // rcupration des infos var legende:String = enfant.@legende; var couleur:Number = enfant.@couleur; var vitesse:Number = enfant.@vitesse; var swf:String = enfant.@url; // cration des boutons monBouton = new Bouton( 60, 40, swf, couleur, vitesse, // positionnement monBouton.y = (monBouton.height + 5) * i; // ajout la liste d'affichage conteneurMenu.addChild ( monBouton ); i++; } } } }

legende );

24 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

La figure 14-4 illustre le rsultat :

Figure 14-4. Menu dynamique XML. Afin dcouter chaque clic bouton, nous importons la classe EvenementBouton cre lors du chapitre 10 puis nous ciblons lvnement EvenementBouton.CLICK en utilisant la phase de capture :
package org.bytearray.document { import import import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; org.bytearray.ui.Bouton; org.bytearray.evenements.EvenementBouton; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.net.URLRequest; flash.events.Event; flash.events.HTTPStatusEvent; flash.events.IOErrorEvent; flash.display.Sprite;

public class Document extends ApplicationDefaut { private var chargeur:URLLoader; private var conteneurMenu:Sprite; public function Document ()

25 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ conteneurMenu = new Sprite(); addChild ( conteneurMenu ); conteneurMenu.addEventListener ( EvenementBouton.CLICK, clicBouton, true ); chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.TEXT; chargeur.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.addEventListener ( HTTPStatusEvent.HTTP_STATUS, codeHTTP

);

chargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeur.load ( new URLRequest ("donnees.xml") ); } private function chargementTermine ( pEvt:Event ) :void { var donnneesXML:XML = new XML ( pEvt.target.data ); creerMenu ( donnneesXML ); } private function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } private function erreurChargement ( pEvent:IOErrorEvent ):void { trace("erreur de chargement"); } private function creerMenu ( pXML:XML ):void { var i:int = 0; var monBouton:Bouton; for each ( var enfant:XML in pXML.* ) { // rcupration des infos

26 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

var var var var

legende:String = enfant.@legende; couleur:Number = enfant.@couleur; vitesse:Number = enfant.@vitesse; swf:String = enfant.@url;

legende );

// cration des boutons monBouton = new Bouton( 60, 40, swf, couleur, vitesse, // positionnement monBouton.y = (monBouton.height + 5) * i; // ajout la liste d'affichage conteneurMenu.addChild ( monBouton ); i++; }

} private function clicBouton( pEvt:EvenementBouton ):void { // affiche : photos.swf trace( pEvt.lien ); } } }

Nous avons ici rutilis la classe Bouton cre lors du chapitre 10, en prvoyant un modle simple dutilisation nous avons pu rutiliser cette classe sans aucun problme. Afin de dsactiver totalement notre menu, nous devons supprimer lobjet conteneurMenu de la liste daffichage, puis passer sa rfrence null :
package org.bytearray.document { import import import import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; org.bytearray.ui.Button; org.bytearray.events.ButtonEvent; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.net.URLRequest; flash.events.Event; flash.events.HTTPStatusEvent; flash.events.IOErrorEvent; flash.events.MouseEvent; flash.display.Sprite;

public class Document extends ApplicationDefaut

27 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ private var chargeur:URLLoader; private var conteneurMenu:Sprite; public function Document () { conteneurMenu = new Sprite(); addChild ( conteneurMenu ); stage.doubleClickEnabled = true; stage.addEventListener ( MouseEvent.DOUBLE_CLICK, desactive ); true ); conteneurMenu.addEventListener ( ButtonEvent.CLICK, clicBouton, chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.TEXT; chargeur.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.addEventListener ( HTTPStatusEvent.HTTP_STATUS, codeHTTP

);

chargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeur.load ( new URLRequest ("donnees.xml") ); } private function chargementTermine ( pEvt:Event ) :void { var donnneesXML:XML = new XML ( pEvt.target.data ); creerMenu ( donnneesXML ); } private function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } private function erreurChargement ( pEvent:IOErrorEvent ):void { trace("erreur de chargement"); } private function creerMenu ( pXML:XML ):void

28 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ var i:int = 0; var monBouton:Button; for each ( var enfant:XML in pXML.* ) { // rcupration des infos var legende:String = enfant.@legende; var couleur:Number = enfant.@couleur; var vitesse:Number = enfant.@vitesse; var swf:String = enfant.@url; // cration des boutons monBouton = new Button( 60, 40, swf, couleur, vitesse, // positionnement monBouton.y = (monBouton.height + 5) * i; // ajout la liste d'affichage conteneurMenu.addChild ( monBouton ); i++; } } private function clicBouton( pEvt:ButtonEvent ):void { // affiche : photos.swf trace( pEvt.lien ); } private function desactive ( pEvt:MouseEvent ):void { removeChild ( conteneurMenu ); conteneurMenu = null; } } }

legende );

Souvenez-vous que pour correctement dsactiver un lment interactif, nous devons supprimer toutes ses rfrences. Dans cet exemple, les boutons sont seulement rfrencs de par leur prsence au sein de lobjet graphique conteneurMenu. En dsactivant ce dernier nous rendons alors inaccessible ses enfants. Ces 29 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

derniers deviennent ainsi ligible la suppression par le ramasse miettes. Nous avons vu lors du chapitre prcdent que le modle de securit du lecteur intgrait des restrictions conernant le chargement de contenu externe. Nous allons au cours de la partie suivante nous intresser aux restrictions de securit dans un contexte de chargement de donnes.

A retenir
Afin de charger un flux XML, nous passons la valeur URLLoaderDataFormat.TEXT la proprit dataFormat de lobjet URLLoader. La classe XML ne soccupe plus du chargement du flux XML. Une fois la chane de caractres charge par lobjet URLLoader, nous crons un objet XML partir de celle-ci.

Chargement de donnes et securit


Le chargement de donnes est soumis aux mmes restrictions de scurit que le chargement de contenu. Imaginons le scnario suivant : Vous devez dvelopper une application Flash permettant de lire des flux XML provenant de diffrents blogs. Cette application sera heberge sur votre serveur et ne peut pour des raisons de securit accder aux flux distants. Au cours du chapitre 13, nous avons vu quil tait possible dautoriser de trois manires un SWF tentant de charger du contenu depuis un serveur distant :
Par un fichier de rgulation (XML). Par lappel de la mthode allowDomain de flash.system.Security dans le SWF charger. la classe

Par lutilisation dun fichier de proxy.

Attention, dans notre cas, nous ne chargeons plus de contenu SWF. Il est donc impossible dappeler la mthode allowDomain de lobjet Security. Ainsi, dans le cas de chargement de flux XML, texte, ou autres seules deux mthodes dautorisations soffrent vous : Par un fichier de rgulation XML comme lillustre la figure 14-5 :

30 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-5. Autorisation par fichier de rgulation. Ou bien par lutilisation dun fichier proxy :

Figure 14-6. Utilisation dun proxy. Reportez vous au chapitre 13 intitul chargement de contenu pour plus dinformations relatives au modle de scurit du lecteur Flash 9 et lutilisation de fichier de rgulation ou proxy.

A retenir

31 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Les mmes restrictions de securit lies au chargement de contenu, sappliquent lors du chargement de donnes. Il est impossible de placer au sein des donnes charges, un appel la mthode allowDomain de lobjet Security.

Charger des donnes binaires


La grande puissance du lecteur Flash 9 en ActionScript 3 rside dans la lecture de donnes au format binaire grce la classe bas niveau flash.utils.ByteArray. Afin de charger des donnes binaires brutes, nous devons passer la proprit dataFormat la valeur URLLoaderDataFormat.BINARY. Dans un nouveau document Flash CS3, nous chargeons un fichier PSD en associant une classe du document contenant le code suivant :
package org.bytearray.document { import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.URLRequest; flash.utils.ByteArray; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.events.Event; flash.events.HTTPStatusEvent; flash.events.IOErrorEvent;

public class Document extends ApplicationDefaut { private var chargeur:URLLoader; public function Document () { chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.BINARY; chargeur.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.addEventListener ( HTTPStatusEvent.HTTP_STATUS, codeHTTP

);

chargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeur.load ( new URLRequest ("maquette.psd") ); } private function chargementTermine ( pEvt:Event ) :void {

32 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

var donneesBinaire:ByteArray = pEvt.target.data; // affiche : 182509 trace( donneesBinaire.length ); } private function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } private function erreurChargement ( pEvent:IOErrorEvent ):void { trace("erreur de chargement"); } } }

La variable donneesBinaire contient le flux binaire du fichier PSD charg. En crant une classe EntetePSD, nous allons lire lentte du fichier afin dextraire diffrentes informations comme la taille du document, la version, ainsi que le modle de couleur utilis. Pour cela, nous crons une classe EntetePSD contenant le code suivant :
package org.bytearray.psd { import flash.utils.ByteArray; public class EntetePSD { private private private private private private private private var var var var var var var var flux:ByteArray; _signature:String; _version:int; _canal:int; _hauteur:int; _largeur:int; _profondeur:int; _mode:int;

private static const MODES_COULEURS:Array = new Array ("Bitmap", "Mode niveaux de gris", "Index", "RVB", "CMJN", "Multi Canal", "Deux tons", "Lab"); public function EntetePSD ( pFlux:ByteArray )

33 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ flux = pFlux; // extrait la signature de l'entte PSD (doit tre 8BPS) _signature = flux.readUTFBytes(4); // extrait la version (doit ter gal 1) _version = flux.readUnsignedShort(); // nous sautons 6 octets flux.position += 6; // extrait le canal utilis _canal = flux.readUnsignedShort(); // extrait la largeur du document _largeur = flux.readInt(); // extrait la hauteur du document _hauteur = flux.readInt(); // bpp _profondeur = flux.readUnsignedShort(); // mode colorimtrique (Bitmap=0, Mode niveaux de gris=1, Index=2, RVB=3, CMJN=4, Multi Canal=7, Deux tons=8, Lab=9) _mode = flux.readUnsignedShort(); } public function toString ( ):String { return "[EntetePSD signature : " + signature +", version : " + version + ", canal : " + canal + ", largeur : " + largeur + ", hauteur : " + hauteur + ", profondeur : " + profondeur + ", mode colorimtrique : " + MODES_COULEURS [ mode ] +"]"; } public function get signature ():String { return _signature; } public function get version ():int { return _version; } public function get canal ():int {

34 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

return _canal; } public function get largeur ():int { return _largeur; } public function get hauteur ():int { return _hauteur; } public function get profondeur ():int { return _profondeur; } public function get mode ():int { return _mode; } } }

Le flux binaire gnr par le lecteur Flash est pass au constructeur, puis nous lisons le flux laide des mthodes de la classe ByteArray. Nous reviendrons sur celles-ci au cours du chapitre 20 intitul ByteArray. Afin dextraire les informations du PSD nous instancions la classe EntetePSD en passant le flux binaire au constructeur :
package org.bytearray.document { import import import import import import import org.bytearray.abstrait.ApplicationDefaut; org.bytearray.psd.EntetePSD; flash.net.URLRequest; flash.utils.ByteArray; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.events.Event;

35 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

import flash.events.HTTPStatusEvent; import flash.events.IOErrorEvent; public class Document extends ApplicationDefaut { private var chargeur:URLLoader; public function Document () { chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.BINARY; chargeur.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.addEventListener ( HTTPStatusEvent.HTTP_STATUS, codeHTTP

);

chargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeur.load ( new URLRequest ("maquette.psd") ); } private function chargementTermine ( pEvt:Event ) :void { var donneesBinaire:ByteArray = pEvt.target.data; var infosPSD:EntetePSD = new EntetePSD( donneesBinaire ); // affiche : [EntetePSD signature : 8BPS, version : 1, canal : 3, largeur : 450, hauteur : 562, profondeur : 8, mode colorimtrique : RVB] trace( infosPSD ); } private function codeHTTP ( pEvt:HTTPStatusEvent ):void { // affiche : 0 trace("code HTTP : " + pEvt.status); } private function erreurChargement ( pEvent:IOErrorEvent ):void { trace("erreur de chargement"); } } }

36 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Grce la classe ByteArray nous pouvons charger nimporte quel type de fichiers puis en extraire des informations. Nous pourrions imaginer une application RIA permettant dhberger tout type de fichier. Celle-ci pourrait extraire les informations provenant de fichiers PSD, AI, FLA ou autres. Nous pourrions optimiser la classe EntetePSD en diffusant un vnement personnalis EvenementEntetePSD.INFOS. Lobjet vnementiel diffus contiendrait toutes les proprits ncessaires la description du fichier PSD. La classe ByteArray ouvre des portes toutes sortes de possibilits. Nous reviendrons en dtail sur la puissance de cette classe au cours du chapitre 20 intitul ByteArray.

A retenir
Afin de charger un flux binaire, nous passons la valeur URLLoaderDataFormat.BINARY la proprit dataFormat de lobjet URLLoader. Le flux binaire gnr est flash.utils.ByteArray. represent par la

classe

Concept denvoi de donnes


Comme nous lavons vu lors du chapitre 13 intitul Chargement de contenu, toute URL doit tre spcifie au sein dun objet URLRequest. Celui-ci offre pourtant bien dautres fonctionnalits que nous navons pas encore exploites. Nous allons nous intresser au cours des prochaines parties au concept denvoi de donnes. Pour cela, voyons en dtail les proprits de la classe URLRequest :
contentType : type de contenu MIME des donnes envoyes en POST. data : contient les donnes envoyer. Peut tre de type String, URLVariables ou ByteArray.

method : permet dindiquer si les donnes doivent tre envoyes par la mthode GET ou POST. requestHeaders : tableau contenant les enttes HTTP dfinies par des objets flash.net.URLRequestHeader. url : contient lurl atteindre.

37 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Nous allons au cours des exercices suivants utiliser ces diffrentes proprits afin de dcouvrir leurs intrts. Au sein du lecteur Flash nous pouvons distinguer trois types denvoi de donnes :
Envoi simple : les variables sont envoyes laide dune nouvelle fentre navigateur. Envoi discret : lenvoi des donnes est transparent pour lutilisateur. Aucune fentre navigateur nest ouverte durant lenvoi. Envoi discret avec validation : lenvoi des donnes est transparent, le lecteur Flash reoit un retour du serveur permettant une validation denvoi des donnes au sein de lapplication Flash.

Nous allons traiter chacun des cas et comprendre les diffrences entre chaque approche.

Envoyer des variables


Nous allons commencer par un envoi simple de donnes en dveloppant un formulaire permettant denvoyer un email depuis Flash. Ce type de dveloppement peut tre intgr dans une rubrique Contactez-nous au sein dun site. Nous verrons quil est possible sous certaines conditions denvoyer un email depuis le lecteur Flash sans passer par un script serveur grce la classe flash.net.Socket que nous traiterons au cours du chapitre 19 intitul Les sockets. Par dfaut, le lecteur Flash na pas la capacit denvoyer un email de manire autonome. Afin dy parvenir, nous devons passer les informations ncessaires un script serveur afin que celui-ci puisse envoyer le message. Dans un nouveau document Flash CS3, nous associons la classe du document suivante :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { }

38 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

} }

Puis nous plaons trois champs texte de saisie et un bouton afin de crer une interface denvoi de mail. La figure 14-7 illustre linterface :

Figure 14-7. Formulaire denvoi demail. Chaque instance est dfinie au sein de la classe du document :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; import flash.display.SimpleButton; import flash.text.TextField; public class Document extends ApplicationDefaut { public public public public var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton;

public function Document ()

39 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ } } }

Nous allons crer prsent le code PHP nous permettant de rceptionner les variables transmises depuis Flash. Nous allons utiliser dans un premier temps la mthode GET. Les variables sont ainsi accessible par lintermdiaire du tableau associatif $_GET :
<?php $destinataire = $_GET ["destinataire"]; $sujet = $_GET ["sujet"]; $message = $_GET ["message"]; if ( isset ( $destinataire ) && isset ( $sujet ) && isset ( $message ) ) { echo $destinataire. "<br>". $sujet. "<br>" . $message;

} else echo "Variables non transmises"; ?>

Il convient de placer ce script sur un serveur local ou distant afin de pouvoir tester lapplication. Dans notre cas, le script serveur est plac sur un serveur local. Le script est donc accessible en localhost :
http://localhost/mail/envoiMail.php

Nous ajoutons prsent le code ncessaire afin denvoyer les variables notre script distant :
package org.bytearray.document { import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.display.SimpleButton; flash.text.TextField; flash.net.navigateToURL; flash.net.URLRequest; flash.events.MouseEvent;

public class Document extends ApplicationDefaut { public public public public var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton;

40 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

public function Document () { boutonEnvoi.addEventListener ( } private function envoiMail ( pEvt:MouseEvent ):void { // affectation des variables envoyer cot serveur var destinaireEmail:String = destinataire.text; var sujetEmail:String = sujet.text; var messageEmail:String = message.text; // cration de l'objet URLRequest var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php"); // ouvre une nouvelle fentre navigateur et envoi les variables navigateToURL ( requete ); } } } MouseEvent.CLICK, envoiMail );

Nous stockons le destinataire, le sujet ainsi que le message au sein de trois variables. Puis nous les ajoutons en fin durl du script distant de la manire suivante :
package org.bytearray.document { import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.display.SimpleButton; flash.text.TextField; flash.net.navigateToURL; flash.net.URLRequest; flash.events.MouseEvent;

public class Document extends ApplicationDefaut { public public public public var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton;

public function Document () { boutonEnvoi.addEventListener ( } MouseEvent.CLICK, envoiMail );

41 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

private function envoiMail ( pEvt:MouseEvent ):void { // affectation des variables envoyer cot serveur var destinaireEmail:String = destinataire.text; var sujetEmail:String = sujet.text; var messageEmail:String = message.text; var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php?destinataire="+destinaireEmail+"&sujet= "+sujetEmail+"&message="+messageEmail); // ouvre une nouvelle fentre navigateur et envoi les variables navigateToURL ( requete ); } } }

Le point dinterrogation au sein de lURL indique au navigateur que le texte suivant contient les variables reprsentes par des paires noms/valeurs spares par des esperluettes (caractre &). Une fois les informations saisies comme lillustre la figure 14-8 :

Figure 14-8. Formulaire denvoi demail.

42 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Nous cliquons sur le bouton boutonEnvoi, une nouvelle fentre navigateur souvre, les variables sont automatiquement places en fin durl :
http://localhost/mail/envoiMail.php?destinataire=thibault@bytearray.org&sujet =Infos%20Album%202008%20!&message=Quand%20pr%C3%A9voyez%20vous%20le%20retour% 20de%20Peter%20mc%20Bryan%20%C3%A0%20la%20basse%20?

Le script serveur rcupre chaque variable et affiche son contenu comme lillustre la figure 14-9 :

Figure 14-9. Variables rcupres. Nous remarquons que les caractres spciaux ne saffichent pas correctement. Cela est du au fait que le lecteur Flash fonctionne en UTF-8, tandis que PHP fonctionne par dfaut en ISO-8859-1. Nous devons donc dcoder la chane UTF-8 laide de la mthode PHP utf8_decode :
<?php $destinataire = $_GET["destinataire"]; $sujet = $_GET["sujet"]; $message = $_GET["message"]; if ( isset ( $destinataire ) && isset ( $sujet ) && isset ( $message ) ) { echo utf8_decode($destinataire)."<br>".utf8_decode($sujet)."<br>".utf8_decode($mes sage); } else echo "Variables non transmises"; ?>

Si nous testons nouveau le code prcdent, les chanes sont correctement dcodes :

43 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-10. Variables correctement dcodes. Une fois assur que les variables passes sont bien rceptionnes, nous pouvons ajouter le code ncessaire pour envoyer lemail. Pour cela, nous ajoutons lappel la fonction PHP mail en passant le destinataire, le sujet ainsi que le contenu du message :
<?php $destinataire = $_GET["destinataire"]; $sujet = $_GET["sujet"]; $message = $_GET["message"]; if ( isset ( $destinataire ) && isset ( $sujet ) && isset ( $message ) ) { if ( @mail ( utf8_decode($destinataire), utf8_decode($sujet), utf8_decode($message) ) ) echo "Mail bien envoy !"; else echo "Erreur d'envoi !"; } else echo "Variables non transmises"; ?>

Nous ajoutons le caractre @ devant la fonction mail afin dviter que celle-ci lve une exception PHP en cas de mauvaise configuration du serveur SMTP. En testant notre application formulaire Flash, nous voyons que le mail est bien envoy si les variables sont correctement transmises :

44 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-11. Email envoy. Mme si le code ActionScript prcdent fonctionne, il ne fait pas usage de la classe URLVariables recommande dans un contexte denvoi de variables. Grce celle-ci nous allons pouvoir choisir quelle mthode utiliser afin denvoyer les variables. Mais avant daller plus loin, quentendons nous par mthode denvoi ?

A retenir
Nous pouvons envoyer des variables un script serveur en les ajoutant en fin dURL. Le format dencodage URL doit alors tre respect.

La mthode GET ou POST


Lorsque des donnes sont passes un script serveur. Celles-ci peuvent tre transmises de deux manires diffrentes. Par lintermdiaire du tableau GET ou POST. En utilisant la mthode GET, les variables sont obligatoirement ajoutes en fin durl. Dans le cas dun site de vente en ligne, ladresse suivante permet daccder un article spcifique :
http://www.funkrecords.com/index.php?rubrique=soul&langue=fr&article=Breakwater

Un script serveur rcupre les variables passes en fin dURL afin dafficher le disque correspondant. Lorsque la mthode GET est utilise, les variables en fin dURL sont automatiquement accessible en PHP au sein du tableau associatif $_GET. Il est important de noter que la mthode GET possde une limitation de 1024 caractres, il nest donc possible de passer un grand volume de donnes par cette mthode. Au contraire, lorsque les donnes sont transmises par la mthode POST, les variables napparaissent pas dans lURL du navigateur. Ainsi, cette mthode ne souffre pas de la limitation de caractres. Le W3C dfinit ladresse suivante quelques rgles concernant lutilisation des deux mthodes : http://www.w3.org/2001/tag/doc/whenToUseGet.html Il est conseill dutiliser la mthode GET lorsque les donnes transmises ne sont pas sensibles ou ne sont pas lies un processus dcriture.

45 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

A linverse, la mthode POST est prfre lorsque les donnes transmises nont pas tre exposes et quune criture en base de donnes intervient par la suite. Lutilisation dun objet URLVariables va nous permettre de spcifier la mthode utiliser dans nos changes entre le lecteur Flash et le script serveur. Au lieu de passer les variables directement aprs lurl du script distant :
var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php?destinataire="+destinaireEmail+"&sujet= "+sujetEmail+"&message="+messageEmail);

Nous allons prfrer lutilisation dun objet URLVariables qui permet de stocker les variables transmettre. Cet objet est ensuite associ la proprit data de lobjet URLRequest utilis. Nous pouvons spcifier la mthode utiliser grce la proprit method de la classe URLRequest et aux constantes de la classe flash.net.URLRequestMethod. Nous modifions le code prcdent en utilisant un objet URLVariables tout en conservant lenvoi des donnes avec la mthode GET :
package org.bytearray.document { import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.display.SimpleButton; flash.text.TextField; flash.net.navigateToURL; flash.net.URLRequest; flash.net.URLVariables; flash.net.URLRequestMethod; flash.events.MouseEvent;

public class Document extends ApplicationDefaut { public public public public var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton;

public function Document () { boutonEnvoi.addEventListener ( } private function envoiMail ( pEvt:MouseEvent ):void MouseEvent.CLICK, envoiMail );

46 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ // cration dun objet URLVariables var variables:URLVariables = new URLVariables(); // affectation des variables envoyer cot serveur variables.sujet = sujet.text; variables.destinataire = destinataire.text; variables.message = message.text; // cration de l'objet URLRequest var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php"); // nous passons les variables dans l'url (tableau GET) requete.method = URLRequestMethod.GET; // nous associons les variables l'objet URLRequest requete.data = variables; // ouvre une nouvelle fentre navigateur et envoi les variables navigateToURL ( requete ); } } }

Si nous testons nouveau notre application Flash. Nous remarquons que le script serveur reoit de la mme manire les informations, le mail est bien envoy. Afin dutiliser la mthode POST, nous passons la valeur URLRequestMethod.POST la proprit method de lobjet URLRequest :
// nous passons les variables dans l'url (tableau POST) requete.method = URLRequestMethod.POST;

Dsormais les donnes seront envoyes au sein du tableau associatif $_POST. Nous devons donc imprativement modifier le script serveur afin de rceptionner les variables au sein du tableau correspondant :
<?php $destinataire = $_POST ["destinataire"]; $sujet = $_POST ["sujet"]; $message = $_POST ["message"]; if ( isset ( $destinataire ) && isset ( $sujet ) && isset ( $message ) ) { if ( @mail ( utf8_decode($destinataire), utf8_decode($sujet), utf8_decode($message) ) ) echo "Mail bien envoy !"; else echo "Erreur d'envoi !"; } else echo "Variables non transmises";

47 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

?>

Lorsque la mthode POST est utilise, les variables sont automatiquement accessible en PHP au sein du tableau associatif $_POST. Attention, si nous tentons denvoyer des variables laide de la fonction navigateToURL laide de la mthode POST depuis lenvironnement de test de Flash CS3, celles-ci seront tout de mme envoyes par la mthode GET. Il convient donc de toujours tester au sein du navigateur, une application Flash utilisant la fonction navigateToURL et la mthode POST. Nous venons de traiter le moyen le plus simple denvoyer des variables un script serveur. Dans la plupart des applications, nous ne souhaitons pas ouvrir de nouvelle fentre navigateur lors de la transmission des donnes. Nous prfrerons gnralement un envoi plus discret . Afin denvoyer discrtement des donnes un script distant, nous prfrerons lutilisation de la classe URLLoader la fonction navigateToURL.

A retenir
Lors de lenvoi de variables par la mthode GET, les variables sont automatiquement ajouts en fin durl. Lors de lenvoi de variables par la mthode POST, les variables ne sont pas affiches au sein de lurl. Il convient dutiliser la mthode GET pour la lecture de donnes. Il convient dutiliser la mthode POST pour lcriture de donnes.

Envoyer des variables discrtement


Nous allons modifier notre formulaire dvelopp prcdemment afin denvoyer les informations au script distant, sans ouvrir de nouvelle fentre navigateur. Ainsi lexprience utilisateur sera plus lgante. Notre application donnera limpression de fonctionner de manire autonome :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut;

48 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

import import import import import import import import

flash.display.SimpleButton; flash.text.TextField; flash.net.navigateToURL; flash.net.URLRequest; flash.net.URLVariables; flash.net.URLRequestMethod; flash.net.URLLoader; flash.events.MouseEvent;

public class Document extends ApplicationDefaut { public public public public public var var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton; echanges:URLLoader;

public function Document () { echanges = new URLLoader(); boutonEnvoi.addEventListener ( } private function envoiMail ( pEvt:MouseEvent ):void { var variables:URLVariables = new URLVariables(); // affectation des variables envoyer cot serveur variables.sujet = sujet.text; variables.destinataire = destinataire.text; variables.message = message.text; // cration de l'objet URLRequest var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php"); // nous passons les variables dans l'url (tableau POST) requete.method = URLRequestMethod.POST; // associe les variables l'objet URLRequest requete.data = variables; // envoi les donnes de manire transparente, sans ouvrir de nouvelle fentre navigateur echanges.load ( requete ); } } } MouseEvent.CLICK, envoiMail );

Aussitt la mthode load excute, les variables sont transmises au script serveur, lemail est envoy. 49 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

En plus de permettre le chargement de donnes, la mthode load permet aussi lenvoi. Cela diffre des prcdentes versions dActionScript o la classe LoadVars possdait une mthode load pour le chargement et send pour lenvoi. Nous allons continuer lamlioration de notre formulaire en ajoutant un mcanisme de validation. Ne serait-il pas intressant de pouvoir indiquer Flash que le mail a bien t envoy ? Pour le moment, notre code nintgre aucune gestion du retour serveur. Cest ce que nous allons ajouter dans la partie suivante.

A retenir
Afin denvoyer des donnes de manire transparente, nous utilisons la mthode load de lobjet URLLoader. La mthode load permet le chargement de donnes mais aussi lenvoi.

Renvoyer des donnes depuis le serveur


Nous allons prsent nous intresser lenvoi de donnes avec validation. Afin de savoir au sein de Flash si lenvoi du message a bien t effectu, nous devons simplement couter lvnement Event.COMPLETE de lobjet URLLoader. Celui-ci est diffus automatiquement lorsque le lecteur reoit des informations de la part du serveur :
package org.bytearray.document { import import import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.display.SimpleButton; flash.text.TextField; flash.net.navigateToURL; flash.net.URLRequest; flash.net.URLVariables; flash.net.URLRequestMethod; flash.net.URLLoader; flash.events.Event; flash.events.MouseEvent;

public class Document extends ApplicationDefaut { public public public public var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton;

50 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

public var echanges:URLLoader; public function Document () { echanges = new URLLoader(); echanges.addEventListener ( Event.COMPLETE, retourServeur ); boutonEnvoi.addEventListener ( } private function envoiMail ( pEvt:MouseEvent ):void { var variables:URLVariables = new URLVariables(); // affectation des variables envoyer cot serveur variables.sujet = sujet.text; variables.destinataire = destinataire.text; variables.message = message.text; // cration de l'objet URLRequest var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php"); // nous passons les variables dans l'url (tableau POST) requete.method = URLRequestMethod.POST; // associe les variables l'objet URLRequest requete.data = variables; // envoi les donnes de manire transparente, sans ouvrir de nouvelle fentre navigateur echanges.load ( requete ); } private function retourServeur ( pEvt:Event ):void { sujet.text = destinataire.text = message.text = ""; message.text = pEvt.target.data; } } } MouseEvent.CLICK, envoiMail );

La mthode retourServeur est excute lorsque le serveur retourne des informations Flash par lintermdiaire du mot cl PHP echo. En testant nouveau lapplication, nous obtenons un retour dans le champ message nous indiquant le mail a bien t envoy, comme lillustre la figure 14-12 : 51 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-12. Accus de rception de lenvoi du message. Pour des questions de localisation, il nest pas recommand de conserver ce script serveur. Car si lapplication devait tre traduite nous serions oblig de modifier le script serveur. Afin dviter cela, nous allons renvoyer la valeur 1 lorsque le mail est bien envoy, et 0 le cas chant. Nous renvoyons la valeur 2 lorsque les variables ne sont pas bien rcptionnes :
<?php $destinataire = $_POST ["destinataire"]; $sujet = $_POST ["sujet"]; $message = $_POST ["message"]; if ( isset ( $destinataire ) && isset ( $sujet ) && isset ( $message ) ) { if ( @mail ( utf8_decode($destinataire), utf8_decode($sujet), utf8_decode($message) ) ) echo "resultat=1"; else echo "resultat=0"; } else echo "resultat=2"; ?>

52 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Nous dcidons ainsi ct client quel message afficher. Nous renvoyons donc une chane encode URL qui devra tre dcode cot client. Pour cela nous demandons lobjet URLLoader de dcoder toutes les chanes reues afin de pouvoir facilement extraire le contenu de la variable resultat. Nous modifions la mthode retourServeur afin dafficher le message appropri :
package org.bytearray.document { import import import import import import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.display.SimpleButton; flash.text.TextField; flash.net.navigateToURL; flash.net.URLRequest; flash.net.URLVariables; flash.net.URLRequestMethod; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.events.Event; flash.events.MouseEvent; flash.events.HTTPStatusEvent; flash.events.IOErrorEvent;

public class Document extends ApplicationDefaut { public public public public public var var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton; echanges:URLLoader;

public function Document () { echanges = new URLLoader(); echanges.dataFormat = URLLoaderDataFormat.VARIABLES; echanges.addEventListener ( Event.COMPLETE, retourServeur ); echanges.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); echanges.addEventListener ( HTTPStatusEvent.HTTP_STATUS, statutHTTP ); boutonEnvoi.addEventListener ( } private function envoiMail ( pEvt:MouseEvent ):void { var variables:URLVariables = new URLVariables(); MouseEvent.CLICK, envoiMail );

53 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

// affectation des variables envoyer cot serveur variables.sujet = sujet.text; variables.destinataire = destinataire.text; variables.message = message.text; // cration de l'objet URLRequest var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php"); // nous passons les variables dans l'url (tableau POST ) requete.method = URLRequestMethod.POST; // associe les variables l'objet URLRequest requete.data = variables; // envoi les donnes de manire transparente, sans ouvrir de nouvelle fentre navigateur echanges.load ( requete ); } private function retourServeur ( pEvt:Event ):void { sujet.text = destinataire.text = message.text = ""; var messageRetour:String; if ( Number ( pEvt.target.data.resultat ) == 1 ) messageRetour = "Mail bien envoy !"; else if ( Number ( pEvt.target.data.resultat ) == 2 ) messageRetour = "Erreur de transmission des donnes !"; else messageRetour = "Erreur d'envoi du message !"; message.text = messageRetour; } private function statutHTTP ( pEvt:HTTPStatusEvent ):void { trace( "Code HTTP : " + pEvt.status ); } private function erreurChargement ( pEvt:IOErrorEvent ):void { trace("Erreur de chargement !"); } } }

54 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Si nous nenvoyons pas les variables au script serveur ou que la fonction mail choue nous obtenons un message appropri dans notre application Flash. Dans le code suivant, nous naffectons aucune variable lobjet URLVariables :
private function envoiMail ( pEvt:MouseEvent ):void { var variables:URLVariables = new URLVariables(); // cration de l'objet URLRequest var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php"); // nous passons les variables dans l'url (tableau POST ) requete.method = URLRequestMethod.POST; // associe les variables l'objet URLRequest requete.data = variables; // envoi les donnes de manire transparente, sans ouvrir de nouvelle fentre navigateur echanges.load ( requete ); }

Lemail ne peut tre envoy, la figure 14-13 illustre le message affich par lapplication :

Figure 14-13. Echec denvoi de lemail.

55 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Nous pourrions aller plus loin dans cet exemple et enregistrer lutilisateur dans une base de donnes et renvoyer dautres types dinformations. Nous reviendrons en dtail sur lenvoi et la rception de donnes issues dune base de donnes au cours du chapitre 19 intitul Remoting.

A retenir
Afin de rcuprer les donnes issues du serveur nous coutons lvnement Event.COMPLETE de lobjet URLLoader. Afin de retourner des donnes Flash, nous devons utiliser le mot cl PHP echo.

Aller plus loin


Souvenez-vous, au cours du chapitre 2, nous avons dcouvert la notion dexpressions rgulires permettant deffectuer des manipulations complexes sur des chanes de caractres. Afin doptimiser notre application nous allons intgrer une vrification de lemail par expression rgulire. Nous allons donc crer une classe OutilsFormulaire globale tous nos projets, intgrant une mthode statique verifieEmail. Celle-ci renverra true si lemail est correcte ou false le cas chant. Rappelez-vous, au cours du chapitre 12 intitul Programmation Bitmap, nous avions cr un rpertoire global de classes nomm classes_as3. Au sein du rpertoire outils nous crons la classe OutilsFormulaire suivante :
package org.bytearray.outils { public class FormulaireOutils { private static var emailModele:RegExp = /^[a-z0-9][-._a-z0-9]*@([a-z09][-_a-z0-9]*\.)+[a-z]{2,6}$/ public static function verifieEmail ( pEmail:String ):Boolean { ); var resultat:Array = pEmail.match( FormulaireOutils.emailModele return resultat != null;

56 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Une fois dfinie, nous pouvons lutiliser dans notre application en limportant :
import org.bytearray.outils.FormulaireOutils;

Puis nous modifions la mthode envoiMail afin dintgrer une vrification de lemail :
private function envoiMail ( pEvt:MouseEvent ):void { if ( FormulaireOutils.verifieEmail ( destinataire.text ) ) { var variables:URLVariables = new URLVariables(); // affectation des variables envoyer cot serveur variables.sujet = sujet.text; variables.destinataire = destinataire.text; variables.message = message.text; // cration de l'objet URLRequest var requete:URLRequest = new URLRequest ("http://localhost/mail/envoiMail.php"); // nous passons les variables dans l'url (tableau POST ) requete.method = URLRequestMethod.POST; // associe les variables l'objet URLRequest requete.data = variables; // envoi les donnes de manire transparente, sans ouvrir de nouvelle fentre navigateur echanges.load ( requete ); } else destinataire.text = "Email non valide !"; }

Ainsi,

entendu, nous pouvons ajouter dautres mthodes pratiques celle-ci.

FormulaireOutils afin de vrifier la validit dun email. Bien

toutes

nos

applications

pourront

utiliser

la

classe

Nous retrouvons ici lintrt dune mthode statique, pouvant tre appele directement sur le constructeur de la classe, sans avoir instancier un objet FormulaireOutils.

Envoyer un flux binaire


Rendez vous au chapitre 20 intitul ByteArray pour dcouvrir comment transmettre un flux binaire grce la classe URLLoader. 57 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Tlcharger un fichier
Nous avons vu jusqu present comment charger ou envoyer des variables partir du lecteur Flash. Certaines applications peuvent nanmoins ncessiter un tlchargement ou un envoi de fichiers entre le serveur et lordinateur de lutilisateur. La classe flash.net.FileReference permet de tlcharger nimporte quel fichier grace la mthode download. Imaginons que nous devions tlcharger un fichier zip depuis une URL spcifique telle : http://www.monserveur.org/archive.zip Au sein dun nouveau document Flash CS3 nous associons la classe de document suivante :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () {

} } }

Puis nous crons un objet FileReference :


package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; import flash.net.FileReference; public class Document extends ApplicationDefaut { private var telechargeur:FileReference; public function Document () {

58 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

telechargeur = new FileReference(); } } }

Afin de grer les diffrentes tapes lies au tlchargement de donnes la classe FileReference diffuse diffrents vnements dont voici le dtail :
Event.CANCEL : diffus lorsquun envoi ou tlchargement est annul par la fentre de parcours. Event.COMPLETE : diffus lorsquun envoi ou un tlchargement a russi. HTTPStatusEvent.HTTP_STATUS : diffus lorsquun envoi ou chargement choue. IOErrorEvent.IO_ERROR : diffus lorsque lenvoi ou la rception des donnes choue. Event.OPEN : diffus lorsque lenvoi ou la rception des donnes commence. ProgressEvent.PROGRESS : chargement est en cours. diffus lorsque lenvoi ou le

SecurityErrorEvent.SECURITY_ERROR : diffus lorsque le lecteur tente denvoyer ou de charger des donnes depuis un domaine non autoris. Event.SELECT : diffus lorsque le bouton OK de la boite de dialogue de recherches de fichiers est relche DataEvent.UPLOAD_COMPLETE_DATA : diffus lorsquune opration denvoi ou de chargement est termin et que le serveur renvoie des donnes.

Afin de tlcharger un fichier nous utilisons la mthode download dont voici la signature :
public function download(request:URLRequest, defaultFileName:String = null):void

Celle-ci requiert deux paramtres dont voici le dtail :


request : un objet URLRequest contenant ladresse de llment tlcharger. defaultFileName : ce paramtre permet de spcifier le nom de llment tlcharg au sein de la boite de dialogue.
package org.bytearray.document

Dans le code suivant, nous tlchargeons un fichier distant :

59 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent;

public class Document extends ApplicationDefaut { private var telechargeur:FileReference; private var lienFichier:String = "http://alivepdf.bytearray.org/wpcontent/images/logo_small.jpg"; private var requete:URLRequest = new URLRequest( lienFichier ); public var boutonTelecharger:SimpleButton; public function Document () { // cration d'un objet FileReference telechargeur = new FileReference(); boutonTelecharger.addEventListener ( MouseEvent.CLICK, telechargeFichier ); } private function telechargeFichier ( pEvt:MouseEvent ):void { // tlchargement du fichier distant telechargeur.download ( requete ); } } }

Lorsque nous cliquons sur le bouton boutonTelecharger une fentre de tlchargement souvre comme lillustre la figure 14-14 :

60 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-14. Boite de dialogue. Une fois lemplacement slectionn, nous cliquons sur le bouton Enregistrer. Le fichier est alors sauv en local sur lordinateur excutant lapplication. Bien que nous nayons pas spcifi le nom du fichier, le lecteur Flash extrait automatiquement celui-ci partir de son URL. Attention, lorsque cette boite de dialogue apparat, la tentative daccs au fichier distant na pas encore dmarr. Si nous modifions ladresse du fichier distant par ladresse suivante :
private var lienFichier:String = "http://www.monserveurInexistant.org/logo_small.jpg";

Le lecteur affiche tout de mme la boite de dialogue ainsi que le nom du fichier. Afin de grer ltat du transfert nous coutons les principaux vnements :
package org.bytearray.document { import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event;

61 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

import flash.events.HTTPStatusEvent; import flash.events.ProgressEvent; import flash.events.IOErrorEvent; public class Document extends ApplicationDefaut { private var telechargeur:FileReference; private var lienFichier:String = "http://alivepdf.bytearray.org/wpcontent/images/logo_small.jpg"; private var requete:URLRequest = new URLRequest( lienFichier ); public var boutonTelecharger:SimpleButton; public function Document () { // cration d'un objet FileReference telechargeur = new FileReference(); telechargeur.addEventListener ( ProgressEvent.PROGRESS, chargementEnCours ); telechargeur.addEventListener ( Event.COMPLETE, chargementTermine ); telechargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); boutonTelecharger.addEventListener ( MouseEvent.CLICK, telechargeFichier ); } private function chargementEnCours ( pEvt:ProgressEvent ):void { trace( "chargement en cours pEvt.bytesTotal ); } private function chargementTermine ( pEvt:Event ):void { trace( "chargement termin" ); } private function erreurChargement ( pEvt:IOErrorEvent ):void { trace( "erreur de chargement du fichier !" ); } private function telechargeFichier ( pEvt:MouseEvent ):void { : " + pEvt.bytesLoaded /

62 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

// tlchargement du fichier distant telechargeur.download ( requete ); } } }

La mthode download naccpte quun seul fichier tlcharger. Il est donc impossible de tlcharger plusieurs fichiers en mme temps laide de la classe FileReference. Il est important de noter que dans un contexte inter domaines, le tlchargement de fichiers est interdit si le domaine distant nest pas autoris. Ainsi, si nous publions lapplication actuelle sur un serveur distant, lvnement SecurityErrorEvent.SECURITY_ERROR est diffus affichant le message suivant :
Error #2048: Violation de la scurit Sandbox : http://monserveur.fr/as3/chap-14-filereference.swf ne peut pas charger de donnes partir de http://alivepdf.bytearray.org/wpcontent/images/logo_small.jpg.

Afin de pouvoir charger le fichier distant nous devons utiliser un fichier de rgulation ou un proxy. Nous allons utiliser dans le code suivant un proxy afin de feindre le lecteur Flash :
package org.bytearray.document { import import import import import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.URLRequestMethod; flash.net.URLVariables; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event; flash.events.HTTPStatusEvent; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent;

public class Document extends ApplicationDefaut { private var telechargeur:FileReference; private var lienFichier:String = "proxy.php"; private var requete:URLRequest = new URLRequest( lienFichier ); public var boutonTelecharger:SimpleButton;

63 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

public function Document () { // cration d'un objet FileReference telechargeur = new FileReference(); // cration d'un objet URLVariables pour passer le chemin du fichier charger au proxy var variables:URLVariables = new URLVariables(); // spcification du chemin variables.chemin = "http://alivepdf.bytearray.org/wpcontent/images/logo_small.jpg"; // affectation des variables et de la mthode utilise requete.data = variables; requete.method = URLRequestMethod.POST; telechargeur.addEventListener ( ProgressEvent.PROGRESS, chargementEnCours ); telechargeur.addEventListener ( Event.COMPLETE, chargementTermine ); telechargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); boutonTelecharger.addEventListener ( MouseEvent.CLICK, telechargeFichier ); } private function chargementEnCours ( pEvt:ProgressEvent ):void { trace( "chargement en cours pEvt.bytesTotal ); } private function chargementTermine ( pEvt:Event ):void { trace( "chargement termin" ); } private function erreurChargement ( pEvt:IOErrorEvent ):void { trace( "erreur de chargement du fichier !" ); } private function telechargeFichier ( pEvt:MouseEvent ):void { // tlchargement du fichier distant telechargeur.download ( requete ); : " + pEvt.bytesLoaded /

64 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

} } }

Une fois lapplication publie, lappel de la mthode download ne lve plus dexeception. La boite de dialogue souvre et propose comme nom de fichier le nom du fichier proxy utilis comme lillustre la figure 14-15 :

Figure 14-15. Nom denregistrement du fichier. Comme nous lavons vu prcdemment, le lecteur devine automatiquement le nom du fichier tlcharg depuis lURL du fichier. Dans notre cas, ce dernier considre que le fichier charger est le proxy et propose donc comme nom de fichier proxy.php. Afin de proposer le nom rel du fichier charg, nous allons utiliser le deuxime paramtre defaultFileName de la mthode download :
package org.bytearray.document { import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.URLRequestMethod; flash.net.URLVariables; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event;

65 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

import import import import

flash.events.HTTPStatusEvent; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent;

public class Document extends ApplicationDefaut { private var telechargeur:FileReference; private var lienFichier:String = "proxy.php"; private var requete:URLRequest = new URLRequest( lienFichier ); private var urlFichier:String; public var boutonTelecharger:SimpleButton; public function Document () { // cration d'un objet FileReference telechargeur = new FileReference(); // cration d'un objet URLVariables pour passer le chemin du fichier charger au proxy var variables:URLVariables = new URLVariables(); // url du fichier distant charger par le proxy urlFichier = "http://alivepdf.bytearray.org/wpcontent/images/logo_small.jpg"; // spcification du chemin variables.chemin = urlFichier; // affectation des variables et de la mthode utilise requete.data = variables; requete.method = URLRequestMethod.POST; telechargeur.addEventListener ( ProgressEvent.PROGRESS, chargementEnCours ); telechargeur.addEventListener ( Event.COMPLETE, chargementTermine ); telechargeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); boutonTelecharger.addEventListener ( MouseEvent.CLICK, telechargeFichier ); } private function chargementEnCours ( pEvt:ProgressEvent ):void { trace( "chargement en cours pEvt.bytesTotal ); } private function chargementTermine ( pEvt:Event ):void { : " + pEvt.bytesLoaded /

66 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

trace( "chargement termin" ); } private function erreurChargement ( pEvt:IOErrorEvent ):void { trace( "erreur de chargement du fichier !" ); } private function telechargeFichier ( pEvt:MouseEvent ):void { // expression rgulire var modele:RegExp = /[A-Za-z0-9_]*\.\D{3}$/ // extrait le nom du fichier de l'url var resultat:Array = urlFichier.match ( modele ); // tlchargement du fichier distant if ( resultat != null ) telechargeur.download ( requete, resultat[0] ); } } }

Le nom du fichier est extrait manuellement puis pass en deuxime paramtre de la mthode download. Lors du clic sur le bouton boutonTelecharger la boite de dialogue souvre en proposant le bon nom de fichier :

67 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-16. Nom denregistrement du fichier. Il est donc possible de rendre totalement transparent lintervention dun proxy dans le tlchargement de notre fichier distant. Lorsque nous cliquons sur le bouton Enregistrer, le fichier est tlcharg et enregistr. Nous allons nous attarder prsent sur lenvoi de fichiers par la classe flash.net.FileReference.

A retenir
Afin de tlcharger un fichier depuis le lecteur Flash nous utilisons la mthode download de la classe FileReference. Lappel de la mthode download entrane louverture dune boite de dialogue permettant de sauver le fichier sur lordinateur de lutilisateur.

Publier un fichier
A linverse si nous souhaitons mettre en ligne un fichier selectionn depuis lordinateur de lutilisateur, nous pouvons utiliser la mthode upload de la classe FileReference. Dans un nouveau document Flash CS3, nous associons la classe de document suivante :
package org.bytearray.document { import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent;

public class Document extends ApplicationDefaut { private var envoyeur:FileReference; private var lienScript:String = "http://localhost/envoi/envoiFichier.php"; private var requete:URLRequest = new URLRequest( lienScript ); public var boutonEnvoyer:SimpleButton; public function Document () { // cration d'un objet FileReference

68 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

envoyeur = new FileReference(); ); envoyeur.addEventListener ( ProgressEvent.PROGRESS, envoiEnCours envoyeur.addEventListener ( Event.COMPLETE, envoiTermine ); envoyeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurEnvoi );

boutonEnvoyer.addEventListener ( MouseEvent.CLICK, parcoursFichiers ); } private function envoiEnCours ( pEvt:ProgressEvent ):void { ); } private function envoiTermine ( pEvt:Event ):void { trace( "envoi termin" ); } private function erreurEnvoi ( pEvt:IOErrorEvent ):void { trace( "erreur d'envoi du fichier !" ); } private function parcoursFichiers ( pEvt:MouseEvent ):void { fichiers } } } // ouvre la boite de dialogue permettant de parcourir les envoyeur.browse (); trace( "envoi en cours : " + pEvt.bytesLoaded / pEvt.bytesTotal

Au sein de lapplication, un bouton boutonEnvoyer dclenche louverture de la boite de dialogue permettant de parcourir les fichiers transfrer laide de la mthode browse de lobjet FileReference. La proprit lienScript pointe vers un script serveur permettant lenregistrement du fichier transfr sur le serveur. Voici le code PHP permettant de sauver le fichier transfr : 69 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

<?php $fichier = $_FILES["Filedata"]; if ( isset ( $fichier ) ) { $nomFichier = $fichier['name']; move_uploaded_file ( $fichier['tmp_name'], "monRepertoire/".$nomFichier); } ?>

Le lecteur Flash place le fichier transfr ua sein du tableau $_FILES et de la proprit Filedata. Lors de la gestion denvoi de fichiers par FileReference il convient dcouter lvnement Event.SELECT diffus lorsque lutilisateur a selectionn un fichier. Ainsi nous pouvons rcuprer diffrentes informations lies au fichier selectionn :
package org.bytearray.document { import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent;

public class Document extends ApplicationDefaut { private var envoyeur:FileReference; private var lienScript:String = "http://localhost/envoi/envoiFichier.php"; private var requete:URLRequest = new URLRequest( lienScript ); public var boutonEnvoyer:SimpleButton; public function Document () { // cration d'un objet FileReference envoyeur = new FileReference(); envoyeur.addEventListener ( Event.SELECT, selectionFichier ); envoyeur.addEventListener ( ProgressEvent.PROGRESS, envoiEnCours envoyeur.addEventListener ( Event.COMPLETE, envoiTermine ); envoyeur.addEventListener ( IOErrorEvent.IO_ERROR, erreurEnvoi );

);

70 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

boutonEnvoyer.addEventListener ( MouseEvent.CLICK, parcoursFichiers ); } private function selectionFichier ( pEvt:Event ):void { var fichier:FileReference = FileReference ( pEvt.target ); // affiche : Tue Oct 23 19:59:27 GMT+0200 2007 trace( fichier.creationDate ); // affiche : Interview.doc trace( fichier.name ); // affiche : 37888 trace( fichier.size ); // affiche : .doc trace( fichier.type ); } private function envoiEnCours ( pEvt:ProgressEvent ):void { ); } private function envoiTermine ( pEvt:Event ):void { trace( "envoi termin" ); } private function erreurEnvoi ( pEvt:IOErrorEvent ):void { trace( "erreur d'envoi du fichier !" ); } private function parcoursFichiers ( pEvt:MouseEvent ):void { fichiers } } // ouvre la boite de dialogue permettant de parcourir les envoyeur.browse (); trace( "envoi en cours : " + pEvt.bytesLoaded / pEvt.bytesTotal

71 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Voici le dtail de chacune des proprits dfinies par la classe FileReference :


creationDate : date de cration du fichier. creator : type de crateur Macintosh du fichier. name : nom du fichier.

modificationDate : date de dernire modification du fichier. size : taille du fichier en octets. type : extension du fichier.

Une fois lobjet slctionn, nous appelons la mthode upload sur lobjet FileReference :
private function selectionFichier ( pEvt:Event ):void { var fichier:FileReference = FileReference ( pEvt.target ); // affiche : Tue Oct 23 19:59:27 GMT+0200 2007 trace( fichier.creationDate ); // affiche : Interview.doc trace( fichier.name ); // affiche : 37888 trace( fichier.size ); // affiche : .doc trace( fichier.type ); // envoi du fichier fichier.upload ( requete ); }

Comme lillustre la figure 14-17, vous remarquerez quil est impossible de slectionner plusieurs fichiers au sein de la boite de dialogue ouverte par la mthode browse de lobjet FileReference :

72 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-17. Selection multiple impossible. Afin de pouvoir slectionner plusieurs fichiers nous devons obligatoirement utiliser la classe flash.net.FileReferenceList.

A retenir
Afin de selectionner un fichier transfrer sur un serveur depuis le lecteur Flash nous utilisons dabord la mthode browse de lobjet FileReference.

Une fois le fichier slectionn, nous appelons la mthode upload en spcifiant lURL du script serveur permettant lenregistrement du fichier.

Publier plusieurs fichiers


Afin de pouvoir envoyer plusieurs fichiers en mme temps, nous modifions la classe du document afin de crer un objet FileReferenceList :
package org.bytearray.document { import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.FileReferenceList; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent;

public class Document extends ApplicationDefaut { private var envoyeurMultiple:FileReferenceList;

73 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

private var lienScript:String = "http://localhost/envoi/envoiFichier.php"; private var requete:URLRequest = new URLRequest( lienScript ); public var boutonEnvoyer:SimpleButton; public function Document () { // cration d'un objet FileReference envoyeurMultiple = new FileReferenceList(); envoyeurMultiple.addEventListener selectionFichier ); envoyeurMultiple.addEventListener envoiEnCours ); envoyeurMultiple.addEventListener ); envoyeurMultiple.addEventListener erreurEnvoi ); ( Event.SELECT, ( ProgressEvent.PROGRESS, ( Event.COMPLETE, envoiTermine ( IOErrorEvent.IO_ERROR,

boutonEnvoyer.addEventListener ( MouseEvent.CLICK, parcoursFichiers ); } private function selectionFichier ( pEvt:Event ):void { // rfrence le tableau contenant chaque objet FileReference var listeFichiers:Array = pEvt.target.fileList; // affiche : n fichiers slctionns trace( listeFichiers.length + " fichier(s) selectionns"); } private function envoiEnCours ( pEvt:ProgressEvent ):void { ); } private function envoiTermine ( pEvt:Event ):void { trace( "envoi termin" ); } private function erreurEnvoi ( pEvt:IOErrorEvent ):void { trace( "erreur d'envoi du fichier !" ); } trace( "envoi en cours : " + pEvt.bytesLoaded / pEvt.bytesTotal

74 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

private function parcoursFichiers ( pEvt:MouseEvent ):void { fichiers } } } // ouvre la boite de dialogue permettant de parcourir les envoyeurMultiple.browse ();

La boite de dialogue ouverte par la mthode browse de lobjet FileReferenceList permet la slection multiple, comme lindique la figure 14-18 :

Figure 14-18. Selection multiple possible. En ralit lorsque nous slectionnons plusieurs fichiers travers la boite de dialogue et que nous validons la slection. La classe FileReferenceList cre un interne un objet FileReference associ chaque fichier rfrence au sein de la proprit fileList de lobjet FileReferenceList. Nous devons donc manuellement parcourir ce tableau interne contenant les objets FileReference et appeler la mthode upload sur chacun dentre eux. Il est important de signaler que lobjet FileReference ne diffuse que les vnements Event.SELECT et Event.CANCEL lis la slection des fichiers. La gestion du chargement se fait par lintermdiaire des objets FileReference crs en interne par lobjet FileReferenceList :
package org.bytearray.document

75 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ import import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.FileReferenceList; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent;

public class Document extends ApplicationDefaut { private var envoyeurMultiple:FileReferenceList; private var lienScript:String = "http://localhost/envoi/envoiFichier.php"; private var requete:URLRequest = new URLRequest( lienScript ); public var boutonEnvoyer:SimpleButton; public function Document () { // cration d'un objet FileReference envoyeurMultiple = new FileReferenceList(); envoyeurMultiple.addEventListener ( Event.SELECT, selectionFichier ); boutonEnvoyer.addEventListener ( MouseEvent.CLICK, parcoursFichiers ); } private function selectionFichier ( pEvt:Event ):void { // rfrence le tableau contenant chaque objet FileReference var listeFichiers:Array = pEvt.target.fileList; // longueur du tableau var lng:int = listeFichiers.length; for ( var i:int = 0; i< lng; i++ ) { var fichier:FileReference = listeFichiers[i]; envoiEnCours ); erreurEnvoi ); fichier.addEventListener ( ProgressEvent.PROGRESS, fichier.addEventListener ( Event.COMPLETE, envoiTermine ); fichier.addEventListener ( IOErrorEvent.IO_ERROR, fichier.upload ( requete );

76 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

} } private function envoiEnCours ( pEvt:ProgressEvent ):void { ); } private function envoiTermine ( pEvt:Event ):void { trace( "envoi termin" ); } private function erreurEnvoi ( pEvt:IOErrorEvent ):void { trace( "erreur d'envoi du fichier !" ); } private function parcoursFichiers ( pEvt:MouseEvent ):void { fichiers } } } // ouvre la boite de dialogue permettant de parcourir les envoyeurMultiple.browse (); trace( "envoi en cours : " + pEvt.bytesLoaded / pEvt.bytesTotal

En testant notre application, les fichiers sont bien transfrs sur le serveur. Nous allons ajouter prsent une jauge de chargement indiquant ltat du transfert. Pour cela nous crons un clip de forme rectangulaire en bibliothque auquel nous associons une classe Prechargeur.
package org.bytearray.document { import import import import import import import org.bytearray.abstrait.ApplicationDefaut; flash.net.FileReferenceList; flash.net.FileReference; flash.net.URLRequest; flash.display.SimpleButton; flash.events.MouseEvent; flash.events.Event;

77 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

import flash.events.ProgressEvent; import flash.events.IOErrorEvent; public class Document extends ApplicationDefaut { private var envoyeurMultiple:FileReferenceList; private var lienScript:String = "http://localhost/envoi/envoiFichier.php"; private var requete:URLRequest = new URLRequest( lienScript ); public var boutonEnvoyer:SimpleButton; private var prechargeur:Prechargeur; public function Document () { // cration d'un objet FileReference envoyeurMultiple = new FileReferenceList(); prechargeur = new Prechargeur(); envoyeurMultiple.addEventListener ( Event.SELECT, selectionFichier ); boutonEnvoyer.addEventListener ( MouseEvent.CLICK, parcoursFichiers ); } private function selectionFichier ( pEvt:Event ):void { // rfrence le tableau contenant chaque objet FileReference var listeFichiers:Array = pEvt.target.fileList; // longueur du tableau var lng:int = listeFichiers.length; for ( var i:int = 0; i< lng; i++ ) { var fichier:FileReference = listeFichiers[i]; envoiEnCours ); erreurEnvoi ); fichier.addEventListener ( ProgressEvent.PROGRESS, fichier.addEventListener ( Event.COMPLETE, envoiTermine ); fichier.addEventListener ( IOErrorEvent.IO_ERROR, fichier.upload ( requete ); } // ajout du prchargeur l'affichage if ( !contains ( prechargeur ) ) addChild ( prechargeur ); }

78 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

private function envoiEnCours ( pEvt:ProgressEvent ):void { transfert } private function envoiTermine ( pEvt:Event ):void { trace( "envoi termin" ); } private function erreurEnvoi ( pEvt:IOErrorEvent ):void { trace( "erreur d'envoi du fichier !" ); } private function parcoursFichiers ( pEvt:MouseEvent ):void { fichiers } } } // ouvre la boite de dialogue permettant de parcourir les envoyeurMultiple.browse (); // ajuste la taille de la jauge par rapport l'tat du

prechargeur.scaleX = pEvt.bytesLoaded / pEvt.bytesTotal;

Lors du transfert des fichiers, la jauge de transfert indique ltat du chargement. En revanche, lors du transfert de multiples fichiers la jauge reoit les vnements de chaque fichier en cours de transfert et indique le chargement de tous les fichiers en mme temps. De la mme manire, nous ne pouvons pas savoir simplement, si le transfert de tous les fichiers est termin. Nous ne bnficions pas dvnement appropri. Pour remdier tout cela nous allons dvelopper notre propre version de la classe FileReferenceList.

A retenir

79 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Seule la classe FileReferenceList permet lenvoi de plusieurs fichiers en mme temps.

Cration de la classe EnvoiMultiple


Nous allons amliorer la gestion denvoi des fichiers en crant une classe EnvoiMultiple. Celle si tend la classe FileReferenceList et amliore le transfert de chaque fichier en sassurant que chaque fichier est transfr lun aprs lautre. De plus, nous allons ajouter un vnement indiquant la fin du transfert de tous les fichiers. Nous commenons par dfinir notre classe EnvoiMultiple en tendant la classe FileReferenceList :
package org.bytearray.envoi { import flash.net.FileReferenceList; import flash.events.Event; import flash.net.URLRequest; public class EnvoiMultiple extends FileReferenceList { private var requete:URLRequest; public function EnvoiMultiple ( pRequete:URLRequest ) { requete = pRequete; addEventListener ( Event.SELECT, selectionFichiers ); } private function selectionFichiers ( pEvt:Event ):void { trace("fichiers selectionns"); } } }

La classe EnvoiMultiple accpte en paramtre un objet URLRequest pointant vers un script serveur permettant lenregistrement du fichier.

80 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Nous souhaitons que la classe EnvoiMultiple puisse grer de manire synchronise chaque envoi de fichiers. Pour cela nous allons lancer le premier transfert puis attendre que celui soit termin pour dclencher les suivants :
package org.bytearray.envoi { import import import import import import flash.events.Event; flash.events.ProgressEvent; org.bytearray.events.ProgressQueueEvent; org.bytearray.events.QueueEvent; flash.net.FileReference; flash.net.URLRequest;

public class EnvoiMultiple extends FileReferenceList { private private private private var var var var historiqueFichiers:Array; fichierEnCours:FileReference; fichiers:Array; requete:URLRequest;

public function EnvoiMultiple ( pRequete:URLRequest ) { requete = pRequete; addEventListener ( Event.SELECT, selectionFichiers ); } private function selectionFichiers ( pEvt:Event ):void { historiqueFichiers = new Array(); fichiers = pEvt.target.fileList; suivant(); } private function suivant ( ):void { var fichierEnCours:FileReference = fichiers.shift(); historiqueFichiers.push ( fichierEnCours ); fichierEnCours.addEventListener ( Event.COMPLETE, transfertTermine ); fichierEnCours.addEventListener ( ProgressEvent.PROGRESS, transfertEnCours ); fichierEnCours.upload ( requete );

81 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

} private function transfertTermine ( pEvt:Event ):void { } private function transfertEnCours ( pEvt:ProgressEvent ):void { } } }

Lorsque le premier fichier a t transfr la mthode couteur transfertTermine est dclenche. Nous devons ajouter au sein de celle-ci le code ncessaire afin de lancer le transfert suivant, si cela est ncessaire. Nous en profitions pour supprimer lcoute des vnements Event.COMPLETE et ProgressEvent.PROGRESS auprs de lobjet FileReference en cours. Nous consommons le tableau fichierEnCours en dclenchant la mthode suivant, si dautres fichiers sont en attendre de transfert au sein du tableau fichiers :
private function transfertTermine ( pEvt:Event ):void { pEvt.target.removeEventListener ( Event.COMPLETE, transfertTermine ); pEvt.target.removeEventListener ( ProgressEvent.PROGRESS, transfertEnCours ); if ( fichiers.length ) suivant(); trace("transfert termin !"); } private function transfertEnCours ( pEvt:ProgressEvent ):void { trace("transfert en cours..."); }

Il ne nous reste plus qu diffuser deux vnements pour chaque phase :


EvenementEnvoiMultiple.TERMINE : cet vnement est diffus lorsque la totalit des fichiers slctionns sont transfrs.

82 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

ProgressionEvenementEnvoiMultiple.PROGRESSION : cet vnement est diffus lorsquun fichier est en cours de transfert.

Au sein du paquetage evenements nous crons la classe EvenementEnvoiMultiple :


package org.bytearray.evenements { import flash.events.Event; public class EvenementEnvoiMultiple extends Event { public static const TERMINE:String = "termine"; public var historique:Array; public function EvenementEnvoiMultiple ( pType:String, pHistorique:Array=null ) { // initialisation du constructeur de la classe Event super( pType, false, false ); historique = pHistorique; } // la mthode clone doit tre surcharge public override function clone ():Event { return new EvenementEnvoiMultiple ( type, historique ); } // la mthode toString doit tre surcharge public override function toString ():String { return '[EvenementEnvoiMultiple type="'+ type +'" bubbles=' + bubbles + ' eventPhase='+ eventPhase + ' cancelable=' + cancelable +']'; } } }

Puis la classe ProgressionEvenementEnvoiMultiple :


package org.bytearray.evenements { import flash.events.ProgressEvent;

83 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

import flash.events.Event; import flash.net.FileReference; public class ProgressionEvenementEnvoiMultiple extends ProgressEvent { public static const PROGRESSION:String = "progression"; public var fichier:FileReference; public function ProgressionEvenementEnvoiMultiple ( pType:String, pOctetsCharges:Number, pOctetsTotaux:Number, pFichier:FileReference ) { // initialisation du constructeur de la classe Event super( pType, false, false, pOctetsCharges, pOctetsTotaux ); fichier = pFichier; } // la mthode clone doit tre surcharge public override function clone ():Event { return new ProgressionEvenementEnvoiMultiple ( type, bytesLoaded, bytesTotal, fichier ); } // la mthode toString doit tre surcharge public override function toString ():String { return '[ProgressionEvenementEnvoiMultiple type="'+ type +'" bubbles=' + bubbles + ' eventPhase='+ eventPhase + ' cancelable=' + cancelable + ' bytesLoaded=' + bytesLoaded +' bytesTotal=' + bytesTotal +']'; } } }

Enfin nous diffusons les vnements appropris au sein des mthodes transfertEnCours et transfertTermine :
package org.bytearray.envoi { import import import import import import import flash.events.Event; flash.events.ProgressEvent; org.bytearray.evenements.EvenementEnvoiMultiple; org.bytearray.evenements.ProgressionEvenementEnvoiMultiple; flash.net.FileReference; flash.net.FileReferenceList; flash.net.URLRequest;

public class EnvoiMultiple extends FileReferenceList

84 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ private private private private var var var var historiqueFichiers:Array; fichierEnCours:FileReference; fichiers:Array; requete:URLRequest;

public function EnvoiMultiple ( pRequete:URLRequest ) { requete = pRequete; addEventListener ( Event.SELECT, selectionFichiers ); } private function selectionFichiers ( pEvt:Event ):void { historiqueFichiers = new Array(); fichiers = pEvt.target.fileList; suivant(); } private function suivant ( ):void { var fichierEnCours:FileReference = fichiers.shift(); historiqueFichiers.push ( fichierEnCours ); fichierEnCours.addEventListener ( Event.COMPLETE, transfertTermine ); fichierEnCours.addEventListener ( ProgressEvent.PROGRESS, transfertEnCours ); fichierEnCours.upload ( requete ); } private function transfertTermine ( pEvt:Event ):void { pEvt.target.removeEventListener ( Event.COMPLETE, transfertTermine ); pEvt.target.removeEventListener ( ProgressEvent.PROGRESS, transfertEnCours ); if ( fichiers.length ) suivant(); else dispatchEvent ( new EvenementEnvoiMultiple ( EvenementEnvoiMultiple.TERMINE, historiqueFichiers ) ); }

85 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

private function transfertEnCours ( pEvt:ProgressEvent ):void { dispatchEvent ( new ProgressionEvenementEnvoiMultiple ( ProgressionEvenementEnvoiMultiple.PROGRESSION, pEvt.bytesLoaded, pEvt.bytesTotal, FileReference ( pEvt.target ) ) ); } } }

Nous pouvons prsent utiliser la classe EnvoiMultiple. Dans un nouveau document Flash CS3, nous associons la classe de document suivante :
package org.bytearray.document { import import import import import import import import org.bytearray.abstrait.ApplicationDefaut; org.bytearray.envoi.EnvoiMultiple; org.bytearray.evenements.EvenementEnvoiMultiple; org.bytearray.evenements.ProgressionEvenementEnvoiMultiple; flash.display.MovieClip; flash.display.SimpleButton; flash.events.MouseEvent; flash.text.TextField;

public class Document extends ApplicationDefaut { private var envoyeur:EnvoiMultiple; public var boutonEnvoyer:SimpleButton; public var prechargeur:MovieClip; public var legende:TextField; public function Document () { // cration de l'objet EnvoiMultiple envoyeur = new EnvoiMultiple( new URLRequest ("http://www.bytearray.org/as3/upload.php" )); // coute des vnements personnaliss lis au chargement envoyeur.addEventListener ( ProgressionEvenementEnvoiMultiple.PROGRESSION, transfertEnCours ); envoyeur.addEventListener ( EvenementEnvoiMultiple.TERMINE, transfertTermine ); boutonEnvoyer.addEventListener ( MouseEvent.CLICK, parcoursFichiers ); } private function transfertEnCours ( pEvt:ProgressionEvenementEnvoiMultiple ):void

86 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ // mise jour de la barre de progression prechargeur.scaleX = pEvt.bytesLoaded / pEvt.bytesTotal; // informations lies au fichier en cours de transfert legende.text = "Transfert en cours : " + pEvt.fichier.name; } private function transfertTermine ( pEvt:EvenementEnvoiMultiple ):void { // indique le nombre de fichiers transfrs legende.text = pEvt.historique.length + " fichiers(s) transfr(s)"; } private function parcoursFichiers ( pEvt:MouseEvent ):void { envoyeur.browse(); } } }

La document en cours contient une barre de prchargement prechargeur ainsi quun champ texte legende poss sur la scne afin dafficher les informations relatives au transfert des fichiers. La figure 14-19 illustre lapplication :

87 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-19. Transfert de fichiers. Lorsque transfres, lvnement EvenementEnvoiMultiple.TERMINE est diffus, cela nous permet dafficher un message appropri et de ragir en consquence : les images ont t

88 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

Figure 14-20. Transfert de fichiers termin. La proprit historique de lobjet vnementiel EvenementEnvoiMultiple est un tableau contenant des objets FileReference associ chaque fichier transfr. En rcuprant sa longueur nous pouvons indiquer le nombre de fichiers transfrs.

A vous dajouter prsent, une instanciation dynamique de la jauge de transfert et de la lgende puis de les supprimer une fois lvnement EvenementEnvoiMultiple.TERMINE est diffus.

Retourner des donnes une fois lenvoi termin


ActionScript 3 ajoute un vnement trs intressant la classe FileReference que nous allons dcouvrir prsent. Dans les prcdentes versions dActionScript, il tait impossible dobtenir des informations du serveur lorsque le transfert tait termin. Trs souvent, les dveloppeurs souhaitaient retourner des informations indiquant si le transfert des donnes avait chou ou non. Lvnement FileFerence.COMPLETE noffre pas la possibilit de renvoyer les informations provenant du serveur. En revanche, ActionScript 3 introduit un vnement DataEvent.UPLOAD_COMPLETE_DATA offrant cette possibilit. Nous ajouter cette fonctionnalit notre classe EnvoiMultiple et renvoyer des informations afin dindiquer lapplication Flash que lcriture sur le serveur a russi ou non. Nous allons modifier le script serveur grant lenregistrement des fichiers de manire indiquer si l :
<?php $fichier = $_FILES["Filedata"]; if ( isset ( $fichier ) ) { $nomFichier = $fichier['name']; if ( move_uploaded_file ( $fichier['tmp_name'], $nomFichier) ) echo "resultat=1"; else echo "resultat=0"; } else echo "resultat=0"; ?>

allons

Voici le code complet de la classe EnvoiMultiple : 89 / 98


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

package org.bytearray.envoi { import import import import import import import import flash.events.DataEvent; flash.events.Event; flash.events.ProgressEvent; org.bytearray.evenements.EvenementEnvoiMultiple; org.bytearray.evenements.ProgressionEvenementEnvoiMultiple; flash.net.FileReference; flash.net.FileReferenceList; flash.net.URLRequest;

public class EnvoiMultiple extends FileReferenceList { private private private private var var var var historiqueFichiers:Array; fichierEnCours:FileReference; fichiers:Array; requete:URLRequest;

public function EnvoiMultiple ( pRequete:URLRequest ) { requete = pRequete; addEventListener ( Event.SELECT, selectionFichiers ); } private function selectionFichiers ( pEvt:Event ):void { historiqueFichiers = new Array(); fichiers = pEvt.target.fileList; suivant(); } private function suivant ( ):void { var fichierEnCours:FileReference = fichiers.shift(); historiqueFichiers.push ( fichierEnCours ); fichierEnCours.addEventListener ( Event.COMPLETE, transfertTermine ); fichierEnCours.addEventListener ( ProgressEvent.PROGRESS, transfertEnCours ); fichierEnCours.addEventListener ( DataEvent.UPLOAD_COMPLETE_DATA, retourServeur ); fichierEnCours.upload ( requete ); }

90 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

private function retourServeur ( pEvt:DataEvent ):void { dispatchEvent ( pEvt ); pEvt.target.removeEventListener ( DataEvent.UPLOAD_COMPLETE_DATA, retourServeur ); } private function transfertTermine ( pEvt:Event ):void { pEvt.target.removeEventListener ( Event.COMPLETE, transfertTermine ); pEvt.target.removeEventListener ( ProgressEvent.PROGRESS, transfertEnCours ); if ( fichiers.length ) suivant(); else dispatchEvent ( new EvenementEnvoiMultiple ( EvenementEnvoiMultiple.TERMINE, historiqueFichiers ) ); } private function transfertEnCours ( pEvt:ProgressEvent ):void { dispatchEvent ( new ProgressionEvenementEnvoiMultiple ( ProgressionEvenementEnvoiMultiple.PROGRESSION, pEvt.bytesLoaded, pEvt.bytesTotal, FileReference ( pEvt.target ) ) ); } } }

Il ne reste plus qu couter cet vnement auprs de lobjet EnvoiMultiple cr :


package org.bytearray.document { import import import import import import import import import import import flash.events.DataEvent; flash.net.URLRequest; flash.net.URLVariables; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.envoi.EnvoiMultiple; org.bytearray.evenements.EvenementEnvoiMultiple; org.bytearray.evenements.ProgressionEvenementEnvoiMultiple; flash.display.MovieClip; flash.display.SimpleButton; flash.events.MouseEvent; flash.text.TextField;

public class Document extends ApplicationDefaut

91 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ private var envoyeur:EnvoiMultiple; public var boutonEnvoyer:SimpleButton; public var prechargeur:MovieClip; public var legende:TextField; public function Document () { // cration de l'objet EnvoiMultiple envoyeur = new EnvoiMultiple( new URLRequest ("http://www.bytearray.org/as3/upload.php" )); // coute des vnements personnaliss lis au chargement envoyeur.addEventListener ( ProgressionEvenementEnvoiMultiple.PROGRESSION, transfertEnCours ); envoyeur.addEventListener ( EvenementEnvoiMultiple.TERMINE, transfertTermine ); envoyeur.addEventListener ( DataEvent.UPLOAD_COMPLETE_DATA, retourServeur ); boutonEnvoyer.addEventListener ( MouseEvent.CLICK, parcoursFichiers ); } private function retourServeur ( pEvt:DataEvent ):void { var variables:URLVariables = new URLVariables ( pEvt.data ); var retour:Number = Number ( variables.resultat ); transfert"; } private function transfertEnCours ( pEvt:ProgressionEvenementEnvoiMultiple ):void { // mise jour de la barre de progression prechargeur.scaleX = pEvt.bytesLoaded / pEvt.bytesTotal; // informations lies au fichier en cours de transfert legende.text = "Transfert en cours : " + pEvt.fichier.name; } private function transfertTermine ( pEvt:EvenementEnvoiMultiple ):void { // indique le nombre de fichiers transfrs legende.text = pEvt.historique.length + " fichiers(s) transfr(s)"; legende.text = retour ? "Transfert russi !" : "Echec de

92 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

} private function parcoursFichiers ( pEvt:MouseEvent ):void { envoyeur.browse(); } } }

Vous remarquerez que nous utilisons la classe URLVariables afin de dcoder la chane encode URL retourne par le serveur. Nous avons achev notre classe EnvoiMultiple qui pourra tre rutilise dans tous les projets ncessitant un transfert de fichiers synchroniss.

A retenir
La classe FileReferenceList envoie tous les fichiers en mme temps et ne diffuse pas dvnement appropri lorsque tous les fichiers ont t transfrs. Afin de corriger EnvoiMultiple. cela, nous avons cr une classe

Aller plus loin


Souvenez-vous, lors du chapitre 10 intitul Diffusion dvnements personnaliss nous avions entam la conception dune classe ChargeurXML pouvant charger un flux XML et diffuser un vnement Event.COMPLETE :
package { import flash.events.Event; import flash.events.EventDispatcher; import flash.net.URLRequest; public class ChargeurXML extends EventDispatcher { public function ChargeurXML () { } public function charge ( pRequete:URLRequest ):void

93 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ } public function chargementTermine ( pEvt:Event ):void { } } }

Nous

URLoader interne :
{ import import import import import import import import

allons

la

complter

en

ajoutant

crant

un

objet

package org.bytearray.xml

flash.events.Event; flash.events.IOErrorEvent; flash.events.HTTPStatusEvent; flash.events.SecurityErrorEvent; flash.events.EventDispatcher; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.net.URLRequest;

public class ChargeurXML extends EventDispatcher { private var chargeur:URLLoader; private var fluxXML:XML; public function ChargeurXML () { chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.TEXT; chargeur.addEventListener chargeur.addEventListener redirigeEvenement ); chargeur.addEventListener chargeur.addEventListener redirigeEvenement ); chargeur.addEventListener redirigeEvenement ); chargeur.addEventListener redirigeEvenement ); } private function redirigeEvenement ( pEvt:Event ):void { ( Event.OPEN, redirigeEvenement ); ( ProgressEvent.PROGRESS, ( Event.COMPLETE, chargementTermine ); ( HTTPStatusEvent.HTTP_STATUS, ( IOErrorEvent.IO_ERROR, ( SecurityErrorEvent.SECURITY_ERROR,

94 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

dispatchEvent ( pEvt ); } public function charge ( pRequete:URLRequest ):void { chargeur.load ( pRequete ); } private function chargementTermine ( pEvt:Event ):void { try { fluxXML = new XML ( pEvt.target.data ); } catch ( pErreur:Error ) { trace (pErreur); } dispatchEvent ( pEvt ); } public function get donnees ( ):XML { return fluxXML; } } }

Une fois le fichier XML charg nous diffusons un vnement Event.COMPLETE. Ainsi, le code permettant de charger un fichier XML est extrmement rduit :
package org.bytearray.document { import import import import org.bytearray.abstrait.ApplicationDefaut; org.bytearray.xml.ChargeurXML; flash.events.Event; flash.net.URLRequest;

public class Document extends ApplicationDefaut {

95 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

public function Document () { var chargeur:ChargeurXML = new ChargeurXML (); chargeur.charge ( new URLRequest ("donnees.xml") ); chargeur.addEventListener ( Event.COMPLETE, chargementTermine ); } private function chargementTermine ( pEvt:Event ):void { /* affiche : <MENU> <BOUTON legende="Accueil" couleur="0x887400" vitesse="1" url="accueil.swf"/> <BOUTON legende="Photos" couleur="0x005587" vitesse="1" url="photos.swf"/> <BOUTON legende="Blog" couleur="0x125874" vitesse="1" url="blog.swf"/> <BOUTON legende="Liens" couleur="0x59CCAA" vitesse="1" url="liens.swf"/> <BOUTON legende="Forum" couleur="0xEE44AA" vitesse="1" url="forum.swf"/> </MENU> */ trace( pEvt.target.donnees ); } } }

La classe ChargeurXML peut ainsi tre utilise dans de nombreux projets ayant recourt au XML. Si un matin, un membre de votre quipe se plaint de la mise en cache des XML par le navigateur. Assurez-lui, quun systme anti-cache peut tre ajout la classe ChargeurXML :
package org.bytearray.xml { import import import import import import import import import import flash.events.Event; flash.events.IOErrorEvent; flash.events.HTTPStatusEvent; flash.events.ProgressEvent; flash.events.SecurityErrorEvent; flash.events.EventDispatcher; flash.net.URLLoader; flash.net.URLLoaderDataFormat; flash.net.URLRequest; flash.net.URLRequestHeader;

96 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

public class ChargeurXML extends EventDispatcher { private var chargeur:URLLoader; private var fluxXML:XML; private var antiCache:Boolean; public function ChargeurXML ( pAntiCache:Boolean=false ) { antiCache = pAntiCache; chargeur = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.TEXT; chargeur.addEventListener chargeur.addEventListener redirigeEvenement ); chargeur.addEventListener chargeur.addEventListener redirigeEvenement ); chargeur.addEventListener redirigeEvenement ); chargeur.addEventListener redirigeEvenement ); } public function charge ( pRequete:URLRequest ):void { if ( antiCache ) pRequete.requestHeaders.push ( new URLRequestHeader ("pragma", "no-cache") ); chargeur.load ( pRequete ); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function chargementTermine ( pEvt:Event ):void { try { fluxXML = new XML ( pEvt.target.data ); } catch ( pErreur:Error ) ( Event.OPEN, redirigeEvenement ); ( ProgressEvent.PROGRESS, ( Event.COMPLETE, chargementTermine ); ( HTTPStatusEvent.HTTP_STATUS, ( IOErrorEvent.IO_ERROR, ( SecurityErrorEvent.SECURITY_ERROR,

97 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 14 Chargement et envoi de donnes version 0.1.1

{ trace (pErreur); } dispatchEvent ( pEvt ); } public function get donnees ( ):XML { return fluxXML; } } }

Grace la classe URLRequestHeader, nous avons modifi lentte HTTP du lecteur Flash lors du chargement du fichier XML et empch sa mise en cache par le navigateur. Ainsi chaque dveloppeur souhaitant tirer profit de cette fonctionnalit devra simplement le prciser lors de linstanciation de lobjet ChargeurXML :
// cre un objet ChargeurXML en prcisant de jamais mettre en cache les fichiers XML chargs var chargeur:ChargeurXML = new ChargeurXML ( true );

Dautres enttes HTTP peuvent tre utilises, nous reviendrons sur la classe URLRequestHeader au cours du chapitre 20 intitul ByteArray.

A retenir
La classe URLRequestHeader permet de modifier les enttes HTTP du lecteur Flash.

Dans le prochain chapitre, nous explorerons les nouveauts apportes par ActionScript 3 en matire dchanges entre le lecteur Flash et les diffrents navigateurs.

98 / 98
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

15
Communication Externe

CONTENEUR ET CONTENU ............................................................................... 1 PASSER DES VARIABLES ................................................................................... 2 INTGRATION PAR JAVASCRIPT .............................................................................. 3 LA PROPRIETE PARAMETERS ................................................................................... 5 LES FLASHVARS ................................................................................................... 10 PASSER DES VARIABLES DYNAMIQUES .................................................................. 11 ACCDER FACILEMENT AUX FLASHVARS ............................................................. 14 APPELER UNE FONCTION ............................................................................... 17 LAPI EXTERNALINTERFACE ........................................................................ 18 APPELER UNE FONCTION EXTERNE DEPUIS ACTIONSCRIPT ................................... 22 APPELER UNE FONCTION ACTIONSCRIPT DEPUIS LE CONTENEUR .......................... 27 COMMUNICATION ET SECURITE ............................................................................. 30

Conteneur et contenu
Lors du dploiement dune application Flash sur Internet, nous intgrons gnralement celle-ci au sein dune page conteneur HTML interprte par diffrents navigateurs tels Internet Explorer, Firefox, Opera ou autres. Dans dautres situations, celle-ci peut tre intgre au sein dune application bureau dveloppe en C#, C++ ou Java. Bien quautonome, lanimation lue par le lecteur Flash peut ncessiter des informations provenant de lapplication conteneur dans le cas de dveloppement dapplications dynamiques. Avant de sintresser la notion dchanges, il convient de dfinir dans un premier temps les deux acteurs concerns par une ventuelle communication :

1 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Le conteneur : Une application contenant le SWF. Il peut sagir dune page HTML ou dune application C#, C++ ou autres. Lapplication : Il sagit du SWF lu par le lecteur Flash.

Au cours de ce chapitre, nous allons nous intresser aux diffrentes techniques de communication possible entre ces deux acteurs tout en se proccupant des ventuelles restrictions de scurit pouvant intervenir.

Passer des variables


Afin dintroduire la notion dchanges entre ces deux acteurs, nous allons nous intresser dans un premier temps au passage de simples variables encodes au format URL. Afin de bien comprendre lintrt de tels changes, mettons nous en situation avec le cas suivant : Vous devez dvelopper un lecteur vido qui puisse charger diffrentes vidos dynamiques pour un portail destin aux cinphiles. Afin de rendre le lecteur le plus dynamique possible, il serait judicieux de pouvoir spcifier en dehors de lanimation quelle bande-annonce jouer. Vous pensez dans un premier temps la cration dun fichier XML contenant le nom de la vido jouer. Certes, cette technique fonctionnerait mais nest pas optimise. Le fichier XML devrait tre dupliqu pour chaque lecteur vido, rendant le dveloppement rigide et redondant. La solution la plus efficace consiste passer dynamiquement le nom de la vido jouer depuis la page navigateur contenant le lecteur vido. Pour cela, deux techniques existent : La premire consiste passer des variables encodes URL aprs le nom du fichier au sein du paramtre movie de la balise object et src de la balise embed :
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab #version=9,0,0,0" width="550" height="400" id="chap-15-variables" align="middle"> <param name="allowScriptAccess" value="sameDomain" /> <param name="allowFullScreen" value="false" /> <param name="movie" value="chap-15-variables.swf?maVar1=15&maVar2=50" /><param name="quality" value="high" /><param name="bgcolor" value="#ffffff" /> <embed src="chap-15-variables.swf?maVar1=15&maVar2=50" quality="high" bgcolor="#ffffff" width="550" height="400" name="chap-15-variables" align="middle" allowScriptAccess="sameDomain" allowFullScreen="false"

2 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> </object>

Automatiquement, les variables maVar1 et maVar2 sont accessibles au sein du SWF intitul chap-15-variables.swf. Cette technique est utilise lorsque lanimation est intgre manuellement au sein dune page conteneur. Dans le cas de Flash CS3 un script JavaScript est gnr automatiquement lors de la publication afin dintgrer dynamiquement lanimation. Flash CS3 intgre un mcanisme dintgration de lanimation Flash par script JavaScript permettant de ne pas avoir demander lautorisation de lutilisateur afin de lire du contenu Flash. Cette astuce vite davoir activer lanimation en cliquant dessus au sein dInternet Explorer. En avril 2007, la socit Eolas avait obtenu de Microsoft la modification dInternet Explorer visant demander obligatoirement lactivation de lutilisateur lorsquun contenu ActiveX tait intgr dans une page. Si la page contenant lanimation est directement gnre depuis Flash CS3, la technique visant intgrer les variables directement au sein des balises object et embed ne fonctionnera pas. Il nous faut passer les variables depuis la fonction JavaScript AC_FL_RunContent.

A retenir
Lutilisation de variables encodes au format URL est le moyen le plus simple de passer des donnes au SWF. Lorsque la page conteneur est gnre depuis Flash CS3, lintgration du SWF dans la page est gre par la fonction JavaScript AC_FL_RunContent.

Intgration par JavaScript


Lorsque la page conteneur est directement gnre depuis Flash CS3, le script suivant est intgr la page HTML afin dintgrer le SWF :
<script language="javascript"> if (AC_FL_RunContent == 0) { alert("Cette page ncessite le fichier AC_RunActiveContent.js."); } else { AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version= 9,0,0,0',

3 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

} </script>

'width', '550', 'height', '400', 'src', 'chap-15-variables', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'chap-15-variables', 'bgcolor', '#ffffff', 'name', 'chap-15-variables', 'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','sameDomain', 'movie', 'chap-15-variables', 'salign', '' );

La fonction AC_FL_RunContent intgre lanimation en ajoutant chaque attribut pass en paramtre. Ainsi, pour passer les variables quivalentes lexemple prcdent, nous passons les variables au sein du paramtre movie de la fonction AC_FL_RunContent :
AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version= 9,0,0,0', 'width', '550', 'height', '400', 'src', 'chap-15-external-interface', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'monApplication', 'bgcolor', '#ffffff', 'name', 'monApplication', 'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','sameDomain', 'movie', 'chap-15-variables?maVar1=15&maVar2=50', 'salign', '' );

Dans les prcdentes versions dActionScript, les variables taient directement places sur le scnario principal _root.

4 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

En ActionScript 3, afin dviter les conflits de variables, celles-ci ne sont plus disponibles partir du scnario principal mais depuis la proprit parameters de lobjet LoaderInfo du scnario du SWF.

A retenir
Dans le cas de lutilisation de la fonction AC_FL_RunContent, les variables doivent tre passes au sein du paramtre movie.

La proprit parameters
Comme nous lavons vu au cours du chapitre 13 intitul Chargement de contenu. Chaque SWF contient un objet LoaderInfo associ, contenant diffrentes informations. Dans le cas de variables passes par le navigateur, les variables passes dynamiquement deviennent des proprits de lobjet rfrenc par la proprit parameters de lobjet LoaderInfo. Dans le code suivant nous dfinissons la classe de document suivante, afin daccder aux deux variables passes :
package org.bytearray.document { import flash.text.TextField; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { var infos:Object = root.loaderInfo.parameters; infosVariables.appendText ( infos.maVar1 + "\n" ); infosVariables.appendText ( infos.maVar2 + "\n" ); } } }

Un champ texte infosVariables est pos sur le scnario principal afin dafficher les variables rceptionnes. Lorsque des variables sont passes au lecteur Flash, les variables sont copies au sein de la proprit parameters de lobjet LoaderInfo. Vous remarquez que nous accdons aux variables comme des proprits de lobjet parameters. 5 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Il est important de noter que les variables sont copies au sein de lobjet LoaderInfo avant lexcution du code. Celles-ci sont donc accessibles instantanment. Dans le code prcdent nous ciblons de manire explicite la proprit loaderInfo du scnario principal, laide de la proprit root. Dans un contexte de classe du document, linstance en cours fait rfrence au scnario principal, nous pouvons donc directement accder lobjet LoaderInfo de la manire suivante :
package org.bytearray.document { import flash.text.TextField; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { var infos:Object = loaderInfo.parameters; infosVariables.appendText ( infos.maVar1 + "\n" ); infosVariables.appendText ( infos.maVar2 + "\n" ); } } }

Au cas o nous ne connaissons pas le nom des variables passes, nous pouvons les numrer laide dune boucle for in :
package org.bytearray.document { import flash.text.TextField; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { var infos:Object = loaderInfo.parameters; for ( var p:String in infos )

6 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

{ infosVariables.appendText ( p + "\n" ); } } } }

Si aucune variable nest passe, la boucle nest pas excute, aucun contenu nest affich dans le champ texte infosVariables. Lors du test de lanimation au sein de Flash CS3, lanimation est lue au sein du lecteur autonome. Aucune variable ne peut donc tre donc passe. De la mme manire, si les variables sont passes dune manire non approprie, nous devons le grer. Il est donc important de sassurer que les variables sont bien dfinies, pour cela nous crivons le code suivant :
package org.bytearray.document { import flash.text.TextField; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public function Document () { var infos:Object = loaderInfo.parameters; if ( infos.maVar1 != undefined && infos.maVar2 != undefined ) { infosVariables.appendText ( infos.maVar1 + "\n" ); infosVariables.appendText ( infos.maVar2 + "\n" ); } else infosVariables.appendText ( "Les variables ne sont pas disponibles" ); } } }

La figure 15-1 illustre le rsultat :

7 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Figure 15-1. Variables non accessibles. Si nous testons lanimation au sein dune page passant correctement les variables, nous obtenons le rsultat illustr en figure 15-2 :

Figure 15-2. Variables correctement passes. Il est important de noter que les variables sont copies en tant que chane de caractres. Ainsi, si nous souhaitons manipuler les variables afin de faire des oprations mathmatiques nous devons au pralable le convertir en nombres. Dans le code suivant, nous tentons dadditionner les deux variables, nous obtenons alors une simple concatnation :
infosVariables.appendText ( infos.maVar1 + infos.maVar2 );

8 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

La figure 15-3 illustre le rsultat :

Figure 15-3. Variables concatnes. Afin dadditionner correctement les variables nous devons au pralable les convertir en tant que nombre :
infosVariables.appendText ( String ( Number ( infos.maVar1 ) + Number ( infos.maVar2 ) ) );

La figure 15-4 illustre le rsultat :

Figure 15-4. Variables additionnes. Cette technique offre en revanche une limitation de volume de donnes passes propre chaque navigateur. Depuis le lecteur 6, nous avons la possibilit de passer ces mmes variables laide dun nouvel attribut des balises object et embed appel flashvars. De plus, le passage de variables sans lutilisation de lattribut flashvars force le rechargement du SWF empchant donc sa mise en cache. Notons que si les variables passes sont identiques plusieurs reprises, il ny a pas de rechargement forc.

A retenir

9 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Les variables passes sont copies au sein de lobjet retourn par la proprit parameters de lobjet LoaderInfo du scnario principal. Les variables sont copies en tant que chanes de caractres. Il convient de convertir les variables en nombre avant deffectuer des oprations mathmatiques sur chacune delles.

Les FlashVars
Lutilisation des FlashVars est recommande depuis Flash MX (Flash 6). Pour passer des variables grce lattribut flashvars, il nous suffit dajouter lattribut flashvars en paramtre de la fonction AC_FL_RunContent :
AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version= 9,0,0,0', 'width', '550', 'height', '400', 'flashvars', 'maVar1=15&maVar2=50', 'src', 'chap-15-external-interface', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'monApplication', 'bgcolor', '#ffffff', 'name', 'monApplication', 'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','sameDomain', 'movie', 'chap-15-external-interface', 'salign', '' );

Si nous testons nouveau lanimation les variables sont accessibles de la mme manire.

A retenir

10 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Lutilisation des FlashVars est recommande depuis le lecteur Flash 6. Les balises object et embed possdent un attribut flashvars permettant un passage de variables optimis.

Passer des variables dynamiques


Afin daller plus loin, nous allons rcuprer dynamiquement les variables passes au sein de lurl de lanimation et les rcuprer dans notre application. Dans le cas dun site Flash traditionnel, nous souhaitons pouvoir rcuprer les variables passes en GET depuis lurl suivante :
http://www.monsite.org/index.php?rubrique=4&langue=fr

Le lecteur Flash na pas la capacit de les rcuprer de manire autonome, nous pouvons utiliser pour cela un script JavaScript ou autre qui se chargera de les passer au SWF grce lattribut flashvars. Pour cela, nous ajoutons au sein de la page conteneur une fonction JavaScript nomme recupVariables :
<script language="javascript"> var position = window.location.href.indexOf ("?")+1; var chaine = window.location.href.substr ( position ); if (AC_FL_RunContent == 0) { alert("Cette page ncessite le fichier AC_RunActiveContent.js."); } else { AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version= 9,0,0,0', 'width', '550', 'height', '400', 'flashvars', chaine, 'src', 'chap-15-flashvars-dynamique', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'chap-15-flashvars-dynamique', 'bgcolor', '#ffffff', 'name', 'chap-15-flashvars-dynamique', 'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','sameDomain', 'movie', 'chap-15-flashvars-dynamique', 'salign', '' ); //end AC code

11 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

} </script>

Nous passons dynamiquement les flashvars en reconstituant une chane encode URL laide des variables rcupres depuis lURL. Afin de tester si les variables sont bien passes, nous accdons notre animation en ajoutant les variables encodes URL en fin dadresse :
http://www.monsite.org/index.php?rubrique=4&langue=fr

Afin dviter les erreurs nous testons si les variables sont dfinies :
package org.bytearray.document { import flash.text.TextField; import flash.display.MovieClip; public class Document extends MovieClip { public function Document () { var infos:Object = loaderInfo.parameters; if ( (infos.rubrique != undefined && infos.langue != undefined) ) { "\n" ); "\n" ); infosVariables.appendText ( "langue = " + infos.langue + infosVariables.appendText ( "rubrique = " + infos.rubrique +

} else infosVariables.appendText ( "Les variables ne sont pas disponibles" ); } } }

En testant notre animation, nous voyons que les variables sont correctement passes. La figure 15-4 illustre le rsultat :

12 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Figure 15-5. FlashVars dynamiques. Souvenez-vous, les variables sont passes en tant que chane de caractres. Ainsi, si aucune variable nest passe dans lurl, la valeur des variables rubrique et langue seront bien diffrentes de undefined, car elles auront pour valeur "undefined" en tant que chane. Pour grer cela, nous ajoutons la condition suivante :
package org.bytearray.document { import flash.text.TextField; import oflash.display.MovieClip; public class Document extends MovieClip { public function Document () { var infos:Object = loaderInfo.parameters; && if ( (infos.rubrique != undefined && infos.langue != undefined) (infos.rubrique != "undefined" && infos.langue != "undefined") ) { "\n" ); "\n" ); infosVariables.appendText ( "langue = " + infos.langue + infosVariables.appendText ( "rubrique = " + infos.rubrique +

} else infosVariables.appendText ( "Les variables ne sont pas disponibles" ); } }

13 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Ainsi, si nous accdons lanimation en omettant les variables au sein de lURL, nous obtenons le message illustr en figure 15-6 :

Figure 15-6. Message derreur. Dans un contexte rel, il est important de sassurer que chaque SWF composant le site puisse accder sans problmes aux variables passes au SWF principal. Dans la partie suivante, nous allons dcouvrir comment faciliter le passage de celles-ci au reste de lapplication.

A retenir
A laide dun script intgr la page conteneur, nous pouvons passer des variables dynamiques lanimation.

Accder facilement aux FlashVars


Nous savons que les variables passes lanimation sont accessibles depuis lobjet LoaderInfo du scnario principal. Afin de garantir un accs simplifi de ces variables aux diffrents SWF composant notre application, nous allons diffuser un vnement personnalis par lintermdiaire de la proprit sharedEvents de lobjet LoaderInfo. Souvenez, vous au cours du chapitre 13 intitul Chargement de contenu, nous avons vu que la proprit sharedEvents tait une excellent passerelle de communication entre le SWF chargeant et charg. Nous
InfosEvenement charge de transporter les variables :
package org.bytearray.evenements { import flash.display.LoaderInfo; import flash.events.Event;

dfinissons

dans

un

premier

temps

une

classe

14 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

public class InfosEvenement extends Event { public static const INFOS:String = "infos"; public var infos:LoaderInfo; ) public function InfosEvenement ( pType:String, pLoaderInfo:LoaderInfo { super ( pType, false, false ); infos = pLoaderInfo; } public override function clone ( ):Event { return new InfosEvenement ( type, infos ); } } }

Puis nous modifions la classe de document du SWF principal afin de charger dynamiquement le SWF :
package org.bytearray.document { import import import import import flash.events.Event; flash.net.URLRequest; flash.display.Loader; flash.display.MovieClip; org.bytearray.evenements.InfosEvenement;

public class Document extends MovieClip { private var loader:Loader; public function Document () { loader = new Loader(); loader.contentLoaderInfo.addEventListener ( Event.COMPLETE, onComplete ); loader.load ( new URLRequest ("chap-15-animation.swf") );

15 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

addChild ( loader ); } private function onComplete ( pEvt:Event ):void { pEvt.target.sharedEvents.dispatchEvent ( new InfosEvenement ( InfosEvenement.INFOS, loaderInfo ) ); } } }

Afin de rcuprer les variables envoyes, nous associons la classe de document suivante au SWF charg :
package org.bytearray.rubriques { import import import import flash.events.Event; flash.text.TextField; flash.display.MovieClip; org.bytearray.abstrait.ApplicationDefaut;

public class Rubrique extends ApplicationDefaut { public var infosVariables:TextField; public function Rubrique () { infos ); } function infos ( pEvt:InfosEvenement ):void { var infos:Object = pEvt.infos.parameters; && if ( (infos.rubrique != undefined && infos.langue != undefined) (infos.rubrique != "undefined" && infos.langue != "undefined") ) { infosVariables.appendText ( infos.rubrique + "\n" ); infosVariables.appendText ( infos.langue + "\n" ); } else infosVariables.appendText ( "Les variables ne sont pas disponibles" ); loaderInfo.sharedEvents.addEventListener ( InfosEvenement.INFOS,

16 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

} } }

A la rception des variables la mthode couteur infos est dclenche est procde un affichage des variables si celles-ci sont correctement dfinies.

A retenir
Afin dassurer un accs simplifi aux variables, il est intressant de diffuser un vnement depuis le SWF principal aux SWF chargs.

Appeler une fonction


La communication entre lapplication conteneur et le lecteur ne se limite pas un simple passage de variables encodes URL. Dans le cas dune page HTML conteneur, il est possible dappeler une fonction JavaScript depuis ActionScript laide de la fonction navigateToURL. Dans le code suivant, nous ouvrons une fentre alert au sein de la page conteneur lorsque nous cliquons sur la scne :
package org.bytearray.document { import import import import import import flash.external.ExternalInterface; flash.text.TextField; flash.events.MouseEvent; flash.net.navigateToURL; flash.net.URLRequest; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { public function Document () { stage.addEventListener ( MouseEvent.CLICK, click ); } private function click ( pEvt:MouseEvent ):void { navigateToURL ( new URLRequest ("javascript: alert('Un message du lecteur Flash !')"), "_self");

17 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

} } }

En prfixant le nom de la fonction de linstruction javascript: il est aussi possible dappeler nimporte quelle fonction JavaScript depuis ActionScript. Dans
fonctionJavaScript :

le

code

suivant,

nous

appelons

une

fonction

navigateToURL ( new URLRequest ("javascript: fonctionJavaScript()" ), "_self");

La fonction navigateToURL est gnralement utilise au sein dune page conteneur afin de lancer le gestionnaire de mail configur sur la machine :
private function click ( pEvt:MouseEvent ):void { navigateToURL ( new URLRequest ("mailto:bob@groove.com") ); }

Bien quefficace, cette technique ne permet pas de rceptionner le retour dune fonction JavaScript ou de passer des types prcis en paramtres tels Number ou Boolean.

LAPI ExternalInterface
La communication entre lapplication conteneur et le lecteur ne se limite pas un simple passage de variables encodes URL. Il est aussi possible dappeler diffrentes mthodes dfinies au sein du conteneur depuis ActionScript et inversement. Cette fonctionnalit tait assure auparavant par la mthode fscommand qui se trouve dsormais dans le paquetage flash.system. Lutilisation de la fonction fscommand est aujourdhui dprcie au profit de lAPI ExternalInterface. Afin de pouvoir utiliser celle-ci dans un contexte de navigateur, celuici doit prendre en charge les contrles ActiveX ou lAPI NPRuntime. Voici un tableau rcapitulatif des diffrents navigateurs compatible fonctionnant avec lAPI ExternalInterface : Navigateur Systme d'exploitation 18 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Systme d'exploitation

Chapitre 15 Communication externe version 0.1.1

Internet Explorer 5.0 et versions ultrieures Netscape 8.0 et versions ultrieures Mozilla 1.7.5 et versions ultrieures Firefox 1.0 et versions ultrieures Safari 1.3 et versions ultrieures

Windows Windows Windows Windows Macintosh Macintosh Macintosh Macintosh

Tableau 1. Navigateurs compatibles. Voici en dtail les trois proprits de la classe ExternalInterface :
available : Renvoie lid de la balise object sous Internet Explorer, ou lattribut name de la balise embed sous Nestcape, Firefox, Opera ou autres. marshallExceptions : Cette proprit dfinit, si les deux acteurs peuvent recevoir les exceptions de chacun. objectID : Permet de savoir si le conteneur est compatible avec lAPI ExternalInterface.

Dans le code suivant, nous testons si lapplication volue dans un contexte compatible avec lAPI ExternalInterface :
package org.bytearray.document { import flash.external.ExternalInterface; import flash.text.TextField; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public var compatible:TextField; public function Document () { compatible.text = String ( ExternalInterface.available ); } } }

19 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Il convient de toujours tester si le contexte actuel permet lutilisation de lAPI ExternalInterface. Pour cela, nous testons la proprit available avant toute tentative de communication. La proprit objectID peut tre aussi utilise afin de dterminer si lapplication volue au sein du navigateur ou non. Ainsi nous pourrions ajouter une proprit navigateur la classe ApplicationDefaut permettant de savoir si lanimation volue au sein du navigateur ou non :
package org.bytearray.abstrait { import import import import flash.display.MovieClip; flash.events.Event; flash.display.Stage; flash.external.ExternalInterface;

public class ApplicationDefaut extends MovieClip { public public public public public static static static static static var var var var var globalStage:Stage; enLigne:Boolean; navigateur:Boolean; stage:Stage; root:ApplicationDefaut;

public function ApplicationDefaut () { ApplicationDefaut.root = this; addEventListener ( Event.ADDED_TO_STAGE, activation ); loaderInfo.addEventListener ( Event.INIT, init ); null; } private function activation ( pEvt:Event ):void { ApplicationDefaut.globalStage = stage; } private function init ( pEvt:Event ):void { ApplicationDefaut.enLigne = pEvt.target.url.match ( new RegExp ("^http://") ) != null; ApplicationDefaut.navigateur = ExternalInterface.objectID !=

20 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

} } }

Puis, dans nimporte quelle classe de document hritant de la classe ApplicationDefaut, nous pouvons savoir facilement si lanimation volue ou non au sein du navigateur :
package org.bytearray.document { import flash.text.TextField; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public var compatible:TextField; public function Document () { if ( ApplicationDefaut.navigateur ) compatible.text = "L'animation est lue au sein d'une page web"; else compatible.text = "L'animation est lue hors du navigateur"; } } }

Si nous testons lapplication hors du navigateur :

21 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Figure 15-7. Dtection du contexte. A linverse, si lanimation est lue au sein du navigateur, nous le dtectons comme lillustre la figure 15-8 :

Figure 15-8. Dtection du contexte. Nous allons prsent nous attarder sur lappel de fonctions externe depuis ActionScript.

A retenir
LAPI ExternalInterface est recommande communication entre ActionScript et JavaScript. ExternalInterface remplace les prcdentes fscommand, callFrame et callLabel. pour la fonctions

Appeler une fonction externe depuis ActionScript


Deux mthodes sont disponibles sur la classe ExternalInterface, voici le dtail de chacune dentre elles : 22 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

addCallBack : Enregistre un alias pour une fonction ActionScript. Lalias est ensuite utilis depuis la fonction externe pour excuter la fonction ActionScript. call : Excute la fonction passe en paramtre au sein du conteneur.

Dans la partie suivante, nous allons nous intresser lappel dune mthode JavaScript depuis ActionScript. La figure 15-9 illustre le concept :

Figure 15-09. Mthode statique call. Afin dexcuter une mthode au sein de lapplication conteneur, nous utilisons la mthode statique call de la classe ExternalInterface dont voici la signature :
public static function call(functionName:String, ... arguments):*

Le premier paramtre concerne le nom de la fonction excuter. Les paramtres suivants sont passs en paramtre la fonction excute. Nous allons commencer par un exemple simple, en appelant la mthode direBonjour lorsque nous cliquons sur le bouton executeFonction :
package org.bytearray.document { import flash.display.SimpleButton; import flash.events.MouseEvent; import flash.external.ExternalInterface;

23 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public var executeFonction:SimpleButton; public function Document () { executeFonction.addEventListener ( MouseEvent.CLICK, declencheAppel ); } private function declencheAppel ( pEvt:MouseEvent ):void { if ( ExternalInterface.available ) { ExternalInterface.call ("direBonjour"); } } } }

La fonction JavaScript direBonjour est dfinie au sein de notre page conteneur :


function direBonjour ( ) { alert ("Bonjour !"); }

Lorsque nous cliquons sur le bouton, la fonction est dclenche et ouvre une fentre dalerte comme lillustre la figure 15-9 :

Figure 15-10. Dtection du contexte.

24 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Nous pouvons passer dynamiquement des paramtres depuis Flash en spcifiant les paramtres la mthode call :
ExternalInterface.call ("direBonjour", "Message de Flash !");

Nous modifions la fonction direBonjour afin daccepter un message en paramtre :


function direBonjour ( pMessage ) { alert (pMessage); }

La figure 15-11 illustre le rsultat :

Figure 15-11. Dtection du contexte. La fonction JavaScript peut retourner une valeur, la valeur sera rcupre lappel de la mthode call. Si nous modifions la fonction JavaScript afin que celle-ci renvoie le total des deux paramtres passs :
function calculTotal ( p1, p2 ) { return p1 + p2; }

Nous affichons dans le champ texte total, le retour de lappel de la mthode call :
package org.bytearray.document { import import import import import flash.display.SimpleButton; flash.events.MouseEvent; flash.external.ExternalInterface; flash.text.TextField; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut {

25 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

public var executeFonction:SimpleButton; public var total:TextField; public function Document () { executeFonction.addEventListener ( MouseEvent.CLICK, declencheAppel ); } private function declencheAppel ( pEvt:MouseEvent ):void { if ( ExternalInterface.available ) { total.text = ExternalInterface.call ("calculTotal", 10, 15); } } } }

La figure 15-12 illustre le rsultat :

Figure 15-12. Dtection du contexte. Le illustre un des avantages de lAPI ExternalInterface concernant le passage de donnes types. code prcdent

Contrairement la fonction fscommand ou navigateToURL, nous ne sommes pas limits au passage de chanes de caractres avec lAPI ExternalInterface. Nous pouvons passer entre JavaScript et ActionScript des donnes de type Number, String, et Boolean. Nous allons maintenant communiquer dans lautre sens en appelant depuis lapplication conteneur une fonction ActionScript.

A retenir
26 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

La mthode call permet dexcuter une fonction dfinie au sein du conteneur depuis ActionScript. Dans le cas de lutilisation dune fonction JavaScript, des types comme Number, Boolean et String peuvent tre changs.

Appeler une fonction ActionScript depuis le conteneur


De la mme manire, nous pouvons dclencher une fonction ActionScript depuis une fonction dfinie au sein du conteneur. Afin denregistrer une fonction ActionScript auprs dune fonction du conteneur, nous utilisons la mthode addCallback, dont voici la signature :
public static function addCallback(functionName:String, closure:Function):void

La figure 15-13 illustre lide :

Figure 15-13. Mthode statique addCallback. Voici le dtail des deux paramtres de la mthode addCallback :
functionName : Il sagit de lidentifiant de la fonction depuis la page conteneur. Nous devrons utiliser cette chane pour excuter la fonction passe au sein du paramtre closure. closure : La fonction dclencher depuis la page conteneur.

Dans le code suivant, nous enregistrons une fonction maFunction laide de lalias aliasFonction : 27 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

package org.bytearray.document { import flash.external.ExternalInterface; import flash.text.TextField; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { public var messageJavascript:TextField; public function Document () { // enregistre l'alias "aliasFonction" vers la mthode maFunction ExternalInterface.addCallback ( "aliasFonction", maFunction ); } private function maFunction ( pMessage:String ):void { // nous affichons le message reu depuis JavaScript messageJavascript.text = pMessage; } } }

Afin de pouvoir appeler une fonction ActionScript nous devons ensuite donner un nom notre application grce aux attributs id et name :
AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version= 9,0,0,0', 'width', '550', 'height', '400', 'src', 'chap-15-external-interface', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'monApplication', 'bgcolor', '#ffffff', 'name', 'monApplication', 'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','sameDomain', 'movie', 'chap-15-external-interface',

28 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

'salign', '' );

Puis au sein du conteneur, nous appelons la fonction maFunction grce lalias aliasFonction :
function recupAnimation ( pAnim ) { if (navigator.appName.indexOf("Microsoft") != -1) return window[pAnim]; else return document[pAnim]; } function declencheFonctionActionScript ( ) { recupAnimation("monApplication").aliasFonction("Message de JavaScript !"); }

La fonction recupAnimation permet de cibler lanimation selon le type de navigateur utilis. Nous passons le nom de lanimation celleci, qui nous renvoie lanimation intgre dans la page, sur laquelle nous appelons la fonction grce lalias pass la mthode addCallback. Il
declencheFonctionActionScript lors du clic bouton :

ne

nous

reste

plus

qu

dclencher

la

fonction

<input type="button" name="monBouton" value="Excute fonction ActionScript" onClick=" declencheFonctionActionScript()">

La figure 15-14 illustre le rsultat :

Figure 15-14. Mthode ActionScript excute. Comme vous pouvez limaginer, la notion de communication entre le lecteur Flash et lapplication conteneur est soumise des restrictions de scurit.

29 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Nous allons nous intresser dans la partie suivante, aux diffrentes restrictions possibles.

A retenir
La mthode addCallback permet dexcuter une fonction ActionScript depuis lapplication conteneur. Dans le cas de lutilisation dune fonction JavaScript, des types comme Number, Boolean et String peuvent tre changs.

Communication et scurit
Afin que les deux acteurs puissent communiquer, nous devons nous intresser la valeur passe lattribut allowScriptAccess. La
AC_FL_RunContent intgre un paramtre allowScriptAccess rgissant la communication entre lapplication

fonction

conteneur et le lecteur Flash :

AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version= 9,0,0,0', 'width', '550', 'height', '400', 'src', 'chap-15-external-interface', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'monApplication', 'bgcolor', '#ffffff', 'name', 'monApplication', 'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','sameDomain', 'movie', 'chap-15-external-interface', 'salign', '' );

Voici les trois valeurs possibles de lattribut allowScriptAccess :


always : La communication entre le lapplication conteneur et ActionScript est toujours possible. sameDomain : La communication entre lapplication conteneur et ActionScript est possible, uniquement si la page conteneur et le SWF voluent dans le mme domaine. never : La communication entre le lapplication conteneur et ActionScript est impossible.

30 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Nous pourrions nous demander dans quels cas lutilisation de lattribut allowScriptAccess serait ncessaire. Imaginons le scnario suivant : Au sein dun forum, les utilisateurs ont la possibilit dintgrer leurs avatars ou signature partir danimations Flash. Certaines dentre elles pourraient scripter la page du forum provoquant alors un dysfonctionnement. Afin de rguler cela, nous pourrions passer la valeur never lattribut allowScriptAccess empchant toute communication entre les pages du forum et les avatars ou signatures. Dans le code suivant, nous passons lattribut allowScriptAccess never :
AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version= 9,0,0,0', 'width', '550', 'height', '400', 'src', 'chap-15-external-interface', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'monApplication', 'bgcolor', '#ffffff', 'name', 'monApplication', 'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','never', 'movie', 'chap-15-external-interface', 'salign', '' );

Si nous tentons dappeler laide de la mthode call une fonction dfinie au sein de la page conteneur, une exception de type SecurityError est leve.

31 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 15 Communication externe version 0.1.1

Figure 15-15. Restriction de scurit. A linverse, pour que la page conteneur puisse scripter un SWF provenant dun domaine diffrent, nous pouvons utiliser la mthode allowDomain de la classe Security au sein du SWF scripter. Pour plus dinformations concernant le modle de scurit du lecteur Flash, reportez vous la partie Modle de scurit du lecteur Flash du chapitre 13 intitul Chargement de contenu.

A retenir
La communication entre le lecteur Flash et lapplication conteneur est soumise au modle de scurit du lecteur Flash. Lattribut allowScriptAccess permet dautoriser ou non le lecteur Flash scripter la page conteneur.

32 / 32
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

16
Le texte

LE TEXTE DANS FLASH ...................................................................................... 1 AFFICHER DU TEXTE DANS FLASH ........................................................................... 2 AFFICHER DU TEXTE EN PROGRAMMANT................................................................. 5 LES TYPES DE CHAMP TEXTE ................................................................................. 13 FORMATAGE DU TEXTE .................................................................................. 15 RENDU HTML ...................................................................................................... 15 LA CLASSE TEXTFORMAT ..................................................................................... 17 ETENDRE LA CLASSE TEXTFIELD .......................................................................... 24 LA CLASSE STYLESHEET ....................................................................................... 30 MODIFIER LE CONTENU DUN CHAMP TEXTE ........................................ 34 REMPLACER DU TEXTE .......................................................................................... 37 LEVENEMENT TEXTEVENT.LINK ............................................................... 39 CHARGER DU CONTENU EXTERNE ............................................................. 44 EXPORTER UNE POLICE DANS LANIMATION ......................................... 49 CHARGER DYNAMIQUEMENT UNE POLICE ............................................................. 53 DETECTER LES COORDONNEES DE TEXTE .............................................. 57 CREER UN EDITEUR DE TEXTE ..................................................................... 68

Le texte dans Flash


La gestion du texte dans le lecteur Flash a souvent t source de discussions au sein de la communaut. Quelque soit le type dapplication produite, lutilisation du texte savre indispensable. Le lecteur Flash 8 a permit lintroduction dun nouveau moteur de rendu du texte appel Saffron. Ce dernier amliorait nettement la lisibilit du texte dans une taille rduite et offrait un meilleur contrle sur le niveau de lissage des polices. 1 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

ActionScript 3 nintgre pas de nouveau moteur de rendu, mais enrichit remarquablement les fonctionnalits offertes par la classe TextField et ouvre de nouvelles possibilits. Trois classes permettent de reprsenter du texte en ActionScript 3 :
flash.text.StaticText : la classe StaticText reprsente les champs texte statiques crs depuis lenvironnement auteur ; flash.text.TextField : la classe TextField reprsente les champs texte dynamiques crs depuis lenvironnement auteur ainsi que la cration de champs texte par programmation ; flash.text.TextSnapShot : la classe TextSnapshot permet de travailler avec le contenu dun champ texte statique cr depuis lenvironnement auteur.

Bien que ces classes ne rsident pas dans le paquetage flash.display, celles-ci hritent de la classe DisplayObject. Nous allons nous intresser au cours de ce chapitre quelques cas pratiques dutilisation du texte qui auraient t difficilement ralisables sans lutilisation dActionScript 3.

Afficher du texte dans Flash


Afin de nous familiariser avec le texte, nous allons crer un simple champ texte statique au sein dun nouveau document comme lillustre la figure 16-1 :

Figure 16-1. Champ texte statique. Il est impossible dattribuer un nom doccurrence un champ texte statique, ActionScript 3 nous permet cependant daccder au champ texte en utilisant la mthode getChildAt :
// affiche : [object StaticText] trace( getChildAt ( 0 ) );

Lorsquun champ texte statique est cr depuis lenvironnement auteur, celui-ci est de type StaticText. Cette classe dfinit une seule et unique proprit text en lecture seule permettant de rcuprer le contenu du champ :
var champTexte:StaticText = StaticText ( getChildAt ( 0 ) );

2 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

// affiche : Contenu statique trace( champTexte.text );

Si nous tentons dinstancier la classe StaticText :


var monChamp:StaticText = new StaticText();

Une erreur lexcution de type ArgumentError est leve :


ArgumentError: Error #2012: Impossible d'instancier la classe StaticText

Il est important de noter que seul lenvironnement auteur de Flash CS3 permet la cration dinstance de la classe StaticText. Bien que celle-ci ne dfinisse quune seule proprit, la classe StaticText hrite de la classe DisplayObject. Il est donc possible de modifier la prsentation du champ. Dans le code suivant nous modifions la rotation du texte, ainsi que lopacit du champ :
var champTexte:StaticText = StaticText ( getChildAt ( 0 ) ); champTexte.rotation = 20; champTexte.alpha = .5;

La figure 16-2 illustre le rsultat :

Figure 16-2. Prsentation du champ texte statique. Dans le cas de champs texte dynamiques, assurez-vous davoir bien intgr les contours de police afin de pouvoir faire subir une rotation au texte sans que celui-ci ne disparaisse. Nous reviendrons trs bientt sur cette notion. Dans les prcdentes versions dActionScript il tait impossible daccder ou de modifier directement la prsentation dun champ texte statique cr depuis lenvironnement auteur. En modifiant le type du champ, en texte dynamique ou de saisie, nous pouvons lui donner un nom doccurrence comme lillustre la figure 16-3 :

3 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-3. Champ texte dynamique. A la compilation, une variable du mme nom est cre afin de rfrencer notre champ texte. Nous ciblons ce dernier travers le code suivant :
// affiche : [object TextField] trace( legende );

Nous pouvons remarquer que le type du champ nest plus StaticText mais TextField. Contrairement la classe StaticText, les diffrentes proprits et mthodes dfinies par la classe TextField permettent une manipulation avance du texte :
legende.text = "Nouveau contenu"; // affiche : 56 trace( legende.textWidth ); // affiche : 0 trace( legende.textColor ); // affiche : dynamic trace( legende.type );

Afin de continuer notre exploration du texte au sein dActionScript 3, nous allons nous intresser prsent la cration de texte dynamique.

A retenir

4 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Trois classes peuvent tre utilises afin de manipuler du texte : StaticText, TextField, TextSnapShot. ActionScript 3 nintgre pas de nouveau moteur de rendu mais enrichit les capacits de la classe TextField. Les champs texte statique sont de type StaticText.

Seul lenvironnement auteur de Flash CS3 permet la cration de champ texte de type StaticText. Il est impossible dinstancier manuellement la classe StaticText. Les champs texte dynamique et de saisie sont de type TextField.

Afficher du texte en programmant


Lorsque nous devons afficher du texte par programmation, nous utilisons la classe flash.text.TextField. Nous allons pour dmarrer, crer un simple champ texte et y ajouter du contenu. La classe TextField sinstancie comme tout autre DisplayObject laide du mot cl new :
var monTexte:TextField = new TextField(); monTexte.text = "Bonjour !";

Il est important de noter que grce au nouveau mode dinstanciation des DisplayObject, il nest plus ncessaire de disposer dune instance de MovieClip afin de crer un champ texte. Rappelez-vous, la mthode createTextField prsente en ActionScript et 2 tait dfinie sur la classe MovieClip. Il tait donc impossible de crer un champ texte sans clip danimation. Une fois le champ texte cr nous pouvons lafficher en lajoutant la liste daffichage :
var monTexte:TextField = new TextField(); monTexte.text = "Bonjour !"; addChild ( monTexte );

Si nous testons le code prcdent nous obtenons le rsultat illustr en figure 16-4 :

5 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-4. Contenu texte. La proprit text de lobjet TextField nous permet daffecter du contenu, nous allons dcouvrir trs vite de nouvelles proprits et mthodes lies laffectation de contenu. Par dfaut, la taille dun champ texte cr par programmation est de 100 pixels en largeur et en hauteur :
var monTexte:TextField = new TextField(); monTexte.text = "Bonjour !"; addChild ( monTexte ); // affiche : 100 100 trace( monTexte.width, monTexte.height );

Afin de vrifier cela visuellement, nous pouvons activer la proprit border de lobjet TextField qui ajoute un contour en bordure du champ texte :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Bonjour !"; addChild ( monTexte );

La figure 16-5 illustre les dimensions dun champ texte par dfaut :

6 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-5. Dimensions par dfaut du champ texte. Si le contenu dpasse cette surface par dfaut, le texte est tronqu. Dans le code suivant nous modifions le contenu du champ afin de le faire dborder :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Bonjour, voici du contenu qui dborde !"; addChild ( monTexte );

Le texte est alors tronqu comme lillustre la figure 16-6 :

Figure 16-6. Texte tronqu. Afin que le texte revienne la ligne automatiquement nous activons la proprit wordWrap qui par dfaut est dfinie false :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Bonjour, voici du texte avec un retour la ligne automatique !"; monTexte.wordWrap = true; addChild ( monTexte );

La figure 16-7 illustre le rsultat :

7 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-7. Texte contraint avec retour la ligne automatique. A linverse si nous souhaitons que le champ sadapte automatiquement au contenu, nous utilisons la proprit autoSize. En plus dassurer un redimensionnement automatique, celle-ci permet de justifier le texte dans diffrentes positions. La proprit autoSize prend comme valeur, une des quatre proprits constantes de la classe TextFieldAutoSize :
TextFieldAutoSize.CENTER : le texte est justifi au centre ; TextFieldAutoSize.LEFT : le texte est justifi gauche ; TextFieldAutoSize.NONE : aucune justification du texte ; TextFieldAutoSize.RIGHT : le texte est justifi droite ;
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Bonjour, voici du contenu qui ne dborde plus !"; monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte );

Dans le code suivant nous justifions le texte gauche du champ :

La figure 16-8 illustre le rsultat :

Figure 16-8. Redimensionnement automatique du champ. Cette fonctionnalit est couramment utilise pour la mise en forme du texte au sein de paragraphes. Nous lutiliserons lorsque nous dvelopperons un diteur de texte la fin de ce chapitre. Si nous souhaitons ajouter une contrainte de dimensions afin de contenir le texte au sein dun bloc spcifique, nous pouvons affecter au champ une largeur et une hauteur spcifique. Un retour la ligne 8 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

automatique devra tre ajout, tout en sassurant de ne pas spcifier de redimensionnement par la proprit autoSize. Pour cela nous utilisons les proprits width et height et wordWrap de lobjet TextField :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Le texte volue dans un paragraphe dune largeur et hauteur de 250 pixels maximum. Si le texte dpasse, un retour la ligne est ajout automatiquement."; monTexte.width = 250; monTexte.height = 250; monTexte.wordWrap = true; addChild ( monTexte );

Dans le code prcdent, le texte est contraint un paragraphe dune largeur et hauteur de 250 pixels maximum. La figure 16-9 illustre le bloc de texte :

Figure 16-9. Retour la ligne automatique. Si nous rduisons la largeur du champ texte, le texte est alors contraint dvoluer dans cette surface :
var monTexte:TextField = new TextField(); monTexte.border = true;

9 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

monTexte.text = "Le texte volue dans un paragraphe dune largeur et hauteur de 120 pixels maximum. Si le texte dpasse, un saut de ligne est ajout automatiquement."; monTexte.width = 120; monTexte.height = 120; monTexte.wordWrap = true; addChild ( monTexte );

La figure 16-10 illustre le rsultat :

Figure 16-10. Champ texte restreint. Quelles que soient les dimensions affectes au champ texte, si nous activons le redimensionnement automatique, le lecteur Flash affichera totalement le texte, quitte tendre la hauteur du champ. Dans le code suivant, nous rduisons les dimensions du champ texte :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Le texte volue dans un paragraphe dune largeur et hauteur de 120 pixels maximum. Si le texte dpasse, un saut de ligne est ajout automatiquement."; monTexte.width = 120; monTexte.height = 30; monTexte.wordWrap = true; addChild ( monTexte );

La figure 16-11 illustre le rendu :

10 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-11. Texte tronqu. Si nous ajoutons un ajustement automatique par la proprit autoSize, le lecteur Flash conserve une largeur maximale de 120 pixels mais augmente la hauteur du champ afin dafficher le contenu total :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Le texte volue dans un paragraphe dune largeur et hauteur de 120 pixels maximum. Si le texte dpasse, un saut de ligne est ajout automatiquement."; monTexte.width = 120; monTexte.height = 30; monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.wordWrap = true; addChild ( monTexte );

La figure 16-12 illustre le comportement :

Figure 16-12. Redimensionnement automatique. Nous pouvons dynamiquement modifier les dimensions du champ afin de comprendre comment le lecteur ajuste le contenu du champ :
var monTexte:TextField = new TextField();

11 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

monTexte.border = true; monTexte.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque at metus a magna bibendum semper. Suspendisse id mauris. Duis consequat dolor et odio. Integer euismod enim ut nulla. Sed quam. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer lobortis. In non erat. Sed ac dui a arcu ultrices aliquam. Aenean neque neque, vulputate ac, dictum eu, vestibulum ut, enim. Sed quis eros. Curabitur eu odio ac nisi suscipit venenatis. Duis ultrices viverra sapien. Fusce interdum, felis eget mollis varius, enim sem imperdiet leo, sed sagittis turpis odio sed quam. Phasellus ac orci. Morbi vestibulum, sem at cursus auctor, metus odio suscipit ipsum, ac sodales erat mi ac velit. Nullam tempus iaculis sem."; monTexte.wordWrap = true; addChild ( monTexte ); stage.addEventListener ( MouseEvent.MOUSE_MOVE, ajustement ); function ajustement ( pEvt:MouseEvent ):void { monTexte.width = pEvt.stageX; monTexte.height = pEvt.stageY; pEvt.updateAfterEvent(); }

La figure 16-13 illustre le rsultat :

Figure 16-13. Taille du paragraphe dynamique. Nous utilisons la mthode updateAfterEvent afin de forcer le rafrachissement du lecteur.

12 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Lorsquun champ texte est cr, celui-ci est par dfaut ditable permettant une slection du texte. Si nous souhaitons rendre le champ non slectionnable nous passons la proprit selectable false :
monTexte.selectable = false;

Nous allons nous intresser au cours de la prochaine partie aux diffrents types de champs texte dynamiques existants.

A retenir
Un champ texte cr par programmation possde une taille par dfaut de 100 pixels en largeur et hauteur. Les proprits width et height permettent de dfinir la taille dun champ texte. La proprit wordWrap permet dactiver le retour la ligne automatique. Afin dadapter le champ texte dynamique au contenu, nous utilisons la proprit autoSize et lune des quatres constantes de la classe TextFieldAutoSize.

La proprit autoSize permet aussi de dfinir lajustement du texte au sein du champ. Si un dimensionnement automatique a t spcifi et que le champ texte est trop petit, le lecteur Flash tend le champ dans le sens de la hauteur pour afficher la totalit du texte.

Les types de champ texte


Deux comportements existent pour les champs texte dynamiques. Il est possible par la proprit type de rcuprer ou de modifier le comportement du champ. Deux types de champs texte dynamiques sont dfinis par la classe TextFieldType :
TextFieldType.DYNAMIC : le champ texte et de type dynamique. TextFieldType.INPUT : le champ texte est de type saisie.

Par dfaut, un champ texte cr partir de la classe TextField est considr comme dynamic :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.text = "Voici du contenu qui ne dborde plus !"; monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte );

13 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

// affiche : dynamic trace( monTexte.type ); // affiche : true trace( monTexte.type == TextFieldType.DYNAMIC );

Dans ce cas, celui ci peut tre manipul par programmation aussi bien au niveau de sa prsentation quau niveau de son contenu. Afin de crer un champ texte de saisie, nous modifions la proprit type et passons la chane TextFieldType.INPUT :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.type = TextFieldType.INPUT; monTexte.text = "Voici du contenu qui ne dbordre plus ! Dans un champ texte de saisie !"; monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte );

Le champ texte permet dsormais la saisie du texte la vole :

Figure 16-14. Champ texte auto-adapt. Notons que dans le code prcdent, nous ne pouvons sauter des lignes manuellement. Afin dactiver cette fonctionnalit, nous utilisons le mode multi-lignes laide de la proprit multiline :
var monTexte:TextField = new TextField(); monTexte.border = true; monTexte.multiline = true; monTexte.type = TextFieldType.INPUT; monTexte.text = "Bonjour, voici du contenu qui ne dbordre plus !"; monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte );

Dans la prochaine partie, nous allons nous intresser aux diffrentes techniques permettant la modification du texte.

14 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Formatage du texte
ActionScript 3 dispose de trois techniques assurant le formatage du texte, voici un rcapitulatif de chacune dentre elles :
Le texte HTML : de simples balises HTML peuvent tre intgres au contenu texte afin dassurer son formatage. La classe flash.text.TextFormat : laide dun objet TextFormat, le texte peut tre mis en forme aisment et rapidement.

La classe flash.text.StyleSheet : la classe StyleSheet permet la cration dune feuille de style CSS, idale pour la mise en forme de contenu consquent.

Dans la mesure o nous affectons du contenu, le formatage du texte ne concerne que les champs texte dynamique ou de saisie. Rappelez vous quil est impossible de modifier le contenu dun champ StaticText ou TextSnapshot.

Rendu HTML
Nous allons nous intresser dans un premier temps une premire technique de formatage, par balises HTML, laide de la proprit htmlText. Consultez laide de Flash CS3 pour la liste des balises HTML gres par le lecteur Flash. Dans le code suivant, nous crons un simple champ texte :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Voici du texte !"; addChild ( monTexte );

Si nous accdons la proprit text, le lecteur Flash renvoie la chane de caractres sans aucune information de formatage :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Voici du texte au format HTML"; addChild ( monTexte ); // affiche : Voici du texte au format HTML trace( monTexte.text );

15 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

A linverse, si nous accdons au contenu, par lintermdiaire de la proprit htmlText, le lecteur renvoie le texte accompagn des diffrentes balises HTML utilises en interne :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Voici du texte au format HTML"; addChild ( monTexte ); /* affiche : <P ALIGN="LEFT"><FONT FACE="Times New Roman" SIZE="12" COLOR="#000000" LETTERSPACING="0" KERNING="0">Voici du texte au format HTML</FONT></P> */ trace( monTexte.htmlText );

En modifiant le couleur du texte laide de la proprit textColor, nous remarquons que lattribut color de la balise <font> est automatiquement mis jour :
var monTexte:TextField = new TextField(); monTexte.textColor = 0x990000; monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Voici du texte au format HTML"; addChild ( monTexte ); /* affiche : <P ALIGN="LEFT"><FONT FACE="Times New Roman" SIZE="12" COLOR="#990000" LETTERSPACING="0" KERNING="0">Voici du texte au format HTML</FONT></P> */ trace( monTexte.htmlText );

Nous affectons ainsi du contenu HTML de manire indirecte, par lintermdiaire des diffrentes proprits de la classe TextField. En utilisant la proprit htmlText, les balises sont automatiquement interprtes :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "Voici du texte au format <b>HTML</b>"; addChild ( monTexte );

La figure 16-15 illustre le rendu :

16 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-15. Champ texte HTML format. Notons que la proprit html de la classe TextField nexiste plus en ActionScript 3. Laffectation du texte par la proprit htmlText active automatiquement linterprtation des balises HTML. Nous pouvons changer la couleur du texte en utilisant lattribut color de la balise <font> :
monTexte.htmlText = "Voici du texte <font color='#FF0000'>rouge</font> au format <b>HTML</b>";

Laffectation de contenu HTML savre extrmement pratique dans la mesure o le contenu texte possde au pralable toutes les balises lies sa prsentation. En revanche, une fois le contenu affect, il savre difficile de modifier la mise en forme du texte laide des balises HTML. Pour cela, nous prfrerons gnralement lutilisation dun objet TextFormat.

La classe TextFormat
Lobjet TextFormat a t conu pour reprsenter la mise en forme du texte. Celui-ci doit tre considr comme un outil de formatage. En ralit, lobjet TextFormat gnre automatiquement les balises HTML appropries afin de styliser le texte. La classe TextFormat bnficie ainsi dun grand nombre de proprits permettant une mise en forme du texte avance. Consultez laide de Flash CS3 pour la liste des proprits lies la mise en forme dfinie par la classe TextFormat. La premire tape consiste crer lobjet TextFormat, puis de dfinir les proprits de mise en forme :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Voici du texte mis en forme"; addChild ( monTexte ); // cration de l'objet TextFormat ( mise en forme )

17 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

var miseEnForme:TextFormat = new TextFormat(); // activation du style gras miseEnForme.bold = true; // modification de la taille du texte miseEnForme.size = 14; // modification de la police miseEnForme.font = "Verdana";

Afin dappliquer cette mise en forme, nous devons present appeler la la mthode setTextFormat de la classe TextField dont voici la signature :
public function setTextFormat(format:TextFormat, beginIndex:int = -1, endIndex:int = -1):void

Analysons chacun des paramtres :


format : lobjet TextFormat dterminant la mise en forme du texte. beginIndex : la position du caractre de dpart auquel appliquer la mise en forme, la valeur par dfaut est -1. endIndex : la position du caractre de fin auquel appliquer la mise en forme, la valeur par dfaut est -1.

Dans le code suivant, nous appliquons la mise en forme la totalit du champ texte :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Voici du texte mis en forme"; addChild ( monTexte ); // cration de l'objet TextFormat ( mise en forme ) var miseEnForme:TextFormat = new TextFormat(); // activation du style gras miseEnForme.bold = true; // modification de la taille du texte miseEnForme.size = 14; // modification de la police miseEnForme.font = "Verdana"; // application de la mise en forme monTexte.setTextFormat( miseEnForme );

La figure 16-16 illustre le rsultat :

18 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-16. Mise en forme du texte par lobjet TextFormat. En spcifiant les positions des caractres de dbut et fin, nous pouvons affecter le style une partie du texte seulement :
// application de la mise en forme une partie du texte monTexte.setTextFormat( miseEnForme, 22, 27);

Comme lillustre la figure 16-17, seul le mot forme est affect :

Figure 16-17. Mise en forme partielle du texte. En interne, laffectation de la mise en forme genre les balises HTML appropries :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Voici du texte mis en forme"; addChild ( monTexte ); // cration de l'objet TextFormat ( mise en forme ) var miseEnForme:TextFormat = new TextFormat(); // activation du style gras miseEnForme.bold = true; // modification de la taille du texte miseEnForme.size = 14; // modification de la police miseEnForme.font = "Verdana"; /* affiche : <P ALIGN="LEFT"><FONT FACE="Times New Roman" SIZE="12" COLOR="#000000" LETTERSPACING="0" KERNING="0">Voici du texte au format HTML</FONT></P> */ trace( monTexte.htmlText ); // application de la mise en forme monTexte.setTextFormat( miseEnForme, 22, 27 );

19 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

/* affiche : <P ALIGN="LEFT"><FONT FACE="Times New Roman" SIZE="12" COLOR="#000000" LETTERSPACING="0" KERNING="0">Voici du texte au format <FONT FACE="Verdana" SIZE="14"><B>HTML</B></FONT></FONT></P> */ trace( monTexte.htmlText );

Il est important de noter que lors de laffectation de contenu par la proprit text ou htmlText, le lecteur cre automatiquement en interne un objet TextFormat associ. Il convient donc dappeler la mthode setTextFormat aprs laffectation du contenu, au risque de voir la mise en forme pralablement applique crase. En revanche, une fois la mthode setTextFormat appele, tout ajout de texte par la mthode appendText conserve la mise en forme en cours. Dans le code suivant, le texte est affect aprs lapplication de la mise en forme, celle-ci est donc crase :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte ); // cration de l'objet TextFormat ( mise en forme ) var miseEnForme:TextFormat = new TextFormat(); // activation du style gras miseEnForme.bold = true; // modification de la taille du texte miseEnForme.size = 14; // modification de la police miseEnForme.font = "Verdana"; // application de la mise en forme monTexte.setTextFormat( miseEnForme ); // le contenu est affect aprs l'application de la mise en forme monTexte.text = "Voici du texte mis en forme";

La figure 16-18 illustre le rsultat :

20 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-18. Mise en forme du texte inchange. Notons que si nous utilisons la mthode appendText aprs lapplication de la mise en forme, celle-ci est conserve. En revanche, si le contenu est rempla, la mise en forme est perdue. ActionScript 3 intgre une nouvelle proprit defaultTextFormat permettant de remdier cela en dfinissant un style par dfaut :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte ); // cration de l'objet TextFormat ( mise en forme ) var miseEnForme:TextFormat = new TextFormat(); // activation du style gras miseEnForme.bold = true; // modification de la taille du texte miseEnForme.size = 14; // modification de la police miseEnForme.font = "Verdana"; // application d'une mise en forme par dfaut monTexte.defaultTextFormat = miseEnForme; // tout contenu affect prend la mise en forme par dfaut monTexte.text = "Voici du texte mis en forme";

Tout texte ajout par la suite prendra automatiquement la mise en forme dfinie par lobjet miseEnForme. Si nous souhaitons modifier le style par dfaut, nous pouvons affecter laide de la mthode setTextFormat une mise en forme spcifique laide dun autre objet TextFormat :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte ); // cration de l'objet TextFormat ( mise en forme ) var miseEnForme:TextFormat = new TextFormat(); // activation du style gras miseEnForme.bold = true; // modification de la taille du texte miseEnForme.size = 14; // modification de la police miseEnForme.font = "Verdana"; // application d'une mise en forme par dfaut monTexte.defaultTextFormat = miseEnForme;

21 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

// tout contenu affect prend la mise en forme par dfaut monTexte.text = "Voici du texte mis en forme"; // cration de l'objet TextFormat ( mise en forme ) var autreMiseEnForme:TextFormat = new TextFormat(); // modification de la police autreMiseEnForme.font = "Arial"; // modification de la taille autreMiseEnForme.size = 18; // affectation partielle de la nouvelle mise en forme monTexte.setTextFormat( autreMiseEnForme, 22, 27 );

La figure 16-19 illustre le rsultat :

Figure 16-19. Mise en forme du texte partielle. La classe TextField dfinit une autre mthode trs pratique en matire de mise en forme du texte nomme getTextFormat dont voici la signature :
public function getTextFormat(beginIndex:int = -1, endIndex:int = 1):TextFormat

Analysons chacun des paramtres :


beginIndex : position du caractre de dbut partir duquel la mise en forme doit tre extraite, la valeur par est dfaut -1. endIndex : position du caractre de fin partir duquel la mise en forme doit tre extraite, la valeur par dfaut est -1.

Cette dernire renvoie un objet TextFormat associ la partie du texte spcifie. Si aucun paramtre nest spcifi, lobjet TextFormat renvoy concerne la totalit du champ texte. Comme nous lavons vu prcdemment, lorsque du contenu est affect un champ texte, le lecteur Flash cre un objet TextFormat interne contenant les options de mise en forme du texte. Dans le code suivant, nous rcuprons les informations de mise en forme du champ monTexte :
var monTexte:TextField = new TextField(); monTexte.text = "Voici du texte !";

22 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte ); // extraction de l'objet TextFormat var miseEnForme:TextFormat = monTexte.getTextFormat(); // affiche : Times New Roman trace( miseEnForme.font ); // affiche : 12 trace( miseEnForme.size ); // affiche : left trace( miseEnForme.align ); // affiche : 0 trace( miseEnForme.color ); // affiche : false trace( miseEnForme.bold ); // affiche : false trace( miseEnForme.italic ); // affiche : false trace( miseEnForme.underline );

En dfinissant diffrentes balises HTML, nous remarquons que lobjet TextFormat renvoy reflte correctement la mise en forme actuelle du champ :
var monTexte:TextField = new TextField(); monTexte.htmlText = "<font size='18' color='#990000'><i><b>Voici du texte !</b></i></font>"; monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte ); // extraction de l'objet TextFormat var miseEnForme:TextFormat = monTexte.getTextFormat(); // affiche : Times New Roman trace( miseEnForme.font ); // affiche : 18 trace( miseEnForme.size ); // affiche : left trace( miseEnForme.align ); // affiche : 990000 trace( miseEnForme.color.toString(16) ); // affiche : true trace( miseEnForme.bold ); // affiche : true trace( miseEnForme.italic );

23 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

// affiche : false trace( miseEnForme.underline );

La classe TextFormat savre extrmement pratique, nous allons lutiliser dans la section suivante afin daugmenter les capacits de la classe TextField.

Etendre la classe TextField


Nous avons vu au cours du chapitre 9 intitul Etendre les classes natives quil tait possible daugmenter les capacits dune classe lorsque celle-ci nous paraissait limite. La classe TextField figure parmi les classes souvent tendues afin doptimiser laffichage ou la gestion du texte. Dans le cas dapplications localises, la taille du texte peut varier selon les langues utilises. La taille du champ doit rester la mme afin de ne pas perturber la prsentation graphique de lapplication. En tendant la classe TextField nous allons ajouter un mcanisme dadaptation automatique du texte au sein du champ. En activant le mcanisme dadaptation le texte est rduit afin dtre affich totalement. La figure 16-20 illustre le rsultat :

Figure 16-20. Champ texte auto-adaptable. Dans un nouveau paquetage org.bytearray.texte nous dfinissons la classe ChampTexteAutoAdaptable contenant le code suivant : 24 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

package org.bytearray.texte { import flash.text.TextField; public class ChampTexteAutoAdaptable extends TextField { public function ChampTexteAutoAdaptable () { } } }

La classe ChampTexteAutoAdaptable tend la classe TextField afin dhriter de toutes ses fonctionnalits. Nous dfinissons une proprit autoAdaptation en lecture criture afin de spcifier si le champ doit redimensionner ou non le contenu texte :
package org.bytearray.texte { import flash.text.TextField; public class ChampTexteAutoAdaptable extends TextField { private var adaptation:Boolean; public function ChampTexteAutoAdaptable () { } public function set autoAdaptation ( pAdaptation:Boolean ):void { adaptation = pAdaptation; } public function get autoAdaptation ():Boolean { return adaptation; } }

25 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Lorsque le contenu texte est ajout, nous devons dclencher le processus dadaptation du texte au sein du champ, pour cela nous devons modifier le fonctionnement de la proprit text. Nous surchargeons celle-ci en dclenchant la mthode adapte lorsque du contenu est affect :
package org.bytearray.texte { import flash.text.TextField; import flash.text.TextFormat; public class ChampTexteAutoAdaptable extends TextField { private var adaptation:Boolean; private var formatage:TextFormat; private var tailleInitiale:int; public function ChampTexteAutoAdaptable () { } public function set autoAdaptation ( pAdaptation:Boolean ):void { adaptation = pAdaptation; } public function get autoAdaptation ():Boolean { return adaptation; } public override function set text ( pText:String ):void { super.text = pText; if ( autoAdaptation ) adapte(); } private function adapte ():void { formatage = getTextFormat();

26 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

tailleInitiale = int(formatage.size); while ( textWidth > width || textHeight > height ) { if ( formatage.size <= 0 ) return; formatage.size = --tailleInitiale; setTextFormat ( formatage ); } } } }

Nous retrouvons ici le concept de surcharge en tendant le fonctionnement de la proprit text. Bien que celle-ci soit surcharge, nous dclenchons la dfinition hrite grce au mot-cl super. La mthode adapte trouve une taille de police adquate afin dadapter le contenu texte au sein du champ. Grce la proprit autoAdaptation nous pouvons activer ou non ladaptation du texte aux dimensions du champ :
import org.bytearray.texte.ChampTexteAutoAdaptable; var monPremierChamp:ChampTexteAutoAdaptable = new ChampTexteAutoAdaptable(); monPremierChamp.border = true; monPremierChamp.wordWrap = true; // activation du mode d'auto adaptation monPremierChamp.autoAdaptation = true; monPremierChamp.text = "Mauris consectetuer sagittis est. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vestibulum gravida, arcu ac ullamcorper semper..."; addChild ( monPremierChamp ); var monSecondChamp:ChampTexteAutoAdaptable = new ChampTexteAutoAdaptable(); monSecondChamp.border = true; monSecondChamp.wordWrap = true; monSecondChamp.text = "Mauris consectetuer sagittis est. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Vestibulum gravida, arcu ac ullamcorper semper..."; monSecondChamp.y = 120; addChild ( monSecondChamp );

27 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Dans le code suivant, nous crons un simple champ texte contenant la chane de caractres suivante "Bienvenue au camping des Bois Fleuris" :
import org.bytearray.texte.ChampTexteAutoAdaptable; var monPremierChamp:ChampTexteAutoAdaptable = new ChampTexteAutoAdaptable(); monPremierChamp.border = true; monPremierChamp.width = 220; monPremierChamp.autoAdaptation = true; monPremierChamp.text = "Bienvenue au camping des Bois Fleuris"; addChild ( monPremierChamp );

La figure 16-21 illustre le rendu :

Figure 16-21. Contenu texte. Le mode dadaptation du contenu est activ, si nous modifions le contenu du champ par un contenu trop long, le champ rduit automatiquement la taille de la police afin dafficher totalement le contenu. Dans le code suivant, le message de bienvenue est localis en Allemand :
import org.bytearray.texte.ChampTexteAutoAdaptable; var monPremierChamp:ChampTexteAutoAdaptable = new ChampTexteAutoAdaptable(); monPremierChamp.border = true; monPremierChamp.width = 220; monPremierChamp.autoAdaptation = true; monPremierChamp.text = "Willkommen auf dem Bois Fleuris Campingplatz"; addChild ( monPremierChamp );

La figure 16-22 illustre le rsultat : 28 / 82


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-22. Reduction du texte automatique. Dans certains cas, les champs texte peuvent dj tre crs depuis lenvironnement auteur afin dtre positionns convenablement au sein dune interface. Au lieu de remplacer chaque champ par une instance de la classe ChampTexteAutoAdaptable, nous pouvons directement ajouter le mcanisme dauto adaptation au sein du prototype de la classe TextField :
TextField.prototype.texteAutoAdapte = function ( pTexte:String ) { this.text = pTexte; this.adapte(); } TextField.prototype.adapte = function () { var formatage = this.getTextFormat(); var tailleInitiale = int(formatage.size); while ( this.textWidth > this.width { || this.textHeight > this.height )

if ( formatage.size <= 0 ) return; formatage.size = --tailleInitiale; this.setTextFormat ( formatage ); } }

Il nous suffit par la suite daffecter le contenu texte aux diffrents champs par la mthode texteAutoAdapte :

29 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

champTexte.texteAutoAdapte ( "Willkommen auf dem Bois Fleuris Campingplatz" );

Si nous tentons de compiler, le compilateur nous affiche le message suivant :


1061: Appel la mthode texteAutoAdapte peut-tre non dfinie, via la rfrence de type static flash.text:TextField.

Bien entendu, la classe TextField ne dispose pas par dfaut dune mthode texteAutoAdapte, le compilateur nous empche donc de compiler. Nous passons outre cette vrification en transtypant notre champ texte vers la classe dynamique Object :
Object(champTexte).texteAutoAdapte ( "Willkommen auf dem Bois Fleuris Campingplatz" );

A lexcution, la mthode texteAutoAdapte est trouve et excute, le contenu du champ est adapt. Nous verrons au cours de la section intitule Crer un diteur de texte un autre exemple dapplication des mthodes setTextFormat et getTextFormat.

A retenir
La classe TextField peut tre tendue afin daugmenter ses capacits. Selon les dimensions du champ, la classe ChampTexteAutoAdaptable permet dadapter la taille de la police afin dafficher totalement le contenu du texte.

La classe StyleSheet
Afin de mettre en forme du texte, il peut tre intressant dexternaliser la mise en forme du texte au sein dune feuille de style CSS. Lavantage de cette technique permet entre autre de mettre jour la mise en forme du texte sans avoir recompiler lapplication. La feuille de style est modifie puis charge dynamiquement afin de mettre en forme le contenu. Consultez laide de Flash CS3 pour la liste des proprits CSS gres par le lecteur Flash. Au sein dun fichier nomm style.css nous dfinissons la feuille de style suivante :
p

30 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

{ font-family:Verdana, Arial, Helvetica, sans-serif; color:#CC9933; } .main { font-style:italic; font-size:12; color:#CC00CC; } .title { font-weight:bold; font-style:italic; font-size:16px; color:#FF6633; }

Puis nous chargeons celle-ci laide de lobjet URLLoader :


var monTexte:TextField = new TextField(); monTexte.wordWrap = true; monTexte.width = 300; monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte ); var chargeurCSS:URLLoader = new URLLoader(); // la feuille de style est charge comme du contenu texte chargeurCSS.dataFormat = URLLoaderDataFormat.TEXT; var requete:URLRequest = new URLRequest ("style.css"); chargeurCSS.load ( requete ); chargeurCSS.addEventListener (Event.COMPLETE, chargementTermine); chargeurCSS.addEventListener (IOErrorEvent.IO_ERROR, erreurChargement); function chargementTermine ( pEvt:Event ):void { // affiche : ( contenu texte de style.css ) trace( pEvt.target.data ); } function erreurChargement ( pEvt:IOErrorEvent ):void

31 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

{ trace( "erreur de chargement" ); }

Nous laffectons au champ texte par la proprit styleSheet dfinie par la classe TextField :
function chargementTermine ( pEvt:Event ):void { // une feuille de style vide est cre var feuilleDeStyle:StyleSheet = new StyleSheet(); // la feuille de style est dfinie en interprtant le contenu de style.css feuilleDeStyle.parseCSS ( pEvt.target.data ); // la feuille de style est affecte au champ monTexte.styleSheet = feuilleDeStyle; // le contenu HTML contenant les balises ncessaires est affect monTexte.htmlText = "<p><span class='main'>Lorem ipsum</span> dolor sit amet, consectetuer adipiscing elit. <span class='title'>Nulla sit amet tellus</span>. Quisque lobortis. Duis tincidunt mollis massa. Maecenas neque orci, vulputate eget, consectetuer vitae, consectetuer ut, urna. Curabitur non nibh eu massa tincidunt consequat. Nullam nulla magna, auctor sed, semper ut, nonummy a, ipsum. Aliquam pulvinar est sit amet tortor. Sed facilisis ligula at est volutpat tristique. Etiam sem felis, facilisis in, fermentum at, consectetuer quis, ipsum. Morbi tincidunt velit. Integer cursus pretium nisl. Fusce et ante.</p>"; }

La mthode statique parseCSS permet dinterprter la chaine de caractre contenue dans le fichier style.css afin de dfinir lobjet StyleSheet. La figure 16-23 illustre la mise en forme :

32 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-23. Mise en forme par feuille de style. Grace aux feuilles de style, nous pouvons dfinir des options de mise en forme lies lintractivit. En ajoutant la proprit a:hover au sein de la feuille de style nous rendons le texte gras et modifions sa couleur lors du survol :
a:hover { font-weight:bold; color:#0033CC; }

En ajoutant un lien hypertexte, le texte change de style automatiquement :


monTexte.htmlText = "<p><span class='main'>Lorem ipsum</span> dolor sit amet, consectetuer adipiscing elit. <span class='title'>Nulla sit amet tellus</span>.<a href='http://www.oreilly.fr'>Quisque lobortis</a>. Duis tincidunt mollis massa. Maecenas neque orci, vulputate eget, consectetuer vitae, consectetuer ut, urna. Curabitur non nibh eu massa tincidunt consequat. Nullam nulla magna, auctor sed, semper ut, nonummy a, ipsum. Aliquam pulvinar est sit amet tortor. Sed facilisis ligula at est volutpat tristique. Etiam sem felis, facilisis in, fermentum at, consectetuer quis, ipsum. Morbi tincidunt velit. Integer cursus pretium nisl. Fusce et ante.</p>";

La figure 16-24 illustre le rsultat :

Figure 16-24. Mise en forme par feuille de style. Malheureusement, lutilisation des CSS savre limite dans certains cas. Un champ texte de saisie nautorise pas la saisie de texte lorsque celui-ci est li une feuille de style.

33 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

De la mme manire, un champ texte li une feuille de style nest pas modifiable. Lutilisation des mthodes replaceText, replaceSelectedText, appendText, setTextFormat nest pas autorise.

A retenir
Trois techniques permettent de mettre du texte en forme : les balises HTML, lobjet TextFormat, la dfinition dune feuille style laide de la classe StyleSheet.

Quelle que soit la technique employe, le lecteur Flash fonctionne en interne par lintrmdiaire de balises HTML.

Modifier le contenu dun champ texte


Durant lexcution dune application, nous avons souvent besoin de modifier le contenu de champs texte. Pour cela, nous pouvons utiliser les proprits suivantes :
text : permet laffectation de contenu non HTML ; htmlText : permet laffectation de contenu HTML ;

Ainsi que les mthodes suivantes :


appendText : remplace loprateur += lors dajout de texte. Ne prend pas en charge le texte au format HTML ; replaceText : permet de remplacer une partie du texte au sein dun champ. Ne prend pas en charge le texte au format HTML.

Dans le cas dajout de contenu nous pouvons utiliser loprateur += sur la proprit text :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "ActionScript"; monTexte.text += " 3" // affiche : ActionScript 3 trace( monTexte.text );

Cependant, en ActionScript 3 lutilisation de loprateur += sur un champ texte est dprcie au profit de la mthode appendText. Si le mode Avertissements du compilateur est activ, celui-ci nous avertit de la dprciation de loprateur += :

34 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Warning: 3551: L'ajout de texte la fin d'un champ texte TextField avec += est beaucoup plus lent que l'utilisation de la mthode TextField.appendText().

Voici la signature de la mthode appendText :


public function appendText(newText:String):void

Nous passons en paramtre le texte ajouter au champ de la manire suivante :


var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "ActionScript"; monTexte.appendText ( " 3" ); // affiche : ActionScript 3 trace( monTexte.text );

En faisant un simple test de performances, nous voyons que loprateur += est en effet plus lent que la mthode appendText :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; var debut:Number = getTimer(); for (var i:int = 0; i< 1500; i++ ) { monTexte.text += } // affiche : 1120 trace( getTimer() - debut ); "ActionScript 3";

Une simple concatnation sur 1500 itrations ncessite 1120 millisecondes. Si nous utilisons cette fois la mthode appendText :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; var debut:Number = getTimer(); for (var i:int = 0; i< 1500; i++ )

35 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

{ monTexte.appendText ( "ActionScript 3" ); } // affiche : 847 trace( getTimer() - debut );

Le temps dexcution est de 847 ms. Lutilisation de la mthode appendText savre tre environ 30% plus rapide que loprateur +=. Nous pourrions penser que la mthode appendText savre donc tre la mthode favorite. En ralit, afin daffecter du texte de manire optimise, il convient de limiter au maximum la manipulation du champ texte. Ainsi, nous pourrions acclrer le code prcdent en crant une variable contenant le texte, puis en laffectant au champ une fois la concatnation acheve :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; var debut:Number = getTimer(); var contenu:String = monTexte.text; for (var i:int = 0; i< 1500; i++ ) { contenu += "ActionScript 3"; } monTexte.text = contenu;

// affiche : 2 trace( getTimer() - debut );

En procdant ainsi, nous passons une vitesse dexcution de 2 millisecondes. Soit un temps dexcution environ 420 fois plus rapide que la mthode appendText et 560 fois plus rapide que loprateur += auprs de la proprit text. La mme approche peut tre utilise dans le cas de manipulation de texte au format HTML grce la proprit htmlText :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT;

36 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

var debut:Number = getTimer(); var contenu:String = monTexte.htmlText; for (var i:int = 0; i< 1500; i++ ) { contenu += "<b>ActionScript<b> 3"; } monTexte.htmlText = contenu;

// affiche : 29 trace( getTimer() - debut );

Afin dobtenir le mme rsultat, si nous concatnons directement le contenu au sein du champ texte, le temps dexcution excde 15 secondes et lve une exception de type ScriptTimeoutError :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; var debut:Number = getTimer(); for (var i:int = 0; i< 1500; i++ ) { monTexte.htmlText += "ActionScript <b>2</b>"; } trace( getTimer() - debut );

Le processus de rendu est trop complexe et lve lexception suivante :


Error: Error #1502: La dure d'excution d'un script excde le dlai par dfaut (15 secondes).

Il convient donc dutiliser la mthode appendText ou les proprits text et htmlText dans un contexte non intensif. Dans tous les autres cas, nous prfrerons crer une variable contenant le texte, puis travailler sur celle-ci avant de laffecter au champ texte.

Remplacer du texte
Si nous souhaitons modifier une partie du texte, nous pouvons utiliser la mthode replaceText de la classe TextField ayant la signature suivante :
public function replaceText(beginIndex:int, endIndex:int, newText:String):void

37 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Cette mthode accepte trois paramtres, dont voici le dtail :


beginIndex : la position du caractre partir duquel le remplacement dmarre, la valeur par dfaut est -1 ; endIndex : la position du caractre partir duquel le remplacement se termine, la valeur par dfaut est -1 ; newText : le texte de remplacement ;

Afin de bien comprendre lutilisation de cette mthode, prenons le cas du texte suivant :
ActionScript 2

Ce texte est ajout un champ texte :


var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "ActionScript 2";

Afin de remplacer le chiffre 2 par 3 nous utilisons la mthode replaceText :


var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "ActionScript 2"; monTexte.replaceText( 13, 14, "3" ); addChild ( monTexte );

Le champ texte contient alors le texte suivant :


ActionScript 3

Nous pouvons rendre le remplacement dynamique et rechercher la position du caractre laide de la mthode indexOf de la classe String :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "ActionScript 2"; var chaine:String = "2"; var position:int = monTexte.text.indexOf ( chaine ); if ( position != -1 ) monTexte.replaceText ( position, position+chaine.length, "3" ); addChild ( monTexte );

38 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Comme nous lavons vu lors du chapitre 2 intitul Langage et API du lecteur Flash, lintgration des expressions regulires en ActionScript 3 rend la manipulation de chanes de caractres extrmement puissante. Il est prfrable dutiliser les mthodes de la classe String telles search, replace ou match pour une recherche plus complexe.

A retenir
Afin de modifier le contenu dun champ texte, nous pouvons utilisons la proprit text, htmlText ainsi que les mthodes appendText ou replaceText.

La mthode appendText ne permet pas laffectation de contenu HTML. Dans tous les cas, lutilisation dune variable temporaire afin de concatner et traiter le contenu savre plus rapide. La mthode replaceText permet de remplacer du texte au sein dun champ de manire simplifie.

Lvnement TextEvent.LINK
Les prcdentes versions dActionScript intgraient une fonctionnalit appele asfunction permettant de dclencher des fonctions ActionScript partir de texte HTML. Ce protocole fut intgr au sein du lecteur Flash 5. Afin de dclencher une fonction ActionScript, le nom de la fonction prcd du mot-cl asfunction tait pass lattribut href de la balise dancrage <a> :
var monTexte:TextField = this.createTextField ("legende", 0, 0, 0, 0, 0); monTexte.autoSize = true; function maFonction ( pArgument ) { trace ( "Le paramtre pass est " + pArgument ); } monTexte.html = true; monTexte.htmlText ="<a href='asfunction:maFonction,Coucou'>Cliquez ici !</a>";

Afin de rester conforme au nouveau modle vnementiel, ActionScript 3 remplace ce protocole en proposant la diffusion dvnements TextEvent.LINK par les champs texte. 39 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Dans le code suivant, nous crons un champ texte comportant une balise <b> et <i> :
var monTexte:TextField = new TextField(); monTexte.width = 250; monTexte.wordWrap = true; addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "En ActionScript 3, il est possible de diffuser un vnement <i>TextEvent.LINK</i> depuis un champ texte en cliquant sur un <b>lien</b>.";

La figure 16-25 illustre le rsultat :

Figure 16-25. Champ texte format. Nous


TextEvent.LINK, laide de la syntaxe suivante :

allons

permettre

la

diffusion

dun

vnement

<a href='event:paramtre'>Texte Cliquable</a>

Lutilisation du mot cl event en tant que valeur de lattribut href permet la diffusion dun vnement TextEvent.LINK lors du clic sur le lien. La valeur prcise aprs les deux points est affecte la proprit text de lobjet vnementiel diffus. Dans le code suivant, nous coutons lvnement TextEvent.LINK auprs du champ texte :
var monTexte:TextField = new TextField(); monTexte.width = 250; monTexte.wordWrap = true; addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "En ActionScript 3, il est possible de diffuser un vnement <i>TextEvent.LINK</i> depuis un champ texte en cliquant sur un <a href='event:Texte cach !'><b>lien</b></a>."; monTexte.addEventListener (TextEvent.LINK, clicLien); function clicLien ( pEvt:TextEvent ):void

40 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

{ // affiche : [TextEvent type="link" bubbles=true cancelable=false eventPhase=2 text="Texte cach !"] trace( pEvt ); // affiche : [object TextField] trace( pEvt.target ); }

La proprit text de lobjet vnementiel contient le texte spcifi aprs lattribut event. Dans le code suivant, nous changeons dynamiquement la cadence de lanimation en cliquant sur diffrents liens associs au champ texte :
var monTexte:TextField = new TextField(); monTexte.width = 250; monTexte.wordWrap = true; addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "La cadence de l'animation peut tre change depuis le champ texte, la cadence peut tre passe <a href='event:10'><b>10 img/s</b></a> ou bien <a href='event:25'><b>25 img/s</b></a> ou encore <a href='event:50'><b>50 img/s</b></a>."; monTexte.addEventListener (TextEvent.LINK, onLinkClick); function onLinkClick ( pEvt:TextEvent ):void { // affiche : 25 trace( pEvt.text ); stage.frameRate = int ( pEvt.text ); }

La figure 16-26 illustre le rsultat :

41 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-26. Modification dynamique de la cadence de lanimation depuis le champ texte. Il nest pas possible de diffuser dautres vnements laide de lattribut event de la balise <a>. De la mme manire, il nest pas prvu de pouvoir passer plusieurs paramtres la fonction couteur, mais laide dun sparateur comme une virgule nous pouvons y parvenir :
<a href='event:paramtre1,paramtre2,paramtre3'>Texte Cliquable</a>

Ainsi, nous pouvons passer plusieurs cadences avec la syntaxe suivante :


<a href='event:10,20,30,40'><b>10 img/s</b></a>

Au sein de la fonction couteur nous transformons la chane en un tableau laide de la mthode split de la classe String :
function clicLien ( pEvt:TextEvent ):void { var parametres:Array = pEvt.text.split (","); /* affiche : 10 20 30 40 */ for each ( var p:* in parametres ) trace( p );

42 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Lutilisation des vnements TextEvent.LINK malheureusement un comportement pnalisant.

entrane

Lorsquun clic droit est dtect sur un lien associ au champ texte, un menu contextuel souvre habituellement comme lillustre la figure 1627 :

Figure 16-27. Menu contextuel de liens hypertexte. Lutilisation dvnements TextEvent.LINK empche le bon fonctionnement du menu contextuel. Si nous dcidons douvrir le lien directement ou dans une nouvelle fentre, le message derreur illustr en figure 16-28 saffiche :

Figure 16-28. Message derreur associ aux liens hypertexte. Le navigateur par dfaut ne reconnat pas le lien associ (protocole event) qui est reprsent sous la forme suivante :
event:paramtre

De plus en slectionnant une option du menu contextuel lvnement TextEvent.LINK nest pas diffus. 43 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Il convient donc de prendre ces comportements en considration.

A retenir
Le protocole asfunction est remplac par la diffusion dvnements TextEvent.LINK. Afin de diffuser un vnement TextEvent.LINK, nous utilisons le mot-cl event en tant que valeur de lattribut href de la balise dancrage <a>. Lutilisation dvnements TextEvent.LINK empche le bon fonctionnement du menu contextuel associ au lien.

Charger du contenu externe


Nous avons vu quil tait possible dintgrer des balises HTML au sein dun champ texte. Dans le cas de lutilisation de la balise <img>, nous pouvons intgrer facilement un lment multimdia telle une image ou un SWF. Dans le code suivant, nous intgrons une image au champ texte :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "<p>Voici une image intgre au sein d'un champ texte HTML :</p><p><img src='http://www.google.com/intl/en_ALL/images/logo.gif'>";

La figure 16-29 illustre le rsultat :

Figure 16-29. Image intgre dans un champ texte. Afin de correctement grer le chargement des mdias intgrs aux champs texte, ActionScript 3 introduit une nouvelle mthode de la

44 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

classe TextField nomme getImageReference dont voici la signature :


public function getImageReference(id:String):DisplayObject

Lorsquune balise <img> est utilise au sein dun champ texte, le lecteur Flash cre automatiquement un objet Loader enfant au champ texte associ au mdia charg. Bien que la classe TextField ne soit pas un objet de type DisplayObjectContainer, lobjet Loader est pourtant contenu par le champ texte. Dans le code suivant, nous ajoutons un identifiant monLogo limage intgre grce lattribut id :
monTexte.htmlText = "<p>Voici une image intgre au sein d'un champ texte HTML :</p><p><img src='http://www.google.com/intl/en_ALL/images/logo.gif' id='monLogo'>";

Afin dextraire lobjet Loader associ au mdia charg, nous passons ce mme identifiant la mthode getImageReference :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "<p>Voici une image intgre au sein d'un champ texte HTML :</p><p><img src='http://www.google.com/intl/en_ALL/images/logo.gif' id='monLogo'>"; var chargeurLogo:Loader = Loader ( monTexte.getImageReference("monLogo") ); // affiche : [object Loader] trace( chargeurLogo ); // affiche : [object TextField] trace( chargeurLogo.parent );

Nous remarquons que la proprit parent de lobjet Loader fait bien rfrence au champ texte. Nous pouvons ainsi grer le prchargement de limage et accder celle-ci grce la proprit content de lobjet Loader :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "<p>Voici une image intgre au sein d'un champ texte HTML :</p><p><img src='http://www.google.com/intl/en_ALL/images/logo.gif' id='monLogo'>"; var chargeurLogo:Loader = Loader ( monTexte.getImageReference("monLogo") ); // coute de l'vnement Event.COMPLETE

45 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

chargeurLogo.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementTermine ); function chargementTermine ( pEvt:Event ):void { // affiche : [object Bitmap] trace( pEvt.target.content ); }

Dans le code suivant, nous affectons un filtre de flou limage charge :


var point:Point = new Point ( 0, 0 ); var filtreFlou:BlurFilter = new BlurFilter ( 8, 8, 2 ); function chargementTermine ( pEvt:Event ):void { if ( pEvt.target.content is Bitmap ) { var imageChargee:Bitmap = Bitmap ( pEvt.target.content ); // affecte un filtre de flou l'image intgre au champ texte imageChargee.bitmapData.applyFilter ( imageChargee.bitmapData, imageChargee.bitmapData.rect, point, filtreFlou ); } }

La figure 16-30 illustre limage floute :

Figure 16-30. Image charge dynamiquement puis floute. Une fois limage charge, celle-ci est entirement manipulable.

46 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Si aucune image nest associe lidentifiant pass la mthode getImageReference, celle-ci renvoie null. Ainsi, le chargement dune image au sein dun champ texte repose sur les mmes mcanismes de chargement quune image traditionnelle charge directement au sein dun objet Loader :
var monTexte:TextField = new TextField(); addChild ( monTexte ); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.htmlText = "<p>Voici une image intgre au sein d'un champ texte HTML :</p><p><img src='http://www.google.com/intl/en_ALL/images/logo.gif' id='monLogo'>"; var chargeurLogo:Loader = Loader ( monTexte.getImageReference("monLogo") ); // coute des diffrents vnements chargeurLogo.contentLoaderInfo.addEventListener chargementDemarre ); chargeurLogo.contentLoaderInfo.addEventListener chargementEnCours ); chargeurLogo.contentLoaderInfo.addEventListener chargementTermine ); chargeurLogo.contentLoaderInfo.addEventListener erreurChargement ); function chargementDemarre ( pEvt:Event ):void { trace("chargement demarr"); } function chargementEnCours ( pEvt:ProgressEvent ):void { trace("chargement en cours : " + pEvt.bytesLoaded + " / " + pEvt.bytesTotal ); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); } var point:Point = new Point ( 0, 0 ); var filtreFlou:BlurFilter = new BlurFilter ( 8, 8, 2 ); function chargementTermine ( pEvt:Event ):void { ( Event.OPEN, ( ProgressEvent.PROGRESS, ( Event.COMPLETE, ( IOErrorEvent.IO_ERROR,

47 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

if ( pEvt.target.content is Bitmap ) { var imageChargee:Bitmap = Bitmap ( pEvt.target.content ); // affecte un filtre de flou l'image intgre au champ texte imageChargee.bitmapData.applyFilter ( imageChargee.bitmapData, imageChargee.bitmapData.rect, point, filtreFlou ); } }

Grce aux diffrents vnements diffuss par lobjet LoaderInfo, nous pouvons grer les erreurs de chargement des images associes au champ texte en remplaant limage non trouve par une image de substitution. De la mme manire, nous pouvons intgrer dans le champ texte un SWF.
monTexte.htmlText = "<p>Voici une animation intgre au sein d'un champ texte HTML :</p><p><img src='animation.swf' id='monAnim'>";

Grce la mthode getImageReference nous extrayons lobjet Loader associ :


var chargeurLogo:Loader = Loader ( monTexte.getImageReference("monAnim") );

Une fois le chargement termin, nous accdons aux informations lies au SWF charg et modifions son opacit :
function chargementTermine ( pEvt:Event ):void { pEvt.target.content.alpha = .5; if ( pEvt.target.content is DisplayObjectContainer ) { // affiche : 12 trace( pEvt.target.frameRate ); // affiche : application/x-shockwave-flash trace( pEvt.target.contentType ); // affiche : 9 trace( pEvt.target.swfVersion ); // affiche : 3 trace( pEvt.target.actionScriptVersion ); } }

48 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

La figure 16-31 illustre le rsultat :

Figure 16-31. Animation avec opacit rduite. de signaler que seule la mthode getImageReference permet dextraire lobjet Loader associ au media charg. Bien que lobjet TextField contienne les objets Loader, la classe TextField nhrite pas de la classe DisplayObjectContainer et noffre malheureusement pas de proprits telles numChildren ou de mthodes getChildAt ou removeChild. Il est important

A retenir
Un objet Loader est cr pour chaque mdia intgr un champ texte. Bien que la classe TextField nhrite pas de la classe DisplayObjectContainer, les objets Loader sont enfants du champ texte. Seule la mthode getImageReference permet dextraire lobjet Loader associ au mdia charg. Afin de pouvoir utiliser la mthode getImageReference un identifiant doit tre affect au mdia laide de lattribut id.

Exporter une police dans lanimation


Lorsque nous crons un champ texte par programmation, le lecteur utilise par dfaut les polices du systme dexploitation. Mme si ce mcanisme possde un intrt vident li au poids de lanimation, si lordinateur visualisant lapplication ne possde pas la police ncessaire, le texte risque de ne pas tre affich. 49 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Pour nous rendre compte dautres limitations lies aux polices systmes, nous pouvons tester le code suivant :
var monTexte:TextField = new TextField() monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Police systme"; monTexte.rotation = 45; addChild ( monTexte );

Nous remarquons que le texte nest pas affich, car nous avons fait subir une rotation de 45 degrs au champ texte. Sans les contours de polices intgrs, le lecteur est incapable de travailler sur la rotation, les effets de masque ou lopacit du texte. Si nous tentons de modifier lopacit, le texte demeure totalement opaque :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.text = "Police systme"; monTexte.alpha = .4; addChild ( monTexte );

Afin de remdier ces limitations nous devons intgrer la police lanimation. Pour cela, nous cliquons sur la partie droite de la bibliothque du document en cours comme lillustre la figure 16-32 :

Figure 16-32. Animation avec opacit rduite. Puis nous slectionnons loption Nouvelle police. 50 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Au sein du nouveau panneau, nous slectionnons la police intgrer puis nous validons. Dans notre cas, nous utilisons la police Lovitz. Notez que le nom de la police spcifi dans le champ associ na pas dincidence sur la suite de lopration. En utilisant le panneau Proprits de liaison du symbole de police, nous associons une classe auto gnre comme lillustre la figure 1633 :

Figure 16-33. Classe de police auto gnre. Bien entendu, le nom de la classe associe ne doit pas ncessairement possder le nom de la police, nous aurions pu utiliser MaPolice, PolicePerso ou autres. Afin dutiliser une police embarque dans un champ texte, nous utilisons dans un premier temps la proprit embedFonts de la classe TextField :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; // le champ texte doit utiliser une police embarque monTexte.embedFonts = true;

Puis, nous indiquons la police utiliser en instanciant la classe Lovitz, et en passant son nom la proprit font dun objet de mise forme TextFormat :
var monTexte:TextField = new TextField(); monTexte.autoSize = TextFieldAutoSize.LEFT; // le champ doit utiliser une police embarque

51 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

monTexte.embedFonts = true; // nous instancions la police var policeBibliotheque:Lovitz = new Lovitz(); // un objet de mise en forme est cr var miseEnForme:TextFormat = new TextFormat(); // nous passons le nom de la police embarque lobjet TextFormat miseEnForme.font = policeBibliotheque.fontName; miseEnForme.size = 64; // affectation de la mise en forme monTexte.defaultTextFormat = miseEnForme; monTexte.text = "Police intgre"; monTexte.rotation = 45; monTexte.alpha = .4; monTexte.x = ( stage.stageWidth - monTexte.width ) / 2; monTexte.y = ( stage.stageHeight - monTexte.height ) / 2; addChild ( monTexte );

La figure 16-34 illustre le champ texte utilisant la police embarque :

Figure 16-34. Champ texte utilisant une police embarque. Malheureusement, en exportant les contours de polices dans lanimation, nous augmentons sensiblement le poids du SWF gnr. 52 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Mme si cela peut paratre ngligeable dans un premier temps, cela peut poser un rel problme dans le cas dapplications localises, o plusieurs polices peuvent tre ncessaires. Au lieu dintgrer plusieurs polices au sein de lanimation, nous prfrerons charger dynamiquement la police ncessaire lorsque lapplication en fait la demande.

Charger dynamiquement une police


Une des limitations des prcedentes versions dActionScript concernait le manque de souplesse li au chargement de polices dynamique. ActionScript 3 simplifie grandement ce processus grce lintroduction de la classe flash.text.Font. Nous avons dcouvert au cours du chapitre 13 intitul Charger du contenu la mthode getDefinition de la classe ApplicationDomain. Au cours de ce chapitre, nous avions extrait diffrentes dfinitions de classes issues dun SWF charg dynamiquement. Le mme concept peut tre appliqu dans le cas de polices associes des classes. En intgrant une police au sein dun SWF, nous allons pouvoir charger ce dernier puis en extraire la police. Une fois la police en bibliothque et associe une classe, nous exportons lanimation sous la forme dun SWF appel bibliotheque.swf. Dans un nouveau document Flash CS3 nous chargeons dynamiquement cette bibliothque partage afin dextraire la dfinition de classe de police :
var chargeur:Loader = new Loader(); chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.load ( new URLRequest("bibliotheque.swf") ); function chargementTermine ( pEvt:Event ):void { var DefinitionClasse:Class = Class ( pEvt.target.applicationDomain.getDefinition("Lovitz") ); // affiche : [class Lovitz] trace( DefinitionClasse ); }

Grce la mthode statique registerFont de la classe flash.text.Font nous enregistrons la dfinition de classe comme nouvelle police disponible au sein de tous les SWF de lapplication : 53 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

function chargementTermine ( pEvt:Event ):void { var DefinitionClasse:Class = Class ( pEvt.target.applicationDomain.getDefinition("Lovitz") ); Font.registerFont ( DefinitionClasse ); }

La mthode statique enumerateFonts de la classe Font nous indique que la police a t correctement intgre :
function chargementTermine ( pEvt:Event ):void { var DefinitionClasse:Class = Class ( pEvt.target.applicationDomain.getDefinition("Lovitz") ); // numration des polices intgres var policeIntegrees:Array = Font.enumerateFonts(); // affiche : 0 trace( policeIntegrees.length ); Font.registerFont ( DefinitionClasse ); policeIntegrees = Font.enumerateFonts(); // affiche : 1 trace( policeIntegrees.length ); // affiche : [object Lovitz] trace( policeIntegrees[0] ); }

Enfin, nous crons un champ texte et affectons la police charge dynamiquement laide dun objet TextFormat :
var monTexte:TextField = new TextField(); monTexte.embedFonts = true; monTexte.autoSize = TextFieldAutoSize.LEFT; addChild ( monTexte ); var chargeur:Loader = new Loader(); chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementTermine ); chargeur.load ( new URLRequest("bibliotheque.swf") ); function chargementTermine ( pEvt:Event ):void {

54 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

var DefinitionClasse:Class = Class ( pEvt.target.applicationDomain.getDefinition("Lovitz") ); Font.registerFont ( DefinitionClasse ); var policeBibliotheque:Font = new DefinitionClasse(); monTexte.defaultTextFormat = new TextFormat(policeBibliotheque.fontName, 64, 0); monTexte.htmlText = "Police charge dynamiquement !"; }

La figure 16-35 illustre le rsultat :

Figure 16-35. Formatage du texte laide dune police dynamique. Une fois la police charge et enregistre parmi la liste des polices disponibles, nous pouvons lutiliser en conjonction avec une feuille de style. Nous ajoutons le nom de la police officiel grce lattribut fontfamily de la feuille de style :
.main { font-family:Lovitz; font-style:italic; font-size:42; color:#CC00CC; }

Attention, le nom de police utilise ici doit tre le nom officiel de la police et non le nom de la classe de police associe. Puis nous associons une feuille de style ayant recours la police dynamique :
var monTexte:TextField = new TextField(); monTexte.embedFonts = true; monTexte.rotation = 45; monTexte.autoSize = TextFieldAutoSize.LEFT; monTexte.wordWrap = true;

55 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

monTexte.width = 250; addChild ( monTexte ); var chargeur:Loader = new Loader(); chargeur.contentLoaderInfo.addEventListener ( Event.COMPLETE, chargementPoliceTermine ); chargeur.load ( new URLRequest("bibliotheque.swf") ); var chargeurCSS:URLLoader = new URLLoader(); chargeurCSS.dataFormat = URLLoaderDataFormat.TEXT; chargeurCSS.addEventListener ( Event.COMPLETE, chargementCSSTermine ); function chargementPoliceTermine ( pEvt:Event ):void { var definitionClasse:Class = Class ( pEvt.target.applicationDomain.getDefinition("Lovitz") ); Font.registerFont ( definitionClasse ); var requete:URLRequest = new URLRequest ("style.css"); // une fois la police charge et enregistre nous chargeons la feuille de style chargeurCSS.load ( requete ); } function chargementCSSTermine ( pEvt:Event ):void { var feuilleDeStyle:StyleSheet = new StyleSheet(); feuilleDeStyle.parseCSS ( pEvt.target.data ); monTexte.styleSheet = feuilleDeStyle; monTexte.htmlText = "<span class='main'>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec ligula. Proin tristique. Sed semper enim at augue. In dictum. Pellentesque pellentesque dui pulvinar nisi. Fusce eu tortor non lorem semper iaculis. Pellentesque nisl dui, lacinia vitae, vehicula a, pellentesque eget, urna. Aliquam erat volutpat. Praesent et massa vel augue aliquam iaculis. In et quam. Nulla ut ligula. Ut porttitor. Vestibulum elit purus, auctor non, commodo sit amet, vestibulum ut, lorem.</span>"; } monTexte.x = ( stage.stageWidth - monTexte.width ) / 2; monTexte.y = ( stage.stageHeight - monTexte.height ) / 2;

La figure 16-36 illustre le rendu du texte :

56 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-36. Police dynamique et feuille de style. La rotation du texte nous confirme que la police Lovitz est correctement intgre lapplication, le rendu du texte est donc assur quelque soit le poste visionnant lanimation.

A retenir
ActionScript 3 simplifie le chargement de police dynamique. La police est intgre un SWF, puis charge dynamiquement. La mthode getDefinition de lobjet ApplicationDomain permet dextraire la dfinition de classe. La mthode statique registerFont de la classe Font permet denregistrer la dfinition de classe dans la liste globale des polices disponibles. La police est alors accessible auprs de tous les SWF de lapplication.

Dtecter les coordonnes de texte


Nous avons vu que de nombreuses mthodes ont t ajoutes la classe TextField en ActionScript 3. Nous allons tirer profit dune nouvelle mthode appele getCharBoundaries dont voici la signature :
public function getCharBoundaries(charIndex:int):Rectangle

57 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Celle-ci attend en paramtre lindex du caractre rechercher et renvoie ses coordonnes sous la forme dune instance de la classe flash.geom.Rectangle. Grce cela, nous allons crer un systme de gestion dmoticnes. Au sein dun nouveau document Flash CS3, nous crons un nouveau symbole clip lie une classe auto-gnre Emoticone. Ce symbole contient diffrentes moticnes sur chaque image le composant. La figure 16-37 illustre les diffrentes images du clip :

Figure 16-37. Symbole content les diffrentes moticnes. Nous crons prsent un symbole clip contenant un champ texte nomm contenuTexte puis nous associons la classe MoteurEmoticone suivante par le panneau Proprits de liaison :
package org.bytearray.emoticones { import flash.display.Sprite; import flash.text.TextField; public class MoteurEmoticone extends Sprite { public var contenuTexte:TextField; public function MoteurEmoticone () {

58 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

} }

Au sein du mme document, nous instancions notre champ texte gestion dmoticnes travers la classe de document suivante :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut; import org.bytearray.emoticones.MoteurEmoticone; public class Document extends ApplicationDefaut { public var champTexteEmoticone:MoteurEmoticone; public function Document () { champTexteEmoticone = new MoteurEmoticone(); champTexteEmoticone.x = (stage.stageWidth champTexteEmoticone.width) / 2; champTexteEmoticone.y = (stage.stageHeight champTexteEmoticone.height) / 2; addChild ( champTexteEmoticone ); } } }

La figure 16-38 illustre le rsultat :

59 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-38. Affichage du moteur dmoticnes. Afin dcouter la saisie utilisateur et le dfilement du contenu, nous coutons les vnements Event.CHANGE et Event.SCROLL auprs du champ texte :
package org.bytearray.emoticones { import flash.display.Sprite; import flash.events.Event; import flash.text.TextField; public class MoteurEmoticone extends Sprite { public var contenuTexte:TextField; public function MoteurEmoticone () { ); ); } private function saisieUtilisateur ( pEvt:Event ):void { contenuTexte.addEventListener ( Event.CHANGE, saisieUtilisateur contenuTexte.addEventListener ( Event.SCROLL, saisieUtilisateur

60 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

// affiche : texte saisie trace( pEvt.target.text ); } } }

Lorsque du contenu est saisi dans le champ la mthode saisieUtilisateur est excute. Afin de savoir si lutilisateur a saisie une combinaison de touches correspondant une moticne, nous ajoutons un tableau nomm codesTouches contenant le code des combinaisons saisir afin dafficher une moticne :
package org.bytearray.emoticones { import flash.display.Sprite; import flash.events.Event; import flash.text.TextField; public class MoteurEmoticone extends Sprite { public var contenuTexte:TextField; public var codesTouches:Array; public var lngTouches:int; public function MoteurEmoticone () { codesTouches = new Array (":)",":D",":(",";)",":p"); lngTouches = codesTouches.length ); ); } private function saisieUtilisateur ( pEvt:Event ):void { // affiche : texte saisie trace( pEvt.target.text ); } } contenuTexte.addEventListener ( Event.CHANGE, saisieUtilisateur contenuTexte.addEventListener ( Event.SCROLL, saisieUtilisateur

61 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Au sein de la mthode saisieUtilisateur nous ajoutons une simple recherche de chaque combinaison laide de la mthode indexOf de la classe String :
private function saisieUtilisateur ( pEvt:Event ):void { var i:int; var j:int; for ( i = 0; i<lngTouches; i++ ) { j = pEvt.target.text.indexOf ( codesTouches[i] ); if ( j != -1 ) trace( "Combinaison trouve lindex : " + j ); } }

Si nous saisissons une combinaison contenue au sein du tableau codeTouches la variable j renvoie alors la position de la combinaison au sein du champ texte. En cas de saisie multiple, seule la premire combinaison est dtecte car la mthode indexOf ne renvoie que la premire chane trouve. Afin de corriger cela, nous ajoutons une boucle imbrique afin de vrifier les multiples combinaisons existantes au sein du champ :
private function saisieUtilisateur ( pEvt:Event ):void { var i:int; var j:int; for ( i = 0; i<lngTouches; i++ ) { j = pEvt.target.text.indexOf ( codesTouches[i] ); while ( j!= -1 ) { trace( "Combinaison trouve lindex : " + j ); j = pEvt.target.text.indexOf ( codesTouches[i], j+1 ); } } }

62 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Si nous testons le code prcdent nous remarquons que les multiples combinaisons sont dtectes. Grce la mthode getCharBoundaries nous pouvons rcuprer les coordonnes x et y de chaque combinaison au sein du champ contenuTexte. Nous ajoutons une proprit coordonnees au sein de la classe, puis nous modifions la mthode saisieUtilisateur :
package org.bytearray.emoticones { import import import import flash.display.Sprite; flash.events.Event; flash.geom.Rectangle; flash.text.TextField;

public class MoteurEmoticone extends Sprite { public public public public var var var var contenuTexte:TextField; codesTouches:Array; lngTouches:int; coordonnees:Rectangle;

public function MoteurEmoticone () { codesTouches = new Array (":)",":D",":(",";)",":p"); lngTouches = codesTouches.length ); ); } private function saisieUtilisateur ( pEvt:Event ):void { var i:int; var j:int; for ( i = 0; i<lngTouches; i++ ) { j = pEvt.target.text.indexOf ( codesTouches[i] ); while ( j!= -1 ) { coordonnees = pEvt.target.getCharBoundaries ( j ); contenuTexte.addEventListener ( Event.CHANGE, saisieUtilisateur contenuTexte.addEventListener ( Event.SCROLL, saisieUtilisateur

63 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

y=2, w=7, h=18) coordonnees );

// affiche : Combinaison trouve la position : (x=62, trace( "Combinaison trouve la position : " + j = pEvt.target.text.indexOf ( codesTouches[i], j+1 ); }

} } } }

La proprit coordonnees dtermine la position de chaque combinaison de touches. Nous devons prsent interprter ces coordonnes afin dafficher lmoticne correspondante. Nous modifions nouveau la mthode saisieUtilisateur en appelant la mthode ajouteEmoticone :
private function saisieUtilisateur ( pEvt:Event ):void { var i:int; var j:int; for ( i = 0; i<lngTouches; i++ ) { j = pEvt.target.text.indexOf ( codesTouches[i] ); while ( j!= -1 ) { coordonnees = pEvt.target.getCharBoundaries ( j ); ); if ( coordonnees != null ) ajouteEmoticone ( coordonnees, i j = pEvt.target.text.indexOf ( codesTouches[i], j+1 ); } } }

Puis nous dfinissons une proprit icone de type Emoticone ainsi que la mthode ajouteEmoticone :
private function ajouteEmoticone ( pRectangle:Rectangle, pIndex:int ):Emoticone {

64 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

icone = new Emoticone(); icone.gotoAndStop ( pIndex + 1 ); icone.x = pRectangle.x + 1; icone.y = pRectangle.y - ((contenuTexte.scrollV 1)*(contenuTexte.textHeight/contenuTexte.numLines))+1; addChild ( icone ); return icone; }

Si nous testons le code actuel, nous remarquons que les moticnes sont affiches correctement. Si nous revenons en arrire lors de la saisie nous devons supprimer les moticnes dj prsentes afin de ne pas les conserver laffichage. Nous devons dfinir une proprit tableauEmoticones contenant la rfrence de chaque moticne ajout, puis crer un nouveau tableau au sein du constructeur :
public function MoteurEmoticone () { tableauEmoticones = new Array(); codesTouches = new Array (":)",":D",":(",";)",":p"); lngTouches = codesTouches.length; contenuTexte.addEventListener ( Event.CHANGE, saisieUtilisateur ); contenuTexte.addEventListener ( Event.SCROLL, saisieUtilisateur ); }

A chaque saisie utilisateur nous supprimons toutes les moticnes cres auparavant afin de nettoyer laffichage :
private function saisieUtilisateur ( pEvt:Event ):void { var i:int; var j:int; var nombreEmoticones:int = tableauEmoticones.length; for ( i = 0; i< nombreEmoticones; i++ ) removeChild (tableauEmoticones[i] ); tableauEmoticones = new Array(); for ( i = 0; i<lngTouches; i++ ) { j = pEvt.target.text.indexOf ( codesTouches[i] );

65 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

while ( j!= -1 ) { coordonnees = pEvt.target.getCharBoundaries ( j ); if ( coordonnees != null ) tableauEmoticones.push ( ajouteEmoticone ( coordonnees, i ) ); j = pEvt.target.text.indexOf ( codesTouches[i], j+1 ); } } }

Voici le code complet de la classe MoteurEmoticone :


package org.bytearray.emoticones { import import import import flash.display.Sprite; flash.events.Event; flash.geom.Rectangle; flash.text.TextField;

public class MoteurEmoticone extends Sprite { public public public public public public var var var var var var contenuTexte:TextField; codesTouches:Array; lngTouches:int; coordonnees:Rectangle; icone:Emoticone; tableauEmoticones:Array;

public function MoteurEmoticone () { tableauEmoticones = new Array(); codesTouches = new Array (":)",":D",":(",";)",":p"); lngTouches = codesTouches.length; ); ); } private function saisieUtilisateur ( pEvt:Event ):void { var i:int; var j:int; contenuTexte.addEventListener ( Event.CHANGE, saisieUtilisateur contenuTexte.addEventListener ( Event.SCROLL, saisieUtilisateur

66 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

var nombreEmoticones:int = tableauEmoticones.length; for ( i = 0; i< nombreEmoticones; i++ ) removeChild (tableauEmoticones[i] ); tableauEmoticones = new Array(); for ( i = 0; i<lngTouches; i++ ) { j = pEvt.target.text.indexOf ( codesTouches[i] ); while ( j!= -1 ) { coordonnees = pEvt.target.getCharBoundaries ( j ); if ( coordonnees != null ) tableauEmoticones.push ( ajouteEmoticone ( coordonnees, i ) ); j = pEvt.target.text.indexOf ( codesTouches[i], j+1 ); } } } private function ajouteEmoticone ( pRectangle:Rectangle, pIndex:int ):Emoticone { icone = new Emoticone(); icone.gotoAndStop ( pIndex + 1 ); icone.x = pRectangle.x + 1; icone.y = pRectangle.y - ((contenuTexte.scrollV 1)*(contenuTexte.textHeight/contenuTexte.numLines))+1; addChild ( icone ); return icone; } } }

La classe MoteurEmoticone peut ainsi tre intgre un chat connect par XMLSocket ou Flash Media Server, nous la rutiliserons au cours du chapitre 18 intitul Sockets. Dautres moticnes pourraient tre intgres, ainsi que dautres fonctionnalits. Afin daller plus loin, nous allons crer un diteur de texte enrichi. Celui-ci nous permettra de mettre en forme du texte de manire simplifie. 67 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

A retenir
La mthode getCharBoundaries permet de connatre la position exate dun caractre au sein dun champ texte en retournant un objet Rectangle.

Crer un diteur de texte


Nous avons trs souvent besoin dditer du texte de manire avane au sein dapplications Flash. La classe TextField a t grandement enrichie en ActionScript 3. De nombreuses mthodes et proprits facilitent le dveloppement dapplications ayant recours au texte. Afin de mettre en application les notions prcdemment tudies, nous allons dvelopper prsent un diteur de texte. La figure 16-39 illustre lditeur en question :

Figure 16-39. Editeur de texte. Nous allons utiliser pour sa conception les diffrentes proprits et mthodes listes ci-dessous :
setTextFormat : la mthode setTextFormat nous permet daffecter un style particulier une slection ; getTextFormat : la mthode getTextFormat nous permet de rcuprer un style existant li une slection afin de le modifier ; selectionBeginIndex : la proprit selectionBeginIndex permet de connatre le debut de selection du texte ; selectionEndIndex : la proprit selectionEndIndex permet de connatre la fin de slection du texte ; alwaysShowSelection : la proprit alwaysShowSelection permet de conserver le texte slectionn bien que la souris clique sur un autre lment interactif ;

Nous commenons par crer la classe EditeurTexte suivante au sein du paquetage org.bytearray.richtext :
package org.bytearray.texte.editeur { import flash.display.Sprite; import flash.events.Event;

68 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

public class EditeurTexte extends Sprite { public function EditeurTexte () { addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { trace("activation"); } private function desactivation ( pEvt:Event ):void { trace("desactivation"); } } }

Nous

dsactivation de lditeur.

coutons les vnements Event.ADDED_TO_STAGE et Event.REMOVED_FROM_STAGE afin de grer lactivation et la

Sur la scne nous crons un clip contenant les diffrents boutons graphiques comme lillustre la figure 16-40 :

Figure 16-40. Editeur de texte enrichi. Grace au panneau Proprits de liaison, nous lions ce clip notre classe EditeurTexte prcdemment dfinie. Nous ajoutons les couteurs auprs des diffrents boutons de linterface au sein de la classe EditeurTexte :
package org.bytearray.texte.editeur {

69 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

import import import import import import import import

flash.display.Sprite; flash.display.SimpleButton; flash.text.TextField; flash.events.Event; flash.events.MouseEvent; fl.controls.ComboBox; fl.controls.ColorPicker; fl.events.ColorPickerEvent;

public class EditeurTexte extends Sprite { public public public public public public public public public public public public var var var var var var var var var var var var boutonAlignerGauche:SimpleButton; boutonAlignerCentre:SimpleButton; boutonAlignerDroite:SimpleButton; boutonAlignerJustifier:SimpleButton; boutonCrenage:SimpleButton; boutonGras:SimpleButton; boutonItalique:SimpleButton; boutonSousLigne:SimpleButton; boutonLien:SimpleButton; champLien:TextField; listeDeroulantesPolices:ComboBox; nuancier:ColorPicker;

public function EditeurTexte () { addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { boutonGras.addEventListener( MouseEvent.CLICK, affecteGras ); boutonItalique.addEventListener( MouseEvent.CLICK, affecteItalique ); boutonSousLigne.addEventListener( MouseEvent.CLICK, affecteSousLigne ); boutonAlignerGauche.addEventListener( MouseEvent.CLICK, alignerGauche ); boutonAlignerCentre.addEventListener( MouseEvent.CLICK, alignerCentre ); boutonAlignerDroite.addEventListener( MouseEvent.CLICK, alignerDroite ); boutonAlignerJustifier.addEventListener( MouseEvent.CLICK, alignerJustifier ); nuancier.addEventListener ( ColorPickerEvent.CHANGE, couleurSelection ); boutonCrenage.addEventListener ( MouseEvent.CLICK, affecteCrenage ); listeDeroulantesPolices.addEventListener ( Event.CHANGE, affectePolice ); boutonLien.addEventListener ( MouseEvent.CLICK, affecteLien ); }

70 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

private function desactivation ( pEvt:Event ):void { boutonGras.removeEventListener( MouseEvent.CLICK, affecteGras ); boutonItalique.removeEventListener( MouseEvent.CLICK, affecteItalique ); boutonSousLigne.removeEventListener( MouseEvent.CLICK, affecteSousLigne ); boutonAlignerGauche.removeEventListener( MouseEvent.CLICK, alignerGauche ); boutonAlignerCentre.removeEventListener( MouseEvent.CLICK, alignerCentre ); boutonAlignerDroite.removeEventListener( MouseEvent.CLICK, alignerDroite ); boutonAlignerJustifier.removeEventListener( MouseEvent.CLICK, alignerJustifier ); nuancier.removeEventListener ( ColorPickerEvent.CHANGE, couleurSelection ); boutonCrenage.removeEventListener ( MouseEvent.CLICK, affecteCrenage ); listeDeroulantesPolices.removeEventListener ( Event.CHANGE, affectePolice ); boutonLien.removeEventListener ( MouseEvent.CLICK, affecteLien ); } function affectePolice ( pEvt:Event ):void { } function affecteCrenage ( pEvt:MouseEvent ):void { } function alignerJustifier ( pEvt:MouseEvent ):void { } function couleurSelection ( pEvt:ColorPickerEvent ):void { } function alignerGauche ( pEvt:MouseEvent ):void { } function alignerCentre ( pEvt:MouseEvent ):void {

71 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

} function alignerDroite ( pEvt:MouseEvent ):void { } function affecteGras ( pEvt:MouseEvent ):void { } function affecteItalique ( pEvt:MouseEvent ):void { } function affecteSousLigne ( pEvt:MouseEvent ):void { } private function affecteLien ( pEvt:MouseEvent ):void { } } }

Nous remplissons la liste droulante en numrant les polices installes. Nous dfinissons une mthode affichePolices :
private function affichePolices ():void { var polices:Array = Font.enumerateFonts(true); var donnees:Array = new Array(); for ( var p:String in polices ) donnees.push ( { label : polices[p].fontName, data : polices[p].fontName } ); listeDeroulantesPolices.dataProvider = new DataProvider ( donnees ); }

Puis nous importons les classes ncessaires :


import flash.text.Font; import fl.data.DataProvider;

Nous modifions la mthode activation en appelant la mthode affichePolices : 72 / 82


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

private function activation ( pEvt:Event ):void { boutonGras.addEventListener( MouseEvent.CLICK, affecteGras ); boutonItalique.addEventListener( MouseEvent.CLICK, affecteItalique ); boutonSousLigne.addEventListener( MouseEvent.CLICK, affecteSousLigne boutonAlignerGauche.addEventListener( MouseEvent.CLICK, alignerGauche boutonAlignerCentre.addEventListener( MouseEvent.CLICK, alignerCentre boutonAlignerDroite.addEventListener( MouseEvent.CLICK, alignerDroite

); ); ); );

boutonAlignerJustifier.addEventListener( MouseEvent.CLICK, alignerJustifier ); nuancier.addEventListener ( ColorPickerEvent.CHANGE, couleurSelection ); boutonCrenage.addEventListener ( MouseEvent.CLICK, affecteCrenage ); listeDeroulantesPolices.addEventListener ( Event.CHANGE, affectePolice ); boutonLien.addEventListener ( MouseEvent.CLICK, affecteLien ); affichePolices(); }

Si nous testons notre application, la liste droulante de lditeur enrichi doit afficher la totalit des polices prsentes sur la machine client comme lillustre la figure 16-41 :

Figure 16-41. Editeur de texte enrichi. Afin de stocker le style en cours nous dfinissons une proprit formatEnCours :
private var formatEnCours:TextFormat;

Pour cela, nous ajoutons une proprit champCible :


private var champCible:TextField;

Puis une mthode cible permettant de spcifier le champ texte enrichir :


public function cible ( pChamp:TextField ):void { if ( pChamp != champCible ) { champCible = pChamp; champCible.alwaysShowSelection = true; }

73 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

La logique de lditeur est quasi complte, il ne nous reste plus qu dfinir la logique ncessaire au sein de chaque fonction daffectation de style. Voici le code complet de la classe EditeurTexte :
package org.bytearray.texte.editeur { import import import import import import import import import import import import flash.display.Sprite; flash.display.SimpleButton; flash.text.TextField; flash.text.TextFormat; flash.text.TextFormatAlign; flash.events.Event; flash.events.MouseEvent; fl.controls.ComboBox; fl.controls.ColorPicker; fl.events.ColorPickerEvent; flash.text.Font; fl.data.DataProvider;

public class EditeurTexte extends Sprite { public public public public public public public public public public public public var var var var var var var var var var var var boutonAlignerGauche:SimpleButton; boutonAlignerCentre:SimpleButton; boutonAlignerDroite:SimpleButton; boutonAlignerJustifier:SimpleButton; boutonCrenage:SimpleButton; boutonGras:SimpleButton; boutonItalique:SimpleButton; boutonSousLigne:SimpleButton; boutonLien:SimpleButton; champLien:TextField; listeDeroulantesPolices:ComboBox; nuancier:ColorPicker;

private var formatEnCours:TextFormat; private var champCible:TextField; public function EditeurTexte () { addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { boutonGras.addEventListener( MouseEvent.CLICK, affecteGras );

74 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

boutonItalique.addEventListener( MouseEvent.CLICK, affecteItalique ); boutonSousLigne.addEventListener( MouseEvent.CLICK, affecteSousLigne ); boutonAlignerGauche.addEventListener( MouseEvent.CLICK, alignerGauche ); boutonAlignerCentre.addEventListener( MouseEvent.CLICK, alignerCentre ); boutonAlignerDroite.addEventListener( MouseEvent.CLICK, alignerDroite ); boutonAlignerJustifier.addEventListener( MouseEvent.CLICK, alignerJustifier ); nuancier.addEventListener ( ColorPickerEvent.CHANGE, couleurSelection ); boutonCrenage.addEventListener ( MouseEvent.CLICK, affecteCrenage ); listeDeroulantesPolices.addEventListener ( Event.CHANGE, affectePolice ); boutonLien.addEventListener ( MouseEvent.CLICK, affecteLien ); affichePolices(); } public function cible ( pChamp:TextField ):void { if ( pChamp != champCible ) { champCible = pChamp; champCible.alwaysShowSelection = true; } } private function desactivation ( pEvt:Event ):void { boutonGras.removeEventListener( MouseEvent.CLICK, affecteGras ); boutonItalique.removeEventListener( MouseEvent.CLICK, affecteItalique ); boutonSousLigne.removeEventListener( MouseEvent.CLICK, affecteSousLigne ); boutonAlignerGauche.removeEventListener( MouseEvent.CLICK, alignerGauche ); boutonAlignerCentre.removeEventListener( MouseEvent.CLICK, alignerCentre ); boutonAlignerDroite.removeEventListener( MouseEvent.CLICK, alignerDroite ); boutonAlignerJustifier.removeEventListener( MouseEvent.CLICK, alignerJustifier ); nuancier.removeEventListener ( ColorPickerEvent.CHANGE, couleurSelection ); boutonCrenage.removeEventListener ( MouseEvent.CLICK, affecteCrenage ); listeDeroulantesPolices.removeEventListener ( Event.CHANGE, affectePolice ); boutonLien.removeEventListener ( MouseEvent.CLICK, affecteLien );

75 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

} private function affichePolices ():void { var polices:Array = Font.enumerateFonts(true); var donnees:Array = new Array(); for (var p in polices ) donnees.push ( { label : polices[p].fontName, data : polices[p].fontName } ); (donnees); } function affecteGras ( pEvt:MouseEvent ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.bold = !formatEnCours.bold; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function affecteItalique ( pEvt:MouseEvent ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.italic = !formatEnCours.italic; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function affecteSousLigne ( pEvt:MouseEvent ):void { listeDeroulantesPolices.dataProvider = new DataProvider

76 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.underline = !formatEnCours.underline; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function alignerGauche ( pEvt:MouseEvent ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.align = ( formatEnCours.align != TextFormatAlign.LEFT ) ? TextFormatAlign.LEFT : TextFormatAlign.LEFT; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function alignerCentre ( pEvt:MouseEvent ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.align = ( formatEnCours.align != TextFormatAlign.CENTER ) ? TextFormatAlign.CENTER : TextFormatAlign.LEFT; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function alignerDroite ( pEvt:MouseEvent ):void {

77 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.align = ( formatEnCours.align != TextFormatAlign.RIGHT ) ? TextFormatAlign.RIGHT : TextFormatAlign.LEFT; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function alignerJustifier ( pEvt:MouseEvent ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.align = ( formatEnCours.align != TextFormatAlign.JUSTIFY ) ? TextFormatAlign.JUSTIFY : TextFormatAlign.LEFT; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function couleurSelection ( pEvt:ColorPickerEvent ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.color = pEvt.color; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function affecteCrenage ( pEvt:MouseEvent ):void {

78 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.kerning = !formatEnCours.kerning; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } function affectePolice ( pEvt:Event ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); formatEnCours.font = ( formatEnCours.font != pEvt.target.selectedItem.data ) ? pEvt.target.selectedItem.data : "Verdana"; champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } private function affecteLien ( pEvt:MouseEvent ):void { if ( champCible != null ) { formatEnCours = champCible.getTextFormat( champCible.selectionBeginIndex, champCible.selectionEndIndex ); : ""; formatEnCours.url = formatEnCours.url == "" ? champLien.text

champCible.setTextFormat( formatEnCours, champCible.selectionBeginIndex, champCible.selectionEndIndex ); } else throw new Error ("Le champ cible n'a pas t dfini."); } } }

79 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Afin de tester notre diteur, nous associons la classe de document suivante :


package org.bytearray.document { import import import import flash.display.Sprite; flash.text.TextField; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.texte.editeur.EditeurTexte;

public class Document extends ApplicationDefaut { private var editeur:EditeurTexte; public var texteCible:TextField; public function Document () { // instanciation de l'diteur enrichi editeur = new EditeurTexte(); // positionnement editeur.x = editeur.y = 15; // ajout l'affichage addChild ( editeur ); // affectation du champ texte cible editeur.cible ( texteCible ); } } }

La figure 16-42 illustre lditeur de texte en action :

80 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Figure 16-42. Editeur de texte finalis. Il convient de sattarder quelques instants sur la proprit alwaysShowSelection de la classe TextField. ActionScript 3 introduit cette proprit facilitant grandement le dveloppement dun diteur comme celui que nous venons de terminer. La puissance de cette proprit rside dans la conservation de la slection du texte bien que lutilisateur entre en interaction avec dautres lments graphiques. Cela nous permet de rcuprer facilement la slection en cours laide des proprits selectionBeginIndex et selectionEndIndex et daffecter le style voulu. Dautres fonctionnalits pourraient tre ajoutes lditeur, nous pourrions imaginer un export de la mise en forme du contenu texte sous la forme de feuille de style CSS, ou encore dautres fonctionnalits lies la mise en forme.

A retenir

81 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 16 Le texte version 0.1.1

Les proprits selectionBeginIndex et selectionEndIndex nous permettent de connatre la selection en cours. Grace aux mthodes getTextFormat et setTextFormat, nous pouvons rcuprer le style dune partie du texte, le modifier et laffecter nouveau. La proprit alwaysShowSelection permet de conserver la selection du texte, mme si la souris entre en intraction avec dautres objets interactifs.

Au cours du prochain chapitre nous dcouvrirons les nouvelles fonctionnalits apportes par ActionScript 3 en matire de son et de vido.

82 / 82
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

17
Son et vido

LECTURE DE SONS .............................................................................................. 1 LIRE UN SON PROVENANT DE LA BIBLIOTHEQUE ..................................................... 2 LIRE UN SON DYNAMIQUE ....................................................................................... 4 LA CLASSE SOUNDLOADERCONTEXT ..................................................................... 7 TRANSFORMATION DU SON ..................................................................................... 9 MODIFICATION GLOBALE DU SON ......................................................................... 30 LIRE LE SPECTRE DUN SON ................................................................................... 31 TRANSFORME DE FOURIER .................................................................................. 55 LE FORMAT MPEG-4 AUDIO ................................................................................ 58 LA VIDEO DANS FLASH .................................................................................... 62 LE FORMAT MPEG-4 VIDEO ................................................................................. 63 LA CLASSE VIDEO ................................................................................................. 65 TRANSFORMATION DU SON LIE A UN OBJET NETSTREAM ...................................... 69 MODE PLEIN-ECRAN .............................................................................................. 70

Lecture de sons
Le son fut dabord considr comme un lment secondaire sur le web. La situation sest inverse peu peu jusqu aujourdhui o le lecteur Flash figure parmi les principaux promoteurs du son. De nombreuses socits ont dailleurs choisi le lecteur Flash comme plateforme de diffusion ou de lecture. Nous allons dcouvrir au cours de ce chapitre comment utiliser les diffrentes classes lies au son et la vido en ActionScript 3, afin dintgrer au mieux ces supports dans nos applications Flash. Nous allons nous attarder ds prsent sur les diffrentes classes lies au son dans Flash :
flash.media.Sound : la classe Sound permet la lecture de sons.

1 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

flash.media.SoundTransform : la classe SoundTransform permet de modifier le son (volume, balance). flash.media.SoundChannel : la classe SoundChannel permet de contrler le son. Chaque son en cours de lecture est associ un objet SoundChannel. flash.media.LoaderContext : la classe LoaderContext est lie au prchargement et au modle de securit du lecteur Flash.

flash.media.SoundMixer : la classe SoundMixer offre un contrle global sur les sons en cours de lecture. flash.net.NetConnection : la classe NetConnection est utilise pour le chargement de fichiers audio MPEG-4. flash.net.NetStream : la classe NetStream est utilise pour la manipulation de fichiers audio MPEG-4.

La liste peut paratre longue, mais nous verrons que leur utilisation savre dune tonnante simplicit.

Lire un son provenant de la bibliothque


Afin dentamer notre aventure, nous allons commencer par lire un son provenant de la bibliothque. Pour cela, nous utilisons le raccourci clavier CTRL+R ou loption Importer dans la bibliothque. Par le panneau Liaisons, nous associons le son en bibliothque une classe auto gnre Rythmique, celle-ci tend la classe Sound :

Figure 17-1. Son associe la classe Rythmique auto gnre. Le son est instanci laide de loprateur new :
// instanciation du son var rythme:Rythmique = new Rythmique();

2 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Afin de dmarrer la lecture du son cr, nous utilisons la mthode play dont voici la signature :
public function play(startTime:Number = 0, loops:int = 0, sndTransform:SoundTransform = null):SoundChannel

Celle-ci accepte trois paramtres :


startTime : la position en milli secondes partir de laquelle la lecture doit commencer. loops : nombre de boucles. sndTransform : un objet de transformation associ au son jou. Nous reviendrons trs vite sur la classe SoundTransform.

Contrairement la classe Sound prsente en ActionScript 1 et 2, la classe Sound ne dispose pas en ActionScript 3 de mthode stop. Lappel de la mthode play affecte le son jou un canal audio, et retourne un objet de type SoundChannel reprsentant ce canal. Cest grce ce dernier que nous pouvons contrler le son et larrter. Notons que le lecteur Flash 9 peut lire dsormais jusqu 32 canaux simultans. Dans le code suivant, nous lisons le son depuis le dpart une seule fois seulement :
// instanciation du son var rythme:Rythmique = new Rythmique(); // lecture du son et recupration de l'objet SoundChannel associ au son var canalRythme:SoundChannel = rythme.play();

En spcifiant le paramtre startTime, nous pouvons dcaler le point de dpart de la lecture du son :
var canalRythme:SoundChannel = rythme.play( 6000 );

Le son est ainsi lu partir de la sixime seconde. Par dfaut, le son est jou une seule fois, mais nous pouvons spcifier un nombre de boucles grce au deuxime paramtre loops :
var canalRythme:SoundChannel = rythme.play( 6000, 2 );

Notons que si un nombre de boucles est spcifi ainsi quune position de dpart, celle-ci est conserve lors de la boucle. Lors de la lecture en boucle dun son, la proprit position de lobjet SoundChannel ne se rinitialise pas zro. Pour un son dune dure de 5000 millisecondes lu en boucle 3 fois, celle-ci vaut 15 000 ms en

3 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

fin de lecture. Il sagit dun bogue du lecteur Flash 9.0.115. Lobjet SoundChannel tant renvoy par lappel de la mthode play, il nest pas possible dtendre la classe SoundChannel afin de corriger ce bogue. A laide de la mthode stop dfinie par la classe SoundChannel, nous stoppons le son lorsque lutilisateur clique sur la scne :
// instanciation du son var rythme:Rythmique = new Rythmique(); // lecture du son et rcupration de l'objet SoundChannel associ au son var canalRythme:SoundChannel = rythme.play( 6000, 2 ); stage.addEventListener( MouseEvent.CLICK, stoppeSon ); function stoppeSon ( pEvt:MouseEvent ):void { // le son est coup canalRythme.stop(); }

Afin de garantir un poids minimum et dassurer un chargement la demande, il peut tre intressant de charger dynamiquement les sons de lapplication, cest ce que nous allons voir dans cette nouvelle partie.

A retenir
La mthode play dmarre la lecture du son et lassocie un canal audio reprsent par un objet SoundChannel. Le lecteur Flash 9 permet la lecture de 32 sons simultans. La lecture du son associ au canal est interrompue grce la mthode stop de lobjet SoundChannel.

Lire un son dynamique


Le chargement de son peut tre ralis de manire dynamique. Les fichiers audio rsident lextrieur de lanimation et sont charges dynamiquement. Deux techniques peuvent tre employes : La premire consiste crer un objet URLRequest valide pointant vers le fichier MP3 et passer ce dernier au constructeur de la classe Sound : 4 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

// cration d'un objet Sound, la mthode load est automatiquement appele var monSon:Sound = new Sound ( new URLRequest ("son.mp3") );

Si la classe Sound dtecte un objet URLRequest valide, le son est charg automatiquement au sein du lecteur laide de la mthode load. Le cas chant, lappel de la mthode load est obligatoire pour dmarrer le chargement du son. La mthode load possde la signature suivante :
public function load(stream:URLRequest, context:SoundLoaderContext = null):void

Voici le dtail de chacun des paramtres :


stream : un objet URLRequest pointant vers le fichier MP3 charger. context : un objet SoundLoaderContext spcifiant la dure de prchargement en mmoire tampon ainsi que des consignes lies au chargement de fichiers de rgulation.

Attention, contrairement lintgration de son au sein de la bibliothque compatible avec la plupart des formats audio, la mthode load de la classe Sound permet uniquement le chargement de fichiers MP3. Si nous tentons de charger un autre format de fichiers audio, aucune erreur spcifique nest leve, le son nest pas jou. La seconde technique consiste crer lobjet Sound puis appeler manuellement la mthode load. Dans le code suivant nous ne passons pas dobjet URLRequest au constructeur de la classe Sound, le son est charg laide de la mthode load :
// cration d'un objet Sound var monSon:Sound = new Sound(); // chargement dynamique du son son.load ( new URLRequest ("son.mp3") );

Afin de grer le chargement de son dynamique, lobjet Sound diffuse diffrents vnements dont voici le dtail :
Event.COMPLETE : diffus lorsque le chargement du son est termin. Event.ID3: diffus lorsque les informations ID3 sont disponibles. IOErrorEvent.IO_ERROR : diffus lorsque le chargement du son choue. ProgressEvent.PROGRESS : diffus lorsque le chargement est en cours. Celui-ci renseigne sur le nombre doctets chargs et totaux. Event.OPEN : diffus lorsque le lecteur commence charger le son.

Dans le code suivant nous coutons chacun des vnements : 5 / 73


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

// instanciation d'un objet Sound var son:Sound = new Sound(); // chargement dynamique du son son.load ( new URLRequest ("son.mp3") ); // coute des diffrents vnements son.addEventListener( Event.OPEN, chargementDemarre ); son.addEventListener( Event.ID3, informationsID3 ); son.addEventListener( ProgressEvent.PROGRESS, chargementEnCours ); son.addEventListener( Event.COMPLETE, chargementTermine ); son.addEventListener( IOErrorEvent.IO_ERROR, erreurChargement ); function chargementDemarre ( pEvt:Event ):void { trace("chargement dmarr"); } function informationsID3 ( pEvt:Event ):void { trace("informations ID3"); } function chargementEnCours ( pEvt:ProgressEvent ):void { ); } function chargementTermine ( pEvt:Event ):void { trace("chargement termin"); } function erreurChargement ( pEvt:IOErrorEvent ):void { trace("erreur de chargement"); } trace("chargement en cours : " + pEvt.bytesLoaded + " / " + pEvt.bytesTotal

Quelle que soit la technique employe pour charger dynamiquement un son, sa lecture doit tre initie laide de la mthode play. Si celle-ci est appele en mme temps que la mthode load, la lecture du son est entame lorsque suffisamment de donnes audio ont t tlcharges. Si nous tentons de dmarrer la lecture du son avant lavoir charg, une erreur de type ArgumentError est leve :
ArgumentError: Error #2068: Son non valide

6 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Attention, dans un contexte de chargement dynamique, la mthode load ne renvoie pas dobjet SoundChannel. Seule la mthode play le permet :
// instanciation d'un objet Sound, la mthode load est automatiquement appele var son:Sound = new Sound ( new URLRequest ("son.mp3") ); // lecture du son, la mthode play retourne un objet SoundChannel var canalSon:SoundChannel = son.play();

Il est important de noter que le chargement dynamique de sons est rgit par le modle de scurit du lecteur. Par dfaut, le chargement et la lecture dun fichier son provenant dun domaine diffrent est autorise, mais laccs aux donnes du fichier son est rgule. Dans un contexte inter-domaine, lutilisation de la proprit id3 de lobjet Sound, de la mthode computeSpectrum, de la classe SoundMixer ou de lobjet SoundTransform lve une erreur de type SecurityError. Il convient alors dutiliser un fichier de rgulation sur le domaine distant afin dautoriser la manipulation du son. Rappelez-vous que le lecteur Flash ne charge pas automatiquement de fichier de rgulation afin de limiter la bande passante utilise. Nous avons vu au cours du chapitre 13 intitul Chargement de contenu que la classe LoaderContext permettait de spcifier si le lecteur Flash devait tenter de charger un fichier de rgulation. Une classe quivalente nomme SoundLoaderContext existe au sein du paquetage flash.media dans le cas de chargement de son dynamique.

A retenir
Le chargement dun son dynamique est assur par la mthode load de la classe Sound. Si la mthode play est appele aprs lappel de la mthode load, la lecture du son dmarre lorsque le lecteur Flash a charg suffisamment de donnes.

La classe SoundLoaderContext
Lorsquun son est charg depuis un domaine diffrent, celui-ci peut seulement tre charg et lu. Afin dextraire des informations du son, ou dutiliser des mthodes telles computeSpectrum de la classe 7 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

SoundMixer, un fichier de rgulation doit tre plac sur le serveur

lhbergeant afin dautoriser le SWF ayant initi le chargement.

Nous avions utilis au cours du chapitre 13 la classe LoaderContext afin dindiquer au lecteur Flash de charger un fichier de rgulation. Dans le cas de chargement de fichiers audio, nous devons utiliser la classe SoundLoaderContext dont voici la signature du constructeur :
public function SoundLoaderContext(bufferTime:Number = 1000, checkPolicyFile:Boolean = false)

Voici le dtail de chaque paramtre :


bufferTime : le paramtre bufferTime permet de dfinir le temps de prchargement en mmoire tampon avant que la lecture du son ne dmarre. La valeur par dfaut est de 1000 milli-secondes. Cette fonctionnalit permet de charger lavance quelques secondes du flux afin dassurer une lecture ininterrompue en cas de problme de connexion. checkPolicyFile : en passant la valeur true au paramtre checkPolicyFile nous demandons au lecteur Flash de tlcharger un fichier de rgulation la racine du serveur hbergeant le fichier audio.

Dans le code suivant, nous chargeons un son hberg sur un domaine distant, nous forons le tlchargement dun fichier de rgulation :
// cration d'un objet son et chargement d'un mp3 var monSon:Sound = new Sound (); // cration d'un objet SoundLoaderContext // 5 secondes sont mises en mmoire tampon // le lecteur Flash tente de charger un fichier de rgulation var contexteAudio:SoundLoaderContext = new SoundLoaderContext ( 5000, true ); // chargement d'un son auprs d'un domaine distant monSon.load ( new URLRequest ("http://www.monDomaineDistant.org/son.mp3") // lecture du son var canalSon:SoundChannel = monSon.play(); );

Au lieu dinitialiser lobjet SoundLoaderContext par les paramtres du constructeur, les proprits quivalentes peuvent tre utilises :
// cration d'un objet SoundLoaderContext var contexteAudio:SoundLoaderContext = new SoundLoaderContext (); // 5 secondes sont mises en mmoire tampon contexteAudio.bufferTime = 5000; // le lecteur Flash tente de charger un fichier de rgulation contexteAudio.checkPolicyFile = true; // chargement d'un son auprs d'un domaine distant

8 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

monSon.load ( new URLRequest ("http://www.monDomaineDistant.org/son.mp3")

);

Rappelez-vous, par dfaut le lecteur Flash tente de charger le fichier de rgulation la racine du serveur. Dans le code prcdent, le lecteur Flash tentera de charger le fichier de rgulation ladresse suivante :
http://www.monDomaineDistant.org/crossdomain.xml

Si nous souhaitons spcifier un emplacement diffrent, nous utiliserons la mthode loadPolicyFile dfinie par la classe flash.system.Security.

A retenir
La classe SoundLoaderContext permet de prciser la dure de mise en mmoire tampon ainsi quune indication concernant le chargement du fichier de rgulation.

Transformation du son
Afin de modifier le volume ou la balance dun son, nous devons utiliser la classe SoundTransform. Le moyen le plus simple pour modifier le son est dextraire lobjet de transformation audio par la proprit soundTransform de lobjet SoundChannel :
// instanciation d'un objet Sound, la mthode load est automatiquement appele var son:Sound = new Sound ( new URLRequest ("son.mp3") ); // lecture du son var canalSon:SoundChannel = son.play(); // rcupration de l'objet SoundTransform associ au son en cours de lecture var transformationSon:SoundTransform = canalSon.soundTransform;

Diffrentes proprits sont dfinies par la classe SoundTransform dont voici le dtail :
leftToLeft : indique la quantit d'entre gauche mettre dans le haut-parleur gauche. leftToRight : indique la quantit d'entre gauche mettre dans le haut-parleur droit. pan : dfinit la balance du son, la valeur du paramtre varie de -1 1. rightToLeft : indique la quantit d'entre droite mettre dans le haut-parleur gauche. rightToRight : indique la quantit d'entre droite mettre dans le haut-parleur droit.

9 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

volume : dtermine la puissance du volume. Le paramtre varie entre 0 pour un son nul, et 1 pour le volume maximal.

Il est important de noter que la modification du son ne se fait plus par lintermdiaire de mthodes telles setVolume ou setPan ou autres. Pour modifier le volume dun son, nous devons procder en plusieurs tapes prcises :
1. Crer ou rcuprer un objet de transformation SoundTransform. 2. Modifier la proprit volume de ce dernier. 3. Affecter nouveau lobjet de transformation la proprit soundTransform de lobjet SoundChannel.

Dans le code suivant nous rduisons le volume du son en cours de lecture de 50% :
// instanciation d'un objet Sound, la mthode load est automatiquement appele var son:Sound = new Sound ( new URLRequest ("son.mp3") ); // lecture du son var canalSon:SoundChannel = son.play(); // rcupration de l'objet SoundTransform associ au son en cours de lecture var transformationSon:SoundTransform = canalSon.soundTransform; // rduction du volume de 50% transformationSon.volume = .5; // application de l'objet de transformation canalSon.soundTransform = transformationSon

De la mme manire nous pouvons modifier la balance horizontale du son laide de la proprit pan :
// instanciation d'un objet Sound, la mthode load est automatiquement appele var son:Sound = new Sound ( new URLRequest ("son.mp3") ); // lecture du son var canalSon:SoundChannel = son.play(); // rcupration de l'objet SoundTransform associ au son en cours de lecture var transformationSon:SoundTransform = canalSon.soundTransform; // rduction du volume de 50% transformationSon.volume = .5; // passage de la totalit du son dans le canal droit transformationSon.pan = 1; // application de l'objet de transformation canalSon.soundTransform = transformationSon;

Nous nous rendons compte que la manipulation du son ne savre pas simplifie, il serait intressant de concevoir une classe approprie permettant de rendre transparentes toutes ces manipulations. 10 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Pour

nous allons concevoir une classe nomme Amplificateur. Celle-ci contiendra diffrentes mthodes telles affecteVolume, affecteBalance, recupereVolume et recupereBalance. Dans un paquetage org.bytearray.media nous crons la classe Amplificateur suivante :
package org.bytearray.media { public class Amplificateur { public function Amplificateur () { } } }

cela,

Le constructeur de la classe Amplificateur ncessite en paramtre le canal audio modifier. Afin de laccueillir nous ajoutons un paramtre pCanal :
package org.bytearray.media { import flash.media.SoundChannel; public class Amplificateur { private var canalSon:SoundChannel; public function Amplificateur ( pCanal:SoundChannel ) { canalSon = pCanal; } } }

Puis nous ajoutons les mthodes spcifiques permettant de modifier le volume ou la balance :
package org.bytearray.media

11 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

{ import flash.media.SoundChannel; import flash.media.SoundTransform; public class Amplificateur { private var canalSon:SoundChannel; private var transformation:SoundTransform; public function Amplificateur ( pCanal:SoundChannel ) { canalSon = pCanal; transformation = pCanal.soundTransform; } public function affecteVolume ( pVolume:Number ):void { transformation.volume = pVolume; canalSon.soundTransform = transformation; } public function recupereVolume ():Number { return canalSon.soundTransform.volume; } public function affecteBalance ( pBalance:Number ):void { transformation.pan = pBalance; canalSon.soundTransform = transformation; } public function recupereBalance ():Number { return canalSon.soundTransform.pan; } } }

12 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

La classe Amplificateur soccupe uniquement de la transformation du son. La cration de lobjet Sound ne lui est pas associe, cela rendrait notre classe rigide. La modification du volume ou de la balance est ainsi simplifie, dans le code suivant nous rduisons le volume de 50 % :
// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // instanciation d'un objet Sound, la mthode load est automatiquement appele var son:Sound = new Sound ( new URLRequest ("son.mp3") ); // lecture du son var canalSon:SoundChannel = son.play(); // cration du mixeur de son var monAmpli:Amplificateur = new Amplificateur( canalSon ); // rduction du volume de 50% monAmpli.affecteVolume ( .5 );

De la mme manire, nous pouvons modifier la balance horizontale du son laide de la mthode affecteBalance :
// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // instanciation d'un objet Sound, la mthode load est automatiquement appele var son:Sound = new Sound ( new URLRequest ("son.mp3") ); // lecture du son var canalSon:SoundChannel = son.play(); // cration du mixeur de son var monAmpli:Amplificateur = new Amplificateur( canalSon ); // modification de la balance horizontale du son dans le haut parleur droit monAmpli.affecteBalance ( 1 );

Nous allons enrichir la classe Amplificateur en ajoutant une nouvelle mthode nomme appliqueEffet. Celle-ci permettra lajout deffets appliqus aux sons. Nous allons revoir quelques notions essentielles propre la programmation oriente objet dans cet exemple. Lide est de pouvoir passer la mthode appliqueEffet un effet spcifique. Le code suivant illustre le concept :
// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // import de la classe d'effet Fondu import org.bytearray.media.effets.Fondu; var son:Sound = new Sound ( new URLRequest ("son.mp3") ); var canalSon:SoundChannel = son.play();

13 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

var monAmpli:Amplificateur = new Amplificateur( canalSon ); // cration d'un effet de fondu var monEffet:Fondu = new Fondu (); // application de l'effet monAmpli.appliqueEffet ( monEffet );

Nous pourrions alors imaginer de signer la mthode appliqueEffet en spcifiant un paramtre de type Fondu :
public function appliqueEffet ( pEffet:Fondu ):void { }

Malheureusement, cette orientation verrait rapidement ses limites, car nous ne pourrions passer en paramtre que des effets de type Fondu. Afin de pouvoir passer nimporte quel type deffets, nous devons trouver un type commun tous les effets et typer notre paramtre du mme type. Afin de bnficier dun type commun, nous pensons immdiatement la notion dhritage traite lors du chapitre 8 intitul Programmation oriente objet. Nous allons donc crer une classe EffetSonore au sein du paquetage org.bytearray.media.effets dont toutes les classes deffets devront hriter. Celle-ci dfinit une mthode executeEffet que toutes les classes enfants doivent surcharger afin dimplmenter leur propre effet :
package org.bytearray.media.effets { import flash.media.SoundChannel; public class EffetSonore { public function EffetSonore () { } public function executeEffet ( pCanal:SoundChannel ):void { }

14 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

} }

Puis nous tendons la classe EffetSonore travers la classe Fondu tout en surchargeant la mthode executeEffet :
package org.bytearray.media.effets { import flash.media.SoundChannel; public class Fondu extends EffetSonore { public function Fondu () { } override public function executeEffet ( pCanal:SoundChannel ):void { trace("application de l'effet sonore"); } } }

Grce cette approche, les classes deffets devront toujours hriter de la classe EffetSonore et possderont ainsi ce type commun. Nous pouvons dsormais ajouter une mthode appliqueEffet la classe Amplificateur en utilisant le type commun EffetSonore en paramtre :
package org.bytearray.media { import flash.media.SoundChannel; import flash.media.SoundTransform; import org.bytearray.media.effets.EffetSonore; public class Amplificateur { private var canalSon:SoundChannel; private var transformation:SoundTransform; public function Amplificateur ( pCanal:SoundChannel ) {

15 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

canalSon = pCanal; transformation = pCanal.soundTransform; } public function affecteVolume ( pVolume:Number ):void { transformation.volume = pVolume; canalSon.soundTransform = transformation; } public function recupereVolume ():Number { return canalSon.soundTransform.volume; } public function affecteBalance ( pBalance:Number ):void { transformation.pan = pBalance; canalSon.soundTransform = transformation; } public function recupereBalance ():Number { return canalSon.soundTransform.pan; } public function appliqueEffet ( pEffet:EffetSonore ):void { pEffet.executeEffet ( canalSon ); } } }

En testant le code suivant :


// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // import de la classe d'effet Fondu import org.bytearray.media.effets.Fondu; var son:Sound = new Sound ( new URLRequest ("son.mp3") );

16 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

var canalSon:SoundChannel = son.play(); var monAmpli:Amplificateur = new Amplificateur( canalSon ); // cration d'un effet de fondu var monEffet:Fondu = new Fondu (); // application de l'effet monAmpli.appliqueEffet ( monEffet );

Le message suivant est affich dans la fentre de sortie :


application de l'effet sonore

Nous retrouvons dans le code prcdent les avantages lis la liaison dynamique du polymorphisme. Lors de la compilation, le compilateur ne sait pas quel sera le type exact de lobjet refrenc par le paramtre pEffet. La dfinition de la mthode executeEffet qui sera dclenche est value lexcution. La classe Amplificateur possde maintenant une nouvelle mthode appliqueEffet. Afin dachever la classe Fondu, il ne nous reste plus qu implmenter leffet au sein de celle-ci :
package org.bytearray.media.effets { import import import import import flash.media.SoundChannel; flash.media.SoundTransform; fl.transitions.Tween; fl.transitions.easing.Regular; fl.transitions.TweenEvent;

public class Fondu extends EffetSonore { private private private private private var var var var var duree:Number; volume:Number; canalSon:SoundChannel; transformation:SoundTransform; objetTween:Tween;

public function Fondu ( pDuree:Number, pVolume:Number ) { duree = pDuree; volume = pVolume; }

17 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

override public function executeEffet ( pCanal:SoundChannel ):void { canalSon = pCanal; transformation = canalSon.soundTransform; objetTween = new Tween ( transformation, "volume", Regular.easeInOut, transformation.volume, volume, duree, true ); objetTween.addEventListener ( TweenEvent.MOTION_CHANGE , appliqueEffet ); objetTween.addEventListener ( TweenEvent.MOTION_FINISH , effetTermine ); } private function appliqueEffet ( pEvt:TweenEvent ):void { canalSon.soundTransform = transformation; } private function effetTermine ( pEvt:TweenEvent ):void { objetTween.removeEventListener( TweenEvent.MOTION_CHANGE, appliqueEffet ); } } }

La classe Fondu accepte deux paramtres dont voici le dtail :


pDuree : la dure du fondu. pVolume : le niveau de volume vers lequel le fondu se dirige.

Dans le code suivant, nous appliquons un effet de fondu de 3 secondes vers un volume 0 :
// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // import de la classe d'effet Fondu import org.bytearray.media.effets.Fondu; var son:Sound = new Sound ( new URLRequest ("son.mp3") ); var canalSon:SoundChannel = son.play(); var monAmpli:Amplificateur = new Amplificateur( canalSon ); // cration d'un effet de fondu vers un volume 0 en 3 secondes var monEffet:Fondu = new Fondu ( 3, 0 );

18 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

// aplication de l'effet monAmpli.appliqueEffet ( monEffet );

Nous pouvons rduire le volume 0 puis augmenter progressivement le son jusqu un volume de 1 en 5 secondes. Un objet SoundTransform peut tre pass en troisime paramtre de la mthode play :
// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // import de la classe d'effet Fondu import org.bytearray.media.effets.Fondu; var son:Sound = new Sound ( new URLRequest ("son.mp3") ); // le son est lu avec un volume 0 var canalSon:SoundChannel = son.play ( 0, 0, new SoundTransform ( 0 ) ); var monAmpli:Amplificateur = new Amplificateur( canalSon ); // cration d'un effet de fondu vers un volume 1 en 5 secondes var monEffet:Fondu = new Fondu ( 5, 1 ); // aplication de l'effet monAmpli.appliqueEffet ( monEffet );

Nous pouvons ainsi crer dautres types deffets. Afin dtendre le concept deffets nous allons crer une classe AutoBalance. Celle-ci provoquera une balance horizontale entre les deux hauts parleurs. Voici le code de la classe AutoBalance :
package org.bytearray.media.effets { import import import import import flash.events.TimerEvent; flash.media.SoundChannel; flash.media.SoundTransform; flash.utils.Timer; fl.transitions.easing.Regular;

public class AutoBalance extends EffetSonore { private private private private private private private private var var var var var var var var duree:Number; vitesse:Number; balance:Number; i:Number; canalSon:SoundChannel; transformation:SoundTransform; minuteur:Timer; minuteurArret:Timer;

public function AutoBalance ( pDuree:Number, pVitesse:Number ) {

19 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

balance = i = 0; duree = pDuree; vitesse = pVitesse; minuteur = new Timer ( 100, 0 ); minuteurArret = new Timer ( pDuree * 1000, 1 ); minuteur.addEventListener ( TimerEvent.TIMER, appliqueEffet ); minuteurArret.addEventListener ( TimerEvent.TIMER_COMPLETE, effetTermine ); } override public function executeEffet ( pCanal:SoundChannel ):void { canalSon = pCanal; transformation = canalSon.soundTransform; minuteur.start(); minuteurArret.start(); } private function appliqueEffet ( pEvt:TimerEvent ):void { balance = Math.sin( i += vitesse ); transformation.pan = balance; canalSon.soundTransform = transformation; } private function effetTermine ( pEvt:TimerEvent ):void { minuteur.stop(); transformation.pan = 0; canalSon.soundTransform = transformation; } } }

La classe AutoBalance accepte deux paramtres dont voici le dtail : 20 / 73


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

pDuree : la dure de la balance.

pVitesse : la vitesse de la balance horizontale.

La mthode couteur appliqueEffet fait osciller la proprit balance entre -1 et 1 grce la mthode sin de la classe Math. Dans le code suivant, nous appliquons un effet de balance pendant 15 secondes avec une vitesse rduite :
// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // import de la classe d'effet AutoBalance import org.bytearray.media.effets.AutoBalance; var son:Sound = new Sound ( new URLRequest ("son.mp3") ); var canalSon:SoundChannel = son.play (); var monAmpli:Amplificateur = new Amplificateur( canalSon ); // cration d'un effet de balance automatique pendant 15 secondes avec une vitesse rduite var monEffet:AutoBalance = new AutoBalance ( 15, .1 ); // application de l'effet monAmpli.appliqueEffet ( monEffet );

Nous avons eu recours lhritage afin de bnficier du polymorphisme et dun type commun, malheureusement notre conception souffre dune faiblesse importante. Que se passe-t-il si nous souhaitons ajouter un nouvel effet, hritant dj dune classe spcifique ? Il nous serait impossible dtendre la classe EffetSonore, lhritage multiple tant impossible en ActionScript 3. Souvenez-vous, nous avons vu lors du chapitre 8 intitul Programmation oriente objet que lhritage ntait pas la seule solution afin dobtenir un ensemble dobjets ayant un type commun. Il est possible de faire partager plusieurs classes un mme type grce aux interfaces. Au lieu de dfinir une classe EffetSonore dont toutes les classes deffets doivent hriter, nous allons simplement crer une interface IEffetSonore que toute classe deffet se devra dimplmenter. De par limplmentation, toutes les classes deffets seront de leurs types respectifs ainsi que du type IeffetSonore. Pour cela, nous dfinissons linterface IEffetSonore suivante au sein du paquetage org.bytearray.media.effets : 21 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

package org.bytearray.media.effets { import flash.media.SoundChannel; public interface IEffetSonore { function executeEffet ( pCanal:SoundChannel ):void; } }

Cette interface dfinit une seule mthode executeEffet que chaque classe deffet se doit dimplmenter. Souvenez-vous, cette mme mthode tait surcharge au sein des sous classes dans notre exemple prcdent. Nous allons a prsent modifier les classes Fondu et AutoBalance en implmentant linterface IEffetSonore. Notez que la mthode executeEffet ne doit plus tre marque comme mthode surchargeante, nous supprimons donc lattribut override :
package org.bytearray.media.effets { import import import import import import import import flash.events.TimerEvent; flash.media.Sound; flash.media.SoundChannel; flash.media.SoundTransform; flash.utils.Timer; fl.transitions.Tween; fl.transitions.easing.Regular; fl.transitions.TweenEvent;

public class Fondu implements IEffetSonore { private private private private private var var var var var duree:Number; volume:Number; canalSon:SoundChannel; transformation:SoundTransform; objetTween:Tween;

public function Fondu ( pDuree:Number, pVolume:Number ) { duree = pDuree; volume = pVolume; }

22 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

public function executeEffet ( pCanal:SoundChannel ):void { canalSon = pCanal; transformation = canalSon.soundTransform; objetTween = new Tween ( transformation, "volume", Regular.easeInOut, transformation.volume, destination, duree, true ); objetTween.addEventListener ( TweenEvent.MOTION_CHANGE , appliqueEffet ); objetTween.addEventListener ( TweenEvent.MOTION_FINISH , effetTermine ); } private function appliqueEffet ( pEvt:TweenEvent ):void { canalSon.soundTransform = transformation; } private function effetTermine ( pEvt:TweenEvent ):void { objetTween.removeEventListener( TweenEvent.MOTION_CHANGE, appliqueEffet ); } } }

La classe AutoBalance implmente aussi la classe IEffetSonore :


package org.bytearray.media.effets { import import import import import flash.events.TimerEvent; flash.media.SoundChannel; flash.media.SoundTransform; flash.utils.Timer; fl.transitions.easing.Regular;

public class AutoBalance implements IEffetSonore { private private private private private var var var var var duree:Number; vitesse:Number; balance:Number; i:Number; canalSon:SoundChannel;

23 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

private var transformation:SoundTransform; private var minuteur:Timer; private var minuteurArret:Timer; public function AutoBalance ( pDuree:Number, pVitesse:Number ) { balance = i = 0; duree = pDuree; vitesse = pVitesse; minuteur = new Timer ( 100, 0 ); minuteurArret = new Timer ( pDuree * 1000, 1 ); minuteur.addEventListener ( TimerEvent.TIMER, appliqueEffet ); minuteurArret.addEventListener ( TimerEvent.TIMER_COMPLETE, effetTermine ); } public function executeEffet ( pCanal:SoundChannel ):void { canalSon = pCanal; transformation = canalSon.soundTransform; minuteur.start(); minuteurArret.start(); } private function appliqueEffet ( pEvt:TimerEvent ):void { balance = Math.sin( i += vitesse ); transformation.pan = balance; canalSon.soundTransform = transformation; } private function effetTermine ( pEvt:TimerEvent ):void { minuteur.stop(); transformation.pan = 0; canalSon.soundTransform = transformation; }

24 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

} }

En implmentant linterface IEffetSonore les classes deffets sont obliges de dfinir une mthode executeEffet, le cas chant la compilation est impossible le message suivant est affich :
1044: La mthode d'interface executeEffet de l'espace de nom org.bytearray.media.effets:IEffetSonore n'est pas implmente par la classe org.bytearray.media.effets:Fondu.

La mthode appliqueEffet de la classe Amplificateur accepte dsormais un paramtre de type IEffetSonore :


package org.bytearray.media { import import import import flash.errors.IllegalOperationError; flash.media.SoundChannel; flash.media.SoundTransform; org.bytearray.media.effets.IEffetSonore;

public class Amplificateur { private var canalSon:SoundChannel; private var transformation:SoundTransform; public function Amplificateur ( pCanal:SoundChannel ) { canalSon = pCanal; transformation = pCanal.soundTransform; } public function affecteVolume ( pVolume:Number ):void { transformation.volume = pVolume; canalSon.soundTransform = transformation; } public function recupereVolume ():Number { return canalSon.soundTransform.volume; } public function affecteBalance ( pBalance:Number ):void

25 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

{ transformation.pan = pBalance; canalSon.soundTransform = transformation; } public function recupereBalance ():Number { return canalSon.soundTransform.pan; } public function appliqueEffet ( pEffet:IEffetSonore ):void { pEffet.executeEffet ( canalSon ); } } }

Grce la notion dinterfaces, les classes Fondu et AutoBalance sont de type IEffetSonore. Si une sous-classe souhaite devenir une classe deffet, celle-ci na qu implmenter linterface IEffetSonore et dfinir la mthode executeEffet. Pour terminer, nous allons ajouter la diffusion dun vnement depuis la classe Fondu afin de faciliter son utilisation. Celle-ci diffusera les deux vnements suivants :
EvenementFondu.TRANSITION : leffet est en cours. EvenementFondu.TERMINE: leffet est termin. EvenementFondu.DEMARRE : leffet est dmarr.

Afin de pouvoir diffuser ces derniers, la classe Fondu tend la classe EventDispatcher :
public class Fondu extends EventDispatcher implements IEffetSonore

Veillez importer la classe EventDispatcher :


import flash.events.EventDispatcher;

Puis nous dfinissons la classe EvenementFondu au sein du paquetage org.bytearray.media.effets.evenements :


package org.bytearray.media.effets.evenements

26 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

{ import flash.events.Event; public class EvenementFondu extends Event { public static const DEMARRE:String = "demarre"; public static const TRANSITION:String = "transition"; public static const TERMINE:String = "termine"; public function EvenementFondu ( pType:String ) { super( pType, false, false ); } public override function clone ():Event { return new EvenementFondu ( type ); } public override function toString ():String { return '[EvenementFondu type="'+ type +'" bubbles=' + bubbles + ' eventPhase='+ eventPhase + ' cancelable=' + cancelable +']'; } } }

Puis nous diffusons les vnements appropris pour chaque phase lie leffet :
package org.bytearray.media.effets { import import import import import import import import import import import flash.events.Event; flash.events.EventDispatcher; flash.events.TimerEvent; flash.media.Sound; flash.media.SoundChannel; flash.media.SoundTransform; flash.utils.Timer; fl.transitions.Tween; fl.transitions.easing.Regular; fl.transitions.TweenEvent; org.bytearray.media.effets.evenements.EvenementFondu;

public class Fondu extends EventDispatcher implements IEffetSonore

27 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

{ private private private private private var var var var var duree:Number; destination:Number; canalSon:SoundChannel; transformation:SoundTransform; objetTween:Tween;

public function Fondu ( pDuree:Number, pDestination:Number ) { duree = pDuree; destination = pDestination; } public function executeEffet ( pCanal:SoundChannel ):void { canalSon = pCanal; transformation = canalSon.soundTransform; objetTween = new Tween ( transformation, "volume", Regular.easeInOut, transformation.volume, destination, duree, true ); dispatchEvent ( new EvenementFondu (EvenementFondu.DEMARRE) ); objetTween.addEventListener ( TweenEvent.MOTION_CHANGE , appliqueEffet ); objetTween.addEventListener ( TweenEvent.MOTION_FINISH , effetTermine ); } private function appliqueEffet ( pEvt:TweenEvent ):void { canalSon.soundTransform = transformation; dispatchEvent ( new EvenementFondu (EvenementFondu.TRANSITION) ); } private function effetTermine ( pEvt:TweenEvent ):void { objetTween.removeEventListener( TweenEvent.MOTION_CHANGE, appliqueEffet ); dispatchEvent ( new EvenementFondu (EvenementFondu.TERMINE) ); } }

28 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Chaque vnement peut ensuite tre cout afin de pouvoir facilement synchroniser lapplication :
// import de la classe Amplificateur import org.bytearray.media.Amplificateur; // import des classes deffets import org.bytearray.media.effets.AutoBalance; import org.bytearray.media.effets.Fondu; // import de la classe EvenementFondu import org.bytearray.media.effets.evenements.EvenementFondu; var son:Sound = new Sound ( new URLRequest ("son.mp3") ); var canalSon:SoundChannel = son.play (); var monMixeur:Amplificateur = new Amplificateur( canalSon ); // cration d'un effet de fondu vers un volume 0 en 10 secondes var monEffet:Fondu = new Fondu ( 10, 0 ); // coute des vnements EvenementFondu.DEMARRE et EvenementFondu.TERMINE monEffet.addEventListener ( EvenementFondu.DEMARRE, effetDemarre ); monEffet.addEventListener ( EvenementFondu.TRANSITION, effetEnCours ); monEffet.addEventListener ( EvenementFondu.TERMINE, effetTermine ); // aplication de l'effet monMixeur.appliqueEffet ( monEffet ); function effetDemarre ( pEvt:EvenementFondu ):void { trace("effet demarr "); } function effetEnCours ( pEvt:EvenementFondu ):void { trace("effet en cours "); } function effetTermine ( pEvt:EvenementFondu ):void { trace("effet termin "); }

A vous dintgrer les mmes vnements au sein de la classe AutoBalance et pourquoi pas dajouter de nouvelles classes deffets !

29 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

A retenir
Afin de modifier le son plus facilement, nous avons cr une classe Amplificateur. Celle-ci possde une mthode appliqueEffet permettant daffecter diffrents effets sonore.

Les classes Fondu et AutoBalance peuvent tre utilises comme effets sonore. Dautres effets peuvent tre ajouts trs simplement. Les classes deffets doivent obligatoirement implmenter linterface IEffetSonore afin dtre compatible. Aprs une premire approche base sur lhritage, nous avons prfr utiliser une interface IEffetSonore afin de rendre plus souple la cration de nouveaux effets.

Modification globale du son


Nous venons dtudier la modification de chaque son de manire individuelle, ActionScript 3 introduit une classe SoundMixer permettant de travailler de manire globale sur les sons dune application. Celle-ci dfinit une mthode stopAll permettant larrt de tous les sons en cours de lecture :
// stoppe tous les sons en cours de lecture SoundMixer.stopAll();

Si nous souhaitons modifier le volume global, nous pouvons utiliser la proprit statique soundTransform de la mme classe. Dans le code suivant, nous rduisons le volume global 10% :
// rduit tous les sons en cours de lecture SoundMixer.soundTransform = new SoundTransform ( .1 );

Nous allons voir dans la partie suivante, que la classe SoundMixer ne se limite pas cela. Celle-ci nous rserve une fonctionnalit fort intressante ouvrant de nouvelles possibilits en matire dapplication audio.

A retenir

30 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

La modification dun son est assure par la classe SoundTransform.

Un objet SoundChannel est renvoy par la mthode play de lobjet Sound. Les objets de type SoundChannel possdent une proprit soundTransform renvoyant un objet de type SoundTransform.

La classe SoundMixer permet de travailler de manire globale sur les sons dune application.

Lire le spectre dun son


ActionScript 3 intgre une nouvelle fonctionnalit trs intressante au travers de la mthode computeSpectrum de la classe SoundMixer. Celle ci permet de rcuprer le spectre de la totalit des sons en cours de lecture afin den offrir une reprsentation graphique. Attention, le son issu de la classe Microphone ne peut tre rdirig vers la classe Sound et nest donc pas pris en considration par la mthode computeSpectrum. Dans le cas contraire, cela nous aurait permis de travailler sur la reconnaissance vocale au sein de Flash. Nous pouvons esprer que cette fonctionnalit soit intgre dans une prochaine version du lecteur. En attendant, revenons la mthode computeSpectrum dont voici la signature :
public static function computeSpectrum(outputArray:ByteArray, FFTMode:Boolean = false, stretchFactor:int = 0):void

Voici le dtail de chacun des paramtres :


outputArray : le tableau binaire dans lequel placer les donnes lies au spectre du son. Une instance de la classe ByteArray est ncessaire.

FFTMode : permet de spcifier si une transformation de Fourier doit tre applique au spectre gnr. Nous verrons plus loin en quoi consiste cette transformation. stretchFactor : chantillonnage des donnes gnres. La valeur par dfaut est 0 c'est--dire 44,1 KHz.

Lorsque la mthode computeSpectrum est excute, celle-ci place au sein du tableau binaire le spectre de la totalit des sons en cours de lecture.

31 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Par dfaut, ce spectre est reprsent par 512 valeurs oscillant entre -1 et 1, dont les 256 premires valeurs concernent le haut parleur gauche, et les 256 suivantes le haut parleur droit. La figure 17-2 illustre loscillation des valeurs retournes par la mthode computeSpectrum :

Figure 17-2. Oscillation des valeurs retournes par la mthode computeSpectrum. Afin de garantir un rafrachissement des donnes au sein du tableau binaire, nous pouvons placer lappel de la mthode computeSpectrum au sein dun vnement Event.ENTER_FRAME :
// cration d'un objet son et chargement d'un mp3 var monSon:Sound = new Sound ( new URLRequest ("son.mp3") ); // dmarrage du son var canalSon:SoundChannel = monSon.play(); // cration d'un tableau binaire vide pour accueillir le flux audio var fluxSpectre:ByteArray = new ByteArray(); // calcul du spectre addEventListener ( Event.ENTER_FRAME, calculSpectre ); function calculSpectre ( pEvt:Event ):void { // calcul du spectre en continu SoundMixer.computeSpectrum( fluxSpectre ); // affiche : 2048 trace( fluxSpectre.length ); }

32 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Notez que lutilisation dun objet Timer serait aussi envisageable si nous ne souhaitons pas tre li la cadence de lanimation. Nous venons de voir que la mthode computeSpectrum renvoie 512 valeurs. Pourtant, lorsque nous accdons la proprit length du tableau fluxSpectre, celle-ci nous renvoie 2048. Comment expliquons-nous cela ? Dans le cas de lutilisation de la mthode computeSpectrum, les valeurs places au sein du tableau fluxSpectre sont stockes sous la forme de nombres virgule flottante 32 bits (IEEE 754) cods sur 4 octets. Chaque index dun tableau binaire reprsentant un octet, le stockage dun flottant requiert 4 octets, donc 4 index. Si nous multiplions les 512 valeurs par 4 nous obtenons bien 2048. Nous allons concevoir une classe Equaliseur afin de reprsenter graphiquement le spectre. Au sein du paquetage org.bytearray.media.spectres nous dfinissons la classe Equaliseur suivante :
package org.bytearray.media.spectres { import flash.display.Bitmap; public class Equaliseur extends Bitmap { public function Equaliseur () { } } }

Afin dassurer des performances optimales nous vitons la manipulation de donnes vectorielles et privilgions lutilisation de donnes bitmap. De ce fait, la classe Equaliseur tend la classe Bitmap. Souvenez-vous, nous avons vu au cours du chapitre 12 intitul Programmation Bitmap quil tait prfrable dutiliser des donnes bitmap lorsque cela tait possible afin dacclrer la vitesse de rendu. 33 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Nous ajoutons prsent notre mcanisme dactivation et de dsactivation de lobjet graphique :


package org.bytearray.media.spectres { import flash.display.Bitmap; import flash.events.Event; public class Equaliseur extends Bitmap { public function Equaliseur () { addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { trace("activation"); } private function desactivation ( pEvt:Event ):void { trace("desactivation"); } } }

Nous en profitons pour ajouter trois paramtres au constructeur permettant de spcifier les dimensions du spectre ainsi que sa couleur :
package org.bytearray.media.spectres { import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; public class Equaliseur extends Bitmap { private var largeur:int; private var hauteur:int; private var couleur:Number;

34 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

public function Equaliseur ( pLargeur:Number, pHauteur:Number, pCouleurSpectre:Number ) { largeur = pLargeur; hauteur = pHauteur; couleur = pCouleurSpectre; addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { bitmapData = new BitmapData ( largeur, hauteur, false, 0 ); } private function desactivation ( pEvt:Event ):void { bitmapData.dispose(); } } }

Notez que le paramtre pCouleurSpectre nest pas li linstance de BitmapData cr. Nous utiliserons la couleur passe en paramtre pour teinter les pixels dessins plus tard au sein du bitmap. Afin de dessiner le spectre nous devons dabord dfinir la surface peindre. Au sein de la mthode activation, nous affectons la proprit bitmapData hrite une instance de la classe flash.display.BitmapData. Lorsquil est supprim de la liste daffichage, lqualiseur est automatiquement dsactiv grce la mthode dispose. Nous pouvons tester la classe en cours, laide du code suivant :
// import de la classe Equaliseur import org.bytearray.media.spectres.Equaliseur; // cration d'un qualiseur de 512 pixels de largeur et 256 pixels de hauteur var monEqualiseur:Equaliseur = new Equaliseur( 512, 256, 0 ); addChild ( monEqualiseur ); // centrage de l'qualiseur // >> 1 permet de diviser par 2 de manire plus optimise ;)

35 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

monEqualiseur.x = (stage.stageWidth - monEqualiseur.width) >> 1; monEqualiseur.y = (stage.stageHeight - monEqualiseur.height) >> 1;

La figure 17-3 illustre le rsultat :

Figure 17-3. Instance de la classe Equaliseur. Nous intgrons au sein de la mthode activation lcoute de lvnement Event.ENTER_FRAME afin de calculer le spectre :
package org.bytearray.media.spectres { import import import import import flash.display.Bitmap; flash.display.BitmapData; flash.events.Event; flash.utils.ByteArray; flash.media.SoundMixer;

public class Equaliseur extends Bitmap { private private private private var var var var largeur:int; hauteur:int; couleur:Number; fluxSpectre:ByteArray;

public function Equaliseur ( pLargeur:Number, pHauteur:Number, pCouleurSpectre:Number ) {

36 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

largeur = pLargeur; hauteur = pHauteur; couleur = pCouleurSpectre; fluxSpectre = new ByteArray(); addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { bitmapData = new BitmapData ( largeur, hauteur, false, 0 ); addEventListener ( Event.ENTER_FRAME, calculSpectre ); } private function desactivation ( pEvt:Event ):void { bitmapData.dispose(); } private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); // affiche : 2048 trace( fluxSpectre.length ); } } }

Nous allons nous attarder sur la mthode calculSpectre et lire les donnes du spectre stockes au sein du tableau binaire fluxSpectre. Nous ajoutons la dfinition de deux proprits i et oscillation :
private var i:int; private var oscillation:Number;

Puis nous modifions la mthode calculSpectre :


private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); i = 512;

37 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

while ( i-- ) { oscillation = fluxSpectre.readFloat(); // affiche : valeur comprise entre -1 et 1 trace (oscillation); } }

La boucle while intgre la mthode calculSpectre nous permet de lire les donnes contenues au sein du tableau fluxSpectre. Contrairement aux tableaux traditionnels, les tableaux binaires possdent de nombreuses mthodes facilitant leur lecture. Afin de lire un nombre virgule flottante, nous devons utiliser la mthode readFloat dfinie par la classe ByteArray. Celle-ci dplace automatiquement la proprit position du tableau binaire de 4 index chaque appel. Les 512 itrations de la boucle permettent donc le parcours des 512 valeurs. Comme nous lavons vu prcdemment, le spectre est dcrit par 512 valeurs oscillant entre entre -1 et 1. Ces valeurs ne sont pas exploitables graphiquement car trop rduites, nous devons donc les multiplier par une valeur spcifique afin dobtenir une amplitude suffisante. Dans le code suivant, nous multiplions les valeurs par la hauteur du spectre divise par 2 :
private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); i = 512; while ( i-- ) { oscillation = fluxSpectre.readFloat() * (hauteur >> 1); // si la hauteur du spectre est de 256 pixels // affiche : valeur comprise entre et -128 et 128 trace (oscillation); } }

38 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

La proprit oscillation volue dsormais entre -128 et 128, cela nous permet de dessiner les btonnets constituant notre futur spectre. La figure 17-4 illustre la nouvelle oscillation pour un spectre dune hauteur de 256 pixels :

Figure 17-4. Oscillation des valeurs retournes par la mthode computeSpectrum. A laide de la mthode fillRect de lobjet BitmapData, nous allons dessiner une succession de btonnets reprsentants le spectre. Un objet Rectangle est utilis afin de dfinir la surface de chaque btonnet. Celui-ci aura une hauteur dfinie par la proprit oscillation. Nous dfinissons une proprit surface afin de stocker lobjet Rectangle :
package org.bytearray.media.spectres { import import import import import import flash.display.Bitmap; flash.display.BitmapData; flash.events.Event; flash.geom.Rectangle; flash.utils.ByteArray; flash.media.SoundMixer;

public class Equaliseur extends Bitmap { private private private private var var var var largeur:int; hauteur:int; couleur:Number; fluxSpectre:ByteArray;

39 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

private var i:int; private var oscillation:Number; private var surface:Rectangle; public function Equaliseur ( pLargeur:Number, pHauteur:Number, pCouleurSpectre:Number ) { largeur = pLargeur; hauteur = pHauteur; couleur = pCouleurSpectre; surface = new Rectangle ( 0, 0, 3, 4 ); fluxSpectre = new ByteArray(); addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { bitmapData = new BitmapData ( largeur, hauteur, false, 0 ); addEventListener ( Event.ENTER_FRAME, calculSpectre ); } private function desactivation ( pEvt:Event ):void { bitmapData.dispose(); } private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); i = 512; while ( i-- ) { oscillation = fluxSpectre.readFloat() * (hauteur >> 1); surface.x = i * 4; if ( oscillation > 0 ) { surface.y = (bitmapData.height >> 1) - oscillation; surface.height = oscillation;

40 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

} else { surface.y = (bitmapData.height >> 1); surface.height = -oscillation; } bitmapData.fillRect ( surface, 0xFFFFFF ); } } } }

Nous crons un objet Rectangle de 3 pixels de large, cette surface va nous permettre de dessiner chaque btonnet de lqualiseur. Au sein de la boucle while nous dplaons lobjet Rectangle afin de dessiner un btonnet tous les 4 pixels. Rappelez-vous que nous devons lire 512 valeurs et que celles-ci doivent tre rendues laffichage. Une largeur de 512 pixels minimum est requise afin de pouvoir dessiner la totalit du spectre. Si nous souhaitons dessiner un spectre de taille rduite, nous devons sauter certaines valeurs du tableau binaire. Nous devons donc tout dabord diviser la largeur du spectre spcifie par 4 afin de dterminer le nombre ditrations ncessaire pour afficher la totalit des btonnets :
private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); i = bitmapData.width / 4; while ( i-- ) { oscillation = fluxSpectre.readFloat() * (hauteur >> 1); surface.x = i * 4; if ( oscillation > 0 ) { surface.y = (bitmapData.height >> 1) - oscillation;

41 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

surface.height = oscillation; } else { surface.y = (bitmapData.height >> 1); surface.height = -oscillation; } bitmapData.fillRect ( surface, 0xFFFFFF ); } }

Grce au code prcdent, nous positionnons les btonnets sur la largeur du spectre spcifie, mais nous ne parcourons plus les donnes totales du tableau binaire spectreFlux. Souvenez-vous que 512 appels la mthode readFloat sont ncessaires pour parcourir le tableau complet, nous devons donc nous arranger pour sauter certaines valeurs tout en sassurant que nous sommes bien alls jusqu la fin du tableau fluxSpectre. Pour cela, nous dfinissons une proprit decalage :
private var decalage:int;

Puis nous modifions la mthode calculSpectre en sautant certaines valeurs laide de la proprit position de lobjet ByteArray :
private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); i = bitmapData.width / 4; decalage = 2048 / i; while ( i-- ) { fluxSpectre.position = i * decalage; oscillation = fluxSpectre.readFloat() * (hauteur >> 1); surface.x = i * 4; if (oscillation > 0 ) { surface.y = (bitmapData.height >> 1) oscillation; surface.height = oscillation;

42 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

} else { surface.y = (bitmapData.height >> 1); surface.height = -oscillation; } bitmapData.fillRect ( surface, 0xFFFFFF ); } }

Afin de comprendre le code prcdent, considrons le scnario suivant : Une largeur de 256 pixels est spcifie dans le constructeur de la classe Equaliseur. En divisant 256 par 4 nous obtenons 64 itrations afin de positionner les btonnets reprsentant le spectre. Nous divisons 2048 par 64 et obtenons un dcalage de 32 octets. Ainsi, pour chaque itration, nous sautons au sein du tableau 32 octets soit 32 index. En fin de boucle nous avons parcouru la totalit du tableau binaire car 64 * 32 = 2048. En testant la classe Equaliseur laide du code suivant :
// import de la classe Equaliseur import org.bytearray.media.spectres.Equaliseur; // cration d'un objet son et chargement d'un mp3 var monSon:Sound = new Sound ( new URLRequest ("son.mp3") ); // dmarrage du son var canalSon:SoundChannel = monSon.play(); // cration d'un qualiseur de 512 pixels de largeur et 300 pixels de hauteur var monEqualiseur:Equaliseur = new Equaliseur( 512, 300, 0 ); addChild ( monEqualiseur ); monEqualiseur.x = (stage.stageWidth - monEqualiseur.width) >> 1; monEqualiseur.y = (stage.stageHeight - monEqualiseur.height) >> 1;

Nous obtenons le rsultat illustr en figure figure 17-5 :

43 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-5. Instance de la classe Equaliseur. En observant notre qualiseur voluer, nous remarquons que le spectre dessin demeure laffichage. Afin de corriger cela, nous ajoutons un nouvel appel la mthode fillRect au sein de la mthode calculSpectre :
private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); bitmapData.fillRect ( bitmapData.rect, 0 ); i = bitmapData.width / 4; decalage = 2048 / i; while ( i-- ) { fluxSpectre.position = i * decalage; oscillation = fluxSpectre.readFloat() * (hauteur >> 1); surface.x = i * 4; if ( oscillation > 0 ) {

44 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

surface.y = (bitmapData.height >> 1) - oscillation; surface.height = oscillation; } else { surface.y = (bitmapData.height >> 1); surface.height = -oscillation; } bitmapData.fillRect ( surface, 0xFFFFFF ); } }

Le premier appel la mthode fillRect permet de supprimer les pixels prcdents. En testant nouveau notre qualiseur, nous remarquons que les btonnets disparaissent prsent, lqualiseur est correctement rafrachi. Nous allons modifier le rendu du spectre en ajoutant une dissolution des pixels progressive afin de donner un effet de fondu plus esthtique. Pour dissoudre les pixels, nous utilisons un filtre de flou appliqu en continu.
BitmapData :
{ import import import import import import import import flash.display.Bitmap; flash.display.BitmapData; flash.events.Event; flash.filters.BlurFilter; flash.geom.Point; flash.geom.Rectangle; flash.utils.ByteArray; flash.media.SoundMixer;

Pour cela, nous utilisons la mthode applyFilter de la classe


package org.bytearray.media.spectres

public class Equaliseur extends Bitmap { private private private private private private private private private private var var var var var var var var var var largeur:int; hauteur:int; couleur:Number; fluxSpectre:ByteArray; i:int; oscillation:Number; surface:Rectangle; decalage:int; filtreFlou:BlurFilter; point:Point;

45 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

public function Equaliseur ( pLargeur:Number, pHauteur:Number, pCouleurSpectre:Number ) { largeur = pLargeur; hauteur = pHauteur; couleur = pCouleurSpectre; surface = new Rectangle ( 0, 0, 3, 4 ); fluxSpectre = new ByteArray(); filtreFlou = new BlurFilter ( 0, 4, 4 ); point = new Point(); addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { bitmapData = new BitmapData ( largeur, hauteur, false, 0 ); addEventListener ( Event.ENTER_FRAME, calculSpectre ); } private function desactivation ( pEvt:Event ):void { bitmapData.dispose(); } private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); i = bitmapData.width / 4; decalage = 2048 / i; while ( i-- ) { fluxSpectre.position = i * decalage; oscillation = fluxSpectre.readFloat() * (hauteur >> 1); surface.x = i * 4; if ( oscillation > 0 )

46 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

{ surface.y = (bitmapData.height >> 1) - oscillation; surface.height = oscillation; } else { surface.y = (bitmapData.height >> 1); surface.height = -oscillation; } bitmapData.fillRect ( surface, 0xFFFFFF ); } filtreFlou ); } } } bitmapData.applyFilter ( bitmapData, bitmapData.rect, point,

Nous passons en paramtre la mthode applyFilter lobjet BitmapData en cours, ainsi que sa proprit rect afin de dfinir la surface sur laquelle appliquer le filtre. Lobjet Point pass en dernier paramtre permet dindiquer le point de dpart daffectation du filtre. La figure 17-6 illustre le rsultat :

47 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-6. Equaliseur avec fondu progressif. Notre qualiseur commence prendre forme, il ne nous reste plus qu ajouter une couleur de spectre alatoire. Pour cela nous importons la classe BitmapOutils du paquetage org.bytearray.outils dveloppe au cours du chapitre 12 intitul Programmation bitmap, puis nous crons un objet ColorTransform appliqu en continu au spectre :
package org.bytearray.media.spectres { import import import import import import import import import import flash.display.Bitmap; flash.display.BitmapData; flash.events.Event; flash.filters.BlurFilter; flash.geom.Point; flash.geom.Rectangle; flash.geom.ColorTransform; flash.utils.ByteArray; flash.media.SoundMixer; org.bytearray.outils.BitmapOutils;

public class Equaliseur extends Bitmap { private var largeur:int; private var hauteur:int; private var couleur:Number;

48 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

private private private private private private private private

var var var var var var var var

fluxSpectre:ByteArray; i:int; oscillation:Number; surface:Rectangle; decalage:int; filtreFlou:BlurFilter; point:Point; transformationCouleur:ColorTransform;

public function Equaliseur ( pLargeur:Number, pHauteur:Number, pCouleurSpectre:Number ) { largeur = pLargeur; hauteur = pHauteur; couleur = pCouleurSpectre; surface = new Rectangle ( 0, 0, 3, 4 ); fluxSpectre = new ByteArray(); filtreFlou = new BlurFilter ( 0, 4, 4 ); point = new Point(); var composants:Object = BitmapOutils.hexRgb ( pCouleurSpectre ); transformationCouleur = new ColorTransform ( composants.rouge/255, composants.vert/255, composants.bleu/255 ); addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { bitmapData = new BitmapData ( largeur, hauteur, false, 0 ); addEventListener ( Event.ENTER_FRAME, calculSpectre ); } private function desactivation ( pEvt:Event ):void { bitmapData.dispose(); } private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre ); i = bitmapData.width / 4;

49 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

decalage = 2048 / i; while ( i-- ) { fluxSpectre.position = i * decalage; oscillation = fluxSpectre.readFloat() * (hauteur >> 1); surface.x = i * 4; if ( oscillation > 0 ) { surface.y = (bitmapData.height >> 1) - oscillation; surface.height = oscillation; } else { surface.y = (bitmapData.height >> 1); surface.height = -oscillation; } bitmapData.fillRect ( surface, 0xFFFFFF ); } filtreFlou ); bitmapData.applyFilter ( bitmapData, bitmapData.rect, point,

bitmapData.colorTransform ( bitmapData.rect, transformationCouleur ); } } }

Puis nous passons la couleur spcifique lors de linstanciation de lobjet Equaliseur :


// cration d'un qualiseur de 512 pixels de largeur et 300 pixels de hauteur de couleur jaune var monEqualiseur:Equaliseur = new Equaliseur( 512, 300, 0xDDDDA5 );

La figure 17-7 illustre le rsultat :

50 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-7. Equaliseur audio en couleurs. Nous pouvons modifier les dimensions de lqualiseur de manire significative afin de le rduire un lment secondaire :
// import de la classe Equaliseur import org.bytearray.media.spectres.Equaliseur; // cration d'un objet son et chargement d'un mp3 var monSon:Sound = new Sound ( new URLRequest ("son.mp3") ); // dmarrage du son var canalSon:SoundChannel = monSon.play(); // cration d'un qualiseur de 64 pixels de largeur et 35 pixels de hauteur var monEqualiseur:Equaliseur = new Equaliseur( 64, 35, 0xDDDDA5 ); addChild ( monEqualiseur ); // placement de lqualiseur monEqualiseur.x = stage.stageWidth - monEqualiseur.width - 15; monEqualiseur.y = stage.stageHeight - monEqualiseur.height - 15;

La figure 17-8 illustre le rsultat :

51 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-8. Spectre audio rduit. Nous pouvons tester prsent la classe Amplificateur dveloppe prcdemment afin de voir le spectre modifi en temps rel. Nous importons la classe Amplificateur puis nous appliquons un effet de balance en plaant la totalit du son dans le haut parleur gauche :
// import de la classe Equaliseur import org.bytearray.media.spectres.Equaliseur; // import de la classe Amplificateur import org.bytearray.media.Amplificateur; // cration d'un objet son et chargement d'un mp3 var monSon:Sound = new Sound ( new URLRequest ("son.mp3") ); // dmarrage du son var canalSon:SoundChannel = monSon.play(); // cration de l'objet Amplificateur var monAmplificateur:Amplificateur = new Amplificateur ( canalSon ); // balance horizontale du son dans le haut parleur gauche monAmplificateur.affecteBalance ( -1 ); // cration d'un qualiseur de 512 pixels de largeur et 200 pixels de hauteur var monEqualiseur:Equaliseur = new Equaliseur( 512, 200, 0xDDDDA5 ); addChild ( monEqualiseur ); // centrage de l'qualiseur monEqualiseur.x = (stage.stageWidth - monEqualiseur.width) >> 1;

52 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

monEqualiseur.y = (stage.stageHeight - monEqualiseur.height) >> 1;

La figure 17-9 illustre le rsultat :

Figure 17-9. Spectre altr par une avec modification de la balance horizontale. Nous voyons que notre spectre est fidle au flux renvoy par la mthode computeSpectrum. En ralit, lorsquune transformation est applique au son, le spectre renvoy par la mthode computeSpectrum est lui aussi modifi. Dans le code suivant, nous appliquons un effet laide de la classe AutoBalance dveloppe auparavant :
// import de la classe Equaliseur import org.bytearray.media.spectres.Equaliseur; // import de la classe Amplificateur import org.bytearray.media.Amplificateur; // import de la classe AutoBalance import org.bytearray.media.effets.AutoBalance; // cration d'un objet son et chargement d'un mp3 var monSon:Sound = new Sound ( new URLRequest ("son.mp3") ); // dmarrage du son var canalSon:SoundChannel = monSon.play(); // cration de l'objet Amplificateur var monAmplificateur:Amplificateur = new Amplificateur ( canalSon );

53 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

// cration d'un effet de balance horizontale pendant 3 secondes vitesse rduite var effetBalance:AutoBalance = new AutoBalance ( 30, .1 ); // application de l'effet monAmplificateur.appliqueEffet ( effetBalance ); // cration d'un qualiseur de 512 pixels de largeur et 200 pixels de hauteur var monEqualiseur:Equaliseur = new Equaliseur( 512, 200, 0xDDDDA5 ); addChild ( monEqualiseur ); // centrage de l'qualiseur monEqualiseur.x = (stage.stageWidth - monEqualiseur.width) >> 1; monEqualiseur.y = (stage.stageHeight - monEqualiseur.height) >> 1;

En appliquant un effet de balance automatique progressif, le spectre est modifi en temps rel, la figure 17-10 illustre le rendu :

Figure 17-10. Spectre altr par une modification progressive de la balance horizontale. La mthode computeSpectrum nous rserve encore quelques surprises, cest ce que nous allons dcouvrir prsent.

A retenir

54 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

La mthode computeSpectrum de la classe SoundMixer permet de calculer le spectre de la totalit des sons en cours de lecture. La proprit position de lobjet ByteArray permet de se dplacer manuellement au sein des octets. Si une transformation est applique un son, le flux renvoy par la mthode computeSpectrum reflte cette transformation. La mthode computeSpectrum renvoie 512 valeurs.

Transforme de Fourier
Comme nous lavons vu lors du dtail des diffrents paramtres de la mthode computeSpectrum, il est possible dappliquer une transforme de Fourier au spectre. En activant celle-ci, le spectre gnr reflte alors les frquences des sons en cours de lecture au lieu de londe sonore. Nous allons ajouter deux proprits constantes au sein de la classe Equaliseur afin de pouvoir facilement choisir entre un qualiseur de frquences ou donde sonore. Pour cela, nous dfinissons trois proprits SPECTRE et FREQUENCE et fourier :
package org.bytearray.media.spectres { import import import import import import import import import import flash.display.Bitmap; flash.display.BitmapData; flash.events.Event; flash.filters.BlurFilter; flash.geom.Point; flash.geom.Rectangle; flash.geom.ColorTransform; flash.utils.ByteArray; flash.media.SoundMixer; org.bytearray.outils.BitmapOutils;

public class Equaliseur extends Bitmap { public static const SPECTRE:Boolean = false; public static const FREQUENCE:Boolean = true; private private private private private private private var var var var var var var largeur:int; hauteur:int; couleur:Number; fluxSpectre:ByteArray; i:int; amplitude:Number; surface:Rectangle;

55 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

private private private private private

var var var var var

decalage:int; filtreFlou:BlurFilter; point:Point; transformationCouleur:ColorTransform; fourier:Boolean;

public function Equaliseur ( pLargeur:Number, pHauteur:Number, pCouleurSpectre:Number, pFourier:Boolean=false ) { largeur hauteur couleur fourier = = = = pLargeur; pHauteur; pCouleurSpectre; pFourier;

surface = new Rectangle ( 0, 0, 3, 4 ); fluxSpectre = new ByteArray(); filtreFlou = new BlurFilter ( 0, 4, 4 ); point = new Point(); var composants:Object = BitmapOutils.hexRgb ( pCouleurSpectre ); transformationCouleur = new ColorTransform ( composants.rouge/255, composants.vert/255, composants.bleu/255 ); addEventListener ( Event.ADDED_TO_STAGE, activation ); addEventListener ( Event.REMOVED_FROM_STAGE, desactivation ); } private function activation ( pEvt:Event ):void { bitmapData = new BitmapData ( largeur, hauteur, false, 0 ); addEventListener ( Event.ENTER_FRAME, calculSpectre ); } private function desactivation ( pEvt:Event ):void { bitmapData.dispose(); } private function calculSpectre ( pEvt:Event ):void { SoundMixer.computeSpectrum( fluxSpectre, fourier ); i = bitmapData.width / 4; decalage = 2048 / i;

56 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

while ( i-- ) { fluxSpectre.position = i * decalage; amplitude = fluxSpectre.readFloat() * ((hauteur - 10) >> 1); surface.x = i * 4; if ( amplitude > 0 ) { surface.y = (bitmapData.height >> 1) - amplitude; surface.height = amplitude; } else { surface.y = (bitmapData.height >> 1); surface.height = -amplitude; } bitmapData.fillRect ( surface, 0xFFFFFF ); } filtreFlou ); bitmapData.applyFilter ( bitmapData, bitmapData.rect, point,

bitmapData.colorTransform ( bitmapData.rect, transformationCouleur ); } } }

Il est important de noter que dans le cas de lutilisation de la transforme de Fourier, les valeurs renvoyes par la mthode computeSpectrum oscillent entre 0 et 1. Nous obtiendrons dans ce cas des btonnets dans la partie suprieure du spectre uniquement. Grce aux deux proprits nous pouvons facilement spcifier le type dqualiseur voulu. Dans le code suivant nous crons un spectre permettant dafficher les frquences des sons :
// cration d'un qualiseur avec transformation de fourier var monEqualiseur:Equaliseur = new Equaliseur( 512, 200, 0xDDDDA5, Equaliseur. FREQUENCE );

La figure 17-11 illustre le rsultat :

57 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-11. Equaliseur avec transformation de fourier.

A retenir
La transforme de Fourier permet disoler les frquences des sons en cours de lecture.

Le format MPEG-4 Audio


La version 9.0.115 du lecteur Flash 9 intgre une compatibilit MPEG-4 et permet la lecture de fichiers audio encods avec lalgorithme Advanced Audio Coding plus communment appel AAC. Dvelopp lorigine par linstitut Fraunhofer, ce format compress est considr comme le remplaant du clbre codec de compression MP3. A qualit dcoute gale, le format AAC est environ 30% plus lgr que le format MP3. Cette optimisation du poids des fichiers audio permet donc une rduction de la bande passante utilise par les sons sur un site large trafic. Des portails audio comme I-Tunes utilisent dj le format AAC comme format de distribution. Des priphriques tels le I-Phone, la PlayStation Portable ainsi quun grand nombre de tlphones portables sont aussi compatibles avec ce format.

58 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Le tableau suivant recense les diffrentes extensions de fichiers MPEG-4 permettant de contenir du son au format AAC compatible avec le lecteur Flash 9 : Extension .M4A .M4V .AAC .3GP .MP4 Description Fichier audio Fichier video i-Tunes Fichier audio AAC Fichier audio et vido utilis sur les tlphones 3G Fichier video

Tableau 1. Extensions de fichiers conteneur du format AAC. Notons que le lecteur Flash ne gre pas la lecture de fichiers AAC contenant une piste MP3, ni les fichiers AAC protgs tlchargs depuis des plates-formes telles I-Tunes. De la mme manire, les fichiers audio AAC protgs par la technologie de gestion des droits numriques FairPlay ne sont pas compatibles. Dans le code suivant nous chargeons un fichier son MPEG-4 AAC :
// instanciation d'un objet NetConnection var chargeurSon:NetConnection = new NetConnection(); // lors d'un chargement de fichier local nous nous connectons null chargeurSon.connect(null); // cration d'un objet NetStream var fluxAudio:NetStream = new NetStream ( chargeurSon ); // lecture du son fluxAudio.play ("son.m4a");

Bien que cela puisse vous surprendre, sachez que la lecture de fichiers audio au format AAC nest pas assure par la classe Sound mais par les classes NetStream et NetConnection. Celles-ci sont utilises dans le cas dapplications connecte un serveur de type Flash Media Server ou dans le cas de lecture de vidos au format FLV. En testant le code prcdent, le fichier son est lu mais lerreur suivante est leve et affiche dans la fentre de sortie :
Error #2044: AsyncErrorEvent non pris en charge : text=Error #2095: flash.net.NetStream na pas t en mesure dappeler llment de rappel onMetaData.

Dans le cas de chargement de fichiers laide de la classe NetStream, il convient de toujours dfinir la proprit client avant dappeler la mthode play.

59 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

La proprit client, permet de prciser lobjet sur lequel est dfinie la mthode onMetaData. Aussi trange que cela puisse paratre, lobjet NetStream ne diffuse pas dvnement li aux mtasdonnes du mdia charg. Nous retrouvons ci le modle vnementiel prsent en ActionScript 1 et 2. Dans le code suivant, nous utilisons le scnario principal comme client :
// instanciation d'un objet NetConnection var chargeurSon:NetConnection = new NetConnection(); // lors d'un chargement de fichier local nous nous connectons null chargeurSon.connect(null); // cration d'un objet NetStream var fluxAudio:NetStream = new NetStream ( chargeurSon ); // lecture du son fluxAudio.play ("son.m4a"); // le scnario joue le rle du client fluxAudio.client = this; /// mthode onMetaData dfinie sur le scnario principal function onMetaData ( pMeta ):void { /* duration : 395.90022675736964 trackinfo : [object Object] audiochannels : 2 aacaot : 2 audiosamplerate : 44100 tags : moovposition : 40 audiocodecid : mp4a */ for ( var p in pMeta ) trace( p + " : " + pMeta[p] ); }

Le paramtre pMeta reoit un objet contenant diffrentes proprits lies aux mtasdonns du mdia charg. Voici en dtail chacune des proprits :
aacaot : le type de fichier audio AAC, cette proprit peut avoir la valeur 0 pour AAC Main, 1 pour AAC LC et 2 pour SBR audio types. audiochannels : le nombre de canaux du mdia charg. Dans le cas de fichiers audio AAC multicanaux, ces derniers sont dcods sur deux canaux seulement par le lecteur Flash. audiocodecid : le codec audio utilis du mdia charg. La chane de caractres mp4a est utilise pour le format AAC, et .mp3 pour les fichiers MP3.

60 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

audiosamplerate : frquence dchantillonnage du fichier audio. duration : la dure en secondes du mdia charg. moovposition : La position de latome moov au sein du mdia charg.

tags : un objet comprenant diffrentes informations lies au mdia charg. Lquivalent des donnes ID3 du format MP3. trackinfo : un objet contenant les eventuelles illustrations lies au mdia (pochettes, photos) sous la forme de ByteArray.

Au cas o le fichier MPEG-4 nest pas compatible nous pouvons couter lvnement NetStatusEvent.NET_STATUS :
// instanciation d'un objet NetConnection var chargeurSon:NetConnection = new NetConnection(); // lors d'un chargement de fichier local nous nous connectons null chargeurSon.connect(null); // cration d'un objet NetStream var fluxAudio:NetStream = new NetStream ( chargeurSon ); // coute de l'vnement NetStatusEvent.NET_STATUS fluxAudio.addEventListener( NetStatusEvent.NET_STATUS, etatLecture ); function etatLecture ( pEvt:NetStatusEvent ):void { if ( pEvt.info.code == "NetStream.FileStructureInvalid" ) trace("fichier non compatible"); else if ( pEvt.info.code == "NetStream.NoSupportedTrackFound" ) trace("aucune piste trouve"); } // lecture du son fluxAudio.play ("son.m4a"); // le scnario joue le rle du client fluxAudio.client = this; /// mthode onMetaData dfinie sur le scnario principal function onMetaData ( pMeta ):void { /* duration : 395.90022675736964 trackinfo : [object Object] audiochannels : 2 aacaot : 2 audiosamplerate : 44100 tags : moovposition : 40 audiocodecid : mp4a */ for ( var p in pMeta ) trace( p + " : " + pMeta );

61 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Lobjet vnementiel diffus par lvnement NetStatusEvent.NET_STATUS possde une proprit info contenant un objet disposant dinformations sur ltat de la connexion. Cet objet possde les deux proprits suivantes :
code : une chane de caractres indiquant ltat de la connexion. Consultez la documentation pour connatre les diffrentes valeurs renvoyes. level : renvoie la chane de caractre status si la connexion est russie ou error si celle-ci choue.

Malheureusement, il nexiste pas de proprits constantes de classe afin de tester les valeurs retournes par les proprits code et level. Nous venons de terminer notre aventure sonore, nous allons nous intresser dans la partie suivante aux diffrentes nouveauts apportes par ActionScript 3 et la dernire version du lecteur Flash 9 en matire de vido.

A retenir
La version 9.0.115 du lecteur Flash 9 intgre un dcodage des fichiers audio AAC. Lalgorithme de compression AAC est considr comme plus performant. C'est beaucoup d'gards un remplaant suprieur au format MP3. Afin de lire un fichier audio AAC, nous utilisons les classes NetConnection et NetStream. La proprit client de lobjet NetStream permet de dfinir lobjet interceptant lvnment onMetaData. Lvnement NetStatusEvent.NET_STATUS diffus par lobjet NetStream permet de savoir si une erreur de dcodage est intervenue. La lecture de fichiers MPEG-4 fonctionne en ActionScript 1, 2 et 3.

La vido dans Flash


Le lecteur Flash sest impos aujourdhui comme lecteur multimdia incontournable sur Internet. En plus doffrir un dcodage audio MPEG-4, la dernire version du lecteur Flash rvolutionne la vido sur rseaux en intgrant une compatibilit avec le codec de compression vido MPEG-4 H.264.

62 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Depuis sa version 9.0.115, le lecteur Flash 9 intgre donc les 4 codecs suivants :
Sorenson Spark : il sagit du premier codec vido tre apparu dans le lecteur Flash. La qualit daffichage nest pas optimale, ce codec est vou disparatre. Screen Video : Il sagit du codec utilis pour la capture d'cran par Connect (anciennement Breeze). On2 VP6 : ce codec fut introduit au sein du lecteur Flash 8. Il introduit une qualit dimage suprieure ainsi que la gestion du canal alpha. H.264 : ce codec fut introduit au sein du lecteur Flash 9.0.115. Il amliore nouveau la qualit de limage tout en garantissant l'interoprabilit et l'universalit des donnes vido.

Voici la liste des diffrentes classes impliques dans la lecture de flux vido au sein du lecteur Flash :
flash.net.NetConnection : permet douvrir la connexion. La classe NetConnection

flash.net.NetStream : La classe NetStream permet de manipuler le flux en cours de lecture. flash.media.Video : La classe Video permet dafficher le flux charg.

Passons la pratique, dans cette nouvelle partie nous allons dcouvrir comment charger et lire une vido MPEG-4 de manire dynamique.

Le format MPEG-4 Video


Pour lire une vido MPEG-4 nous utilisons les classes NetStream et NetConnection, de la mme manire quune vido au format FLV. Sachez que le lecteur Flash ne sappuie pas sur les extensions de fichiers audio ou video afin de tester le type de la vido mais sur l'entte (binaire) du fichier en question. Dans le cas dune ancienne application cense charger des fichiers vido au format FLV, il est tout fait possible de renommer lextension dune vido MPEG-4 comme mov ou mp4 en flv, celle-ci sera lue sans problmes. Dans le code suivant nous chargeons une vido MPEG-4 stocke dans un fichier QuickTime mov :
// instanciation d'un objet NetConnection var chargeurVideo:NetConnection = new NetConnection(); // lors d'un chargement de fichier local nous nous connectons null chargeurVideo.connect(null); // cration d'un objet NetStream

63 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

var fluxVideo:NetStream = new NetStream ( chargeurVideo ); // coute de l'vnement NetStatusEvent.NET_STATUS fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, etatLecture ); // lecture du fichier vido MPEG-4 fluxVideo.play ("video_hd.mov"); // le scnario joue le rle du client fluxVideo.client = this; /// mthode onMetaData dfinie sur le scnario principal function onMetaData ( pMeta ):void { /* trackinfo : [object Object],[object Object],[object Object] audiochannels : 2 width : 640 videoframerate : 23.976 height : 268 duration : 95.15537414965986 videocodecid : avc1 audiosamplerate : 44100 seekpoints : [object Object],[object Object],[object Object] moovposition : 40 avcprofile : 77 aacaot : 2 audiocodecid : mp4a avclevel : 21 */ for ( var p in pMeta ) trace( p + " : " + pMeta[p] ); } function etatLecture ( pEvt:NetStatusEvent ):void { if ( pEvt.info.code == "NetStream.FileStructureInvalid" ) trace("fichier non compatible"); else if ( pEvt.info.code == "NetStream.NoSupportedTrackFound" ) trace("aucune piste trouve"); }

Lobjet pass la mthode onMetaData possde des proprits quelque peu diffrentes de la lecture dun fichier audio AAC. Voici le dtail de chacune des proprits :
aacaot : le type de fichier audio AAC, cette proprit peut avoir la valeur 0 pour AAC Main, 1 pour AAC LC et 2 pour SBR audio types. audiochannels : le nombre de canaux du mdia charg. Dans le cas de fichiers audio AAC multicanaux, ces derniers sont dcods sur deux canaux seulement par le lecteur Flash.

64 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

audiocodecid : le codec audio utilis du mdia charg. La chane de caractres mp4a est utilise pour le format AAC, et .mp3 pour les fichiers MP3. audiosamplerate : frquence dchantillonnage du fichier audio. avclevel : cette proprit renvoie un nombre compris entre 10 et 51 donnant des informations au dcodeur concernant les ressources ncessaires pour le dcodage de la vido. avcprofile : le profil du fichier H.264, une valeur pouvant tre 66, 77, 88, 100, 110, 122 ou 144. duration : la dure en secondes du mdia charg. height : la hauteur en pixels de la vido. moovposition : la position de latome moov au sein du mdia charg. seekpoints : points de repres permettant le chapitrage du mdia.

tags : un objet comprenant diffrentes informations lies au mdia charg. Lquivalent des donnes ID3 du format MP3. trackinfo : un objet contenant diffrentes informations lies au mdia charg. videoframerate : un objet contenant diffrentes informations lies au mdia charg. videocodecid : un objet contenant diffrentes informations lies au mdia charg. width : la largeur en pixels de la vido.

Nous venons de voir comment charger une vido dynamiquement, il nous faut maintenant lafficher. Pour cela, nous devons lier le flux de lobjet NetStream un objet Video.

A retenir
Les fichiers vido MPEG-4 sont lus laide des classes NetConnection et NetStream. Lintroduction du codec MPEG-4 nempche pas la lecture de vidos au format FLV.

La classe Video
La classe Video rside au sein du paquetage flash.media. Contrairement aux prcdentes versions dActionScript, celle-ci est dsormais instanciable par programmation. La classe Video hrite de la classe DisplayObject, et possde donc toutes les proprits et mthodes propres un objet graphique.

65 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Grce la mthode attachNetStream nous pouvons lier le flux vido lobjet Video :
// instanciation d'un objet NetConnection var chargeurVideo:NetConnection = new NetConnection(); // lors d'un chargement de fichier local nous nous connectons null chargeurVideo.connect(null); // cration d'un objet NetStream var fluxVideo:NetStream = new NetStream ( chargeurVideo ); // coute de l'vnement NetStatusEvent.NET_STATUS fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, etatLecture ); // cration de l'objet Video var ecranVideo:Video = new Video(); // on attache le flux l'cran vido ecranVideo.attachNetStream( fluxVideo ); // ajout la liste d'affichage addChild ( ecranVideo ); // lecture du fichier vido MPEG-4 fluxVideo.play ("wall-e-tsr1_h.640.mov"); // le scnario joue le rle du client fluxVideo.client = this; /// mthode onMetaData dfinie sur le scnario principal function onMetaData ( pMeta ):void { for ( var p in pMeta ) trace( p + " : " + pMeta[p] ); } function etatLecture ( pEvt:NetStatusEvent ):void { if ( pEvt.info.code == "NetStream.FileStructureInvalid" ) trace("fichier non compatible"); else if ( pEvt.info.code == "NetStream.NoSupportedTrackFound" ) trace("aucune piste trouve"); }

Le code prcdent gnre le rsultat illustr par la figure 17-12 :

66 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-12. Lecture de vido MPEG-4. Lobjet Video ne se redimensionne pas automatiquement aux dimensions du mdia. Celui-ci possde une largeur de 320 pixels en largeur et 240 pixels en hauteur. Nous allons utiliser les proprits width et height de lobjet pass la mthode onMetaData afin de redimensionner lobjet Video :
// mthode onMetaData dfinie sur le scnario principal function onMetaData ( pMeta ):void { ecranVideo.width = pMeta.width; ecranVideo.height = pMeta.height; if ( !contains ( ecranVideo ) ) { addChild ( ecranVideo ); } ecranVideo.x = (stage.stageWidth - ecranVideo.width) >> 1; ecranVideo.y = (stage.stageHeight - ecranVideo.height) >> 1; }

Nous ajoutons lobjet Video la liste daffichage au sein de la mthode onMetaData. Nous testons si lobjet Video nest pas dj prsent laffichage, le cas chant nous lajoutons, puis nous centrons la vido. La figure 17-13 illustre le rsultat : 67 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-13. Vido MPEG-4 adapte et centre. Afin de conserver une image lisse, nous pouvons activer la proprit smoothing de lobjet Video dont la valeur par dfaut est false. Il est conseill dactiver cette proprit lors de la lecture de vidos au sein du lecteur Flash 9.0.115 et versions ultrieures afin de tirer profit de loptimisation de limage par mip-mapping. Attention toutefois l'utilisation conjointe du mode plein cran et de cette proprit, peut engendrer une surcharge du processeur importante sur les machines peu puissantes. Pour plus dinformations concernant le mip-mapping, consultez le chapitre 12 intitul Programmation Bitmap.

A retenir

68 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

En ActionScript 3 la classe Video est instanciable par programmation. Lobjet Video est li au flux charg laide de la mthode attachNetStream.

Transformation du son li un objet NetStream


Nous avons en dbut de chapitre comment modifier le son laide de la proprit soundTransform de lobjet SoundChannel. Lorsquun mdia est lu laide de la classe NetStream, aucun objet SoundChannel nest disponible. Lobjet SoundTransform li au mdia en cours de lecture est accessible par la proprit soundTransform de lobjet NetStream. Dans le code suivant, nous rduisons le volume de la vido de 50 % :
// instanciation d'un objet NetConnection var chargeurVideo:NetConnection = new NetConnection(); // lors d'un chargement de fichier local nous nous connectons null chargeurVideo.connect(null); // cration d'un objet NetStream var fluxVideo:NetStream = new NetStream ( chargeurVideo ); // rcupration de l'objet SoundTransform associ au mdia charg var transformation:SoundTransform = fluxVideo.soundTransform; // modification du volume transformation.volume = .5; // application de la modification fluxVideo.soundTransform = transformation; // coute de l'vnement NetStatusEvent.NET_STATUS fluxVideo.addEventListener( NetStatusEvent.NET_STATUS, etatLecture ); // cration de l'objet Video var ecranVideo:Video = new Video(); // on attache le flux l'cran vido ecranVideo.attachNetStream( fluxVideo ); // ajout la liste d'affichage addChild ( ecranVideo ); // lecture du fichier vido MPEG-4 fluxVideo.play ("wall-e-tsr1_h.640.mov"); // le scnario joue le rle du client fluxVideo.client = this; /// mthode onMetaData dfinie sur le scnario principal function onMetaData ( pMeta ):void {

69 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

for ( var p in pMeta ) trace( p + " : " + pMeta[p] ); } function etatLecture ( pEvt:NetStatusEvent ):void { if ( pEvt.info.code == "NetStream.FileStructureInvalid" ) trace("fichier non compatible"); else if ( pEvt.info.code == "NetStream.NoSupportedTrackFound" ) trace("aucune piste trouve"); }

Le mme code sapplique dans le cas du chargement dun son MPEG4 AAC.

A retenir
Afin de modifier le son dun mdia associ un objet NetStream nous utilisons sa proprit soundTransform.

Mode plein-cran
Afin de bnficier pleinement du dcodage video MPEG-4 du lecteur Flash 9, celui-ci intgre en plus depuis la version 9.0.28 la capacit de passer laffichage en mode plein cran. Jusqu prsent, cette fonctionnalit ntait reserve quau lecteur Flash autonome qui permettait le passage en mode plein cran par le biais du mode projecteur. Dans le cas de moniteurs multiples, lcran ayant le plus de contenu Flash en cours daffichage est choisi automatiquement par le lecteur. La classe Stage dfinit une proprit displayState permettant le passage du lecteur en plein cran, celle-ci accepte deux valeurs stockes au sein de la classe StageDisplayState :
StageDisplayState.NORMAL : Mode normal. StageDisplayState.FULL_SCREEN : Mode plein cran.

Attention, le passage en mode plein cran est soumis aux diffrentes restrictions suivantes :
Le mode plein-cran ne peut pas tre dclench de manire autonome. Seule une action utilisateur clavier ou souris permet dactiver le mode plein cran. Dans le cas contraire une erreur est leve lexcution. La page contenant le lecteur Flash doit autoriser le mode plein-cran en activant lattribut allowFullScreen des balises <embed> et

70 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

qui empche lactivation du mode plein-cran.

<object> laide du boolen true. La valeur par dfaut est false ce

Les touches du clavier sont vrrouilles lexception de la touche ESC permettant de repasser en mode normal, la saisie de texte est donc impossible.

Dans le code suivant, nous passons en mode plein-cran lorsque lutilisateur clique sur la scne :
// coute de l'vnement MouseEvent.CLICK auprs de l'objet Stage stage.addEventListener ( MouseEvent.CLICK, gestionAffichage ); function gestionAffichage ( pEvt:MouseEvent ):void { // passage en mode plein-cran stage.displayState = StageDisplayState.FULL_SCREEN; }

Par dfaut le code prcdent nest pas suffisant, afin dautoriser le mode plein cran lanimation doit tre lue au sein du lecteur Flash du navigateur et lattribut allowFullScreen des balises <embed> et <object> doit tre pass true. Afin dautomatiser lactivation du mode plein cran au sein de la page conteneur, il est conseill de slectionner au sein de longlet HTML de panneau Paramtres de publication le modle Flash seulement autorisation du plein cran. La figure 17-14 illustre le modle prdfini :

Figure 17-14 : Onglet HTML du panneau Paramtres de publication. Une nouvelle proprit fullScreenSourceRect fut introduite au sein du lecteur Flash 9.0.115. Celle-ci permet de spcifier la surface passer en plein cran. Ce paramtre est idal pour dterminer quelle partie de lapplication doit tre redimensionne.
flash.geom.Rectangle afin de dfinir la surface voulue :

Pour lutiliser nous devons crer une instance de la classe


// coute de l'vnement MouseEvent.CLICK auprs de l'objet Stage

71 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

stage.addEventListener ( MouseEvent.CLICK, gestionAffichage ); function gestionAffichage ( pEvt:MouseEvent ):void { // une surface est dfinie comme zone passer en plein cran var surfacePleinEcran:Rectangle = new Rectangle ( 0, 0, 150, 150 ); // la zone est spcifie stage.fullScreenSourceRect = surfacePleinEcran; // passage en mode plein-cran stage.displayState = StageDisplayState.FULL_SCREEN; }

Ainsi, nous pouvons choisir comme zone agrandir la surface occupe par la vido en cours de lecture :
// coute de l'vnement MouseEvent.CLICK auprs de l'objet Stage stage.addEventListener ( MouseEvent.CLICK, goFull ); function goFull ( pEvt:MouseEvent ):void { // la surface occuppe par la vido est dfinie comme zone agrandir var surfacePleinEcran:Rectangle = new Rectangle ( ecranVideo.x, ecranVideo.y, ecranVideo.width, ecranVideo.height ); // la zone est spcifie stage.fullScreenSourceRect = surfacePleinEcran; // passage en mode plein-cran stage.displayState = StageDisplayState.FULL_SCREEN; }

Afin de faciliter ce processus de redimensionnement, le lecteur Flash peut allouer cette tche au processeur de la carte graphique afin de rendre le redimensionnement moins gourmand et plus fluide. Le lecteur Flash sappuie sur Direct X sous Windows et Open GL sur Mac OS X. Au cas o la carte graphique ne serait pas compatible, une acclration dite logicielle est applique, le processeur est donc charge de la tche. Afin dactiver lacclration matrielle, il suffit de slectionner loption Paramtres de la liste droulante du lecteur Flash et de cocher la case Activer lacclration matrielle au sein de longlet Affichage illustr par la figure 17-15 :

72 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 17 Son et vido version 0.1.1

Figure 17-15 : Onglet Affichage du lecteur Flash. Il nest pas possible dactiver ou dsactiver lacclration matrielle par programmation.

A retenir
La classe Stage dfinit une proprit displayState pouvant prendre comme valeur StageDisplayState.NORMAL et StageDisplayState.FULL_SCREEN. Le mode plein cran ne peut tre dclench de manire autonome. Le mode plein cran doit tre autoris au sein de la page conteneur grce lattribut allowFullScreen.

Les touches du clavier sont vrrouilles, lexception de la touche ESC. La saisie du texte est impossible, ce qui limite malheureusement lexploitation du mode plein-cran.

ActionScript 3 nous rserve encore des surprises, au cours du prochain chapitre nous allons dcouvrir un nouveau moyen de communiquer avec lextrieur grce aux connexions par socket.

73 / 73
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

18
Sockets

UNE PASSERELLE UNIVERSELLE ................................................................... 1 CREER UN SERVEUR DE SOCKET XML ......................................................... 6 LA CLASSE XMLSOCKET ........................................................................................ 7 CREER UN TCHAT MULTI-UTILISATEUR ................................................................ 11 LA CLASSE SOCKET .......................................................................................... 23 CREER UN SERVEUR DE SOCKET BINAIRE .............................................................. 24 ECHANGER DES DONNES ..................................................................................... 28

Une passerelle universelle


Nous avons vu au cours des chapitres prcdents comment le lecteur Flash pouvait communiquer avec lextrieur. Nous allons aller plus loin en abordant prsent la notion de communication par socket. Une socket doit tre considre comme une passerelle de communication entre deux applications fonctionnant sur un rseau. Dans la vie courante, une socket pourrait tre assimile une connexion tlphonique entre deux personnes. Pour entreprendre une communication par socket, deux acteurs au minimum sont ncessaires :
Le serveur : le serveur de socket coute les connexions entrantes, il se charge de grer les clients connects et communique avec eux. Le client : le client se connecte au serveur par la socket et communique avec le serveur.

Notons que lavantage principal dune connexion par socket rside dans le caractre persistent de la communication entre les deux acteurs. Contrairement une connexion HTTP traditionnelle, la socket 1 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

maintient la connexion ouverte entre les deux acteurs et permet au serveur de transmettre des informations aux clients sans que ces derniers nen fassent la demande. Ce comportement offre ainsi de nombreuses possibilits en termes dapplications dynamiques temps rel. Afin de bien comprendre la notion de socket, prenons lexemple suivant : Lorsque vous naviguez sur Internet laide votre navigateur favori, ce dernier demande au serveur distant de lui transmettre la page que vous souhaitez afficher. Ce langage commun entre les deux acteurs est appel protocole dapplication et fait partie de ce que lon appelle la suite de protocoles Internet. Internet est bas sur cet ensemble de protocole gnralement appel modle TCP/IP. Afin dafficher une page spcifique, le navigateur envoie au serveur HTTP la requte suivante :
GET /index.php HTTP/1.1 Host: www.bytearray.org

Nous pouvons remarquer la prsence de mots rservs tels GET ou Host faisant partie du protocole dapplication HTTP. Par ces mots cls, le serveur comprend quil doit transmettre le contenu de la page index.php au client connect, ici notre navigateur. Aussitt la requte reue, le serveur lanalyse et rpond laide du message suivant :
HTTP/1.x 200 OK Date: Sun, 20 Jan 2008 14:30:34 GMT Server: Apache/2.0.54 (Debian GNU/Linux) PHP/4.3.10-22 mod_ssl/2.0.54 OpenSSL/0.9.7e mod_perl/1.999.21 Perl/v5.8.4 X-Powered-By: PHP/4.3.10-22 X-Pingback: http://www.bytearray.org/xmlrpc.php Content-Encoding: gzip Vary: Accept-Encoding Content-Length: 4260 Keep-Alive: timeout=15, max=97 Connection: Keep-Alive Content-Type: text/html; charset=UTF-8

En ralit, lapplication serveur et le client communiquent par socket laide dun protocole commun. Un nombre illimit de protocoles dapplications peuvent ainsi tre implments ou crs puis utiliss laide dune connexion par socket. Nous sommes donc en mesure dutiliser au sein de Flash nimporte quel protocole dapplication tel HTTP, FTP, SMTP ou autres. 2 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Pour diffrencier les applications dun ordinateur associes chaque protocole, chaque application est associe un port spcifique appel couramment port logiciel. Diffrentes applications sont donc accessibles partir dune mme adresse IP mais un port unique. Parmi les protocoles dapplications les plus connus, nous pouvons tablir le tableau suivant : Port 21 25 80 110 Application FTP (transfert de fichiers) SMTP (envoi de courrier lectronique) HTTP (transfert hypertexte) POP (stockage de courrier lectronique) Tableau 1. Liste de protocoles communs. Afin de connatre les ports logiciels associs chaque protocole. Vous pouvez consulter la liste disponible aux adresses suivantes :
http://fr.wikipedia.org/wiki/Liste_des_ports_logiciels http://www.iana.org/assignments/port-numbers

Ainsi, nous pouvons placer les diffrents ports dans trois catgories :
Les ports bien connus (0 1023) : Ces ports sont couramment utiliss par des processus systmes ayant un accs privilgi. Il est donc dconseill dy avoir recours pour un serveur de socket personnalis, un risque de collision serait encouru. Les ports enregistrs (1024 49151) : Ils sont ddis aux dveloppements dapplications courantes. La plupart de ces ports demeurent libres et peuvent tre utiliss. Les ports dynamiques privs (49152 65535) : Ces ports demeurent gnralement libres.

Il est important de prciser que les classes de socket ActionScript 3 sappuient sur un modle de transmission TCP et sont considres comme des passerelles dchanges sres, sopposant au modle de transmission UDP considr comme peu fiable. Mais quentendons-nous par passerelle dchange sre ? En ralit, deux types de protocoles peuvent tre utiliss sur Internet, le premier de type TCP puis le second de type UDP :
TCP (Transmission Control Protocol) : Dans un contexte de connexion TCP, les donnes changes sont contrles. Au cas o la transmission est trop rapide ou si des donnes sont perdues, le serveur ralentit le dbit, et rexpdie les donnes non correctement transmises. UDP (User Datagram Packet) : Dans un contexte de connexion UDP, afin de gagner en vitesse de transfert aucun contrle nest effectu lors

3 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

du transfert des donnes. En revanche certains paquets peuvent ne jamais parvenir. Ce type de socket est gnralement utilis dans le cas de jeux en temps rel o la perte de quelques paquets ninfluence pas le bon fonctionnement de lapplication.

Les classes de socket ActionScript 3 sappuient sur le protocole TCP, une compatibilit future avec le protocole UDP pourrait tre intressante pour certains types dapplications. Une communication par socket est gnralement divise en plusieurs phases distinctes :
1. Le client se connecte au serveur de socket en envoyant une commande spcifique afin dinitier la conversation ou de sauthentifier. 2. Le serveur dcide daccepter ou non le client. Cette tape peut tre vrifie de par la provenance du client ou les informations soumises par ce dernier lors de la procdure dauthentification. 3. La communication est tablie.

A tout moment, un des acteurs de la communication peut dcider de clore la connexion. Le serveur peut dcider dinterrompre la communication au cas o un client ne serait plus autoris ou simplement lorsquun client souhaite se dconnecter. Voici les deux classes permettant de se connecter un serveur de Socket en ActionScript 3 :
flash.net.XMLSocket : la classe XMLSocket permet lchange de donnes au format XML ou texte. flash.net.Socket : la classe Socket permet lchange de donnes brutes au format binaire.

Il est important de noter quActionScript 3 nintgre pas de classes permettant la cration de serveurs de sockets, seules des applications clients pourront tre dveloppes. Il est donc impossible de connecter directement deux applications Flash au travers dune connexion socket. Pour cela, lune des applications devrait se comporter comme hte ce qui est impossible en ActionScript 3. Si tel tait le cas, la cration dun rseau peer 2 peer entre diffrentes applications Flash serait envisageable.

4 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Figure 18-1. Connexion peer 2 peer non supporte. Le choix du langage utilis pour la cration du serveur dpend de vos besoins. Des langages tels le C, C++, C#, Java ou encore PHP offrent tous la possibilit de crer des serveurs de socket.

Figure 18-2. Connexion un serveur de socket. Nous allons nous intresser dans la partie suivante aux diffrents types de sockets disponibles en ActionScript 3.

A retenir

5 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Une socket est une passerelle de communication entre deux applications voluant sur un rseau. Les classes de Socket ActionScript 3 sont bases sur le modle TCP/IP. Le protocole UDP nest pas pris en charge. Les classes de socket ActionScript 3 ne permettent pas la cration de serveur de socket. Le nombre de messages changs travers une connexion socket est illimit. ActionScript 3 intgre deux classes lies aux sockets : XMLSocket et Socket.

Crer un serveur de socket XML


Avant dentamer le dveloppement du serveur de socket, il convient de dfinir son rle ainsi que son fonctionnement. Le rle dun serveur de socket est dcouter les connexions entrantes puis de grer par la suite la connexion ainsi que les changes entre les acteurs impliqus dans la communication. Nous avons vu prcdemment quil tait impossible de connecter directement plusieurs applications Flash entre elles. A linverse, le serveur de socket peut agir comme relais afin de faire transiter les messages entre les diffrents clients. Vous pensiez peut tre que cela tait rserv des technologies telles Flash Media Server ou Red 5, nous allons voir que quelques lignes de PHP vont nous permettre de connecter plusieurs applications Flash entre elles. Nous allons dvelopper au cours de cette partie un serveur de socket XML afin de crer un tchat multi utilisateurs. Comme nous lavons vu prcdemment, de nombreux langages permettent la cration de serveur de socket de manire simplifie. Parmi ceux l nous pouvons citer Java, C#, C++ ou PHP qui savre tre le moyen le langage le plus simple pour dployer votre serveur de socket. Une explication approfondie de lAPI de socket PHP serait hors sujet, cest la raison pour laquelle nous ne rentrerons pas dans les dtails du code du serveur. Nous allons donc commencer par un simple serveur renvoyant un message de bienvenue lorsque nous nous connectons. Le code PHP suivant est plac au sein dun fichier nomm ServeurXMLSocket.php :
#!/usr/local/bin/php -q

6 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

<?php set_time_limit(0); $adresse = "localhost"; $port = "10000"; $connexion = socket_create (AF_INET, SOCK_STREAM, SOL_TCP); socket_bind ($connexion, $adresse, $port); socket_listen ($connexion, 1); echo "Le serveur de socket est en route !"; $client = socket_accept ($connexion); socket_close ($client); socket_close ($connexion); ?>

Afin de dmarrer convenablement le serveur, il convient de le lancer par ligne de commande. Pour cela, nous crons un nouveau fichier texte contenant le code suivant :
C:/wamp/bin/php/php5.2.5/php.exe -q c:/wamp/www/serveur/ServeurXMLSocket.php

Puis, nous sauvons le fichier texte sous le nom serveur.bat. Depuis la ligne de commande nous accdons au fichier .bat puis nous lexcutons, le message illustr par la figure 18-3 doit safficher :

Figure 18-3. Dmarrage du serveur depuis la ligne de commande. Une fois le serveur dmarr, nous devons nous y connecter, cest ce que nous allons dcouvrir dans la partie suivante.

La classe XMLSocket
Afin de pouvoir se connecter notre serveur de socket, nous pouvons utiliser une instance de la classe XMLSocket dont voici le constructeur :

7 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

public function XMLSocket(host:String = null, port:int = 0)

Voici le dtail de chacun des paramtres :


host : il sagit de ladresse du serveur de socket. Attention, si le serveur volue au sein dun autre domaine que le SWF, la connexion doit tre autorise par un fichier de rgulation. port : le numro de port TCP utilis lors de la connexion. Par dfaut il est impossible de se connecter un port infrieur 1024 pour des raisons de prcautions. Si vous souhaitez tout de mme utiliser un port infrieur vous devez utiliser un fichier de rgulation.

Dans le code suivant, nous tablissons une connexion avec notre serveur de socket XML :
// cration de la connexion var connexion:XMLSocket = new XMLSocket("localhost", 10000);

Au cas o ladresse et le port utilis ne seraient pas prciss lors de linstanciation, nous pouvons utiliser la mthode connect :
// cration de l'objet XMLSocket var connexion:XMLSocket = new XMLSocket(); // connexion au serveur de socket connexion.connect("localhost", 10000);

Attention, les classes XMLSocket et Socket nont pas la possibilit de se connecter des ports suprieurs 65535 et infrieurs 1024. Lutilisation de ports infrieurs 1024 pourrait entraner une collision avec des serveur tels HTTP, FTP ou autres. Si vous souhaitez utiliser un port infrieur 1024, lutilisation dun fichier de rgulation est ncessaire. Afin dcouter les diffrentes phases lies la connexion nous utilisons les vnements diffuss par lobjet XMLSocket dont voici la liste :
Event.CLOSE : diffus lorsque la connexion socket est interrompue. Event.CONNECT: diffus lorsque la connexion au serveur a pu tre ralise. DataEvent.DATA : diffus lors de lenvoi ou rception de donnes. IOErrorEvent.IO_ERROR : diffus lorsque la connexion au serveur de socket na pas aboutie. SecurityError.SECURITY : diffus lorsque la connexion socket tente de se connecter auprs dun serveur no autoris ou port infrieur 1024.

Nous coutons les diffrents vnements de connexions ainsi que lvnement DataEvent.DATA : 8 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

// cration d'une instance de XMLSocket var connexion:XMLSocket = new XMLSocket("localhost", 10000); // coute des vnements connexion.addEventListener ( Event.CONNECT, connexionReussie ); connexion.addEventListener ( Event.CLOSE, fermetureConnexion ); connexion.addEventListener ( DataEvent.DATA, receptionDonnees ); function connexionReussie ( pEvt:Event ):void { trace("connexion russie"); } function fermetureConnexion ( pEvt:Event ):void { trace("fermeture de la connexion"); } function receptionDonnees ( pEvt:DataEvent ):void { trace("rception des donnes"); }

En testant le code prcdent, nous voyons que la connexion aboutit, puis nous sommes immdiatement dconnects. Les messages suivants sont affichs par la fentre de sortie :
connexion russie fermeture de la connexion

Il serait intressant de pouvoir parler au serveur de socket. Pour cela nous modifions les lignes PHP suivantes :
#!/usr/local/bin/php -q <?php set_time_limit(0); $adresse = "localhost"; $port = "10000"; $connexion = socket_create (AF_INET, SOCK_STREAM, SOL_TCP); socket_bind ($connexion, $adresse, $port); socket_listen ($connexion, 1); echo "Le serveur de socket est en route !"; $client = socket_accept($connexion);

9 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

$messageEntrant = socket_read ($client, 1024); $messageSortie = "Vous avez dit : ".$messageEntrant."\r\n"; socket_write ($client, $messageSortie); socket_close ($client); socket_close ($connexion); ?>

Afin de prendre en considration les modifications, nous redmarrons le serveur de socket. Dsormais, celui-ci est en attente dun message du client. Nous allons envoyer une simple chane de caractres laide de la mthode send de lobjet XMLSocket :
// envoie d'une chane de caractre au serveur connexion.send ("il y'a quelqu'un ?");

En observant la fentre de sortie nous pouvons voir les messages suivants :


connexion russie rception des donnes fermeture de la connexion

Aussitt la mthode send excute, le lecteur Flash transmet au serveur de socket la chane de caractre spcifie en terminant le message par un octet nul. Les donnes transmises par le serveur sont aussi termines par un octet nul, ce marqueur permet au lecteur Flash de connatre en interne la fin du paquet transmis. Comme son nom lindique, la classe XMLSocket est prvue pour changer des donnes au format XML. Pourtant, nous changeons ici une chane de caractre traditionnelle. Le format XML peut tre utilis si vous souhaitez changer des donnes structures, dans dautres cas, des simples chanes de caractres peuvent tre utilises. Afin de lire les donnes provenant du serveur, nous utilisons la lvnement DataEvent.DATA. Lobjet vnementiel diffus possde une proprit data contenant les donnes transmises par le serveur :
// cration d'une instance de XMLSocket var connexion:XMLSocket = new XMLSocket("localhost", 10000); // envoie d'une chane de caractre au serveur connexion.send ("il y'a quelqu'un ?");

10 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

// coute des vnements connexion.addEventListener ( Event.CONNECT, connexionReussie ); connexion.addEventListener ( Event.CLOSE, fermetureConnexion ); connexion.addEventListener ( DataEvent.DATA, receptionDonnees ); function connexionReussie ( pEvt:Event ):void { trace("connexion russie"); } function fermetureConnexion ( pEvt:Event ):void { trace("fermeture de la connexion"); } function receptionDonnees ( pEvt:DataEvent ):void { // affiche : Vous avez dit : il y'a quelqu'un ? trace ( pEvt.data ); }

Attention, la mthode send nest pas bloquante, nous devons garder lesprit le caractre asynchrone des changes raliss par une connexion socket. Seul lvnement DataEvent.DATA nous permet de rcuprer les donnes transmises par le serveur et de dterminer quel moment celles-ci sont disponibles. Nous allons aller plus loin en dveloppant un tchat multi-utilisateur. Le serveur de socket XML que nous allons dvelopper servira de relais afin de faire transiter les messages des clients connects.

A retenir
La classe XMLSocket permet la connexion auprs dun serveur de socket. Les donnes changes entre les deux obligatoirement tre au format XML ou texte. acteurs doivent

Lobjet XMLSocket diffuse diffrents vnements permettant dindiquer ltat de la connexion et du transfert des donnes.

Crer un tchat multi-utilisateur


Afin de dvelopper notre tchat multi utilisateur, nous allons rutiliser le moteur dmoticones dvelopp au cours du chapitre 16 intitul Le texte. 11 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Nous retrouvons nouveau lintrt de la programmation oriente objet en facilitant la rutilisation dobjets prdfinis. La figure 18-3 illustre le fonctionnement dun chat multi utilisateur, comme nous lavons vu prcdemment, le serveur agit comme relais entre les diffrents clients connects :

Figure 18-3. Tchat multi-utilisateurs. Au sein dun fichier nomm ServeurMessagerieXML.php nous dfinissons le code suivant :
#!/usr/bin/php -q <?php set_time_limit(0); $adresse = 'localhost'; $port = 10000; $connexion = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($connexion, SOL_SOCKET,SO_REUSEADDR, 1); $ret = socket_bind($connexion, $adresse, $port); $ret = socket_listen($connexion, 5); $tabSockets = array($connexion); while (true) { $sockets = $tabSockets; socket_select($sockets, $write = NULL, $except = NULL, NULL); foreach($sockets as $socket) {

12 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

if ($socket == $connexion) { if (($client = socket_accept($connexion)) < 0) continue; else array_push($tabSockets, $client); } else { $flux = socket_recv($socket, $buffer, 2048, 0); if ($flux == 0) { $index = array_search($socket, $tabSockets); unset($tabSockets[$index]); socket_close($socket); }else { $allclients = $tabSockets; array_shift($allclients); send_Message($allclients, $socket, $buffer); } } } } function send_Message($clients, $socket, $donnees) { foreach($clients as $client) { socket_write($client, $donnees); } } ?>

Nous ne dmarrons pas le serveur pour le moment, nous allons dvelopper prsent la partie client de lapplication de messagerie instantane. Lors 16, nous avions dvelopp une classe MoteurEmoticone, celle-ci va nous permettre dafficher le texte de la conversation. Grce ses fonctionnalits, les utilisateurs auront la possibilit dutiliser un ensemble dmoticones prdfinis. Nous ajoutons les lignes suivantes la classe MoteurEmoticone : 13 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

du

chapitre

Chapitre 18 Sockets version 0.1

package org.bytearray.emoticones { import import import import import import import import flash.display.Sprite; flash.events.Event; flash.events.KeyboardEvent; flash.geom.Rectangle; flash.text.TextField; flash.ui.Keyboard; org.bytearray.evenements.EvenementSaisie; org.bytearray.evenements.EvenementMessage;

public class MoteurEmoticone extends Sprite { public public public public public public public public var var var var var var var var contenuHistorique:TextField; chaineHistorique:String; contenuSaisie:TextField; codesTouches:Array; tableauSmileys:Array; lngTouches:int; coordonnees:Rectangle; emoticone:Emoticone;

public function MoteurEmoticone () { chaineHistorique = new String(); codesTouches = new Array (":)",":D",":(",";)",":p"); tableauSmileys = new Array (); lngTouches = codesTouches.length; addEventListener ( KeyboardEvent.KEY_DOWN, envoiMessage ); contenuHistorique.addEventListener ( Event.SCROLL, saisieUtilisateur ); } private function envoiMessage ( pEvt:KeyboardEvent ):void { if ( pEvt.keyCode == Keyboard.ENTER ) { dispatchEvent ( new EvenementSaisie ( EvenementSaisie.ENVOI_MESSAGE, contenuSaisie.text ) ); contenuSaisie.htmlText = ""; } }

14 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

public function afficheMessage ( pEvt:EvenementMessage ):void { chaineHistorique += pEvt.message; contenuHistorique.text = chaineHistorique; contenuHistorique.dispatchEvent ( new Event ( Event.SCROLL ) ); } private function saisieUtilisateur ( pEvt:Event ):void { var i:int; var j:int; var nombreSmileys:int = tableauSmileys.length; for ( i = 0; i< nombreSmileys; i++ ) removeChild ( tableauSmileys[i] ); tableauSmileys = new Array(); for ( i = 0; i<lngTouches; i++ ) { j = pEvt.target.text.indexOf ( codesTouches[i] ); while ( j!= -1 ) { coordonnees = pEvt.target.getCharBoundaries ( j ); if ( coordonnees != null ) tableauSmileys.push ( ajouteSmiley ( coordonnees, i ) ); j = pEvt.target.text.indexOf ( codesTouches[i], j+1 ); } } } private function ajouteSmiley ( pRectangle:Rectangle, pIndex:int ):Emoticone { emoticone = new Emoticone(); emoticone.gotoAndStop ( pIndex + 1 ); emoticone.x = pRectangle.x + 1; emoticone.y = pRectangle.y - ((contenuHistorique.scrollV 1)*(contenuHistorique.textHeight/contenuHistorique.numLines))+1; addChild ( emoticone ); return emoticone;

15 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

} } }

La classe MoteurEmoticone est lie un clip contenant deux champs texte contenuHistorique et contenuSaisie. Afin de pouvoir informer le reste de lapplication de la saisie utilisateur, la classe MoteurEmoticone diffuse un vnement EvenementSaisie.ENVOI_MESSAGE. Au sein du paquetage org.bytearray.evenements dfinissons la classe EvenementSaisie suivante :
package org.bytearray.evenements { import flash.events.Event; public class EvenementSaisie extends Event { public static const ENVOI_MESSAGE:String = "envoiMessage"; public var saisie:String; public function EvenementSaisie ( pType:String, pSaisie:String ) { super( pType, false, false ); saisie = pSaisie; } public override function clone ():Event { return new EvenementSaisie ( type, saisie ); } public override function toString ():String { return '[EvenementSaisie type="'+ type +'" bubbles=' + bubbles + ' eventPhase='+ eventPhase + ' cancelable=' + cancelable +' saisie=' + saisie +']'; } }

nous

16 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

La classe EvenementMessage permet dinformer lapplication de larrive de nouveaux messages. Au sein du mme paquetage que la classe EvenementSaisie nous dfinissons la classe EvenementMessage suivante :
package org.bytearray.evenements { import flash.events.Event; public class EvenementMessage extends Event { public static const RECEPTION_MESSAGE:String = "receptionMessage"; public var message:String; public function EvenementMessage ( pType:String, pMessage:String ) { super( pType, false, false ); message = pMessage; } public override function clone ():Event { return new EvenementMessage ( type, message ); } public override function toString ():String { return '[EvenementMessage type="'+ type +'" bubbles=' + bubbles + ' eventPhase='+ eventPhase + ' cancelable=' + cancelable +' message=' + message + ']'; } } }

Afin de tester la classe MoteurEmoticone nous associons la classe de document suivante un nouveau document Flash CS3 :
package org.bytearray.document { import org.bytearray.abstrait.ApplicationDefaut;

17 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

import org.bytearray.emoticones.MoteurEmoticone; public class Document extends ApplicationDefaut { public var client:MoteurEmoticone; public function Document () { client = new MoteurEmoticone(); client.x = (stage.stageWidth - client.width) >> 1; client.y = (stage.stageHeight - client.height) >> 1; addChild ( client ); } } }

La figure 18-4 illustre linterface de tchat :

Figure 18-4. Client chat multi-utilisateur. Lobjet MoteurEmoticone diffuse un vnement EvenementSaisie.ENVOI_MESSAGE que nous coutons afin de rcuprer le texte saisi :
package org.bytearray.document

18 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

{ import org.bytearray.abstrait.ApplicationDefaut; import org.bytearray.emoticones.MoteurEmoticone; import org.bytearray.evenements.EvenementSaisie; public class Document extends ApplicationDefaut { public var client:MoteurEmoticone; public function Document () { client = new MoteurEmoticone(); client.x = (stage.stageWidth - client.width) >> 1; client.y = (stage.stageHeight - client.height) >> 1; addChild ( client ); client.addEventListener ( EvenementSaisie.ENVOI_MESSAGE, envoiMessage ); } private function envoiMessage ( pEvt:EvenementSaisie ):void { // affiche : [EvenementSaisie type="envoiMessage" bubbles=false eventPhase=2 cancelable=false saisie=salut !] trace( pEvt ); } } }

Puis nous envoyons les donnes au serveur de socket grce la mthode send de lobjet XMLSocket :
package org.bytearray.document { import import import import import flash.events.DataEvent; flash.net.XMLSocket; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.emoticones.MoteurEmoticone; org.bytearray.evenements.EvenementSaisie;

public class Document extends ApplicationDefaut { private static const ADRESSE:String = "localhost"; private static const PORT:int = 10000; public var client:MoteurEmoticone; public var connexionSocket:XMLSocket;

19 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

public function Document () { client = new MoteurEmoticone(); client.x = (stage.stageWidth - client.width) >> 1; client.y = (stage.stageHeight - client.height) >> 1; addChild ( client ); client.addEventListener ( EvenementSaisie.ENVOI_MESSAGE, envoiMessage ); ); connexionSocket = new XMLSocket ( Document.ADRESSE, Document.PORT

connexionSocket.addEventListener ( DataEvent.DATA, receptionDonnees ); } private function envoiMessage ( pEvt:EvenementSaisie ):void { // affiche : [EvenementSaisie type="envoiMessage" bubbles=false eventPhase=2 cancelable=false saisie=salut !] trace( pEvt ); // envoi des donnes auprs du serveur de socket connexionSocket.send ( pEvt.saisie ); } private function receptionDonnees ( pEvt:DataEvent ):void { // affiche : salut ! trace( pEvt.data ); } } }

Le principe est trs simple, aussitt les donnes envoyes, le serveur de socket transmet le texte saisi par un des participants tous les clients connects. Pour que lobjet MoteurEmoticone soit averti des messages provenant du serveur et affiche la conversation, nous diffusons un vnement EvenementMessage.RECEPTION_MESSAGE :
package org.bytearray.document { import flash.events.DataEvent;

20 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

import import import import import

flash.net.XMLSocket; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.emoticones.MoteurEmoticone; org.bytearray.evenements.EvenementMessage; org.bytearray.evenements.EvenementSaisie;

public class Document extends ApplicationDefaut { private static const ADRESSE:String = "localhost"; private static const PORT:int = 10000; public var client:MoteurEmoticone; public var connexionSocket:XMLSocket; public function Document () { client = new MoteurEmoticone(); client.x = (stage.stageWidth - client.width) >> 1; client.y = (stage.stageHeight - client.height) >> 1; addChild ( client ); client.addEventListener ( EvenementSaisie.ENVOI_MESSAGE, envoiMessage ); // coute de l'vnement EvenementMessage.RECEPTION_MESSAGE afin d'afficher le texte addEventListener ( EvenementMessage.RECEPTION_MESSAGE, client.afficheMessage ); ); connexionSocket = new XMLSocket ( Document.ADRESSE, Document.PORT

connexionSocket.addEventListener ( DataEvent.DATA, receptionDonnees ); } private function envoiMessage ( pEvt:EvenementSaisie ):void { // affiche : [EvenementSaisie type="envoiMessage" bubbles=false eventPhase=2 cancelable=false saisie=salut !] trace( pEvt ); // envoi des donnes auprs du serveur de socket connexionSocket.send ( pEvt.saisie ); } private function receptionDonnees ( pEvt:DataEvent ):void { // diffusion d'un vnement EvenementMessage.RECEPTION_MESSAGE afin d'afficher les donnes reues du serveur dispatchEvent ( new EvenementMessage ( EvenementMessage.RECEPTION_MESSAGE, pEvt.data ) );

21 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

} } }

En testant notre client nous remarquons que le texte est bien affich lorsque nous lenvoyons, comme lillustre la figure 18-5 :

Figure 18-5. Client flash connect. Si nous connectons un deuxime client, notre chat multi utilisateur fonctionne. Vous pouvez prsent dployer le serveur de socket ainsi que le client Flash sur votre serveur et diffuser ladresse aux personnes avec qui vous souhaitez discuter. La classe XMLSocket permet le dveloppement dapplications temps rel de manire trs souple. Bien entendu, de nombreux serveurs existent dj et sont conus pour fonctionner avec Flash laide de la classe XMLSocket. Parmi ceux l nous pouvons citer les serveurs suivants :
Unity : serveur de socket XML payant de Colin Moock http://www.moock.org/unity SmartFox Server : serveur de socket XML payant http://www.smartfoxserver.com

22 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Jabber : serveur de socket XML gratuit destin la cration dapplications de messageries instantanes - http://www.jabber.org

Une des limitations de la classe XMLSocket est lie limitation du format des donnes changes. Nous allons voir dans cette nouvelle partie dans quelle mesure la classe Socket peut savrer utile.

A retenir
La classe XMLSocket permet dchanger des donnes au format XML et texte entre un client et un serveur de socket. En tant que relais, le serveur de socket permet de faire transiter des informations entre plusieurs clients connects. Chaque message est termin par un octet nul.

La classe Socket
ActionScript 3 intgre une nouvelle classe Socket se diffrenciant quelque peu de la classe XMLSocket. Comme nous lavons vu prcdemment, la classe XMLSocket transmet chaque message en le terminant par un octet nul. Ce comportement provoque un troncage des donnes binaire. La classe Socket ne souffre pas de cette limitation et offre la possibilit de transfrer un flux binaire brut sans altrations. Si nous devions schmatiser le fonctionnement dun serveur de socket binaire nous pourrions lillustrer de la manire suivante :

23 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Figure 18-6. XMLSocket et Socket. Nous allons ensemble dvelopper un serveur de socket binaire afin de transfrer par une connexion socket un lment graphique telle une image ou un SWF. Outre lexprimentation technique, ce procd permet dviter la mise en cache des lments graphiques au sein du navigateur. Llment graphique est charg directement en mmoire par le lecteur Flash puis affich, cette approche vite la mise en cache de fichiers SWF et rend leur dcompilation difficile.

A retenir
La classe Socket permet dchanger des donnes au format binaire brutes. Grce au caractre bas niveau de la classe Socket, il est possible dimplmenter nimporte quel protocole dapplication.

Crer un serveur de socket binaire


Nous allons nouveau utiliser PHP afin de crer notre serveur de socket binaire. Lintrt de ce dernier sera de pouvoir recevoir des requtes et dy rpondre. Nous allons ainsi dfinir notre propre protocole afin de normaliser les changes entre les deux acteurs. Dans un fichier PHP nomm ServeurSocket.php nous dfinissons le code suivant :

24 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

#!/usr/bin/php -q <?php $adresse = "localhost"; $port = "10000"; $connexion = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($connexion, $adresse, $port); socket_listen($connexion, 1); echo "Le serveur de socket binaire est en route !"; $client = socket_accept($connexion); socket_close($client); socket_close($connexion); ?>

Une fois le serveur dmarr nous pouvons nous y connecter de la manire suivante :
// cration de la connexion var connexion:Socket = new Socket(); // connexion au serveur de socket connexion.connect( "localhost", 10000 ); // coute des diffrents vnements connexion.addEventListener( Event.CONNECT, clientConnecte ); connexion.addEventListener( Event.CLOSE, clientDeconnecte ); function clientConnecte ( pEvt:Event ):void { trace("client connect"); } function clientDeconnecte ( pEvt:Event ):void { trace("client dconnect"); }

Si nous testons le code prcdent, nous remarquons que la connexion est aussitt ferme. Le panneau de sortie affiche les messages suivants :
client connect client dconnect

25 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Nous modifions le code du serveur de socket binaire en ajoutant un appel la mthode socket_write pour envoyer les donnes au client en cours :
#!/usr/bin/php -q <?php $adresse = "localhost"; $port = "10000"; $connexion = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($connexion, $adresse, $port); socket_listen($connexion, 1); echo "Le serveur de socket binaire est en route !"; $client = socket_accept($connexion); $messageSortant = "Bienvenue sur le serveur de socket, mais dsol vous tes dconnect !"; socket_write ($client, $messageSortant ); socket_close($client); socket_close($connexion); ?>

Nous envoyons dsormais des donnes depuis le serveur avant de clore la connexion. Lorsque le serveur transmet les donnes, lvnement ProgressEvent.SOCKET_DATA est diffus :
// cration de la connexion var connexion:Socket = new Socket(); // connexion au serveur de socket connexion.connect( "localhost", 10000 ); // coute des diffrents vnements connexion.addEventListener( Event.CONNECT, clientConnecte ); connexion.addEventListener( ProgressEvent.SOCKET_DATA, donneesRecues ); connexion.addEventListener( Event.CLOSE, clientDeconnecte ); function clientConnecte ( pEvt:Event ):void { trace("client connect"); } function donneesRecues ( pEvt:ProgressEvent ):void {

26 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

trace("donnes reues"); } function clientDeconnecte ( pEvt:Event ):void { trace("client dconnect"); }

Si nous testons le code prcdent nous voyons que le client parvient se connecter, puis les donnes sont reues, enfin le client est automatiquement dconnect. Le panneau de sortie affiche les messages suivants :
client connect donnes reues client dconnect

Contrairement la classe XMLSocket, les donnes reues par la classe Socket doivent tre dcodes. Afin de lire les donnes transmises par le serveur nous utilisons ici la mthode readUTFBytes de lobjet Socket dont voici la signature :
public function readUTFBytes(length:uint):String

Celle-ci accepte un paramtre length correspondant au nombre doctets lire et renvoyer sous forme de chane de caractres UTF-8. Afin de lire les donnes disponibles, nous utilisons la proprit bytesAvailable :
function donneesRecues ( pEvt:ProgressEvent ):void { // affiche : Bienvenue sur le serveur de socket, mais dsol vous tes dconnect ! trace( pEvt.target.readUTFBytes ( pEvt.target.bytesAvailable ) ); }

Noubliez pas que dans un contexte de transfert de donnes TCP/IP, les donnes arrivent par paquet doctets. Il est donc ncessaire de lire le flux de donnes au fur et mesure que les donnes arrivent au sein du lecteur. Celles-ci saccumulent au sein de lobjet Socket et peuvent tre lues laide des diffrentes mthodes dfinies par la classe Socket. La proprit bytesAvailable nous permet de rcuprer le nombre doctets disponibles actuellement au sein du flux tlcharg et vite de sortir du flux de donnes. 27 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Si nous tentons de lire des octets non disponibles, lerreur suivante est leve lexcution :
Error: Error #2030: Fin de fichier dtecte.

Nous reviendrons en dtail sur les diffrentes mthodes de lecture et dcriture de flux binaire au cours du chapitre 20 intitul ByteArray.

A retenir
Contrairement la classe XMLSocket, la classe Socket reoit un flux binaire brut de la part du serveur. Ces donnes doivent tre dcodes laide des diffrentes mthodes de la classe Socket.

Echanger des donnes


Nous avons voqu en dbut de chapitre la notion de protocole dapplication afin dchanger des messages entre un client un serveur. Les protocoles dapplications tels FTP, SMTP, ou POP sappuient sur des messages prdfinis afin dappeler certaines fonctionnalits auprs du serveur de socket. Nous allons reproduire ce mme comportement en demandant notre serveur de socket de transfrer un fichier graphique prsent ses cts. En passant la chane de caractre ENVOIE suivie du nom du fichier, le serveur renverra le flux du fichier demand. Afin dintgrer ce protocole dapplication nous modifions les lignes suivantes au sein du serveur de socket binaire :
#!/usr/bin/php -q <?php $adresse = "localhost"; $port = "10000"; $connexion = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($connexion, $adresse, $port); socket_listen($connexion, 1); echo "Le serveur de socket binaire est en route !"; $client = socket_accept($connexion); $actif = true; $COMMANDE_ENVOIE = "ENVOIE"; do

28 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

{ $actif = socket_read($client, 1024, PHP_BINARY_READ); $array = split (" ", trim($actif)); $commande = $array[0]; $fichier = $array[1]; $pointeur = fopen ($fichier, "rb"); $donnees = fread ($pointeur, filesize ($fichier)); fclose ($pointeur); if ( $commande == $COMMANDE_ENVOIE ) { $longueur = pack('N', strlen($donnees)); socket_write($client, $longueur); socket_write($client, $donnees); } else socket_write ($client, "commande non reconnue"); } while ( $actif ); socket_close($client); socket_close($connexion); ?>

Rappelez vous dun point essentiel, le protocole TCP/IP transfre les donnes par paquets, cest nous de nous assurer que la totalit des donnes ont t transfres. Il est donc impratif de rcuprer lensemble des paquets cots client avant de tenter dafficher le fichier transfr :
// cration de la connexion var connexion:Socket = new Socket(); // connexion au serveur de socket connexion.connect( "localhost", 10000 ); // coute des diffrents vnements connexion.addEventListener( Event.CONNECT, clientConnecte ); connexion.addEventListener( ProgressEvent.SOCKET_DATA, donneesRecues ); connexion.addEventListener( Event.CLOSE, clientDeconnecte ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); // demande du chargement du fichier connexion.writeUTFBytes ("ENVOIE:DSC02602.JPG\r\n"); connexion.flush(); function clientConnecte ( pEvt:Event ):void { trace("client connect"); } function clientDeconnecte ( pEvt:Event ):void

29 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

{ trace("client dconnect"); } function erreurConnexion ( pEvt:Event ):void { trace("erreur de connexion au serveur"); } // cration d'un tableau binaire pour contenir les donnes entrantes var donnees:ByteArray = new ByteArray(); // nombre d'octets totaux transfrer var longueur:Number; function donneesRecues ( pEvt:ProgressEvent ):void { // nous rcuprons le nombre d'octets totaux if ( !longueur ) longueur = pEvt.target.readUnsignedInt(); // les donnes sont progressivement stockes au sein du tableau donnees pEvt.target.readBytes ( donnees, donnees.length, pEvt.target.bytesAvailable ); // donnes charges if ( donnees.length == termine"); } longueur ) trace ("transfert des donnes

Grce la mthode writeUTFBytes, nous encodons la chane de caractre passe en paramtre en flux binaire. Afin de transmettre celle-ci au serveur nous poussons les donnes laide de la mthode flush. Aussitt la commande reue, le serveur renvoie la longueur totale des donnes recevoir, puis le flux de llment graphique est transmis. Afin de sauver les donnes entrantes, nous crons un tableau de sauvegarde dans lequel nous plaons les donnes tlcharges lors de la diffusion de lvnement ProgressEvent.SOCKET_DATA. Souvenez-vous, nous avions dcouvert lobjet Loader lors du chapitre 13, ce dernier permettait le chargement de contenu externe. La classe Loader dfinit une mthode loadBytes permettant de rendre graphiquement un flux binaire pass en paramtre. Voici la signature de la mthode loadBytes :
public function loadBytes(bytes:ByteArray, context:LoaderContext = null):void

30 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Dtail de chacun des paramtres :


bytes : le flux binaire rendre graphiquement. context : le contexte de chargement, li au modle de scurit.

Attention, la mthode loadBytes naccepte que des flux binaires compatibles avec lobjet Loader. En dautres termes, seuls les flux dimages ou de SWF pourront tre affichs. Si un flux non compatible est pass la mthode loadBytes, une erreur de type IOErrorEvent.IO_ERROR est leve. Dans le code suivant, un objet Loader est cr. Une fois les donnes totalement tlcharges, nous les injectons au sein de ce dernier :
// cration de la connexion var connexion:Socket = new Socket(); // connexion au serveur de socket connexion.connect( "localhost", 10000 ); // coute des diffrents vnements connexion.addEventListener( Event.CONNECT, clientConnecte ); connexion.addEventListener( ProgressEvent.SOCKET_DATA, donneesRecues ); connexion.addEventListener( Event.CLOSE, clientDeconnecte ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); // demande du chargement du fichier connexion.writeUTFBytes ("ENVOIE:DSC02602.JPG\r\n"); connexion.flush(); function clientConnecte ( pEvt:Event ):void { trace("client connect"); } function clientDeconnecte ( pEvt:Event ):void { trace("client dconnect"); } function erreurConnexion ( pEvt:Event ):void { trace("erreur de connexion au serveur"); } var chargeur:Loader = new Loader();

31 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

addChild ( chargeur ); // cration d'un tableau binaire pour contenir les donnes entrantes var donnees:ByteArray = new ByteArray(); // nombre d'octets totaux transfrer var longueur:Number; function donneesRecues ( pEvt:ProgressEvent ):void { // nous rcuprons le nombre d'octets totaux if ( !longueur ) longueur = pEvt.target.readUnsignedInt(); // les donnes sont progressivement stockes au sein du tableau donnees pEvt.target.readBytes ( donnees, donnees.length, pEvt.target.bytesAvailable ); // lorsque le flux binaire est totalement charg nous laffichons grce lobjet Loader if ( donnees.length == longueur ) chargeur.loadBytes( donnees ); }

En testant le code prcdent nous remarquons que le fichier est tlcharg par la connexion socket puis affich au sein du lecteur. Une fois les donnes injectes, lobjet Loader diffuse un vnement Event.COMPLETE. Dans le code suivant, nous redimensionnons limage et la centrons une fois le flux affich :
chargeur.contentLoaderInfo.addEventListener( Event.COMPLETE, injectionTerminee ); function injectionTerminee ( pEvt:Event ):void { var contenu:DisplayObject = pEvt.target.content; var objetChargeur:Loader = pEvt.target.loader; if ( contenu is Bitmap ) Bitmap ( contenu ).smoothing = true; var ratio:Number = Math.min ( 350 / pEvt.target.content.width, 350 / pEvt.target.content.height ); objetChargeur.scaleX = objetChargeur.scaleY = ratio; objetChargeur.x = (stage.stageWidth - objetChargeur.width) / 2; objetChargeur.y = (stage.stageHeight - objetChargeur.height) / 2; }

Le rsultat est illustr par la figure 18-7 :

32 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Figure 18-7. Image charge par connexion socket. Il serait intressant de rendre cette communication transparente au sein dun objet spcifique. Nous allons crer une classe ChargeurFlux qui procdera en interne aux diffrentes commandes du protocole. Au sein dun paquetage org.bytearray.chargeur nous dfinissons la classe ChargeurFlux suivante :
package org.bytearray.chargeur { import import import import import flash.events.EventDispatcher; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.Socket;

public class ChargeurFlux extends EventDispatcher { private var connexion:Socket; public function ChargeurFlux ( pAdresse:String=null, pPort:int=0 ) { connexion = new Socket(pAdresse, pPort); connexion.addEventListener ( Event.CONNECT, redirigeEvenement );

33 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

connexion.addEventListener ( ProgressEvent.SOCKET_DATA, transfertDonnees ); connexion.addEventListener ( IOErrorEvent.IO_ERROR, redirigeEvenement ); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function transfertDonnees ( pEvt:ProgressEvent ):void { } } }

Puis nous ajoutons un objet ByteArray interne afin daccueillir le flux tlcharg :
package org.bytearray.chargeur { import import import import import import flash.events.EventDispatcher; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.Socket; flash.utils.ByteArray;

public class ChargeurFlux extends EventDispatcher { private var connexion:Socket; private var donnees:ByteArray; public function ChargeurFlux ( pAdresse:String=null, pPort:int=0 ) { connexion = new Socket(pAdresse, pPort); donnees = new ByteArray(); connexion.addEventListener ( Event.CONNECT, redirigeEvenement ); connexion.addEventListener ( ProgressEvent.SOCKET_DATA, transfertDonnees ); connexion.addEventListener ( IOErrorEvent.IO_ERROR, redirigeEvenement ); }

34 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function transfertDonnees ( pEvt:ProgressEvent ):void { } } }

Puis nous implmentons les mthodes charge et connecte :


package org.bytearray.chargeur { import import import import import import flash.events.EventDispatcher; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.Socket; flash.utils.ByteArray;

public class ChargeurFlux extends EventDispatcher { private var connexion:Socket; private var donnees:ByteArray; private static const ENVOIE:String = "ENVOIE"; public function ChargeurFlux ( pAdresse:String=null, pPort:int=0 ) { connexion = new Socket(pAdresse, pPort); donnees = new ByteArray(); connexion.addEventListener ( Event.CONNECT, redirigeEvenement ); connexion.addEventListener ( ProgressEvent.SOCKET_DATA, transfertDonnees ); connexion.addEventListener ( IOErrorEvent.IO_ERROR, redirigeEvenement ); } public function charge ( pFichier:String ):void {

35 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

"\r\n" ); }

connexion.writeUTFBytes ( ChargeurFlux.ENVOIE + " " + pFichier + connexion.flush();

public function connecte ( pAdresse:String, pPort:int ):void { connexion.connect ( pAdresse, pPort ); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function transfertDonnees ( pEvt:ProgressEvent ):void { } } }

Enfin, nous ajoutons la logique associe afin de dterminer la fin du chargement du flux :
package org.bytearray.chargeur { import import import import import import import flash.events.EventDispatcher; flash.events.Event; flash.events.ProgressEvent; flash.events.IOErrorEvent; flash.net.Socket; flash.utils.ByteArray; org.bytearray.chargeur.evenements.EvenementChargeurFlux;

public class ChargeurFlux extends EventDispatcher { private var connexion:Socket; private var donnees:ByteArray; private var longueur:Number; private static const ENVOIE:String = "ENVOIE"; public function ChargeurFlux ( pAdresse:String=null, pPort:int=0 ) {

36 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

connexion = new Socket(pAdresse, pPort); donnees = new ByteArray(); connexion.addEventListener ( Event.CONNECT, redirigeEvenement ); connexion.addEventListener ( ProgressEvent.SOCKET_DATA, transfertDonnees ); connexion.addEventListener ( IOErrorEvent.IO_ERROR, redirigeEvenement ); } public function charge ( pFichier:String ):void { "\r\n" ); } public function connecte ( pAdresse:String, pPort:int ):void { connexion.connect ( pAdresse, pPort ); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } private function transfertDonnees ( pEvt:ProgressEvent ):void { if ( !longueur ) longueur = pEvt.target.readUnsignedInt(); pEvt.target.readBytes ( donnees, donnees.length, pEvt.target.bytesAvailable ); if ( donnees.length == longueur ) { dispatchEvent ( new EvenementChargeurFlux ( EvenementChargeurFlux.TERMINE, donnees ) ); donnees = new ByteArray(); } } } } connexion.writeUTFBytes ( ChargeurFlux.ENVOIE + " " + pFichier + connexion.flush();

37 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

Afin de charger un lment graphique stock sur le serveur, nous instancions simplement lobjet ChargeurFlux puis nous appelons sa mthode charge :
import org.bytearray.chargeur.ChargeurFlux; import org.bytearray.chargeur.evenements.EvenementChargeurFlux; var monChargeur:ChargeurFlux = new ChargeurFlux("localhost", 10000); monChargeur.charge ("blason.png"); var chargeur:Loader = new Loader(); chargeur.contentLoaderInfo.addEventListener( Event.COMPLETE, injectionTerminee ); addChild ( chargeur ); monChargeur.addEventListener ( EvenementChargeurFlux.TERMINE, affiche ); function affiche ( pEvt:EvenementChargeurFlux ):void { chargeur.loadBytes( pEvt.donnees ); } function injectionTerminee ( pEvt:Event ):void { var contenu:DisplayObject = pEvt.target.content; var objetChargeur:Loader = pEvt.target.loader; if ( contenu is Bitmap ) Bitmap ( contenu ).smoothing = true; var ratio:Number = Math.min ( 350 / pEvt.target.width, 350 / pEvt.target.height ); objetChargeur.scaleX = objetChargeur.scaleY = ratio; objetChargeur.x = (stage.stageWidth - objetChargeur.width) / 2; objetChargeur.y = (stage.stageHeight - objetChargeur.height) / 2; }

La classe ChargeurFlux peut ainsi tre utilise pour le chargement de fichiers SWF afin dviter leur mise en cache au sein du navigateur et rendre leur dcompilation moins facile :
monChargeur.charge ("monsite.swf");

A vous dimaginer de nouvelles fonctionnalits !

A retenir

38 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 18 Sockets version 0.1

La mthode writeUTFBytes permet dcrire une chane de caractres au format binaire. La mthode flush permet denvoyer les donnes binaires au serveur de socket.

Dans le prochain chapitre nous dcouvrirons la technologie Flash Remoting afin de dialoguer de manire optimise avec diffrents langages serveurs et bases de donnes.

39 / 39
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

19
Flash Remoting

LA TECHNOLOGIE ............................................................................................... 1 UN FORMAT OPTIMIS ............................................................................................. 2 PASSERELLE REMOTING .................................................................................. 4 DPLOIEMENT ......................................................................................................... 5 LE SERVICE............................................................................................................ 7 SE CONNECTER AU SERVICE .................................................................................. 15 LA CLASSE RESPONDER ........................................................................................ 16 APPELER UNE MTHODE DISTANTE ....................................................................... 18 ECHANGER DES DONNEES PRIMITIVES ................................................................... 22 ECHANGER DES DONNEES COMPOSITES ................................................................. 27 ECHANGER DES DONNEES TYPEES ......................................................................... 32 ENVOYER UN EMAIL AVEC FLASH REMOTING ....................................................... 33 EXPORTER UNE IMAGE .......................................................................................... 40 SE CONNECTER A UNE BASE DE DONNEES.............................................................. 49 SECURITE ............................................................................................................. 61 LA CLASSE SERVICE ......................................................................................... 62

La technologie
Dvelopp lorigine par Macromedia, Flash Remoting est une technologie visant optimiser grandement les changes client-serveur. Le lecteur Flash 6 fut le premier en bnficier, mais la puissance de celle-ci ne fut pas perue immdiatement par la communaut due en partie un manque de documentation et dinformations ce sujet. Grce Flash Remoting nous allons optimiser notre temps de dveloppement dapplications dynamiques et apprendre penser diffremment nos changes client-serveur. Des frameworks tels Flex

1 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

ou AIR sappuient aujourdhui fortement sur Flash Remoting trop souvent inconnu des dveloppeurs Flash. Nous allons dcouvrir au sein de ce chapitre comment tirer profit de cette technologie en ActionScript 3 travers diffrentes applications.

A retenir
La technologie Flash Remoting vu le jour en 2001 au sein du lecteur 6 (Flash MX). Flash Remoting permet doptimiser les changes client-serveur.

Un format optimis
Toute la puissance de Flash Remoting rside dans le format dchanges utilis, connu sous le nom dAMF (Action Message Format). Afin de bien comprendre lintrt de ce format, il convient de revenir sur le fonctionnement interne du lecteur Flash dans un contexte de chargement et denvoi de donnes. Nous avons vu au cours du chapitre 14 intitul Chargement et envoi de donnes que les donnes changes entre le lecteur Flash et le script serveur taient par dfaut ralises au format texte. Dans le cas dchanges de variables encodes URL, une reprsentation texte des variables doit tre ralise. Nous devons donc nous assurer manuellement de la srialisation et dsrialisation des donnes ce qui peut savrer long et fastidieux, surtout dans un contexte de large flux de donnes. Nous pouvons alors utiliser le format XML, mais l encore bien que lintroduction de la norme ECMAScript pour XML (E4X) ait sensiblement amlior les performances dinterprtation de flux XML, dans un contexte dchanges, le flux XML reste transport au format texte ce qui nest pas optimis en terme de bande passante. Grce au format AMF, le lecteur Flash se charge de srialiser et dsrialiser les donnes ActionScript nativement. Nous pouvons ainsi changer des donnes complexes types, sans se soucier de la manire dont cela est ralis. Lorsque nous souhaitons transmettre des donnes par Flash Remoting, le lecteur encode un paquet AMF binaire compress contenant les donnes srialises, puis le transmet au script serveur par la mthode POST par le biais du protocole HTTP. Voici un extrait de la transaction HTTP : 2 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

POST gateway.php HTTP/1.1 Host: www.bytearray.org User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0 .8,image/png,*/*;q=0.5 Accept-Language: fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://www.bytearray.org/wpcontent/projects/jpegencoder/media_snapshot.swf Content-type: application/x-amf Content-length: 4944 Paquet AMF

Une fois le paquet AMF dcod par le serveur, ce dernier rpond au lecteur Flash en lui transmettant un nouveau paquet AMF de rponse :
HTTP/1.1 200 OK Date: Sat, 02 Feb 2008 02:55:40 GMT Server: Apache/2.0.54 (Debian GNU/Linux) PHP/4.3.10-22 mod_ssl/2.0.54 OpenSSL/0.9.7e mod_perl/1.999.21 Perl/v5.8.4 X-Powered-By: PHP/4.3.10-22 Expires: Sat, 2 Feb 2008 03:55:40 GMT Cache-Control: no-store Pragma: no-store Content-length: 114 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: application/x-amf Paquet AMF

Il est important de comprendre que contrairement au format texte, le format AMF est un format binaire compress natif du lecteur Flash. Lutilisation du format AMF nous permet donc doptimiser la bande passante utilise et daugmenter les performances dune application en saffranchissant totalement de toute srialisation manuelle des donnes. James Ward a dernirement publi une application comparant les performances dchanges au format JSON, XML et AMF. Lapplication est disponible ladresse suivante :
http://www.jamesward.org/blazebench/

Bien que le lecteur Flash puisse nativement encoder ou dcoder un paquet AMF, lapplication serveur se doit elle aussi de pouvoir dcoder ce mme paquet. Cest ici quintervient la notion de passerelle remoting.

A retenir
3 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Lacronyme AMF signifie Action Message Format. Flash Remoting est bas sur un change des donnes au format AMF binaire par le biais du protocole HTTP. Encoder et dcoder un paquet AMF est beaucoup plus rapide et moins gourmand quune srialisation et dsrialisation au format texte. Deux versions du format AMF existent aujourdhui : AMF0 (ActionScript 1 et 2) et AMF3 (ActionScript 3). Les formats AMF0 et AMF3 sont ouverts et documents. Aujourdhui, le format AMF est utilis au sein des classes NetConnection, LocalConnection, SharedObject, ByteArray, Socket et URLStream.

Passerelle remoting
Lorsque le lecteur Flash transmet un paquet AMF un script serveur, celui-ci nest pas par dfaut en mesure de le dcoder. Des projets appels passerelles remoting ont ainsi vu le jour, permettant des langages serveurs tels PHP, Java ou C# de comprendre le format AMF. Chaque passerelle est ainsi lie un langage serveur et se charge de convertir le flux AMF transmis en donnes compatibles. Afin de dvelopper ces passerelles, le format AMF a du tre pirat par la communaut afin de comprendre comment le dcoder. Initialement non document, Adobe a finalement rendu public les spcifications du format AMF en dcembre 2007. Toute personne souhaitant dvelopper une passerelle pour un langage spcifique peut donc se baser sur ces spcifications fournies par Adobe. Celles ci peuvent tre tlcharges ladresse suivante : http://labs.adobe.com/technologies/blazeds/ En plus douvrir le format AMF, Adobe a dcid de fournir une passerelle officielle pour la plateforme Java J2EE nomme BlazeDS tlchargeable la mme adresse. Il existe aujourdhui un grand nombre de passerelles remoting pour la plupart open source, le tableau suivant regroupe les plus connues associes chaque langage : Nom Langage Open Source Lien

4 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

AMFPHP Web ORB BlazeDS Ruby AMF Fluorine

PHP PHP, .NET, Ruby, Java. Java Ruby .NET

Oui Non Oui Oui Oui

http://www.amfphp.org http://www.themidnightcod ers.com http://labs.adobe.com/techn ologies/blazeds/ http://www.rubyamf.org http://fluorine.thesilentgrou p.com

Tableau 1. Passerelles AMF. La figure 19-1 illustre le modle de communication entre le lecteur Flash et la passerelle remoting :

Figure 19-1. Communication entre le lecteur Flash et la passerelle remoting. Les paquets AMF devant forcment transiter par la passerelle afin dtres dcods, le lecteur ne se connecte donc jamais directement auprs du script serveur, mais auprs de la passerelle. Nous allons utiliser tout au long du chapitre, la passerelle remoting AMFPHP afin de simplifier nos changes avec le serveur.

Dploiement
Avant de dployer AMFPHP il convient de tlcharger le projet ladresse suivante : http://sourceforge.net/projects/amfphp

5 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Attention, le format AMF3 nest pris en charge que depuis la version 1.9, nous veillerons donc tlcharger une version gale ou ultrieure. Souvenez-vous, ActionScript 3 utilise le format AMF3, les anciennes versions dActionScript utilisent le format AMF0. Une fois AMFPHP tlcharg, nous extrayons larchive et obtenons les rpertoires illustrs par la figure 19-2 :

Figure 19-2. Fichiers AMFPHP. Voici le dtail des trois rpertoires ainsi que du fichier gateway.php :

browser : contient une application de dboguage que nous allons

dcouvrir trs bientt.

core : il sagit des sources dAMFPHP. Dans la majorit des cas nous nirons jamais au sein de ce rpertoire.

Cest le rpertoire le plus important pour nous. Flash.

services : les services distants sont placs au sein de ce rpertoire. gateway.php : la passerelle laquelle nous nous connectons depuis

Une des forces des diffrentes passerelles remoting rside dans leur simplicit de dploiement sur le serveur. Les passerelles sont dployables sur des serveurs mutualiss et ne ncessitent aucun accs privilgi au niveau de ladministration du serveur. AMFPHP requiert une version PHP 4.3.0 ou suprieure, lutilisation de PHP 5 ajoutant quelques fonctionnalits intressantes AMFPHP. Nous plaons le rpertoire amfphp contenant les fichiers illustrs prcdemment sur notre serveur au sein dun rpertoire echanges et accdons ladresse suivante pour vrifier que tout fonctionne correctement : http://localhost/echanges/gateway.php 6 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Si tout fonctionne, nous obtenons le message suivant :


amfphp and this gateway are installed correctly. You may now connect to this gateway from Flash. Note: If you're reading an old tutorial, it will tell you that you should see a download window instead of this message. This confused people so this is the new behaviour starting from amfphp 1.2.

Si vous souhaitez utiliser une autre passerelle remoting que AMFPHP, soyez rassurs, le fonctionnement de la plupart dentre elles est similaire.

A retenir
La passerelle remoting joue le rle dintermdiaire entre le lecteur Flash et lapplication serveur. Le dploiement dune passerelle remoting ne prend que quelques secondes. Aucun droit spcifique nest ncessaire auprs du serveur. Il est donc possible dutiliser Flash Remoting sur un serveur mutualis. AMFPHP requiert une version de PHP 4.3.0 au minimum.

Le service
Contrairement aux moyens de communication tudis prcdemment, Flash Remoting dfinit la notion de service. Le service est une classe dfinissant les mthodes que nous souhaitons appeler depuis lapplication Flash. Nous pourrions ainsi appeler le service classe distante. Un des avantages de Flash Remoting est li sa conception oriente objet. Au lieu dappeler diffrents scripts serveurs stocks au sein de plusieurs fichiers PHP, nous appelons directement depuis Flash des mthodes dfinies au sein du service. La figure 19-3 illustre lide :

7 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Figure 19-3. Service distant associ la passerelle. Nous allons crer notre premier service en plaant au sein du rpertoire services, une classe PHP nomme Echanges.php. Celle-ci est dfinie au sein dun paquetage org.bytearray.test :
<?php class Echanges { function Echanges ( ) { } function premierAppel () { } } ?>

La classe Echanges doit donc tre accessible ladresse suivante : http://localhost/echanges/services/org/bytearray/test/Echanges.php Notons que contrairement ActionScript 3, PHP 4 et 5 nintgrent pas de mot cl package, ainsi la classe ne contient aucune indication lie son paquetage. La classe PHP Echanges dfinit une seule mthode premierAppel que nous allons pouvoir tester directement depuis un navigateur grce un outil extrmement pratique appel Service Browser (explorateur de services). Dans notre exemple, lexplorateur de service est accessible ladresse suivante : http://localhost/echanges/browser/ Lexplorateur de service est une application Flex dveloppe afin de pouvoir tester depuis notre navigateur les diffrentes mthodes de notre service. La partie gauche de lapplication indique les services actuellement dploys. En droulant les nuds nous pouvons accder un service spcifique. La figure 19-4 illustre lexplorateur :

8 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Figure 19-4. Explorateur de service. En slectionnant un service, la partie droite de lapplication dcouvre les mthodes associes au service. Chaque mthode est accessible et peut tre teste directement depuis le navigateur. Cela permet de pouvoir dvelopper la logique serveur sans avoir tester depuis Flash. Longlet Results affiche alors le rsultat de lexcution de la mthode distante premierAppel :

Figure 19-5. Explorateur de mthodes lies au service. Dans notre exemple, la mthode premierAppel ne renvoie aucune valeur, longlet Results affiche donc null. Afin de retourner des informations Flash, la mthode doit obligatoirement utiliser le mot cl return : 9 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

<?php class Echanges { function Echanges ( ) { } function premierAppel () { return "cela fonctionne sans problme !"; } } ?>

Si nous testons nouveau la mthode, nous pouvons voir directement les valeurs retournes :

Figure 19-6. Explorateur de mthodes lies au service. La possibilit de pouvoir excuter depuis le navigateur les mthodes distantes est un avantage majeur. Lexplorateur service doit tre considr comme un vritable outil de dboguage de service. Au cas o une erreur PHP serait prsente au sein du service, lexplorateur de service nous lindique au sein de longlet Results

10 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Dans le code suivant, nous oublions dajouter un point virgule en fin dexpression :
<?php class Echanges { function Echanges ( ) { } function premierAppel () { return "cela fonctionne sans problme !" } } ?>

En tentant dexcuter la mthode, le message suivant est affich au sein de longlet Results :
Parse error: parse error, unexpected '}' in C:\Program Files\EasyPHP 2.0b1\www\echanges\services\org\bytearray\test\Echanges.php on line 18

Les onglets situs en dessous de longlet Test nous apportent dautres informations comme les temps dexcution ou le poids des donnes transfres. La figure 19-7 illustre les informations lies lappel de la mthode premierAppel :

11 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Figure 19-7. Onglet Info. Imaginons que nous souhaitions afficher lors de lexcution de la mthode premierAppel la longueur dune chane. Nous pouvons utiliser pour cela la mthode statique trace de la classe NetDebug intgre AMFPHP. Dans le code suivant, nous affichons la longueur de la variable $chaine :
<?php class Echanges { function Echanges ( ) { }

12 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

function premierAppel () { $chaine = "cela fonctionne sans problme !"; NetDebug::trace( "infos persos : " . strlen ( $chaine ) ); } } ?>

En excutant la mthode, longlet Trace retourne le message personnalis telle une fentre de sortie classique. La figure 19-7 illustre le message affich :

Figure 19-7. Utilisation du dboguage personnalis. Cette fonctionnalit savre trs prcieuse lors de dboguage de scripts, nous dcouvrirons lintrt des autres onglets trs rapidement. Si vous disposez de PHP 5 sur votre serveur, vous avez la possibilit dutiliser les modificateurs de contrle daccs similaire ActionScript 3.

13 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

En dfinissant une mthode comme prive, celle-ci ne pourra plus tre appele depuis Flash, mais seulement depuis une autre mthode de la classe :
<?php class Echanges { function Echanges ( ) { } private function premierAppel () { return "cela fonctionne sans problme !"; } } ?>

Lexplorateur de mthodes considre alors que cette mthode nest plus disponible depuis lextrieur. La figure 19-8 illustre ltat de lexplorateur de service :

Figure 19-8. Aucune mthode disponible auprs du service. Nous retrouvons ici encore lintrt de la technologie Flash Remoting o nous voluons dans un contexte orient objet. Lutilisation de tels mcanismes amliore fortement la souplesse de dveloppement dune application dynamique. Il est temps de coder quelques lignes dActionScript, nous allons ds maintenant nous connecter au service Echanges et excuter la mthode premierAppel.

A retenir

14 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Le service distant est une classe dfinissant diffrentes mthodes accessibles depuis Flash. Lexplorateur de service nous permet de tester les mthodes en direct afin doptimiser le dboguage.

Se connecter au service
Afin de se connecter un service distant en ActionScript 3, nous disposons de diffrentes classes dont voici le dtail :
flash.net.NetConnection : la classe NetConnection permet de se connecter un service distant flash.net.Socket : la classe Socket offre une connectivit TCP/IP offrant la possibilit dchanger des donnes au format AMF laide du protocole HTTP.

Afin dappeler la mthode premierAppel, nous crons une instance de la classe NetConnection :
// cration de la connexion var connexion:NetConnection = new NetConnection ();

La classe NetConnection dfinit une proprit objectEncoding permettant de dfinir la version dAMF utilis pour les changes. En ActionScript 3, le format AMF3 est utilis par dfaut :
// cration de la connexion var connexion:NetConnection = new NetConnection (); // affiche : 3 trace( connexion.objectEncoding ); // affiche : true trace( connexion.objectEncoding == ObjectEncoding.AMF3 );

Au cas o nous souhaiterions nous connecter une passerelle ntant pas compatible avec AMF3, nous devons spcifier explicitement dutiliser le format AMF0 :
// cration de la connexion var connexion:NetConnection = new NetConnection (); // utilisation du format AMF0 pour les changes connexion.objectEncoding = ObjectEncoding.AMF0;

La version 1.9 dAMFPHP tant compatible avec le format AMF3, nous ne modifions pas la proprit objectEncoding et nous connectons la passerelle gateway.php laide de la mthode connect :
// cration de la connexion var connexion:NetConnection = new NetConnection (); // connexion la passerelle AMFPHP

15 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

connexion.connect ("http://localhost/echanges/gateway.php");

Mme si ladresse de la passerelle est errone ou inaccessible, lobjet NetConnection ne renverra aucune erreur lors de lappel de la mthode connect. Lobjet NetConnection diffuse diffrents vnements dont voici le dtail :
AsyncErrorEvent.ASYNC_ERROR : asynchrone intervient. diffus lorsquune erreur

IOErrorEvent.IO_ERROR : diffus lorsque la transmission des donnes choue. NetStatusEvent.NET_STATUS : diffus lorsquun erreur fatale intervient. SecurityErrorEvent.SECURITY_ERROR : diffus lorsque nous tentons dappeler la mthode dun service voluant sur un autre domaine sans en avoir lautorisation.

Nous coutons les diffrents vnements en passant une seule fonction couteur afin de centraliser la gestion des erreurs :
// cration de la connexion var connexion:NetConnection = new NetConnection (); // connexion la passerelle amfphp connexion.connect ("http://localhost/echanges/gateway.php"); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion); function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); }

Nous devons maintenant grer le retour du serveur, Flash CS3 intgre pour cela une classe spcifique.

La classe Responder
Avant dappeler une mthode distante, il convient de crer au pralable un objet de gestion de retour des appels grce la classe flash.net.Responder. Le constructeur de la classe Responder possde la signature suivante : 16 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

public function Responder(result:Function, status:Function = null)

Seul le premier paramtre est obligatoire :


result : rfrence la fonction grant le retour du serveur en cas de russite de lappel. status : rfrence la fonction grant le retour du serveur en cas dchec de lappel.

Dans le code suivant nous crons un objet Responder en passant deux fonctions succes et echec :
// cration de la connexion var connexion:NetConnection = new NetConnection (); // connexion la passerelle amfphp connexion.connect ("http://localhost/echanges/gateway.php"); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } // cration des fonctions de gestion de retour serveur function succes ( pRetour:* ):void { trace("retour serveur"); } function echec ( pErreur:* ):void { trace("echec de l'appel"); } // cration d'un de gestionnaire de retour des appels var retourServeur:Responder = new Responder (succes, echec);

Une fois lobjet Responder dfinit, nous pouvons appeler la mthode distante.

A retenir

17 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Les classes NetConnection et Socket permettent de se connecter une passerelle remoting laide de la mthode connect. En ActionScript 3, le format AMF3 est utilis par dfaut. Afin de grer le retour du serveur, nous devons crer un objet Responder.

Ce dernier ncessite deux fonctions qui seront dclenches en cas de succs ou chec de lappel.

Appeler une mthode distante


Afin dappeler la mthode distante, nous utilisons la mthode call de lobjet NetConnection dont voici la signature :
public function call(command:String, responder:Responder, ... arguments):void

Les deux premiers paramtres sont obligatoires :


command : la mthode du service appeler. responder : lobjet Responder traitant les retours serveurs.

arguments : les paramtres transmettre la mthode distante.

Lorsque la mthode call est appele, un paquet AMF est cr contenant le nom de la mthode excuter au sein du service ainsi que les donnes transmettre. Dans le code suivant, nous passons le chemin complet de la mthode appeler ainsi quune instance de la classe Responder pour grer le retour du serveur :
// cration de la connexion var connexion:NetConnection = new NetConnection (); // connexion la passerelle amfphp connexion.connect ("http://localhost/echanges/gateway.php"); // cration des fonctions de gestion de retour serveur function succes ( pRetour:* ):void { trace("retour serveur"); } function echec ( pErreur:* ):void { trace("echec de l'appel"); } // cration d'un objet de gestion de l'appel var retourServeur:Responder = new Responder (succes, echec);

18 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

// appel de la mthode distante connexion.call ("org.bytearray.test.Echanges.premierAppel", retourServeur);

En testant le code prcdent, nous voyons que la fonction succes est excute. Attention, noubliez pas que Flash fonctionne de manire asynchrone. La mthode call de lobjet NetConnection ne renvoie donc aucune valeur. Seul lobjet Responder pass en paramtre permet de grer le rsultat de lappel. Pour rcuprer les donnes renvoyes par le serveur nous utilisons le paramtre pRetour de la fonction succes :
function succes ( pRetour:* ):void { // affiche : cela fonctionne sans problme ! trace ( pRetour ); }

Nous venons dappeler notre premire mthode distante par Flash Remoting en quelques lignes seulement. Souvenez-vous, lors du chapitre 14 intitul Chargement et envoi de donnes nous devions nous assurer manuellement du dcodage et de lencodage UTF-8 afin de prserver les caractres spciaux. Le format AMF encode les chanes de caractres en UTF-8, et AMFPHP se charge automatiquement du dcodage. Il nest donc plus ncessaire de se soucier de lencodage des caractres spciaux avec Flash Remoting. Nous allons prsent modifier la mthode premierAppel afin que celle-ci accepte un paramtre $pMessage :
<?php class Echanges { function Echanges ( ) { } function premierAppel ( $pMessage ) { return "vous avez dit : $pMessage ?"; }

19 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

} ?>

Lorsquun paramtre est ajout une mthode, lexplorateur de service nous donne la possibilit de passer dynamiquement les valeurs au sein dun champ texte de saisie :

Figure 19-9. Paramtre. Si nous testons nouveau le code ActionScript prcdent, nous remarquons que la fonction echec est dclenche. La mthode premierAppel attend dsormais un paramtre. En lomettant, une erreur est leve que nous pouvons intercepter grce la fonction echec passe en paramtre lobjet Responder. En ciblant la proprit description de lobjet retourn nous obtenons des informations lies lerreur en cours :
function echec ( pErreur:* ):void { // affiche : Missing argument 1 for Echanges::premierAppel() trace( pErreur.description ) ; }

20 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Les informations renvoyes par AMFPHP nous permettent ainsi de dboguer lapplication plus facilement. Ce type de mcanisme illustre la puissance de Flash Remoting en matire de dboguage. Dans le cas prsent, nous apprenons de manire explicite que nous avons omis le paramtre attendu de la mthode distante premierAppel. Si nous itrons sur lobjet retourn nous dcouvrons de nouvelles proprits :
function echec ( pErreur:* ):void { /* affiche : details : C:\wamp\www\echanges\services\org\bytearray\test\Echanges.php level : Unknown error type description : Missing argument 1 for Echanges::premierAppel() line : 12 code : AMFPHP_RUNTIME_ERROR */ for ( var p:String in pErreur ) { trace( p + " : " + pErreur[p] ); } }

Dans le code suivant, nous tentons dappeler une mthode inexistante d une faute de frappe :
connexion.call ("org.bytearray.test.Echanges.premierApel", retourServeur);

La proprit description de lobjet retourn par le serveur nous indique quune telle mthode nexiste pas au sein du service :
function echec ( pErreur:* ):void { // affiche : The method {premierApel} does not exist in class {Echanges}. trace( pErreur.description ); }

De la mme manire, si nous nous trompons de service :


connexion.call ("org.bytearray.test.Echange.premierAppel", retourServeur);

AMFPHP nous oriente nouveau vers la cause de lerreur :


function echec ( pErreur:* ):void { // affiche : The class {Echange} could not be found under the class path {C:\wamp\www\echanges\services\/org/bytearray/test/Echange.php} trace( pErreur.description );

21 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Nous allons prsent nous intresser aux diffrents types de donnes changeables et dcouvrir lintrt de la srialisation automatique assure par Flash Remoting.

A retenir
La mthode call de lobjet NetConnection permet dappeler une mthode distante. Si lappel russit, la fonction succes passe en paramtre lobjet Responder est excute. Flash Remoting automatiquement. Dans le cas contraire, la fonction echec est dclenche. gre lencodage de caractres spciaux

Echanger des donnes primitives


Comme nous lavons expliqu prcdemment, Flash Remoting se charge de la srialisation et dsrialisation des donnes. Afin de nous rendre compte de ce comportement, nous modifions la mthode premierAppel en la renommant echangesTypes :
<?php class Echanges { function Echanges ( ) { } function echangeTypes ( $pDonnees ) { return gettype ( $pDonnees ); } } ?>

Celle-ci accepte un paramtre et retourne dsormais son type grce la mthode PHP gettype. Celle-ci est similaire linstruction typeof dActionScript 3. Cette mthode va nous permettre de savoir sous quel type les donnes envoyes depuis Flash arrivent cot PHP. 22 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Nous modifions les paramtres passs la mthode call en spcifiant le nouveau nom de mthode ainsi que le valeur boolenne true :
connexion.call ("org.bytearray.test.Echanges.echangeTypes", retourServeur, true);

La fonction succes reoit alors la chane boolean :


function succes ( pRetour:* ):void { // affiche : boolean trace ( pRetour ); }

Nous voyons que la passerelle AMFPHP a automatiquement conserv le type boolen entre ActionScript et PHP, on dit alors que les donnes changes sont supportes. Mais que se passe t-il si AMFPHP ne trouve pas de type quivalent ? Dans ce cas, AMFPHP interprte le type de donnes et convertit les donnes en un type quivalent. Dans le code suivant, nous passons la valeur 5 de type int :
connexion.call ("org.bytearray.Echanges.echangeTypes", retourServeur, 5);

La mthode distante echangeTypes renvoie alors le type reu, la fonction succes affiche les donnes retournes :
function succes ( pRetour:* ):void { // affiche : double trace ( pRetour ); }

Nous voyons que la passerelle AMFPHP a automatiquement convertit le type int en double, c'est--dire son quivalent PHP. La valeur 5 est envoye au sein dun paquet AMF la mthode distante, AMFPHP dsrialise automatiquement le paquet AMF et convertit le type int en un type compatible PHP. On dit alors que les donnes changes sont interprtes. Le tableau ci-dessous regroupe les diffrents types primitifs supports et interprts par AMFPHP : Type ActionScript Type PHP Conversion automatique Version AMF

23 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

null int uint Number Boolean String

NULL double double double boolean string

Oui Oui Oui Oui Oui Oui

AMF0 / AMF3 AMF3 AMF3 AMF0 / AMF3 AMF0 / AMF3 AMF0 / AMF3

Tableau 2. Tableau des types primitifs supports et interprts. Cette conversion automatique permet dchanger des donnes primitives sans aucun travail de notre part. AMFPHP se charge de toute la srialisation dans les deux sens, ce qui nous permet de nous concentrer sur dautres parties plus importantes de lapplication telles que linterface ou autres. Sans Flash Remoting, nous aurions du srialiser les donnes sous la forme dune chane de caractres, puis dsrialiser les donnes ct serveur manuellement. Grce AMFPHP, ce processus est simplement supprim. Bien entendu, dans la plupart des applications dynamiques, nous nchangeons pas de simples donnes comme celle-ci. Si nous souhaitons concilier lavantage des deux technologies, nous pouvons faire transiter une chane de caractres compresse au sein dAMF puis transformer celle-ci en objet XML au sein de Flash. Nous ajoutons une nouvelle mthode recupereMenu :
<?php class Echanges { function Echanges ( ) { } function echangeTypes ( $pDonnees ) {

24 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

return gettype ( $pDonnees ); } function recupereMenu () { $menuXML = '<MENU> <RUBRIQUE titre = "Nouveauts" id="12"> <RUBRIQUE titre = "CD" id="487"/> <RUBRIQUE titre = "Vinyls" id="540"/> </RUBRIQUE> <RUBRIQUE titre = "Concerts" id="25"> <RUBRIQUE titre = "Funk" id="15"/> <RUBRIQUE titre = "Soul" id="58"/> </RUBRIQUE> </MENU>'; return $menuXML; } } ?>

En appelant la mthode recupereMenu, nous recevons la chane de caractre que nous transformons aussitt en objet XML :
// cration de la connexion var connexion:NetConnection = new NetConnection (); // connexion la passerelle amfphp connexion.connect ("http://localhost/echanges/gateway.php"); // cration des fonctions de gestion de retour serveur function succes ( pRetour:* ):void { try { // conversion de la chane en objet XML var menu:XML = new XML ( pRetour ); /* affiche : <RUBRIQUE titre="Concerts" id="25"> <RUBRIQUE titre="Funk" id="15"/> <RUBRIQUE titre="Soul" id="58"/> </RUBRIQUE> */ trace(menu.RUBRIQUE.(@id == 25) ); //affiche : Funk trace(menu..RUBRIQUE.(@id == 15).@titre ); } catch ( pErreur:Error ) {

25 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

trace( "erreur d'interprtation du flux XML"); } } function echec ( pErreur:* ):void { trace("echec de l'appel"); } // cration d'un objet de gestion de l'appel var retourServeur:Responder = new Responder (succes, echec); // appel de la mthode recupereMenu distante, rcupration du flux xml du menu connexion.call ("org.bytearray.test.Echanges.recupereMenu", retourServeur);

En utilisant cette approche, nous conservons :


La puissance de dboguage de Flash Remoting. Lappel de mthodes distantes directement depuis Flash. La simplicit du format XML. La puissance de la norme E4X.

Mme si la conversion de la chane en objet XML par ActionScript pourrait ralentir les performances de lapplication si le flux XML devient important, cela resterait minime dans notre exemple. Il peut tre ncessaire dchanger des donnes plus complexes, nous allons voir que Flash Remoting va nouveau nous faciliter les changes.

A retenir

26 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Grce Flash Remoting les donnes automatiquement srialises et dsrialises.

primitives

sont

LorsquAMFPHP trouve un type correspondant, on dit que les donnes sont supportes. Dans le cas contraire, AMFPHP interprte les donnes en un type quivalent. Il est tout fait possible de concilier un format de reprsentation de donnes tel XML avec Flash Remoting. Le compromis XML et AMF est une option lgante prendre en considration.

Echanger des donnes composites


Dans la plupart des applications dynamiques, nous souhaitons gnralement changer des donnes plus complexes, comme des tableaux ou des objets associatifs. Voici le tableau des diffrents types composites supports et interprts par AMFPHP : Type ActionScript Object Array XML (E4X) XMLDocument Date RecordSet Type PHP associative array array string string double Ressource MySQL Conversion automatique Oui Oui Non Non Non Oui (Uniquement de MySQL vers Flash) Oui Version AMF AMF0 / AMF3 AMF0 / AMF3 AMF3 AMF0 / AMF3 AMF0 / AMF3 AMF0 / AMF3

ByteArray

ByteArray (classe AMFPHP)

AMF3

27 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Tableau 3. Tableau des types composites supports et interprts.


Date :

Dans le code suivant nous envoyons la mthode distante un objet


connexion.call ("org.bytearray.test.Echanges.echangeTypes", retourServeur, new Date());

Lobjet Date est transmis, AMFPHP ne trouvant pas de type quivalent convertit alors lobjet Date en timestamp Unix compatible. Ainsi, nous recevons ct PHP un double :
function succes ( pRetour:* ):void { // affiche : double trace( pRetour ); }

Trs souvent, nous avons besoin denvoyer au serveur un objet contenant diffrentes informations. Dans le code suivant, nous envoyons au serveur un tableau associatif contenant les informations lies un utilisateur :
// cration d'un joueur fictif var joueur:Object = new Object(); joueur.nom = "Groove"; joueur.prenom = "Bob"; joueur.age = 29; joueur.ville = "Paris"; joueur.points = 35485; // appel de la mthode distante, le joueur est envoy la mthode distante connexion.call ("org.bytearray.test.Echanges.echangeTypes", retourServeur, joueur);

Nous modifions la mthode distante en renvoyant simplement les donnes passes en paramtre :
<?php class Echanges { function Echanges ( ) { } function echangeTypes ( $pDonnees ) {

28 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

return $pDonnees; } } ?>

Lobjet est retourn Flash sous sa forme originale :


function succes ( pRetour:* ):void { /* affiche : nom : Groove prenom : Bob age : 29 ville : Paris points : 35485 */ for ( var p:String in pRetour ) { trace( p, " : " + pRetour[p] ); } }

Nous remarquons donc que la srialisation automatique est opre dans les deux sens et nous permet de gagner un temps prcieux. Cot PHP nous accdons aux proprits de lobjet de manire traditionnelle. Nous pouvons par exemple augmenter lge et le score du joueur pass en paramtre :
<?php class Echanges { function Echanges ( ) { } function echangeTypes ( $pDonnees ) { $pDonnees["age"] += 10; $pDonnees["points"] += 500; return $pDonnees; } ?> }

29 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Lobjet est retourn modifi au lecteur Flash :


function succes ( pRetour:* ):void { /* affiche : nom : Groove ville : Paris prenom : Bob age : 39 points : 35985 */ for ( var p:String in pRetour ) { trace( p, " : " + pRetour[p] ); } }

Grce au format AMF3 et la version 1.9 dAMFPHP, il est possible dchanger des objets de types ByteArray. Dans le code suivant, nous crons un tableau doctets vierge, puis nous crivons des donnes texte :
// cration d'un tableau d'octets vierge var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte fluxBinaire.writeUTFBytes("Voil du texte encod en binaire !");

Linstance de ByteArray est ensuite transmise la mthode distante :


// cration d'un tableau d'octets vierge var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte fluxBinaire.writeUTFBytes("Voil du texte encod en binaire !"); // appel de la mthode distante, le ByteArray est envoy au serveur connexion.call ("org.bytearray.test.Echanges.echangeTypes", retourServeur, fluxBinaire);

Lorsque la fonction succes est excute, lobjet ByteArray retourn par le serveur est intact et les donnes prserves :
function succes ( pRetour:* ):void { // affiche : true trace( pRetour is ByteArray ); // affiche : Voil du texte encod en binaire ! trace( pRetour.readUTFBytes ( pRetour.bytesAvailable ) ); }

30 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Bien que lintrt puisse paratre limit, cette fonctionnalit savre extrmement puissante. Nous reviendrons trs bientt sur lintrt dchanger des instances de ByteArray. Malheureusement, certains objets ne peuvent tre srialiss au format AMF, cest le cas des objets de type DisplayObject. En modifiant la mthode echangeTypes nous pourrions penser pouvoir renvoyer lobjet graphique transmis :
<?php class Echanges { function Echanges ( ) { } function echangeTypes ( $pDonnees ) { return $pDonnees; } } ?>

Si nous tentons de passer une instance de MovieClip cette mme mthode :


// cration d'une instance de MovieClip var animation:MovieClip = new MovieClip(); // appel de la mthode distante, un objet graphique est pass la mthode distante connexion.call ("org.bytearray.test.Echanges.echangeTypes", retourServeur, animation);

La srialisation AMF choue, la mthode distante echangeTypes renvoie la valeur null :


function succes ( pRetour:* ):void { // affiche : null trace( pRetour ); // affiche : true trace( pRetour == null ) }

31 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Cette limitation nest pas due la passerelle remoting AMFPHP mais au format AMF qui ne gre pas au jour daujourdhui la srialisation et dsrialisation dobjets graphiques. Dans certains cas, nous pouvons avoir besoin de transmettre un flux XML. En passant un objet de type XML, celui-ci est alors aplati sous la forme dune chane de caractres UTF-8 :
// cration d'un objet XML var donneesXML:XML = <SCORE><JOUEUR id='25' score='15888'/></SCORE>; // appel de la mthode echangeTypes distante, nous transmettons un objet XML connexion.call ("org.bytearray.test.Echanges.echangeTypes", retourServeur, donneesXML);

Le flux XML revient sous la forme dune chane de caractres :


function succes ( pRetour:* ):void { // l'objet XML a t converti sous forme de chane de caractres trace( pRetour is String ); }

AMFPHP prfre conserver une chane et nous laisser grer la manipulation de larbre XML.

A retenir
Grce Flash Remoting les donnes automatiquement srialises et dsrialises. composites sont LorsquAMFPHP trouve un type correspondant, on dit que les donnes sont supportes. Dans le cas contraire, AMFPHP interprte les donnes en un type quivalent. Les objets graphiques ne peuvent ne sont pas compatibles avec le format AMF.

Echanger des donnes types


Au cas o nous souhaiterions changer des types personnaliss avec la passerelle Remoting, AMFPHP intgre un mcanisme appropri. Une instance de classe personnalise de type Utilisateur peut par exemple tre dfinie ct Flash et transmise au service distant, en conservant ct PHP le type Utilisateur. De la mme manire, une mthode distante peut renvoyer Flash des instances de classes en assurant une conservation des types personnaliss.

32 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Pour plus dinformations, rendez vous ladresse suivante : http://amfphp.org/docs/classmapping.html Passons maintenant de la thorie la pratique.

Envoyer un email avec Flash Remoting


Au cours du chapitre 14 intitul Chargement et envoi de donnes nous avions dvelopp un formulaire permettant denvoyer un email depuis Flash. Nous allons reprendre lapplication et remplacer les changes raliss laide la classe URLLoader par un change par Flash Remoting. La figure 19-10 illustre le formulaire :

Figure 19-10. Formulaire denvoi demail. La classe de document suivante est associe :
package org.bytearray.document { import flash.text.TextField; import flash.display.SimpleButton; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut

33 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ public public public public var var var var destinataire:TextField; sujet:TextField; message:TextField; boutonEnvoi:SimpleButton;

public function Document () { } } }

Afin de pouvoir envoyer notre email, nous devons tout dabord prvoir une mthode denvoi. Pour cela nous ajoutons une mthode envoiMessage notre service distant :
<?php class Echanges { function echangeTypes ( $pDonnees ) { return $pDonnees; } function envoiMessage ( $pInfos ) { $destinaire = $pInfos["destinataire"]; $sujet = $pInfos["sujet"]; $message = $pInfos["message"]; return @mail ( $destinataire, $sujet, $message ); } } ?>

La mthode envoiMessage reoit un tableau associatif contenant les informations ncessaires et procde lenvoi du message. La valeur renvoye par la fonction PHP mail indiquant le succs ou lchec de lenvoi est retourne Flash. Afin dappeler la mthode envoiMessage nous ajoutons les lignes suivantes la classe de document dfinie prcdemment :
package org.bytearray.document

34 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ import import import import import import import import import flash.events.Event; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.net.NetConnection; flash.text.TextField; flash.display.SimpleButton; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { public var destinataire:TextField; public var sujet:TextField; public var message:TextField; public var boutonEnvoi:SimpleButton; private var connexion:NetConnection; public function Document () { //cration de la connexion connexion = new NetConnection(); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); } private function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } } }

Puis nous ajoutons une proprit constante PASSERELLE contenant ladresse de la passerelle et nous nous connectons celle-ci :
package org.bytearray.document { import flash.events.Event;

35 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

import import import import import import import import

flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.text.TextField; flash.display.SimpleButton; flash.net.NetConnection; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { public var destinataire:TextField; public var sujet:TextField; public var message:TextField; public var boutonEnvoi:SimpleButton; private var connexion:NetConnection; // adresse de la passerelle AMFPHP private static const PASSERELLE:String = "http://localhost/echanges/gateway.php"; public function Document () { //cration de la connexion connexion = new NetConnection(); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); // connexion la passerelle connexion.connect ( Document.PASSERELLE ); } private function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } } }

Nous ajoutons une instance de Responder afin de grer les retours serveurs :
package org.bytearray.document

36 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ import import import import import import import import import import flash.events.Event; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.net.Responder; flash.text.TextField; flash.display.SimpleButton; flash.net.NetConnection; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { public var destinataire:TextField; public var sujet:TextField; public var message:TextField; public var boutonEnvoi:SimpleButton; private var connexion:NetConnection; private var retourServeur:Responder; // adresse de la passerelle AMFPHP private static const PASSERELLE:String = "http://localhost/echanges/gateway.php"; public function Document () { //cration de la connexion connexion = new NetConnection(); // cration de l'objet de gestion des retours serveur retourServeur = new Responder ( succes, echec ); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, ecouteurCentralise ); connexion.addEventListener( IOErrorEvent.IO_ERROR, ecouteurCentralise ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, ecouteurCentralise ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, ecouteurCentralise ); // connexion la passerelle connexion.connect ( Document.PASSERELLE ); } private function succes ( pRetour:* ):void { trace ( pRetour ); } private function echec ( pErreur:* ):void

37 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ trace ( pErreur ); } private function ecouteurCentralise ( pEvt:Event ):void { trace( pEvt ); } } }

Puis nous appelons la mthode distante envoiMessage lorsque le bouton boutonEnvoi est cliqu :
package org.bytearray.document { import import import import import import import import import import import flash.events.Event; flash.events.MouseEvent; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.net.Responder; flash.text.TextField; flash.display.SimpleButton; flash.net.NetConnection; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { public var destinataire:TextField; public var sujet:TextField; public var message:TextField; public var boutonEnvoi:SimpleButton; private var connexion:NetConnection; private var retourServeur:Responder; private var infos:Object; // adresse de la passerelle AMFPHP private static const PASSERELLE:String = "http://localhost/echanges/gateway.php"; public function Document () { //cration de la connexion connexion = new NetConnection(); // cration de l'objet de gestion des retours serveur

38 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

retourServeur = new Responder ( succes, echec ); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); // connexion la passerelle connexion.connect ( Document.PASSERELLE ); boutonEnvoi.addEventListener ( } private function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } private function succes ( pRetour:* ):void { trace ( pRetour ); } private function echec ( pErreur:* ):void { trace ( pErreur ); } private function envoiMail ( pEvt:MouseEvent ):void { infos = new Object(); infos.destinataire = destinataire.text; infos.sujet = sujet.text; infos.message = message.text; connexion.call ("org.bytearray.Echanges.envoiMessage", retourServeur, infos ); } } } MouseEvent.CLICK, envoiMail );

39 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

En testant le code prcdent, la mthode couteur succes est dclenche et reoit la valeur true du serveur indiquant que le mail a bien t envoy. Afin de finaliser cet exemple, nous pouvons utiliser la classe OutilsFormulaire dveloppe au cours du chapitre 14 et limporter :
import org.bytearray.outils.FormulaireOutils;

Puis nous modifions la mthode envoiMail afin de tester la validit de ladresse saisie :
private function envoiMail ( pEvt:MouseEvent ):void { if ( FormulaireOutils.verifieEmail ( destinataire.text ) ) { infos = new Object(); infos.destinataire = destinataire.text; infos.sujet = sujet.text; infos.message = message.text; infos ); connexion.call ("org.bytearray.Echanges.envoiMessage", retourServeur,

} else destinataire.text = "Email non valide !"; }

Afin de valider lenvoi du message au sein de lapplication nous ajoutons la condition suivante au sein de la mthode de retour succes :
private function succes ( pRetour:* ):void { if ( pRetour ) message.text = "Message bien envoy !"; else message.text = "Erreur d'envoi du message"; }

Ainsi, nous travaillons de manire transparente avec le script serveur sans avoir nous soucier de srialiser et dsrialiser les donnes envoyes et reues.

Exporter une image


Nous avons vu prcdemment quil tait possible de transmettre une instance de ByteArray par Flash Remoting. 40 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Comme nous le verrons au cours du chapitre 21 intitul ByteArray il est possible de gnrer en ActionScript 3 un flux binaire grce la classe ByteArray. Afin de pouvoir exploiter ce flux binaire, nous pouvons le transmettre au service distant afin que celui-ci le sauvegarde sur le serveur. Nous allons reprendre lapplication de dessin dveloppe au cours du chapitre 9 intitul Etendre les classes natives et ajouter une fonctionnalit dexport de notre dessin sous la forme dune image PNG. Nous dfinissons une classe de document associe :
package org.bytearray.document { import flash.display.SimpleButton; import flash.display.Sprite; import org.bytearray.abstrait.ApplicationDefaut; public class Document extends ApplicationDefaut { private var dessin:Sprite; private var stylo:Stylo; public var boutonEnvoi:SimpleButton; public function Document () { // cration du conteneur de tracs vectoriels dessin = new Sprite(); // ajout du conteneur la liste d'affichage addChild ( dessin ); // cration du symbole stylo = new Stylo( .1 ); // passage du conteneur de tracs stylo.affecteToile ( dessin ); // ajout du symbole la liste d'affichage addChild ( stylo ); // positionnement en x et y stylo.x = 250; stylo.y = 200; } } }

41 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Nous ajoutons un bouton boutonEnvoi, permettant dexporter le dessin sous forme bitmap. La figure 19-11 illustre lapplication :

Figure 19-11. Application de dessin. Lors du clic bouton nous devons gnrer une image bitmap du dessin vectoriel, pour cela nous allons utiliser une classe dencodage dimage EncodeurPNG. Nous coutons lvnement MouseEvent.CLICK du bouton dexport :
package org.bytearray.document { import import import import flash.display.SimpleButton; flash.display.Sprite; flash.events.MouseEvent; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { private var dessin:Sprite; private var stylo:Stylo; private var renduBitmap:BitmapData; public function Document ()

42 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ // cration du conteneur de tracs vectoriels dessin = new Sprite(); // ajout du conteneur la liste d'affichage addChild ( dessin ); // cration du symbole stylo = new Stylo( .1 ); // passage du conteneur de tracs stylo.affecteToile ( dessin ); // ajout du symbole la liste d'affichage addChild ( stylo ); // positionnement en x et y stylo.x = 250; stylo.y = 200; boutonEnvoi.addEventListener ( MouseEvent.CLICK, exportBitmap ); } private function exportBitmap ( pEvt:MouseEvent ):void { trace("export bitmap"); } } }

Afin dencoder notre dessin vectoriel en image PNG nous devons tout dabord rendre sous forme bitmap les tracs vectoriels. Souvenez-vous, nous avons dcouvert lors du chapitre 12 intitul Programmation bitmap quil tait possible de rasteriser un lment vectoriel grce la mthode draw de la classe BitmapData. Nous modifions la classe de document afin dintgrer une rasterisation du dessin vectoriel lorsque le bouton dexport est cliqu :
package org.bytearray.document { import import import import import import import flash.display.BitmapData; flash.display.SimpleButton; flash.display.Sprite; flash.events.MouseEvent; flash.utils.ByteArray; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.encodage.images.EncodeurPNG;

public class Document extends ApplicationDefaut

43 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ private var dessin:Sprite; private var stylo:Stylo; private var renduBitmap:BitmapData; public var boutonEnvoi:SimpleButton; public function Document () { // cration du conteneur de tracs vectoriels dessin = new Sprite(); // ajout du conteneur la liste d'affichage addChild ( dessin ); // cration du symbole stylo = new Stylo( .1 ); // passage du conteneur de tracs stylo.affecteToile ( dessin ); // ajout du symbole la liste d'affichage //addChild ( stylo ); // positionnement en x et y stylo.x = 250; stylo.y = 200; boutonEnvoi.addEventListener ( MouseEvent.CLICK, exportBitmap ); } private function exportBitmap ( pEvt:MouseEvent ):void { // cration d'une image bitmap vierge renduBitmap = new BitmapData ( stage.stageWidth, stage.stageHeight ); // rasterisation des tracs renduBitmap.draw ( dessin ); // encodage de l'image bitmap au format PNG var fluxBinaire:ByteArray = EncodeurPNG.encode ( renduBitmap ); // affiche : 1488 trace( fluxBinaire.length ); } } }

La variable fluxBinaire reprsente un objet ByteArray contenant limage encode au format PNG, nous devons prsent transmettre le flux de limage. Nous ajoutons une nouvelle mthode sauveImage notre service distant : 44 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

<?php class Echanges { function Echanges ( ) { } function echangeTypes ( $pDonnees ) { return $pDonnees; } function sauveImage ( $pFluxImage ) { //le ByteArray est plac au sein de la proprit data de l'objet reu $flux = $pFluxImage->data; // nous dcompressons le flux compress cot Flash $flux = gzuncompress($flux); $nomImage = "capture.png"; // sauvegarde de l'image $fp = @fopen("./../../../../images/".$nomImage, 'wb'); $ecriture = @fwrite($fp, $flux); @fclose($fp); return $ecriture !== FALSE; } } ?>

La mthode sauveImage reoit le flux binaire en paramtre sous ma forme dune instance de la classe ByteArray intgre AMFPHP. Le flux est accessible par la proprit data de linstance de ByteArray, nous sauvegardons le flux laide des fonctions dcriture PHP fopen et fwrite. Attention, veillez crer un rpertoire nomm images, afin daccueillir les futures images sauves. Dans notre exemple, ce dernier est plac au mme niveau que le rpertoire services. Nous modifions la mthode exportBitmap afin de transmettre limage la mthode sauveImage :
package org.bytearray.document

45 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ import import import import import import import import import import import import import import flash.display.BitmapData; flash.display.SimpleButton; flash.display.Sprite; flash.events.Event; flash.events.MouseEvent; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.utils.ByteArray; flash.net.Responder; flash.net.NetConnection; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.encodage.images.EncodeurPNG;

public class Document extends ApplicationDefaut { private var dessin:Sprite; private var stylo:Stylo; private var renduBitmap:BitmapData; public var boutonEnvoi:SimpleButton; private var connexion:NetConnection; private var retourServeur:Responder; // adresse de la passerelle AMFPHP private static const PASSERELLE:String = "http://localhost/echanges/gateway.php"; public function Document () { //cration de la connexion connexion = new NetConnection(); // cration de l'objet de gestion des retours serveur retourServeur = new Responder ( succes, echec ); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); // connexion la passerelle connexion.connect ( Document.PASSERELLE ); // cration du conteneur de tracs vectoriels dessin = new Sprite(); // ajout du conteneur la liste d'affichage addChild ( dessin );

46 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

// cration du symbole stylo = new Stylo( .1 ); // passage du conteneur de tracs stylo.affecteToile ( dessin ); // ajout du symbole la liste d'affichage addChild ( stylo ); // positionnement en x et y stylo.x = 250; stylo.y = 200; boutonEnvoi.addEventListener ( MouseEvent.CLICK, exportBitmap ); } private function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } private function succes ( pRetour:* ):void { if ( pRetour ) trace("image sauvegarde !"); else trace("erreur d'enregistrement"); } private function echec ( pErreur:* ):void { trace( pErreur ); } private function exportBitmap ( pEvt:MouseEvent ):void { // cration d'une image bitmap vierge renduBitmap = new BitmapData ( stage.stageWidth, stage.stageHeight ); // rasterisation des tras renduBitmap.draw ( dessin ); // encodage de l'image bitmap au format PNG var fluxBinaire:ByteArray = EncodeurPNG.encode ( renduBitmap ); // compression zlib du flux fluxBinaire.compress(); // transmission du ByteArray par Flash Remoting connexion.call ("org.bytearray.test.Echanges.sauveImage", retourServeur, fluxBinaire );

47 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

} } }

Nous dessinons quelques tracs, puis nous cliquons sur le bouton dexport comme lillustre la figure 19-12 :

Figure 19-12. Dessin. Voici les diffrentes tapes lorsque nous cliquons sur le bouton dexport :
Les tracs vectoriels sont rasteriss sous la forme dun objet BitmapData.

Une image PNG est gnre laide des pixels accessibles depuis lobjet BitmapData.

Le flux de limage est envoy au service distant qui se charge de la sauver sur le serveur.

Lorsque la mthode succes est dclenche, limage est sauve sur le serveur. En accdant au rpertoire images nous dcouvrons limage sauvegarde comme lillustre la figure 19-13 :

48 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Figure 19-13. Image sauvegarde. Limage pourrait aussi tre sauve directement en base de donnes au sein dun champ de type BLOB. Lobjet ByteArray pourrait ainsi tre rcupr plus tard et affich grce la mthode loadBytes de lobjet Loader. Il serait aussi envisageable de transmettre uniquement les pixels de limage laide de la mthode getPixels de la classe BitmapData. Puis de gnrer nimporte quel type dimage ct serveur laide dune librairie spcifique telle GD ou autres.

Se connecter une base de donnes


Flash Remoting prend tout son sens lors de la manipulation dune base de donnes. Nous allons intgrer Flash Remoting au menu dvelopp au cours du chapitre 7 intitul Interactivit. Pour cela nous devons dfinir une base de donnes, nous utiliserons dans cet exemple une base de donnes MySQL.

Figure 19-14. Base de donne MySQL. Nous crons une base de donnes intitule maBase puis nous crons une table associe menu. Afin de crer celle-ci nous pouvons excuter la requte MySQL suivante :
--- Structure de la table `menu` -CREATE TABLE `menu` ( `id` int(11) NOT NULL auto_increment, `intitule` varchar(30) NOT NULL default '', `couleur` int(11) NOT NULL default '0',

49 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ; --- Contenu de la table `menu` -INSERT INTO `menu` (`id`, `intitule`, `couleur`) VALUES (1, 'Accueil', 16737536), (2, 'Nouveauts', 16737536), (3, 'Photos', 16737536), (4, 'Liens', 16737536), (5, 'Contact', 16737536);

Quelques donnes sont prsentes dans la table menu :

Figure 19-15. Donnes de la table menu. Avant de rcuprer les donnes de la table, notre service distant doit au pralable se connecter la base MySQL. Nous ajoutons la connexion la base au sein du constructeur du service distant :
<?php class Echanges { function Echanges ( ) { // connexion au serveur MySQL mysql_connect ("localhost", "thibault", "20061982"); // slection de la base mysql_select_db ("maBase"); } } ?>

Puis nous ajoutons une mthode recupereMenu :


<?php class Echanges

50 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ function Echanges ( ) { // connexion au serveur MySQL mysql_connect ("localhost", "bobgroove", "20061982"); // slection de la base mysql_select_db ("maBase"); } function recupereMenu ( ) { return mysql_query ("SELECT * FROM menu"); } } ?>

En nous rendant auprs de lexplorateur de services nous pouvons tester directement la mthode recupereMenu. A laide de longlet RecordSet view voyons directement au sein dune liste le rsultat de notre requte. La figure 19-16 illustre le rsultat :

51 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Figure 19-16. Aperu en direct du rsultat de la requte. Chaque champ de colonne correspond au champ de la table menu, grce ce mcanisme les donnes sont prsentes en une seule ligne de code PHP. Dans un nouveau document Flash nous associons la classe de document suivante :
package org.bytearray.document { import import import import import import import import flash.events.Event; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.net.Responder; flash.net.NetConnection; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { private var connexion:NetConnection;

52 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

private var retourServeur:Responder; // adresse de la passerelle AMFPHP private static const PASSERELLE:String = "http://localhost/echanges/gateway.php"; public function Document () { //cration de la connexion connexion = new NetConnection(); // cration de l'objet de gestion des retours serveur retourServeur = new Responder ( succes, echec ); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); // connexion la passerelle connexion.connect ( Document.PASSERELLE ); // appel de la mthode distante recupereMenu connexion.call ("org.bytearray.test.Echanges.recupereMenu", retourServeur ); } private function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } private function succes ( pRetour:* ):void { // affiche : [object Object] trace( pRetour ); } private function echec ( pErreur:* ):void { trace( pErreur.description ); } }

53 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Ds linitialisation de lapplication nous appelons la mthode distante recupereMenu. AMFPHP retourne les donnes issues de la requte MySQL sous la forme dun objet couramment appel jeu denregistrements (RecordSet). Un objet RecordSet possde une proprit serverInfo contenant les proprits suivantes :
totalCount : le nombre denregistrements renvoys. columnNames : un tableau contenant le nom des champs de la table.

initialData : un tableau de tableaux, contenant les donnes de la table. Id : identifiant de la session en cours cre par AMFPHP. Version : la version de lobjet RecordSet. serviceName: nom du service distant. cursor : point de dpart de la lecture des donnes.

Si nous itrons au sein de lobjet serverInfo nous dcouvrons chacune des proprits et leur valeurs :
private function succes ( pRetour:* ):void { /* affiche : serviceName : PageAbleResult columnNames : id,intitule,couleur id : 952b03b6b26e39ff52418985866801ab initialData : 1,Accueil,16737536,2,Nouveauts,16737536,3,Photos,16737536,4,Liens,16737536,5,C ontact,16737536 totalCount : 5 version : 1 cursor : 1 */ for ( var p in pRetour.serverInfo ) { trace( p, " : " + pRetour.serverInfo[p] ); } }

En utilisant AMFPHP laide du framework Flex ou AIR, les ressources MySQL sont converties sous la forme dobjet ArrayCollection permettant un accs facilit aux donnes.

54 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Au sein de Flash CS3, aucune classe nest prvue pour grer linterprtation des RecordSet, nous allons donc devoir dvelopper une petite fonction de reorganisation des donnes. Nous ajoutons une mthode conversion :
private function conversion ( pSource:Object ):Array { var donnees:Array = new Array(); var element:Object; for ( var p:String in pSource.initialData ) { element = new Object(); for ( var q:String in pSource.columnNames ) { element[pSource.columnNames[q]] = pSource.initialData[p][q]; } donnees.push ( element ); } return donnees; }

Lorsque les donnes sont charges nous construisons un tableau dobjets laide de la mthode conversion :
private function succes ( pRetour:* ):void { // le RecordSet est converti en tableau d'objets var donnees:Array = conversion ( pRetour.serverInfo ); }

Lidal tant disoler cette mthode afin de pouvoir la rutiliser tout moment, dans nimporte quel projet. Nous pourrions imaginer une mthode conversion au sein dune classe OutilsRemoting. Nous allons prsent intgrer le menu dvelopp au cours du chapitre 7. Assurez vous davoir bien import le bouton que nous avions cr associ la classe Bouton :
package org.bytearray.document {

55 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

import import import import import import import import import import import import

flash.display.Sprite; flash.events.Event; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.events.MouseEvent; flash.net.Responder; flash.net.NetConnection; fl.transitions.Tween; fl.transitions.easing.Elastic; org.bytearray.abstrait.ApplicationDefaut;

public class Document extends ApplicationDefaut { private var connexion:NetConnection; private var retourServeur:Responder; private var conteneurMenu:Sprite; // adresse de la passerelle AMFPHP private static const PASSERELLE:String = "http://localhost/echanges/gateway.php"; public function Document () { conteneurMenu = new Sprite(); conteneurMenu.x = 140; conteneurMenu.y = 120; addChild ( conteneurMenu ); conteneurMenu.addEventListener ( MouseEvent.ROLL_OVER, survolBouton, true ); conteneurMenu.addEventListener ( MouseEvent.ROLL_OUT, quitteBouton, true ); //cration de la connexion connexion = new NetConnection(); // cration de l'objet de gestion des retours serveur retourServeur = new Responder ( succes, echec ); // coute des diffrents vnements connexion.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); connexion.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); connexion.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); connexion.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); // connexion la passerelle connexion.connect ( Document.PASSERELLE ); // appel de la mthode distante recupereMenu

56 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

connexion.call ("org.bytearray.test.Echanges.recupereMenu", retourServeur ); } private function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } private function succes ( pRetour:* ):void { // le RecordSet est converti en tableau d'objets var donnees:Array = filtre ( pRetour.serverInfo ); var lng:int = donnees.length; var monBouton:Bouton; var angle:int = 360 / lng; for ( var i:int = 0; i< lng; i++ ) { // instanciation du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton monBouton.buttonMode = true; // dsactivation des objets enfants monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = donnees[i].intitule; // disposition des occurrences monBouton.tween = new Tween ( monBouton, "rotation", Elastic.easeOut, 0, angle * (i+1), 2, true ); // on cre un objet Tween pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); // ajout la liste d'affichage conteneurMenu.addChild ( monBouton ); } } private function echec ( pErreur:* ):void { trace( pErreur.description );

57 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

} function survolBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo (1.1, 2); } function quitteBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo (1, 2); } private function filtre ( pSource:Object ):Array { var donnees:Array = new Array(); var element:Object; for ( var p:String in pSource.initialData ) { element = new Object(); for ( var q:String in pSource.columnNames ) { element[pSource.columnNames[q]] = pSource.initialData[p][q]; } donnees.push ( element ); } return donnees; } } }

En testant le code prcdent, nous obtenons un menu dynamique connect notre base de donnes. La figure 19-17 illustre le rsultat :

58 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Figure 19-17. Menu dynamique connect. Nous avons un champ de la base inutilis pour le moment au sein de notre menu. Nous allons remdier cela en liant la couleur de chaque bouton au champ couleur associ :
private function succes ( pRetour:* ):void { // le RecordSet est converti en tableau d'objets var donnees:Array = filtre ( pRetour.serverInfo ); var lng:int = donnees.length; var monBouton:Bouton; var transformationCouleur:ColorTransform; var angle:int = 360 / lng; for ( var i:int = 0; i< lng; i++ ) { // instanciation du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton monBouton.buttonMode = true; // dsactivation des objets enfants monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = donnees[i].intitule;

59 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

// recupration de l'objet de transformation de couleur transformationCouleur = monBouton.fondBouton.transform.colorTransform; // affectation d'une couleur dynamique transformationCouleur.color = donnees[i].couleur; // mise jour de la couleur monBouton.fondBouton.transform.colorTransform = transformationCouleur; // disposition des occurrences monBouton.tween = new Tween ( monBouton, "rotation", Elastic.easeOut, 0, angle * (i+1), 2, true ); // on cre un objet Tween pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); // ajout la liste d'affichage conteneurMenu.addChild ( monBouton ); } }

En modifiant les champs couleur de chaque rubrique nous obtenons le rsultat illustr par la figure 19-18 :

Figure 19-18. Menu connect avec couleurs dynamiques.

60 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Grce Flash Remoting, lchange de donnes est rellement simplifi, nous pourrions ajouter de nombreuses fonctionnalits notre menu. A vous de dvelopper le gestionnaire dadministration de ce menu laide de mthodes de mise jour de la base !

A retenir
Flash Remoting permet de retourner directement des ressources MySQL Flash. La ressource MySQL est convertie en un objet appel RecordSet. Dans Flash CS3, cet objet nest pas exploitable directement, nous devons rorganiser les donnes manuellement. Pour cela, un script peut tre developp et rutilis pour dautres projets Flash Remoting.

Scurit
Une fois le dveloppement de notre service distant termin et notre application dploye, nous devons imprativement supprimer lexplorateur de services. Cela vite de laisser disposition laccs aux mthodes distantes auprs dune personne mal intentionne. Pour supprimer lexplorateur de services, nous supprimons le rpertoire browser. Garder lesprit que les paquets AMF changs peuvent tre dcods trs facilement. Comme nous lavons vu prcdemment, chaque paquet AMF contient de nombreuses informations. Parmi celles-ci nous retrouvons le nom de la mthode distante excuter, ladresse de la passerelle ainsi que les donnes transmettre. De nombreux logiciels de dboguage tels ServiceCapture ou Charles permettent de lire ces informations. Ces derniers sont disponibles ladresse suivante :
Service Capture : http://kevinlangdon.com/serviceCapture/ Charles : http://www.xk72.com/charles/

Une personne mal intentionne pourrait tre tente de recuprer ladresse de notre passerelle AMFPHP et de sy connecter en appelant les mthodes distantes affiches par un logiciel de capture de paquets AMF tel Service Capture.

61 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Afin de supprimer les ventuels messages mis par la mthode trace de la classe NetDebug, nous pouvons activer le mode production dAMFPHP. Pour activer cette fonctionnalit il convient de passer la constante PRODUCTION_SERVER true au sein du fichier gateway.php :
define("PRODUCTION_SERVER", true);

Si lapplication Flash tentant dappeler les mthodes de notre service est hberge sur un autre serveur, lappel de la mthode call de lobjet NetConnection entrane une erreur de scurit. Attention, si nous avons plac sur notre serveur un fichier de rgulation autorisant tous les domaines, lappel russira et rendra notre service vulnrable. Le risque peut donc venir dune personne se connectant notre service distant depuis lenvironnement auteur de Flash, car aucune restriction de scurit nest applique dans ce cas. Les versions 1.9 et ultrieures dAMFPHP intgrent une nouvelle fonctionnalit permettant dinterdire toute connexion au service distant depuis le lecteur Flash autonome. Lactivation de la constante PRODUCTION_SERVER active automatiquement ce contrle et amliore la securit de notre service distant. Pour une scurit optimale nous pouvons utiliser la mthode addHeader de la classe NetConnection afin dauthentifier lutilisateur en cours au sein dAMFPHP. Pour plus dinformations ce sujet, rendez-vous ladresse suivante : http://www.amfphp.org/docs/authenticate.html

A retenir
Une fois lapplication Flash Remoting en production il faut imprativement supprimer lexplorateur de services. Il est fortement recommand dactiver le mode production dAMFPHP grce la constante PRODUCTION_SERVER du fichier gateway.php.

La classe Service
Bien que Flash Remoting savre extrmement pratique, lutilisation de la classe NetConnection savre peu souple. La mthode distante appeler doit tre passe sous forme de chane de caractres la mthode call ce qui savre peu pratique. 62 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Nous allons dvelopper dans la partie suivante, une classe Service permettant de reprsenter le service distant, comme si ce dernier tait un objet ActionScript. En dautres termes, la classe Service nous permettra de reprsenter sous forme dobjet ActionScript le service distant. Pour appeler la mthode distante recupereMenu, nous crirons :
// cration du service cot Flash var monService:Service = new Service(); // appel de la mthode distante monService.recupereMenu();

Tous les mcanismes internes lis lobjet NetConnection seront totalement transparents, rendant le dveloppement dapplications Flash Remoting encore plus simple. Au sein dun paquetage org.bytearray.remoting nous dfinissons la classe Service suivante :
package org.bytearray.remoting { import import import import import flash.events.EventDispatcher; flash.events.IEventDispatcher; flash.events.Event; flash.utils.Proxy; flash.utils.flash_proxy;

public dynamic class Service extends Proxy implements IEventDispatcher { private var diffuseur:EventDispatcher; public function Service () { diffuseur = new EventDispatcher(); } override flash_proxy function hasProperty(name:*):Boolean { return false; } override flash_proxy function getProperty(name:*):* { return undefined; } public function toString ( ):String

63 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

{ return "[object Service]"; } public function addEventListener( type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false ):void { diffuseur.addEventListener( type, listener, useCapture, priority, useWeakReference ); } public function dispatchEvent( event:Event ):Boolean { return diffuseur.dispatchEvent( event ); } public function hasEventListener( type:String ):Boolean { return diffuseur.hasEventListener( type ); } public function removeEventListener( type:String, listener:Function, useCapture:Boolean=false ):void { diffuseur.removeEventListener( type, listener, useCapture ); } public function willTrigger( type:String ):Boolean { return diffuseur.willTrigger( type ); } } }

Dans un premier temps nous remarquons que la classe Service tend la classe flash.utils.Proxy et permet donc dintercepter lappel de mthodes inexistantes. Le code suivant illustre le concept :
import org.bytearray.remoting.Service; var monService:Service = new Service(); monService.methodeInexistante();

En testant le code prcdent, lerreur suivante est leve lexcution :


Error: Error #2090: La classe Proxy ne met pas en oeuvre callProperty. Elle doit tre remplace par une sous-classe.

Afin de pouvoir intercepter les mthodes ou appels de proprits nous devons dfinir une mthode callProperty surchargeante :
override flash_proxy function callProperty ( nomMethode:*, ...parametres:* ):* {

64 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

trace ( nomMethode ); return null; }

Lorsquune mthode est appele sur linstance de Service, la mthode callProperty est excute et renvoie en paramtre le nom de la proprit rfrence. Rappelez-vous quune mthode est considre dans le modle objet dActionScript telle une fonction rfrence par une proprit. La fonction sex cutant dans le contexte de linstance. Cest la raison pour laquelle la mthode interceptant les appels est appele callProperty. En appelant nouveau une mthode inexistante, nous voyons que la mthode interne callProperty est excute et affiche le nom de la mthode appele :
import org.bytearray.remoting.Service; var monService:Service = new Service(); // affiche : methodeInexistante monService.methodeInexistante();

Nous allons profiter de ce comportement pour capturer le nom de la mthode et dlguer son appel auprs de lobjet NetConnection. Ainsi, pour appeler la mthode distante envoiMessage nous ncrirons plus le code peu digeste suivant :
connexion.call ("org.bytearray.Echanges.envoiMessage", retourServeur, infos );

Nous prfrerons lcriture simplifie suivante :


monService.envoiMessage( infos );

Pour mettre en place ce mcanisme, un objet NetConnection est envelopp au sein de lobjet Service afin de lui dlguer les mthodes appeles :
package org.bytearray.remoting { import import import import import import import import flash.events.EventDispatcher; flash.events.IEventDispatcher; flash.events.Event; flash.events.NetStatusEvent; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.net.NetConnection;

65 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

import flash.utils.Proxy; import flash.utils.flash_proxy; public dynamic class Service extends Proxy implements IEventDispatcher { private private private private var var var var diffuseur:EventDispatcher; connection:NetConnection; cheminService:String; passerelle:String;

public function Service ( pService:String, pPasserelle:String, pEncodage:int=0 ) { diffuseur = new EventDispatcher(); connection = new NetConnection(); connection.objectEncoding = pEncodage; connection.client = this; cheminService = pService; passerelle = pPasserelle; connection.addEventListener( ecouteurCentralise ); connection.addEventListener( ecouteurCentralise ); connection.addEventListener( ecouteurCentralise ); connection.addEventListener( ecouteurCentralise ); NetStatusEvent.NET_STATUS, IOErrorEvent.IO_ERROR, SecurityErrorEvent.SECURITY_ERROR, AsyncErrorEvent.ASYNC_ERROR,

connection.connect( passerelle ); } private function ecouteurCentralise ( pEvt:Event ):void { diffuseur.dispatchEvent ( pEvt ); } override flash_proxy function callProperty ( nomMethode:*, ...parametres:* ):* { trace ( nomMethode ); return null; } override flash_proxy function hasProperty(name:*):Boolean { return false; }

66 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

override flash_proxy function getProperty(name:*):* { return undefined; } public function toString ( ):String { return "[object Service]"; } public function addEventListener( type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false ):void { diffuseur.addEventListener( type, listener, useCapture, priority, useWeakReference ); } public function dispatchEvent( event:Event ):Boolean { return diffuseur.dispatchEvent( event ); } public function hasEventListener( type:String ):Boolean { return diffuseur.hasEventListener( type ); } public function removeEventListener( type:String, listener:Function, useCapture:Boolean=false ):void { diffuseur.removeEventListener( type, listener, useCapture ); } public function willTrigger( type:String ):Boolean { return diffuseur.willTrigger( type ); } } }

Nous instancions la classe Service en passant les paramtres ncessaires :


import org.bytearray.remoting.Service; // cration de la connexion var monService:Service = new Service( "org.bytearray.test.Echanges", "http://localhost/echanges/gateway.php", ObjectEncoding.AMF3 );

Linstance de la classe Service doit prsent appeler la mthode distante grce lobjet NetConnection interne. La classe AppelProcedure se charge dappeler la mthode distante : 67 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

package org.bytearray.remoting { import flash.events.EventDispatcher; import flash.net.NetConnection; import flash.net.Responder; public final class ProcedureAppel extends EventDispatcher { private private private private private var var var var var recepteur:Responder; connexion:NetConnection; requete:String; parametresFinal:Array; parametres:Array;

public function ProcedureAppel( pConnection:NetConnection, pRequete:String, pParametres:Array ) { recepteur = new Responder ( succes, echec ); connexion = pConnection; requete = pRequete; parametres = pParametres; } private function succes ( pEvt:Object ):void { } private function echec ( pEvt:Object ):void { } internal function appel ():void { parametresFinal = new Array( requete, recepteur ); connexion.call.apply ( connexion, parametresFinal.concat(parametres) ); } } }

Afin que la procdure renseigne notre service le rsultat du serveur, nous dfinissons deux classes vnementielles permettant de faire transiter les rsultats. La classe EvenementResultat est dfinie au sein du paquetage org.bytearray.remoting.evenements :
package org.bytearray.remoting.evenements { import flash.events.Event;

68 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

public final class EvenementResultat extends Event { public var resultat:Object; public static const RESULTAT:String = "resultat"; public function EvenementResultat (pType:String, pDonnees:Object) { super (pType, false, false); resultat = pDonnees; } } }

Nous dfinissons une classe EvenementErreur au sein du mme paquetage :


package org.bytearray.remoting.evenements { import flash.events.Event; public final class EvenementErreur extends Event { public var erreur:Object; public static const ERREUR:String = "erreur"; public function EvenementErreur (pType:String, pFault:Object) { super (pType, false, false); erreur = pFault; } } }

La classe ProcedureAppel est modifie afin de diffuser deux vnements EvenementResultat.RESULTAT et EvenementErreur.ERREUR :
package org.bytearray.remoting { import flash.events.EventDispatcher; import flash.net.NetConnection; import flash.net.Responder; import org.bytearray.remoting.evenements.EvenementResultat; import org.bytearray.remoting.evenements.EvenementErreur; public final class ProcedureAppel extends EventDispatcher {

69 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

private private private private private

var var var var var

recepteur:Responder; connexion:NetConnection; requete:String; parametresFinal:Array; parametres:Array;

public function ProcedureAppel( pConnection:NetConnection, pRequete:String, pParametres:Array ) { recepteur = new Responder ( succes, echec ); connexion = pConnection; requete = pRequete; parametres = pParametres; } private function succes ( pEvt:Object ):void { dispatchEvent ( new EvenementResultat ( EvenementResultat.RESULTAT, pEvt ) ); } private function echec ( pEvt:Object ):void { pEvt ) ); } internal function appel ():void { parametresFinal = new Array( requete, recepteur ); connexion.call.apply ( connexion, parametresFinal.concat(parametres) ); } } } dispatchEvent ( new EvenementErreur ( EvenementErreur.ERREUR,

La classe Service instancie donc un objet ProcedureAppel chaque appel de mthode et appel la mthode appel :
override flash_proxy function callProperty ( nomMethode:*, ...parametres:* ):* { appelEnCours = cheminService + "." + nomMethode; procedureAppel = new ProcedureAppel ( connection, appelEnCours, parametres ); procedureAppel.appel(); return procedureAppel;

70 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

Lorsque nous appelons une mthode de lobjet Service, celui-ci dlgue lappel lobjet NetConnection interne, la mthode est appele grce linstance de ProcedureAppel. Nous retournons celle-ci afin de pouvoir couteur lvnement EvenementResultat.RESULTAT et EvenementErreur.ERREUR. Nous pouvons prsent intgrer la classe Service notre menu dynamique cr auparavant :
package org.bytearray.document { import import import import import import import import import import import import import import import import import import flash.display.Sprite; flash.events.Event; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.events.AsyncErrorEvent; flash.events.NetStatusEvent; flash.events.MouseEvent; flash.geom.ColorTransform; flash.net.Responder; flash.net.NetConnection; flash.net.ObjectEncoding; fl.transitions.Tween; fl.transitions.easing.Elastic; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.remoting.Service; org.bytearray.remoting.ProcedureAppel; org.bytearray.remoting.evenements.EvenementResultat; org.bytearray.remoting.evenements.EvenementErreur;

public class Document extends ApplicationDefaut { private var service:Service; private var conteneurMenu:Sprite; // adresse de la passerelle AMFPHP private static const PASSERELLE:String = "http://www.bytearray.org/tmp/echanges/gateway.php"; private static const SERVICE_DISTANT:String = "org.bytearray.test.Echanges"; public function Document () { conteneurMenu = new Sprite(); conteneurMenu.x = 140; conteneurMenu.y = 120; addChild ( conteneurMenu );

71 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

conteneurMenu.addEventListener ( MouseEvent.ROLL_OVER, survolBouton, true ); conteneurMenu.addEventListener ( MouseEvent.ROLL_OUT, quitteBouton, true ); //cration de la connexion service = new Service( Document.PASSERELLE, Document.SERVICE_DISTANT, ObjectEncoding.AMF3 ); // appel de la mthode distante var procedureAppel:ProcedureAppel = service.recupereMenu(); // coute du retour du serveur procedureAppel.addEventListener ( EvenementResultat.RESULTAT, procedureAppel.addEventListener ( EvenementErreur.ERREUR, echec

succes ); );

// coute des diffrents vnements service.addEventListener( NetStatusEvent.NET_STATUS, erreurConnexion ); service.addEventListener( IOErrorEvent.IO_ERROR, erreurConnexion ); service.addEventListener( SecurityErrorEvent.SECURITY_ERROR, erreurConnexion ); service.addEventListener( AsyncErrorEvent.ASYNC_ERROR, erreurConnexion ); } private function erreurConnexion ( pEvt:Event ):void { trace( pEvt ); } private function succes ( pRetour:EvenementResultat ):void { // le RecordSet est converti en tableau d'objets var donnees:Array = filtre ( pRetour.resultat.serverInfo ); var lng:int = donnees.length; var monBouton:Bouton; var transformationCouleur:ColorTransform; var angle:int = 360 / lng; for ( var i:int = 0; i< lng; i++ ) { // instanciation du symbole Bouton monBouton = new Bouton(); // activation du comportement bouton monBouton.buttonMode = true; // dsactivation des objets enfants

72 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

monBouton.mouseChildren = false; // affectation du contenu monBouton.maLegende.text = donnees[i].intitule; // recupration de l'objet de transformation de couleur transformationCouleur = monBouton.fondBouton.transform.colorTransform; // affectation d'une couleur dynamique transformationCouleur.color = donnees[i].couleur; // mise jour de la couleur monBouton.fondBouton.transform.colorTransform = transformationCouleur; // disposition des occurrences monBouton.tween = new Tween ( monBouton, "rotation", Elastic.easeOut, 0, angle * (i+1), 2, true ); // on cre un objet Tween pour les effets de survol monBouton.tweenSurvol = new Tween ( monBouton.fondBouton, "scaleX", Elastic.easeOut, 1, 1, 2, true ); // ajout la liste d'affichage conteneurMenu.addChild ( monBouton ); } } private function echec ( pErreur:EvenementErreur ):void { trace( pErreur.erreur.description ); } function survolBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo (1.1, 2); } function quitteBouton ( pEvt:MouseEvent ):void { var monTween:Tween = pEvt.target.tweenSurvol; monTween.continueTo (1, 2); } private function filtre ( pSource:Object ):Array {

73 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 19 Flash Remoting version 0.1

var donnees:Array = new Array(); var element:Object; for ( var p:String in pSource.initialData ) { element = new Object(); for ( var q:String in pSource.columnNames ) { element[pSource.columnNames[q]] = pSource.initialData[p][q]; } donnees.push ( element ); } return donnees; } } }

Dsormais les couteurs de lobjet ProcedureAppel reoivent un objet vnementiel de type EvenementResultat contenant les donnes au sein de la proprit resultat. De la mme manire si une erreur lors de lappel est dtecte, lobjet ProcedureAppel diffuse un vnement EvenementErreur contenant les informations lies lerreur au sein de sa proprit erreur. La classe Service peut ainsi tre rutilise dans nimporte quel projet ncessitant une connexion Flash Remoting optimise. Cet exemple illustre une des limitations de lhritage et met en avant la notion de composition.

A retenir
Grce la composition, nous dlguons les mthodes appeles auprs de linstance de la classe Service lobjet NetConnection interne. La classe Proxy permet dintercepter laccs une proprit.

74 / 74
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

20
ByteArray

LE CODAGE BINAIRE .......................................................................................... 1 POSITION ET POIDS DU BIT ....................................................................................... 4 OCTET ................................................................................................................... 10 ORDRE DES OCTETS ............................................................................................... 13 LA CLASSE BYTEARRAY ................................................................................. 15 METHODES DE LECTURE ET DECRITURE ............................................................... 16 COPIER DES OBJETS ............................................................................................... 28 ECRIRE DES DONNEES AU FORMAT TEXTE ............................................................. 30 LIRE DES DONNEES AU FORMAT TEXTE ................................................................. 31 COMPRESSER DES DONNEES ......................................................................... 34 SAUVEGARDER UN FLUX BINAIRE .............................................................. 38 GENERER UN PDF .............................................................................................. 40

Le codage binaire
Il faut revenir lorigine de linformatique afin de comprendre les raisons de lexistence de la notation binaire. La plus petite unit de mesure en informatique est reprsente par les chiffres 0 et 1 dfinissant un tat au sein dun circuit lectrique :
0 : le circuit est ferm. 1 : le circuit est ouvert.

Les processeurs quipant les ordinateurs ne comprennent donc que la notation binaire. Rassurez-vous, bien que tout cela puisse paratre compliqu dans un premier temps, il sagit simplement dune notation diffrente pour exprimer des donnes.

1 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Avant de sintresser au fonctionnement de la classe ByteArray, nous devons tout dabord tre laise avec le concept de base arithmtique. De par lhistoire, lhomme compte en base 10 car ce dernier possde 10 doigts, cest ce que nous appelons la base dcimale. Pour exprimer la notion de temps, nous utilisons une base sexagsimale compose de 60 symboles. Ainsi, lorsque 60 secondes se sont coules, nous ne passons pas 61 secondes, nous ajoutons une nouvelle unit, c'est-dire une minute et revenons 0 en nombre de secondes. Nous utilisons dans la vie de tous les jours les 10 symboles suivant pour reprsenter les nombres :
0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Au del de 9 nous devons donc combiner les symboles prcdents. Pour cela, nous ajoutons une nouvelle unit, puis nous repartons de 0. A chaque dplacement sur la gauche, nous ajoutons une puissance de 10. La figure 20-1 illustre comment dcomposer un nombre en diffrents groupes de puissances de 10 :

Figure 20-1. Groupes de puissance de 10. Notre systme dcimal fonctionne par puissance de 10, nous pouvons donc exprimer le nombre 750 de la manire suivante :
7 * 100 + 5 * 10 = 7 * 102 + 5 * 101

Contrairement la notation dcimale, la notation binaire autorise lutilisation des symboles 0 et 1 seulement. Le nombre 150 sexprime en binaire de la manire suivante :
10010110

Nous allons apprendre convertir la notation binaire en notation dcimale. Pour cela, nous appliquons le mme concept que pour la base dcimale en travaillant cette fois par puissance de 2. La figure 20-2 illustre comment dcomposer un nombre exprim sous forme binaire en diffrents groupes :

2 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Figure 20-2. Groupes de puissance de 2. Trs jeune, nous avons appris compter jusqu 10 en base dcimale. Si la norme avait t lutilisation de la base binaire, nous aurions mmoris la colonne de droite du tableau suivant : Base dcimale 0 1 2 3 4 5 6 7 8 9 10 Base binaire 0 1 10 11 100 101 110 111 1000 1001 1010 Tableau 1. Notation binaire. Afin de bien comprendre la notation binaire, il convient de sattarder sur le concept de poids du bit. Une fois cette notion intgre, nous introduirons la notion doctet.

A retenir

3 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

La notation binaire permet lorigine de reprsenter un tat au sein dun circuit. Un processeur ne comprend que la notation binaire. Parmi les bases les plus courantes, nous comptons les bases dcimales (10) et sexagsimales (60). La base 2 est appele base binaire.

Position et poids du bit


Prenons le cas du nombre 150, que nous pouvons reprsenter de la mme manire sous forme binaire :
10010110

Les symboles utiliss en notation binaire sont appels bit, le terme provenant de lAnglais binary digit. A linverse de la base dcimale, o chaque dplacement sur la gauche incrmente dune puissance de 10. En notation binaire, chaque dplacement du bit sur la gauche, augmente dune puissance de 2. Nous exprimons la position de chaque bit composant lexpression sous forme de poids. Le bit de poids le plus fort (most significant bit ou msb) est toujours positionn lextrme gauche, linverse le bit le plus faible (less significant bit ou lsb) est positionn lextrme droite. La figure 20-3 illustre lemplacement du bit le plus fort :

Figure 20-3. Bit le plus fort. La figure 20-4 illustre lemplacement du bit le plus faible :

Figure 20-4. Bit le plus faible. Plus le poids dun bit est fort, plus sa modification provoque un changement important du nombre. En continuant avec le nombre 150 nous voyons que si nous passons le bit le plus fort 0 nous soustrayons 27 (128) au nombre 150 : 4 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Figure 20-5. Modification du bit le plus fort. A linverse, si nous modifions un bit de poids plus faible, la rpercussion est moindre sur le nombre :

Figure 20-6. Modification dun bit de poids plus faible. Afin de convertir la notation binaire en notation dcimale, nous multiplions chaque bit par son poids et additionnons le rsultat :
1*27 + 0*26 + 0*25 + 1*24 + 0*23 + 1*22 + 1*21 + 0*20

Soit, sous une forme plus compacte :


27 + 24 + 22 + 21 = 150

Nous retrouvons la notion de bits au sein des couleurs. Nous avons vu lors du chapitre 12 intitul Programmation Bitmap que les couleurs taient gnralement codes en 8 bit, 16 bit ou 32 bit. Une blague dinformaticiens consiste dire quil existe 10 types de personnes dans le monde. Ceux qui comprennent le binaire et ceux qui ne le comprennent pas. Nous savons que 10 en binaire correspond 21 soit la valeur 2. Vous comprendrez donc sans problme la blague inscrite sur ce fameux t-shirt illustr par la figure 20-7 :

5 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Figure 20-7. Humour et notation binaire. Comme pour la base 2, dautres bases existent comme la base 16 appele plus couramment base hexadcimale. Celle-ci est gnralement utilise pour reprsenter les couleurs. Nous avons dj aborde cette notation au cours du chapitre 12 intitul Programmation bitmap. Contrairement la notation binaire compose de 2 symboles, la base 16 est compose de 16 symboles. Dans le code suivant nous valuons la valeur 120 en base 16 :
var entier:int = 120; // affiche : 78 trace ( entier.toString( 16 ) );

A linverse de la base 2, ou de la base 10, la base 16 utilise 16 symboles allant de 0 F afin dexprimer un nombre. Nous travaillons donc non plus en puissance de 2 ou 10 mais 16. La figure 20-8 illustre lide :

Figure 20-8. Groupes de puissance de 16. Le tableau suivant regroupe les symboles utiliss en base 16 :

6 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Base hexadcimale 0 1 2 3 4 5 6 7 8 9 A B C D E F

Base dcimale 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Tableau 2. Notation hexadcimale. La valeur 1A peut donc tre exprime de la manire suivante :
1*161 + 10*160 = 26

Lorsque nous travaillons avec un flux binaire, il peut tre ncessaire dexprimer un nombre sous diffrentes notations. Au lieu deffectuer la conversion manuellement, nous pouvons utiliser la mthode

7 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

toString de la classe Number. Celle-ci accepte en paramtre la base

dans laquelle convertir le nombre.

Dans le code suivant nous exprimons le nombre dcimal 5 en notation binaire :


var entier:int = 5; // affiche : 101 trace ( entier.toString( 2 ) );

En comptant de 1 10 en binaire nous retrouvons les valeurs du tableau 3 prcdent :


var var var var var var var var var var var zero:int = 0; un:int = 1; deux:int = 2; trois:int = 3; quatre:int = 4; cinq:int = 5; six:int = 6; sept:int = 7; huit:int = 8; neuf:int = 9; dix:int = 10;

// affiche : 0 trace( zero.toString( 2 ) ); // affiche : 1 trace( un.toString( 2 ) ); // affiche : 10 trace( deux.toString( 2 ) ); // affiche : 11 trace( trois.toString( 2 ) ); // affiche : 100 trace( quatre.toString( 2 ) ); // affiche : 101 trace( cinq.toString( 2 ) ); // affiche : 110 trace( six.toString( 2 ) ); // affiche : 111 trace( sept.toString( 2 ) ); // affiche : 1000 trace( huit.toString( 2 ) ); // affiche : 1001 trace( neuf.toString( 2 ) ); // affiche : 1010 trace( dix.toString( 2 ) );

8 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

De la mme manire, nous pouvons convertir un nombre en base dcimale en notation hexadcimale :
var var var var var var var var var var var var var var var var zero:int = 0; un:int = 1; deux:int = 2; trois:int = 3; quatre:int = 4; cinq:int = 5; six:int = 6; sept:int = 7; huit:int = 8; neuf:int = 9; dix:int = 10; onze:int = 11; douze:int = 12; treize:int = 13; quatorze:int = 14; quinze:int = 15;

// affiche : 0 trace( zero.toString( 16 ) ); // affiche : 1 trace( un.toString( 16 ) ); // affiche : 2 trace( deux.toString( 16 ) ); // affiche : 3 trace( trois.toString( 16 ) ); // affiche : 4 trace( quatre.toString( 16 ) ); // affiche : 5 trace( cinq.toString( 16 ) ); // affiche : 6 trace( six.toString( 16 ) ); // affiche : 7 trace( sept.toString( 16 ) ); // affiche : 8 trace( huit.toString( 16 ) ); // affiche : 9 trace( neuf.toString( 16 ) ); // affiche : a trace( dix.toString( 16 ) ); // affiche : b trace( onze.toString( 16 ) ); // affiche : c trace( douze.toString( 16 ) ); // affiche : d trace( treize.toString( 16 ) );

9 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// affiche : e trace( quatorze.toString( 16 ) ); // affiche : f trace( quinze.toString( 16 ) );

Pour des raisons pratiques, les ingnieurs dcidrent alors de grouper les bits par paquets, cest ainsi que naquit la notion doctet.

A retenir
On parle de poids du bit pour exprimer sa puissance. Le bit de poids le plus fort est toujours positionn lextrme gauche. Le bit de poids le plus faible est toujours positionn lextrme droite. La mthode toString de la classe Number permet dexprimer un nombre dans une base diffrente.

Octet
Loctet permet dexprimer une quantit de donnes. Nous lutilisons tous les jours dans le monde de linformatique pour indiquer par exemple le poids dun fichier. Bien entendu, nous ne dirons pas quun fichier MP3 pse 3145728 octets mais plutt 3 mga-octets. Nous ajoutons donc gnralement un prfixe comme kilo ou mga permettant dexprimer un volume doctets :
1 kilooctet (ko) = 10 octets (1 000 octets) 1 mgaoctet (Mo) = 106 octets = 1 000 ko (1 000 000 octets)

Attention ne pas confondre le terme de bit et byte : 1 octet = 8 bit Un octet est donc compos de 8 bit :
11111111

Ce dernier peut contenir un entier naturel compris entre 0 et 255 lorsque celui-ci est dit non-sign (non ngatif). Afin de convertir cette notation binaire sous forme dcimale, nous utilisons la technique aborde prcdemment. Nous multiplions chaque bit par son poids et additionnons le rsultat :
1*27 + 1*26 + 1*25 + 1*24 + 1*23 + 1*22 + 1*21 + 1*20

Ce qui nous donne le rsultat suivant :

10 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

Nous verrons quil est aussi possible dexprimer au sein dun octet une valeur oscillant entre -128 et 127 laide dun octet dit sign (ngatif). Il est important de noter que dans un contexte de manipulation de donnes binaire la base 16 est trs souvent utilise au sein de logiciels tels les diteurs hexadcimaux afin doptimiser la reprsentation dun octet. Voici une liste non exhaustive de quelques diteurs hexadcimaux gratuits ou payants :
Free Hex Editor Neo (gratuit) http://www.hhdsoftware.com/Family/hex-editor.html HxD (gratuit) http://mh-nexus.de/hxd/ Hex Workshop (payant) http://www.hexworkshop.com Hexprobe (payant) http://www.hexprobe.com/hexprobe

Il est plus facile de lire la valeur dun octet sous la notation hexadcimale suivante :
4E

Que sous une notation binaire :


1001110

Un octet non sign dune valeur de 255 peut donc tre exprim par deux symboles seulement en notation hexadcimale :
FF

En additionnant le poids de chaque symbole et en additionnant le rsultat nous obtenons la valeur 255 :
15*161 + 15*160 = 240 + 15 = 255

Afin de mettre cette notion en pratique, nous avons ouvert une image au format PNG au sein dun diteur hexadcimal. La figure 20-9 illustre une partie des donnes :

11 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Figure 20-9. Editeur hexadcimal. Nous pouvons remarquer que chaque colonne est compose de deux symboles reprsentant un octet. Comme son nom lindique, lditeur hexadcimal reprsente chaque octet en base 16 laide de deux symboles. Pourquoi un tel choix ? Pour des raisons pratiques, car le couple de symboles FF permet de reprsenter la valeur maximale dun octet, ce qui est optimis en termes despaces et moins pnible lire et mmoriser. Comme nous le verrons plus tard, chaque fichier peut tre identifi par son entte. En lisant la spcification du format PNG disponible ladresse suivante : http://www.w3.org/TR/PNG/ Nous voyons que tout fichier PNG doit obligatoirement commencer par une signature compose de cette srie de valeurs dcimales :
137 80 78 71 13 10 26 10

En convertissant en notation hexadcimale chacune de ces groupes, nous retrouvons les mmes valeurs dans notre fichier PNG :
var var var var var var var var premierOctet:int = 137; deuxiemeOctet:int = 80; troisiemeOctet:int = 78; quatriemeOctet:int = 71; cinquiemeOctet:int = 13; sixiemeOctet:int = 10; septiemeOctet:int = 26; huitiemeOctet:int = 10;

// affiche : 89 trace( premierOctet.toString ( 16 ) ); // affiche : 50 trace( deuxiemeOctet.toString ( 16 ) ); // affiche : 4e

12 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

trace( troisiemeOctet.toString ( 16 ) ); // affiche : 47 trace( quatriemeOctet.toString ( 16 ) ); // affiche : d trace( cinquiemeOctet.toString ( 16 ) ); // affiche : a trace( sixiemeOctet.toString ( 16 ) ); // affiche : 1a trace( septiemeOctet.toString ( 16 ) ); // affiche : a trace( huitiemeOctet.toString ( 16 ) );

La figure 20-10 illustre la correspondance entre chaque octet :

Figure 20-10 Signature dun fichier PNG. Nous allons nous intresser prsent lordre des octets.

A retenir
Attention ne pas confondre 1 octet (byte) et 1 bit. Un octet reprsente 8 bits. Un octet peut contenir une valeur maximale de 255 soit 27 + 26 + 25 + 2 4 + 2 3 + 2 2 + 2 1 + 2 0. La notation hexadcimale est couramment utilise pour reprsenter les octets.

Ordre des octets


Lorsque nous devons utiliser plusieurs octets afin dexprimer un nombre, il est important de prendre en considration lordre de stockage. Cette notion est appele en Anglais byte ordering ou endian nature.

13 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Souvenez-vous, dans la partie intitule Position et poids du bit nous avons vu que le bit le plus gauche tait considr comme celui de poids le plus fort, et inversement le bit le plus droite tait celui de poids le plus faible. Le mme concept sapplique au sein dun groupe doctets. Nous parlons alors doctet le plus fort (most significant byte ou MSB) et doctet le plus faible (less significant byte ou LSB). Attention, notez que nous utilisons lacronyme MSB et LSB en majuscule pour exprimer lordre des octets. A linverse nous utilisons des minuscules dans les acronymes msb et lsb pour exprimer le poids dun bit. Imaginons que nous devions stocker le nombre entier suivant :
550000000

Ce nombre est un entier non ngatif 32 bits et ncessite 4 octets afin dtre stock, sa valeur en hexadcimale est la suivante :
20 C8 55 80

Les processeurs tels les Motorola 68000 ou les processeurs SPARC quipant les plateformes Sun Microsystems utilise cet ordre de stockage et sont considrs comme gros-boutiste ou big-endian en Anglais. Nous utilisons ce terme car loctet de poids le plus fort (le plus gros) est gauche de lexpression : Gros-boutiste (big-endian) 0 20 1 C8 2 55 3 80

Tableau 3. Stockage des octets gros-boutiste. Dautres processeurs comme le fameux 5602 de Motorola ou x86 dIntel sont petit-boutiste ou little-endian et stockent les octets dans le sens inverse : Petit-boutiste (little-endian) 0 1 2 3

14 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

80

55

C8

20

Tableau 4. Stockage des octets petit-boutiste. Lors du dveloppement dapplications ActionScript 3 utilisant la classe ByteArray nous devrons prendre en considration cet ordre, surtout dans un contexte de communication externe. La notion dordre des octets na pas de relle consquence dans le cas de lecture dun seul octet la fois. A linverse, lorsque plusieurs octets sont interprts, nous devons absolument prendre lordre des octets en considration.

A retenir
Certaines architectures utilisent lordre petit-boutiste, dautres lordre gros-boutiste. Lordre des octets est important dans un contexte de communication entre plusieurs machines. Nous verrons que les classes ByteArray, Socket et URLStream possdent une proprit endian permettant de spcifier lordre dans lequel stocker les octets.

La classe ByteArray
Il est temps de mettre en application toutes les notions abordes prcdemment grce la classe flash.utils.ByteArray. Mme si la classe ByteArray figure parmi lune des classes les plus puissantes du lecteur Flash, une des questions les plus courantes conerne lintrt de pouvoir crire un flux binaire. Lintrt de la classe ByteArray rside dans laccs bas niveau au niveau des donnes. En dautres termes, nous allons pouvoir manipuler des types de donnes non existants et travailler sur des octets. Cela peut nous permettre par exemple dinterprter ou de gnrer nimporte quel type de fichiers en ActionScript 3. Comme son nom lindique, la classe ByteArray reprsente un tableau doctets. Pour exprimer 1 kilo-octet nous utiliserons donc 1000 index du tableau. La figure 20-11 illustre le fonctionnement dun tableau doctets :

15 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Figure 20-11. Tableau doctets. Comme son nom lindique, la classe ByteArray possde quelques similitudes avec la classe Array. Les deux objets demeurent des tableaux et possdent donc une longueur, mais chaque index composant un tableau doctet ne peut tre cod que sur 8 bits. Voici une liste non exhaustive de quelques projets utilisant la classe ByteArray :
FC64 : Emulateur Commodore 64. http://codeazur.com.br/stuff/fc64_final/ AlivePDF : Librairie de gnration de PDF. http://www.alivepdf.org FZip : Librairie de compression et dcompression darchives ZIP. http://codeazur.com.br/lab/fzip/ GIF Player : Librairie de lecture de GIF anims. http://www.bytearray.org/?p=95 GIF Player : Librairie dencodage de GIF anims. http://www.bytearray.org/?p=93 WiiFlash : Librairie de gestion de la manette Nintendo Wiimote. www.wiiflash.org Encodeurs dimages : Deux classes issues de la librairie corelib fournie par Adobe permettent lencodage PNG et JPEG. http://code.google.com/p/as3corelib/ Popforge Audio : Librairie de gnration de sons. http://www.popforge.de/

Attention, les mmes capacits que la classe ByteArray sont aussi prsentes dans la classe flash.net.Socket. Grce celle-ci nous pouvons dialoguer avec lextrieur au format brut binaire.

Mthodes de lecture et dcriture


Afin de crer un flux binaire nous utilisons la classe flash.utils.ByteArray. Bien que nous puissions crer un tableau traditionnel laide de lcriture littrale suivante :
var monTableau:Array = [ ];

Seul le mot cl new permet la cration dun tableau doctets :


// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray();

Pour rcuprer la longueur du tableau doctets nous utilisons sa proprit length :


// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray();

16 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// affiche : 0 trace ( fluxBinaire.length );

Lorsquun tableau doctets est cr, celui-ci est vide, de la mme manire quun tableau traditionnel. Notons quen ActionScript 3, la taille dun tableau doctets est dynamique et ne peut tre fixe comme cest le cas dans certains langages tels Java, C# ou autres. Comme nous lavons abord prcdemment, lordre des octets peut varier selon chaque plateforme. La proprit endian dfinie par la classe ByteArray permet de spcifier lordre des octets. Par dfaut, le tableau doctets est gros-boutiste :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); // affiche : bigEndian trace ( fluxBinaire.endian );

Afin de modifier lordre dcriture des octets nous utiliser les constantes de la classe flash.utils.Endian :
Endian.BIG_ENDIAN : octet le plus fort en premire position. Endian.LITTLE_ENDIAN : octet le plus faible en premire position.

Dans le code suivant, nous testons si lordre des octets est grosboutiste :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); // affiche : true trace( fluxBinaire.endian == Endian.BIG_ENDIAN );

Nous pouvons modifier lordre :


// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); // criture dans l'ordre petit-boutiste fluxBinaire.endian == Endian.LITTLE_ENDIAN;

Pour stocker lentier non sign 5 au sein dune instance de la classe Array nous pouvons crire le code suivant :
var donnees:Array = new Array(); var nombre:uint = 5; donnees[0] = nombre; // affiche : 1 trace( donnees.length );

17 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// affiche : 5 trace( donnees[0] );

Pour crire le mme nombre au sein dun tableau doctets, le nombre doit tre spar en groupe doctets. Ainsi, afin dcrire un nombre entier non sign 32 bits comme cest le cas pour le type uint nous devons sparer lentier en 4 groupes de 8 bits :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture manuelle en reprsentation fluxBinaire[0] = (nombre & 0xFF000000) fluxBinaire[1] = (nombre & 0x00FF0000) fluxBinaire[2] = (nombre & 0x0000FF00) fluxBinaire[3] = nombre & 0xFF; gros-boutiste >> 24; >> 16; >> 8;

Notez que nous venons de stocker lentier en reprsentation grosboutiste. Une fois lentier spar, nous pouvons le reconstituer laide des oprateurs de manipulation de bits :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture manuelle en reprsentation fluxBinaire[0] = (nombre & 0xFF000000) fluxBinaire[1] = (nombre & 0x00FF0000) fluxBinaire[2] = (nombre & 0x0000FF00) fluxBinaire[3] = nombre & 0xFF; gros-boutiste >> 24; >> 16; >> 8; fluxBinaire[1] << 16 |

var nombreStocke:uint = fluxBinaire[0] << 24 | fluxBinaire[2] << 8 | fluxBinaire[3]; // affiche : 5 trace( nombreStocke );

Si nous devions stocker les donnes au format petit-boutiste nous devrions inverser lordre dcriture :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture manuelle en reprsentation fluxBinaire[3] = (nombre & 0xFF000000) fluxBinaire[2] = (nombre & 0x00FF0000) fluxBinaire[1] = (nombre & 0x0000FF00) fluxBinaire[0] = nombre & 0xFF; petit-boutiste >> 24; >> 16; >> 8; fluxBinaire[2] << 16 |

var nombreStocke:uint = fluxBinaire[3] << 24 | fluxBinaire[1] << 8 | fluxBinaire[0];

18 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// affiche : 5 trace( nombreStocke );

Il savre extrmement fastidieux dcrire des donnes au sein dun tableau doctets laide du code prcdent. Rassurez-vous, afin de nous faciliter la tche le lecteur Flash va automatiquement grer le dcoupage des octets ainsi que la reconstitution laide de mthodes de lecture et dcriture. Ainsi, pour crire un entier non sign au sein du tableau, nous utilisons la mthode writeUnsignedInt dont voici la signature :
public function writeUnsignedInt(value:int):void

Voici le dtail du paramtre attendu :


value : un entier non sign de 32 bits.

Le code fastidieux prcdent peut donc tre rcrit de la manire suivante :


// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre);

Un entier non sign ncessite 32 bits, en testant la longueur du tableau binaire, nous voyons que 4 octets sont inscrits dans le tableau :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); // affiche : 4 trace( fluxBinaire.length );

Afin de lire lentier non sign crit, nous utilisons simplement la mthode readUnsignedInt :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); // lve une erreur l'excution var nombreStocke:uint = fluxBinaire.readUnsignedInt();

En testant le code prcdent, lerreur lexcution suivante est leve : 19 / 44


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Error: Error #2030: Fin de fichier dtecte.

Lors de lcriture du flux de donnes, un pointeur interne dfini par la proprit position de lobjet ByteArray se dplace automatiquement auprs de loctet suivant :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // affiche : 0 trace( fluxBinaire.position ); // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); // affiche : 4 trace( fluxBinaire.position );

La proprit position nous indique que le pointeur est lindex 4 du tableau doctets. Ainsi, la prochaine mthode dcriture commencera crire les donnes partir de cet index. La figure 20-12 illustre le principe :

Figure 20-12. Positionnement du pointeur interne. Afin de grer les ventuelles erreurs de lecture du flux, nous pouvons placer les appels aux diffrentes mthodes de lecture au sein dun bloc try catch :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); try { // lve une erreur lexcution trace( fluxBinaire.readUnsignedInt() ); } catch ( pErreur:Error ) { trace ("erreur de lecture"); }

20 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Pour pouvoir lire le flux, nous devons obligatoirement remettre zro le pointeur interne puis appeler la mthode de lecture :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); fluxBinaire.position = 0; // lecture de lentier de 32 bit non sign var nombreStocke:uint = fluxBinaire.readUnsignedInt(); // affiche : 5 trace( nombreStocke );

Nous allons nous attarder sur un dtail important. Dans le code prcdent, nous avons nouveau utilis le type uint afin de stocker la variable nombre ainsi que le rsultat de la mthode readUnsignedInt. Tout au long de louvrage, nous avons prfr lutilisation du type int qui savre dans la plupart des cas plus rapide que le type uint. Dans un contexte de manipulation de flux binaire, il convient de toujours utiliser le type li la mthode de lecture ou dcriture. Nous utilisons donc le type uint lors de lutilisation des mthodes writeUnsignedInt et readUnsignedInt. Si nous ne respectons pas cette prcaution, nous risquons dobtenir des valeurs errones. Afin de confirmer cela, nous pouvons prendre lexemple suivant. Dans le code suivant, nous inscrivons au sein du tableau doctets un entier non sign dune valeur de 3 000 000 000. En stockant le retour de la mthode readUnsignedInt au sein dune variable de type int, nous obtenons un rsultat erron :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 3000000000; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); fluxBinaire.position = 0; // lecture de lentier de 32 bit non sign var nombreStocke:int = fluxBinaire.readUnsignedInt(); // la machine virtuelle conserve le type l'excution

21 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// affiche : -1294967296 trace( nombreStocke );

Lentier retourn par la mthode readUnsignedInt na pu tre stock correctement car le type int ne peut accueillir un entier suprieur 2 147 483 647. En utilisant le type appropri, nous rcuprons correctement lentier stock :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 3000000000; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); fluxBinaire.position = 0; // lecture de lentier de 32 bit non sign var nombreStocke:uint = fluxBinaire.readUnsignedInt(); // la machine virtuelle conserve le type l'excution // affiche : 3000000000 trace( nombreStocke );

En analysant la figure 20-13 ci dessous, nous voyons que le nombre entier non sign dune valeur de 3 000 000 000 occupe les 4 octets (32 bit) allous par la machine virtuelle pour le type uint :

Figure 20-13. Entier non sign au sein du tableau doctets. Avez-vous remarqu que la figure prcdente utilisait la notation hexadcimale afin de simplifier la reprsentation dun tel nombre ? Nous utiliserons dsormais la notation hexadcimale afin de simplifier la reprsentation des donnes. Nous pourrions donc exprimer une telle valeur laide de la notation hexadcimale :
var nombre:uint = 0xB2D05E00; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre);

A linverse, si nous stockons un entier non sign dune valeur de 5 laide de la mthode writeUnsignedInt :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray();

22 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

var nombre:uint = 5; // criture dun entier de 32 bit non sign fluxBinaire.writeUnsignedInt(nombre); fluxBinaire.position = 0; // lecture de lentier de 32 bit non sign var nombreStocke:uint = fluxBinaire.readUnsignedInt(); // affiche : 5 trace( nombreStocke );

Comme lillustre la figure 20-14, seuls les 8 bits de poids le plus faible suffisent :

Figure 20-14. Le nombre 5 au sein du tableau doctets. Nous pouvons donc stocker lentier non sign au sein dun seul octet laide de la mthode writeByte :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture dun octet fluxBinaire.writeByte(nombre);

Loctet est inscrit lindex 0 du tableau, la figure 20-15 illustre loctet sous notation hexadcimale:

Figure 20-15. Un octet stock lindex 0. Une fois loctet crit, nous pouvons le lire au sein du tableau sous la forme dun entier non sign laide la mthode readUnsignedByte :
// cration d'un tableau doctets var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 5; // criture dun octet fluxBinaire.writeByte(nombre); fluxBinaire.position = 0; // lecture de l'octet non sign var nombreStocke:uint = fluxBinaire.readUnsignedByte(); // affiche : 5 trace( nombreStocke );

23 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Si nous tentons dcrire une valeur dpassant un octet en stockage, seuls les bits infrieurs sont crits au sein du flux. Dans le code suivant nous valuons la valeur 260 en binaire :
var nombre:uint = 260; // affiche : 100000100 trace ( nombre.toString( 2 ) );

Le nombre entier non sign 260 occupe 9 bits, dont voici la reprsentation :
1 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8 0 0 0 0 0 1 0 0 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8

Comme nous lavons vu prcdemment, chaque index dun tableau doctets ne peut contenir quun seul octet, soit 8 bits. Ainsi, lorsque nous tentons dcrire une valeur ncessitant plus de 8 bits, seuls les bits de poids le plus faible sont inscrits. Rappelez-vous, les bits dits de poids le plus faible sont droite. Les bits dits de poids le plus fort sont ceux situs gauche. Un octet ne pouvant contenir que 8 bit, le 9me bit dbordant est ignor :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:uint = 260; // criture d'un entier non sign fluxBinaire.writeByte(nombre); fluxBinaire.position = 0; // lecture de l'entier non-sign // affiche : 4 trace ( fluxBinaire.readUnsignedByte() );

En convertissant lentier non sign 4 en notation binaire :


// lecture de l'entier non-sign au format binaire // affiche : 100 trace ( fluxBinaire.readUnsignedByte().toString(2) );

Nous retrouvons les 3 bits 6, 7 et 8 de loctet crit :


1 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8 0 0 0 0 0 1 0 0 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8

24 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Comme nous lavons vu prcdemment, un octet peut contenir une valeur oscillant 0 et 255 lorsque celui-ci est dit non sign. A linverse un octet sign, peut contenir un entier relatif allant de -128 127. Ainsi si nous inscrivons un entier sign dune valeur de -80, nous devons lire loctet avec la mthode readByte :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:int = -80; // criture d'un entier sign fluxBinaire.writeByte(nombre); fluxBinaire.position = 0; // lecture de l'octet sign // affiche : -80 trace( fluxBinaire.readByte() );

Nous pouvons alors nous poser la question du type utiliser lorsque nous utilisons les mthodes writeByte et readByte et readUnsignedByte. Nous avons vu prcdemment quil tait recommand dutiliser le type uint lors de lutilisation des mthodes writeUnsignedInt et readUnsignedInt. Il nexiste pas, lheure daujourdhui de type byte ou ubyte en ActionScript 3. Nous utilisons donc le type int en remplacement qui permet de stocker toutes les valeurs possibles dun octet sign ou non sign. Afin de stocker une valeur suprieure 255 nous devons travailler sur deux octets. Prenons le cas du nombre entier 1200 sous sa forme binaire :
1 0 0 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8 1 0 1 1 0 0 0 0 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8

Deux octets sont ncessaires lcriture de ce nombre, pour cela nous utilisons la mthode writeShort dont voici la signature :
public function writeShort(value:int):void

Voici le dtail du paramtre attendu :


value : Un entier de 32 bits. Seuls les 16 bits infrieurs sont crits dans le flux d'octets.

Un couple de deux octets est gnralement appel mot ou word en Anglais, celui-ci pouvant contenir une valeur allant de -32768 32767 ou 0 65 535 si celui est dit non sign. 25 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Lintrt de la mthode writeShort rside dans lcriture dun entier de 16 bit au lieu de 32 prvus par les types int et uint. Comme pour le type byte ou ubyte, il nexiste pas lheure daujourdhui de type short ou ushort en ActionScript 3. Nous utilisons donc nouveau le type int en remplacement. Dans le code suivant nous inscrivons un entier de 16 bits non sign :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:int = 1200; // criture d'un entier de 16 bit non sign fluxBinaire.writeShort(nombre); // nombre d'octets stocks // affiche : 2 trace( fluxBinaire.length );

En testant le code prcdent nous remarquons que deux octets sont crits dans le flux. Afin de lire la valeur stocke nous utilisons la mthode readShort :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:int = 1200; // criture d'un entier de 16 bit non sign fluxBinaire.writeShort(nombre); fluxBinaire.position = 0; // lecture de l'entier de 16 bit sign // affiche : 1200 trace( fluxBinaire.readShort() );

Si nous analysons la reprsentation binaire de lentier 1200 nous obtenons la reprsentation suivante :
1 0 0 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8 1 0 1 1 0 0 0 0 |_ _ _ _ _ _ _ _| 1 2 3 4 5 6 7 8

Lentier 1200 est donc cod sur 16 bits, si nous appelons successivement la mthode readUnsignedByte nous valuons lentier octet par octet :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:int = 1200; // criture d'un entier de 16 bit non sign fluxBinaire.writeShort(1200);

26 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

fluxBinaire.position = 0; // lecture du premier octet var premierOctet:int = fluxBinaire.readUnsignedByte(); // lecture du deuxime octet var secondOctet:int = fluxBinaire.readUnsignedByte(); // affiche : 4 trace( premierOctet ); // affiche : 176 trace( secondOctet );

Bien entendu, nous pouvons stocker un nombre virgule flottante au sein du tableau. ActionScript 3 utilise la norme IEEE 754 afin de traiter les nombres virgule flottante. Pour cela nous utilisons la mthode writeFloat :
public function writeFloat(value:Number):void

Voici le dtail du paramtre attendu :


value : un nombre virgule flottante de 32 bits.
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:Number = 12.5; // criture d'un flottant de 32 bits fluxBinaire.writeFloat(nombre); // rinitialisation du pointeur fluxBinaire.position = 0; // lecture du flottant var flottant:Number = fluxBinaire.readFloat(); // affiche : 12.5 trace( flottant ); // affiche : 1100 trace( flottant.toString(2) );

Dans le code suivant, nous inscrivons un flottant de 32 bits :

La mthode writeFloat crit un nombre au format 32 bits, 4 octets sont donc ncessaire au stockage dun nombre flottant :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); var nombre:Number = 12.5; // criture d'un flottant de 32 bits fluxBinaire.writeFloat(nombre); // affiche : 4 trace( fluxBinaire.length );

27 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Il nexiste pas en ActionScript 3 de type float, nous utilisons donc le type Number en remplacement. Si nous souhaitons stocker un nombre virgule flottante plus grand, quivalent au type Number cod sur 64 bits nous pouvons utiliser la mthode writeDouble :
// cration d'un flux binaire var fluxBinaire:ByteArray = new ByteArray(); // criture d'un nombre virgule flottante fluxBinaire.writeDouble(12.5); // affiche : 8 trace( fluxBinaire.length );

Notons quun double est lquivalent du type Number. ECMAScript 4 dfinit un type double, nous pourrions donc voir ce type apparatre un jour en ActionScript 3.

A retenir
La classe ByteArray dfinit un ensemble de mthodes permettant dcrire diffrents types de donnes. Certains types de donnes crits au sein du tableau doctets nont pas dquivalent en ActionScript 3. Cest le cas des types float, byte, et short.

Ces mthodes dcoupent automatiquement les valeurs passes en paramtres en groupes doctets.

Copier des objets


La classe ByteArray intgre un srialiseur et dsrialiseur AMF permettant lcriture de types au format AMF. Nous pouvons utiliser la mthode writeObject de manire dtourne en passant un objet, celui-ci est alors inscrit au sein du flux :
var fluxBinaire:ByteArray = new ByteArray(); // dfinition d'un objet var parametres:Object = { age : 25, nom : "Bob" }; // criture de l'objet au sein du flux fluxBinaire.writeObject( parametres );

Afin de crer une copie de ce dernier nous appelons la mthode readObject :


var fluxBinaire:ByteArray = new ByteArray(); // dfinition d'un objet var parametres:Object = { age : 25, nom : "Bob" };

28 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// criture de l'objet au sein du flux fluxBinaire.writeObject( parametres ); fluxBinaire.position = 0; // copie de l'objet parametres var copieParametres:Object = fluxBinaire.readObject();

Nous pouvons ainsi crer une fonction personnalise ralisant en interne tout ce processus :
function copieObjet ( pObjet:* ):* { var fluxBinaire:ByteArray = new ByteArray(); fluxBinaire.writeObject( pObjet ); fluxBinaire.position = 0; return fluxBinaire.readObject(); }

Afin de copier un objet, nous devons simplement appeler la fonction copieObjet :


// dfinition d'un objet var parametres:Object = { age : 25, nom : "Bob" }; var copieParametres:Object = copieObjet ( parametres ); /* affiche : nom : Bob age : 25 */ for ( var p:String in copieParametres ) trace( p, " : ", copieParametres[p] );

En modifiant les donnes de lobjet dorigine, nous voyons que lobjet copieParametres est bien dupliqu :
var copieParametres:Object = copieObjet ( parametres ); // modification du nom dans l'objet d'origine parametres.nom = "Stevie"; /* affiche : nom : Bob age : 25 */ for ( var p:String in copieParametres ) trace( p, " : ", copieParametres[p] );

Notez que cette technique ne fonctionne que pour les objets littraux et non les instances de classes.

A retenir
29 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

La mthode writeObject permet dcrire un objet composite au sein du tableau doctets. Grace la mthode readObject nous pouvons crer une copie de lobjet crit.

Ecrire des donnes au format texte


Afin de stocker des donnes au format texte nous pouvons utiliser la mthode writeUTFBytes dont voici la signature :
public function writeUTFBytes(value:String):void

Chaque caractre composant la chane est encod sur une suite dun quatre octets. Dans le code suivant, nous crivons un texte simple :
var fluxBinaire:ByteArray = new ByteArray(); var chaine:String = "Bonjour Bob"; // affiche : 11 trace( chaine.length ); fluxBinaire.writeUTFBytes(chaine); // affiche : 11 trace( fluxBinaire.length );

Si nous utilisons les 127 premiers caractres de lalphabet, nous voyons que chaque caractre est cod sur un octet. A laide de la mthode readByte, nous pouvons lire rcuprer le caractre de chaque octet :
var fluxBinaire:ByteArray = new ByteArray(); var chaine:String = "Bonjour Bob"; // affiche : 19 trace( chaine.length ); fluxBinaire.writeUTFBytes(chaine); // affiche : 20 trace( fluxBinaire.length ); fluxBinaire.position = 0; // affiche : 66 trace( fluxBinaire.readByte() ); // affiche : 111 trace( fluxBinaire.readByte() );

A linverse, si nous utilisons des caractres spciaux, ils seront alors cods sur plusieurs octets :
var fluxBinaire:ByteArray = new ByteArray(); var chaine:String = "Bonjour Bob a va ?";

30 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// affiche : 19 trace( chaine.length ); fluxBinaire.writeUTFBytes(chaine); // affiche : 20 trace( fluxBinaire.length );

Dans le code prcdent, le caractre est cod sur deux octets.

Lire des donnes au format texte


Comme nous lavons vu prcdemment, il est primordial de ne pas tenter sortir du flux. Souvenez-vous, prcdemment nous avions tent de lire un octet non disponible au sein du flux, lerreur suivante fut leve lexcution :
Error: Error #2030: Fin de fichier dtecte.

Afin de garantir de ne jamais sortir du flux, nous utilisons la proprit bytesAvailable. Celle-ci renvoie le nombre doctets disponible, autrement dit la soustraction de la longueur totale du flux et de la position du pointeur interne. Dans le code suivant nous inscrivons des donnes texte au sein dun tableau doctets et calculons manuellement le nombre doctets disponibles la lecture :
var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte fluxBinaire.writeUTFBytes("Bob Groove"); // rinitialisation du pointeur fluxBinaire.position = 0; // calcul manuel du nombre d'octets disponibles la lecture // affiche : 10 trace( fluxBinaire.length - fluxBinaire.position );

En rinitialisant le pointeur laide de la proprit position, nous obtenons 10 octets disponibles. Dans lexemple suivant, nous utilisons la proprit bytesAvailable, qui permet de renvoyer automatiquement le nombre doctets disponibles la lecture :
var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte fluxBinaire.writeUTFBytes("Bob Groove"); // rinitialisation du pointeur fluxBinaire.position = 0; // affiche : 10

31 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

trace( fluxBinaire.bytesAvailable );

Nous utilisons trs souvent cette proprit afin dtre sur de ne pas aller trop loin dans la lecture des octets. Afin dextraire le texte stock au sein du flux, nous utilisons la mthode readUTFBytes ayant la signature suivante :
public function readUTFBytes(length:uint):String

En passant le nombre doctets lire, celle-ci dcode automatiquement chaque octet en caractre UTF-8. Ainsi dans le code suivant, nous lisons uniquement les 3 premiers caractres du flux :
var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte fluxBinaire.writeUTFBytes("Bob Groove"); // rinitialisation du pointeur fluxBinaire.position = 0; // extrait les 3 premiers caractres du flux // affiche : Bob trace( fluxBinaire.readUTFBytes( 3 ) );

Nous pouvons donc utiliser la proprit bytesAvailable afin dtre sur de lire la totalit du texte stocke dans le flux :
var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte fluxBinaire.writeUTFBytes("Bob Groove"); // rinitialisation du pointeur fluxBinaire.position = 0; // extrait la totalit de la chane de caractres // affiche : Bob Groove trace( fluxBinaire.readUTFBytes( fluxBinaire.bytesAvailable ) );

Nous utilisons gnralement la proprit bytesAvailable avec une boucle while afin dextraire chaque caractre :
var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte fluxBinaire.writeUTFBytes("Bob Groove"); // rinitialisation du pointeur fluxBinaire.position = 0; while ( fluxBinaire.bytesAvailable > 0 ) { /* affiche : B o b

32 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

G r o o v e */ trace( String.fromCharCode( fluxBinaire.readByte()) ); }

Nous lisons chaque octet, tant quil en reste lire. Afin de trouver le caractre correspondant au nombre, nous utilisons la mthode fromCharCode de la classe String. Si nous insrons des caractres spciaux, lappel de la mthode readUTFBytes dcode correctement les caractres encods sur plusieurs octets :
var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte avec des caractres spciaux fluxBinaire.writeUTFBytes("Bob a Groove"); // rinitialisation du pointeur fluxBinaire.position = 0; // extrait la totalit de la chane de caractres // affiche : Bob a Groove trace( fluxBinaire.readUTFBytes( fluxBinaire.bytesAvailable ) );

A linverse, si nous utilisons la mthode readByte en valuant chaque caractre, nous ne pourrons interprter correctement le caractre encod sur deux octets :
var fluxBinaire:ByteArray = new ByteArray(); // criture de donnes texte avec des caractres spciaux fluxBinaire.writeUTFBytes("Bob a Groove"); // rinitialisation du pointeur fluxBinaire.position = 0; while ( fluxBinaire.bytesAvailable > 0 ) { /* affiche : B o b a G r o

33 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

o v e */ trace( String.fromCharCode( fluxBinaire.readByte()) ); }

La mthode readByte nest pas adapte au dcodage de chanes encodes.

A retenir
Afin de dcoder sous forme de chane de caractres UTF-8 un ensemble doctets, nous utilisons la mthode readUTFBytes. Celle-ci accepte en paramtre, le nombre doctets lire au sein du flux.

Compresser des donnes


La classe ByteArray dispose dune mthode de compression de donnes utilisant lalgorithme zlib dont la spcification est disponible ladresse suivante : http://www.ietf.org/rfc/rfc1950.txt Il peut tre intressant dutiliser cette fonctionnalit afin de compresser des donnes devant tre sauvegardes. Prenons le cas dapplication suivant : Nous devons sauver sur le poste de lutilisateur un volume de donnes important. Afin de faciliter la reprsentation de ces donnes, un objet XML est utilis. Pour stocker des donnes sur le poste client, nous utilisons la classe flash.net.SharedObject permettant de crer des cookies permanents. Dans le code suivant, nous chargeons un fichier XML dune taille de 1,5Mo au format binaire afin dobtenir directement un objet ByteArray contenant le flux XML :
var chargeur:URLLoader = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.BINARY; chargeur.load( new URLRequest ("donnees.xml") ); chargeur.addEventListener( Event.COMPLETE, chargementTermine ); function chargementTermine ( pEvt:Event ):void {

34 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

// accs au flux binaire XML var fluxXML:ByteArray = pEvt.target.data; // affiche : 1547.358 trace( fluxXML.length / 1024 ); }

Puis nous sauvegardons le flux XML au sein dun cookie permanent :


var chargeur:URLLoader = new URLLoader(); chargeur.dataFormat = URLLoaderDataFormat.BINARY; chargeur.load( new URLRequest ("donnees.xml") ); chargeur.addEventListener( Event.COMPLETE, chargementTermine ); // cre un cookie permanent du nom de "cookie" var monCookie:SharedObject = SharedObject.getLocal("cookie"); function chargementTermine ( pEvt:Event ):void { // accs au flux binaire XML var fluxXML:ByteArray = pEvt.target.data; // affiche : 1547.358 trace( fluxXML.length / 1000 ); // criture du flux XML dans le cookie monCookie.data.donneesXML = fluxXML; // sauvegarde du cookie sur le disque dur monCookie.flush(); }

A lappel de la mthode flush, le lecteur Flash tente de sauvegarder les donnes sur le disque. Le flux XML de large poids ncessite plus de 1 Mo despace disque et provoque louverture du panneau Enregistrement local afin que lutilisateur accepte la sauvegarde. La figure 20-16 illustre le panneau :

Figure 20-16. Panneau Enregistrement local. 35 / 44


Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Nous remarquons que le lecteur Flash ncessite alors une autorisation de 10 mo afin de sauver le cookie. 1548 Ko sont ncessaire la sauvegarde du cookie : La figure 20-17 illustre lespace actuellement utilis par le cookie :

Figure 20-17. Espace utilis par le cookie permanent. En compressant simplement le flux XML laide de la mthode compress, nous rduisons la taille du flux de prs de 700% :
function chargementTermine ( pEvt:Event ):void { // accs au flux binaire XML var fluxXML:ByteArray = pEvt.target.data; // affiche : 1547.358 trace( fluxXML.length / 1024 ); // compression du flux XML fluxXML.compress(); // affiche : 212.24 trace( fluxXML.length / 1024 ); // criture du flux XML dans le cookie monCookie.data.donneesXML = fluxXML; // sauvegarde du cookie sur le disque dur monCookie.flush(); }

En testant le code prcdent, si nous affichons le panneau Enregistrement local, nous voyons que le cookie ne ncessite que 213 Ko despace disque :

36 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Figure 20-18. Un octet stock lindex 0. Afin de lire le flux sauvegard, nous devons appeler la mthode uncompress, puis extraire la chane compresse laide de la mthode readUTFBytes :
function chargementTermine ( pEvt:Event ):void { // accs au flux binaire XML var fluxXML:ByteArray = pEvt.target.data; // affiche : 1547.358 trace( fluxXML.length / 1024 ); fluxXML.compress(); // affiche : 212.24 trace( fluxXML.length / 1024 ); // ecriture du flux XML dans le cookie monCookie.data.donneesXML = fluxXML; // sauvegarde du cookie sur le disque dur monCookie.flush(); var fluxBinaireXML:ByteArray = monCookie.data.donneesXML; // dcompression du flux XML binaire fluxXML.uncompress(); // lecture de la chane XML var chaineXML:String = fluxBinaireXML.readUTFBytes ( fluxBinaireXML.bytesAvailable ); // reconstitution d'un objet XML var donneesXML:XML = new XML ( chaineXML ); }

Grce la chane extraite du tableau doctets, nous rcrons un objet XML utilisable. La mthode compress nous a permis de rduire le poids du fichier XML de prs de 1335 Ko.

A retenir
37 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

La mthode compress de la classe ByteArray permet la compression des donnes binaires laide de lalgorithme zlib. La mthode uncompress permet de dcompresser le flux.

Sauvegarder un flux binaire


Le lecteur Flash 9 na pas la capacit dexporter un flux gnr en ActionScript 3 par la classe FileReference. Il serait intressant de pouvoir passer la mthode download de lobjet FileReference un tableau doctets afin que le lecteur nous propose de sauver le flux, mais une telle fonctionnalit nest pas prsente dans le lecteur Flash 9. Souvenez-vous, au cours du prcdent chapitre nous avons export une image du mme type grce AMFPHP. Dans lexemple suivant nous allons exporter le tableau doctets sans utiliser AMFPHP. La premire tape consiste utiliser une classe gnrant un fichier spcifique telle une image, une archive zip, ou nimporte quel type de fichier. Nous allons rutiliser la classe dencodage EncodeurPNG que nous avons utilis au cours du prcdent chapitre. Rappelez-vous, la classe EncodeurPNG ncessite en paramtre, une image de type BitmapData afin dencoder les pixels dans une enveloppe PNG. Nous crons dans le code suivant, une image dune dimension de 320 par 240 pixels :
// import de la classe EncodeurPNG import org.bytearray.encodage.images.EncodeurPNG ; // cration d'une image var donneesBitmap:BitmapData = new BitmapData ( 320, 240, false, 0x990000 ); // encodage au format PNG var fluxBinairePNG:ByteArray = EncodeurPNG.encode ( donneesBitmap ); // affiche : 690 trace( fluxBinairePNG.length );

Nous obtenons une longueur de 690 octets. Souvenez, vous en dbut de chapitre, nous avions abord la structure dun fichier PNG. Pour exporter le flux nous devons transmettre par connexion HTTP le flux binaire, puis prvoir un script serveur afin de rendre le flux disponible en tlchargement par une fentre approprie. Pour permettre cela, nous crons un fichier export.php contenant le code PHP suivant :
<?php

38 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

if ( isset ( $GLOBALS["HTTP_RAW_POST_DATA"] )) { $flux = $GLOBALS["HTTP_RAW_POST_DATA"]; header('Content-Type: image/png'); header("Content-Disposition: attachment; filename=".$_GET['nom']); echo $flux; } ?> else echo 'An error occured.';

Nous sauvons le fichier export.php sur notre serveur local, puis nous transmettons le flux binaire au script distant en passant le tableau doctets la proprit data de lobjet URLRequest :
// import de la classe EncodeurPNG import org.bytearray.encodage.images.EncodeurPNG ; // cration d'une image var donneesBitmap:BitmapData = new BitmapData ( 320, 240, false, 0x990000 ); // encodage au format PNG var fluxBinairePNG:ByteArray = EncodeurPNG.encode ( donneesBitmap ); // entte HTTP au format brut var enteteHTTP:URLRequestHeader = new URLRequestHeader ("Content-type", "application/octet-stream"); var requete:URLRequest = new URLRequest("http://localhost/export_image/export.php?nom=sketch.jpg"); requete.requestHeaders.push(enteteHTTP); requete.method = URLRequestMethod.POST; requete.data = fluxBinairePNG; navigateToURL(requete, "_blank");

Notons que grce la classe la classe URLRequestHeader, nous indiquons au lecteur Flash de ne pas traiter les donnes transmettre en HTTP en tant que chane mais en tant que flux binaire. Souvenez-vous, lors du chapitre 14 intitul Charger et envoyer des donnes, nous avions dcouvert quil tait impossible depuis lenvironnement de dveloppement de Flash, dutiliser la mthode POST en utilisant la fonction navigateToURL. Il est donc obligatoire de tester le code prcdent au sein dune page navigateur. Dans le cas contraire, le flux binaire sera transmis par la mthode GET et sera donc trait telle une chane de caractres.

39 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Si nous avions voulu sauver limage sur le serveur, nous aurions utilis le code PHP suivant au sein du fichier export.php :
<?php if ( isset ( $GLOBALS["HTTP_RAW_POST_DATA"] )) { $flux = $GLOBALS["HTTP_RAW_POST_DATA"];

$fp = fopen($_GET['nom'], 'wb'); fwrite($fp, $im); fclose($fp); } ?>

Bien entendu, le code prcdent peut tre modifi afin de sauver le flux binaire dans un rpertoire spcifique.

A retenir
Le lecteur Flash est incapable dexporter un flux binaire sans un script serveur. Lors de lenvoi de donnes binaire depuis le lecteur Flash, nous devons obligatoirement utiliser la classe URLRequestHeader afin de prciser que les donnes transmises sont de type binaire. Les donnes binaires arrivent en PHP au sein du la proprit HTTP_RAW_POST_DATA du tableau $GLOBALS : $GLOBALS["HTTP_RAW_POST_DATA"].

Gnrer un PDF
Afin de dmontrer lintrt de la classe ByteArray dans un projet rel, nous allons gnrer un fichier PDF dynamiquement en ActionScript 3. Pour cela, nous allons utiliser la librairie AlivePDF qui sappuie sur la classe ByteArray pour gnrer le fichier PDF. En ralit il est aussi possible de gnrer un fichier PDF en ActionScript 1 et 2 sans avoir recours la classe ByteArray, car le format PDF est bas sur une simple chane de caractres. En revanche lintgration dimages ou autres fichiers ncessite une manipulation des donnes binaire ce qui est rserv ActionScript 3. Voici le contenu dun fichier PDF ouvert avec le bloc notes :
%PDF 1.4 1 0 obj << /Type /Catalog /Outlines 2 0 R /Pages 3 0 R >>

40 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

endobj 2 0 obj << /Type Outlines /Count 0 >> endobj 3 0 obj << /Type /Pages /Kids [4 0 R] /Count 1 >> endobj 4 0 obj << /Type /Page /Parent 3 0 R /MediaBox [0 0 612 792] /Contents 5 0 R /Resources << /ProcSet 6 0 R >> >> endobj 5 0 obj << /Length 35 >> stream Page-marking operators endstream endobj 6 0 obj [/PDF] endobj xref 0 7 0000000000 65535 f 0000000009 00000 n 0000000074 00000 n 0000000120 00000 n 0000000179 00000 n 0000000300 00000 n 0000000384 00000 n trailer << /Size 7 /Root 1 0 R >> startxref 408 %%EOF

Afin de gnrer un PDF en ActionScript nous utilisons la librairie AlivePDF disponible ladresse suivante : http://code.google.com/p/alivepdf/downloads/list Nous allons utiliser la version 0.1.4 afin de crer un PDF de deux pages contenant du texte sur la premire page, puis une image sur la deuxime page. Une fois les sources de la librairie tlcharges. Nous plaons le rpertoire org contenant les classes ct dun nouveau document FLA, puis nous importons la classe PDF :

41 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

import import import import import import import import

org.alivepdf.pdf.PDF; org.alivepdf.layout.Orientation; org.alivepdf.layout.Unit; org.alivepdf.layout.Size; org.alivepdf.layout.Layout; org.alivepdf.display.Display; org.alivepdf.saving.Download; org.alivepdf.saving.Method;

var myPDF:PDF = new PDF ( Orientation.PORTRAIT, Unit.MM, Size.A4 );

Nous dfinissons le mode daffichage du PDF :


myPDF.setDisplayMode( Display.FULL_PAGE, Layout.SINGLE_PAGE );

Puis nous ajoutons une nouvelle page laide de la mthode addPage :


myPDF.addPage();

Enfin nous sauvons le PDF :


myPDF.savePDF ( Method.REMOTE, 'http://localhost/pdf/create.php', Download.ATTACHMENT, 'monPDF.pdf' );

Nous devons passer en deuxime paramtre la mthode savePDF, ladresse du script create.php prsent dans les sources du projet AlivePDF. A lexcution, le SWF ouvre la fentre illustre en figure 20-19 :

Figure 20-19. Sauvegarde du document PDF. A louverture le PDF contient une seule page blanche. Afin denrichir notre document PDF, nous allons ajouter du texte, en important deux nouvelles classes :

42 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

import org.alivepdf.fonts.Style; import org.alivepdf.fonts.FontFamily; import org.alivepdf.colors.RGBColor;

Puis nous utilisons les diffrentes mthodes dcriture de texte :


myPDF.textStyle ( new RGBColor ( 255, 100, 0 ) ); myPDF.setFont( FontFamily.HELVETICA, Style.BOLD ); myPDF.setFontSize ( 20 ); myPDF.addText ('Voil du texte !', 70, 12); myPDF.addLink ( 70, 4, 52, 16, "http://alivepdf.bytearray.org");

En testant le code prcdent, un document PDF est gnr, louverture, le texte est affich et redirige sur le site du projet AlivePDF lors du clic. La figure 20-20 illustre le rsultat :

Figure 20-20. Texte intgr au PDF. Afin dintgrer une image, nous importons une image dans la bibliothque, puis nous lassocions une classe nomme Logo. Grce la mthode addImage, nous intgrons limage bitmap en deuxime page du PDF :
myPDF.addPage(); var donneesBitmap:Logo = new Logo(0,0); var imageLogo:Bitmap = new Bitmap ( donneesBitmap ); myPDF.addImage ( imageLogo );

La figure 20-21 illustre le rsultat :

Figure 20-21. Image intgre au PDF.

43 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 20 ByteArray version 0.1.1

Ainsi se termine notre aventure binaire. Au cours du prochain chapitre, nous mettrons en application la plupart des concepts abords durant lensemble de louvrage.

44 / 44
Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

21
Application finale

DECOMPOSER LAPPLICATION ...................................................................... 1 GERER LES CHEMINS DACCES ...................................................................... 3 CREATION DUN FICHIER XML DE CONFIGURATION ............................................... 3 UTILISER DES LIBRAIRIES PARTAGEES ...................................................... 6 GENERER ET CHARGER LES LIBRAIRIES ................................................................... 8 UTILISER LES LIBRAIRIES PARTAGEES ................................................................... 15

Dcomposer lapplication
Au cours de ce chapitre, nous allons nous intresser aux diffrents points essentiels chaque application ActionScript 3. Afin de permettre chacun dapprcier ce chapitre, nous ne traiterons pas de cas particulier mais nous nous attarderons sur diffrents concepts rutilisables pour chaque type dapplication. Le premier point que nous allons aborder concerne la dcomposition de notre application. La premire approche consiste ce que lapplication se prcharge elle-mme, cette technique savre limite car nous devons nous assurer quun minimum dlments soient export sur la premire image de notre application, au risque de voir notre barre de prchargement safficher partir de 50%. Nous allons donc opter pour une deuxime technique en utilisant un premier SWF qui se chargera de prcharger notre application En utilisant cette approche, nous devons nous assurer que lapplication prchargeant notre application soit la plus lgre possible afin de ne pas ncessiter un prchargement elle aussi.
1 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

La figure 21-1 illustre le principe :

Figure 21-1. Chargement de lapplication. Chaque SWF possde une classe de document associe la figure 21-2 illustre lide :

Figure 21-2. Classe de document. Nous allons dfinir une premire classe de document nomme Prechargeur pour le SWF de prchargement. Celle-ci va se charger de charger tous les lments ncessaires notre application.

A retenir

2 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Il est prfrable dutiliser un SWF ddi au prchargement de notre application. Ce SWF se charge de charger tous les lments ncessaires notre application.

Grer les chemins daccs


Dans la plupart des applications ActionScript, un problme se pose ternellement li aux chemins daccs aux fichiers. Afin de simplifier cela nous allons dfinir au sein dun fichier XML de configuration les chemins daccs. La premire chose que fera le SWF de prchargement sera de charger ce fichier XML de configuration afin de rcuprer les chemins daccs.

Cration dun fichier XML de configuration


Au sein dun rpertoire init nous crons un fichier XML nomm initialisation.xml contenant larbre XML suivant :
<CONFIG> <CHEMIN_XML chemin="xml/"/> <CHEMIN_IMAGES chemin="images/"/> <CHEMIN_SWF chemin="swf/"/> <CHEMIN_POLICES chemin="polices/"/> </CONFIG>

Chaque nud dfinit un chemin associ un type de mdias. Nous spcifions sont emplacement par FlashVars au sein de la page HTML :
<script language="javascript"> if (AC_FL_RunContent == 0) { alert("Cette page ncessite le fichier AC_RunActiveContent.js."); } else { AC_FL_RunContent( 'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9, 0,0,0', 'width', '550', 'height', '400', 'src', 'chap-21-prechargeur', 'flashvars', 'initChemin=init/', 'quality', 'high', 'pluginspage', 'http://www.macromedia.com/go/getflashplayer', 'align', 'middle', 'play', 'true', 'loop', 'true', 'scale', 'showall', 'wmode', 'window', 'devicefont', 'false', 'id', 'chap-21-prechargeur', 'bgcolor', '#ffffff', 'name', 'chap-21-prechargeur',

3 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

} </script>

'menu', 'true', 'allowFullScreen', 'false', 'allowScriptAccess','sameDomain', 'movie', 'chap-21-prechargeur', 'salign', '' ); //end AC code

Nous rcuprons ce chemin afin dindiquer au SWF de prchargement o se situe le XML de configuration. Nous associons la classe de document suivante notre SWF de prchargement, nous rutilisons la classe ChargeurXML cre au cours du chapitre 14 intitul Charger et envoyer des donnes :
package org.bytearray.document { import import import import import import flash.events.Event; flash.events.IOErrorEvent; flash.events.SecurityErrorEvent; flash.net.URLRequest; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.xml.ChargeurXML;

public class Prechargeur extends ApplicationDefaut { // adresse du fichier de configuration private const INITIALISATION:String = "initialisation.xml"; // chemins private var private var private var private var cheminXML:String; cheminImages:String; cheminSWF:String; cheminPolices:String;

// chargement private var chargeurInitialisation:ChargeurXML; public function Prechargeur () { chargeurInitialisation = new ChargeurXML (); chargeurInitialisation.addEventListener ( Event.COMPLETE, chargementInitTermine ); chargeurInitialisation.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeurInitialisation.addEventListener ( SecurityErrorEvent.SECURITY_ERROR, erreurChargement ); var chemin:String = loaderInfo.parameters.initChemin; if ( chemin == null ) chemin = "init/";

4 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

chargeurInitialisation.charge ( new URLRequest ( chemin + INITIALISATION ) ); } private function chargementInitTermine ( pEvt:Event ):void { var initXML:XML = pEvt.target.donnees; cheminXML = initXML.CHEMIN_XML.@chemin; cheminImages = initXML.CHEMIN_IMAGES.@chemin; cheminSWF = initXML.CHEMIN_SWF.@chemin; cheminPolices = initXML.CHEMIN_POLICES.@chemin; // affiche : xml/ images/ swf/ polices/ trace( cheminXML, cheminImages, cheminSWF, cheminPolices ); } private function erreurChargement ( pEvt:Event ):void { trace ( pEvt ); } } }

Le fichier de configuration est automatiquement charg depuis son dossier. Grce la variable initChemin, nous pouvons passer dynamiquement le chemin daccs au fichier de configuration. Afin de pouvoir tester notre application au sein de lenvironnement auteur, nous avons ajout le test suivant :
if ( chemin == null ) chemin = "init/";

Ce test permet simplement de dfinir la variable chemin lorsque nous testons lapplication lors du dveloppement au sein de lenvironnement auteur et que lutilisation des FlashVars est impossible. Ainsi, il nous est facile de spcifier lemplacement de ce fichier de configuration. Si lors du dploiement de notre application, le chargement dlments choue, nous pouvons modifier les chemins grce au fichier de configuration XML.

A retenir

5 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Lutilisation dun fichier XML de configuration est recommande afin de pouvoir facilement spcifier les chemins daccs aux fichiers. Le chemin daccs au fichier de configuration XML est spcifi par FlashVars. Une fois lapplication mise en production, la modification des chemins daccs aux fichiers est simplifie.

Utiliser des librairies partages


Lors du dveloppement dune application ActionScript 3, celle-ci est gnralement divise en plusieurs modules. Nous devons donc prendre en considration plusieurs points. Le premier concerne loptimisation du poids de lapplication globale. Afin dviter de dupliquer les lments graphiques et classes au sein des diffrents SWF nous allons utiliser le concept de librairie partage aborde au cours du chapitre 13 intitul Chargement de contenu. Souvenez-vous, en plaant les dfinitions de classes au sein dun SWF nous avions extrait dynamiquement la dfinition associe et utilise la classe au sein de notre application. Nous allons utiliser ce mcanisme de librairie partage pour les lments graphiques ainsi que les polices utilises. Les lments graphiques seront chargs par le SWF de prchargement et rendues disponibles tous les SWF constituant lapplication. Nous conomiserons ainsi du poids et gagnerons en facilit de mise jour. En mettant jour la librairie partage, toute lapplication sera mise jour sans la ncessite dtre recompile car tout sera extrait dynamiquement. Imaginons que nous souhaitions utiliser le logo dune socit au sein de lapplication Si nous noptimisons pas celle-ci, voici comment celle-ci serait organise :

6 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Figure 21-3. Duplication du poids de limage. Notre application est compose de nombreux SWF contenant chacun limage, ce qui augmente sensiblement le poids total de lapplication. En utilisant une librairie partage, le SWF de prchargement va extraire les classes ncessaires et permettre aux diffrents SWF constituant lapplication leur utilisation. Comme la figure 21-3 lillustre, une image bitmap est utilise par chacun des SWF. En dupliquant la classe BitmapData dans chaque SWF nous dupliquons et augmentons le poids de lapplication globale ainsi que la bande passant utilise. Nous allons gnrer un SWF contenant limage rutiliser puis la rutiliser au sein des diffrents SWF, la figure 21-4 illustre le rsultat :

7 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Figure 21-4. Chargement de limage laide dune librairie partage. Grce ce procd nous optimisons le poids de lapplication mais bnficions dun autre avantage li la mise jour de notre application. En centralisant les lments chargs et en les rutilisant, nous facilitons la mise jour de lapplication. Nous allons nous intresser cette partie dans cette nouvelle partie.

A retenir
Afin doptimiser la bande passante utilise il est recommand dutiliser les librairies partages. Leur utilisation permet de faciliter la mise jour de lapplication. Toutes les dfinitions de classes sont extraites dynamiquement, la mise jour de lapplication ne ncessite donc pas de recompilation.

Gnrer et charger les librairies


Afin de gnrer une librairie utilisable, nous crons un nouveau document Flash CS3 et importons une image dans la bibliothque que nous associons une classe nomme Logo.
libraire.swf.

Une fois dfinie, nous exportons un SWF sous le nom de

8 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Afin dutiliser les dfinitions de classes issues de la librairie, le SWF grant le chargement doit dabord charger la librairie partage afin de rendre les classes disponibles par tous les SWF de lapplication. Pour cela, nous devons charger la librairie dans le domaine dapplication en cours. Souvenez-vous nous avions utilis un objet LoaderContext lors du chapitre 13 afin de spcifier le domaine dapplication dans lequel placer les classes charges. Nous modifions la classe de document Prechargeur afin de charger en premier temps la librairie partage, puis le SWF principal application.swf. Nous devons donc charger notre librairie dans un premier temps, puis charger le module application.swf. Afin de charger ce contenu de manire squentielle, nous utilisons la classe de chargement squentielle suivante :
package org.bytearray.chargement { import import import import import import import import import import flash.display.Loader; flash.display.LoaderInfo; flash.display.Sprite; flash.events.Event; flash.events.IOErrorEvent; flash.events.ProgressEvent; flash.events.SecurityErrorEvent; flash.net.URLRequest; flash.system.ApplicationDomain; flash.system.LoaderContext;

public class ChargeurSequentiel extends Sprite { private var liste:Array; private var chargeur:Loader; private var contexte:LoaderContext; private var domaine:ApplicationDomain; public var contentLoaderInfo:LoaderInfo; public function ChargeurSequentiel ( pDomaine:ApplicationDomain ) { liste = new Array(); domaine = pDomaine; chargeur = new Loader(); contentLoaderInfo = chargeur.contentLoaderInfo; addChild ( chargeur ); contexte = new LoaderContext ( false, domaine );

9 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

chargeur.contentLoaderInfo.addEventListener redirigeEvenement ); chargeur.contentLoaderInfo.addEventListener ProgressEvent.PROGRESS, redirigeEvenement ); chargeur.contentLoaderInfo.addEventListener chargementTermine ); chargeur.contentLoaderInfo.addEventListener IOErrorEvent.IO_ERROR, redirigeEvenement ); chargeur.contentLoaderInfo.addEventListener SecurityErrorEvent.SECURITY_ERROR, redirigeEvenement ); }

( Event.INIT, ( ( Event.COMPLETE, ( (

private function chargementTermine ( pEvt:Event ):void { suivant(); } private function redirigeEvenement ( pEvt:Event ):void { dispatchEvent ( pEvt ); } public function ajoute ( pFichier:String ):void { liste.push ( new URLRequest ( pFichier ) ); } public function demarre ( ):void { suivant(); } private function suivant ( ):void { if ( liste.length ) chargeur.load ( liste.shift(), contexte ); else dispatchEvent ( new Event ( Event.COMPLETE ) ); } } }

10 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Cette classe possde une mthode ajoute qui place chaque mdia en file dattente, puis nous lanons le chargement des lments laide de la mthode demarre. Nous mettons jour le code de la classe Prechargeur afin de charger la librairie partage :
package org.bytearray.document { import import import import import import import import import flash.events.Event; flash.events.IOErrorEvent; flash.events.ProgressEvent; flash.events.SecurityErrorEvent; flash.net.URLRequest; flash.system.ApplicationDomain; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.xml.ChargeurXML; org.bytearray.chargement.ChargeurSequentiel;

public class Prechargeur extends ApplicationDefaut { private const INITIALISATION:String = "initialisation.xml"; // chemins private var private var private var private var cheminXML:String; cheminImages:String; cheminSWF:String; cheminPolices:String;

// chargement private var chargeurInitialisation:ChargeurXML; private var chargeur:ChargeurSequentiel; public function Prechargeur () { chargeur = new ChargeurSequentiel ( ApplicationDomain.currentDomain ); chargeur.addEventListener chargeur.addEventListener chargementEnCours ); chargeur.addEventListener erreurChargement ); chargeur.addEventListener erreurChargement ); addChild ( chargeur ); chargeurInitialisation = new ChargeurXML (); chargeurInitialisation.addEventListener ( Event.COMPLETE, chargementInitTermine ); chargeurInitialisation.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); ( Event.COMPLETE, chargementTermine ); ( ProgressEvent.PROGRESS, ( IOErrorEvent.IO_ERROR, ( SecurityErrorEvent.SECURITY_ERROR,

11 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

chargeurInitialisation.addEventListener ( SecurityErrorEvent.SECURITY_ERROR, erreurChargement ); var chemin:String = loaderInfo.parameters.initChemin; if ( chemin == null ) chemin = "init/"; chargeurInitialisation.charge ( new URLRequest ( chemin + INITIALISATION ) ); } private function chargementTermine ( pEvt:Event ):void { trace("chargement des libs et swf termin !"); } private function chargementEnCours ( pEvt:ProgressEvent ):void { trace("chargement en cours"); } private function chargementInitTermine ( pEvt:Event ):void { var initXML:XML = pEvt.target.donnees; cheminXML = initXML.CHEMIN_XML.@chemin; cheminImages = initXML.CHEMIN_IMAGES.@chemin; cheminSWF = initXML.CHEMIN_SWF.@chemin; cheminPolices = initXML.CHEMIN_POLICES.@chemin; chargeur.ajoute ( cheminSWF + "librairie.swf" ); chargeur.demarre(); } private function erreurChargement ( pEvt:Event ):void { trace ( pEvt ); } } }

Nous passons au constructeur de la classe ChargeurSequentiel le domaine dapplication en cours. Ainsi les classes issues des SWF chargs dynamiquement sont places au sein du domaine dapplication en cours.
12 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Afin dindiquer ltat de chargement, nous ajoutons une barre de prchargement :


package org.bytearray.document { import import import import import import import import import import flash.events.Event; flash.events.IOErrorEvent; flash.events.ProgressEvent; flash.events.SecurityErrorEvent; flash.net.URLRequest; flash.system.ApplicationDomain; org.bytearray.abstrait.ApplicationDefaut; org.bytearray.evenements.EvenementInfos; org.bytearray.xml.ChargeurXML; org.bytearray.chargement.ChargeurSequentiel;

public class Prechargeur extends ApplicationDefaut { private const INITIALISATION:String = "initialisation.xml"; // chemins private var private var private var private var cheminXML:String; cheminImages:String; cheminSWF:String; cheminPolices:String;

// chargement private var chargeurInitialisation:ChargeurXML; private var chargeur:ChargeurSequentiel; // barre de prchargement private var jauge:Jauge; public function Prechargeur () { jauge = new Jauge(); jauge.x = int((stage.stageWidth - jauge.width) / 2); jauge.y = int((stage.stageHeight - jauge.height) / 2); chargeur = new ChargeurSequentiel ( ApplicationDomain.currentDomain ); chargeur.addEventListener chargeur.addEventListener chargementEnCours ); chargeur.addEventListener erreurChargement ); chargeur.addEventListener erreurChargement ); addChild ( chargeur ); chargeurInitialisation = new ChargeurXML (); ( Event.COMPLETE, chargementTermine ); ( ProgressEvent.PROGRESS, ( IOErrorEvent.IO_ERROR, ( SecurityErrorEvent.SECURITY_ERROR,

13 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

chargeurInitialisation.addEventListener ( Event.COMPLETE, chargementInitTermine ); chargeurInitialisation.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); chargeurInitialisation.addEventListener ( SecurityErrorEvent.SECURITY_ERROR, erreurChargement ); var chemin:String = loaderInfo.parameters.initChemin; if ( chemin == null ) chemin = "init/"; chargeurInitialisation.charge ( new URLRequest ( chemin + INITIALISATION ) ); } private function chargementTermine ( pEvt:Event ):void { stage.removeChild ( jauge ); } private function chargementEnCours ( pEvt:ProgressEvent ):void { jauge.scaleX = pEvt.bytesLoaded / pEvt.bytesTotal; } private function chargementInitTermine ( pEvt:Event ):void { var initXML:XML = pEvt.target.donnees; cheminXML = initXML.CHEMIN_XML.@chemin; cheminImages = initXML.CHEMIN_IMAGES.@chemin; cheminSWF = initXML.CHEMIN_SWF.@chemin; cheminPolices = initXML.CHEMIN_POLICES.@chemin; chargeur.ajoute ( cheminSWF + "librairie.swf" ); chargeur.demarre(); stage.addChild ( jauge ); } private function erreurChargement ( pEvt:Event ):void { trace ( pEvt ); } } }

14 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Le SWF de prchargement charge dans un premier temps la libraire contenant les dfinitions de classes :

Figure 21-5. Prchargement des dfinitions de classes. Une fois les dfinitions charges, celles-ci pourront tre utilises par les SWF constituant lapplication.

A retenir
En chargeant la librairie partage dans le domaine dapplication en cours. Chaque SWF constituant lapplication peut extraire les dfinitions de classe ncessaires.

Utiliser les librairies partages


Au sein du SWF application.swf, nous associons la classe de document suivante :
package org.bytearray.document { import import import import flash.display.BitmapData; flash.display.Bitmap; flash.system.ApplicationDomain; org.bytearray.abstrait.ApplicationDefaut;

public class Application extends ApplicationDefaut {

15 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

private var logo:BitmapData; private var domaineEnCours:ApplicationDomain; private const NOM_LOGO:String = "Logo"; public function Application () { domaineEnCours = ApplicationDomain.currentDomain; if ( domaineEnCours.hasDefinition ( NOM_LOGO ) ) { NOM_LOGO ) ); var defLogo:Class = Class ( domaineEnCours.getDefinition ( logo = new defLogo (0,0); addChild ( new Bitmap ( logo ) ); } } } }

Nous extrayons dynamiquement la dfinition de classe Logo laide de la mthode getDefinition puis nous linstancions. Puis le SWF application.swf est charg en modifiant la classe de document du SWF de prchargement :
private function chargementInitTermine ( pEvt:Event ):void { var initXML:XML = pEvt.target.donnees; cheminXML = initXML.CHEMIN_XML.@chemin; cheminImages = initXML.CHEMIN_IMAGES.@chemin; cheminSWF = initXML.CHEMIN_SWF.@chemin; cheminPolices = initXML.CHEMIN_POLICES.@chemin; chargeur.ajoute ( cheminSWF + "librairie.swf" ); chargeur.ajoute ( cheminSWF + "application.swf" ); chargeur.demarre(); stage.addChild ( jauge ); }

En testant notre application, le SWF de prchargement charge la librairie partage puis charge le SWF application.swf comme lillustre la figure 21-6 :

16 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Figure 21-6. Utilisation de la dfinition de classe Logo. Afin de nous rendre compte de la facilit de mise jour de notre application nous ouvrons le fichier librairie.fla et modifions limage Logo dans la bibliothque. Nous exportons nouveau la librairie partage puis nous rechargeons notre site sans aucune recompilation, la figure 21-7 illustre le rsultat :

17 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Figure 21-7. Remplacement de limage travers une librairie partage. Sans recompiler notre application nous avons mis jour le logo utilis au sein du module application.swf. Bien que cette pratique ait un avantage certain en matire de poids et de mise jour, celle-ci possde nanmoins un dsavantage. Nous sommes obligs de passer par le SWF de prchargement afin de tester notre module SWF car les dfinitions de classes utilises par ce dernier sont charges par le module de prchargement principal. En testant notre application nous remarquons que la dfinition de classe Logo est correctement extraire et utilise au sein du SWF application.swf. Nous allons prsent dvelopper un menu au sein de notre module application.swf. Ce menu aura pour but de charger les autres modules portfolio.swf, infos.swf et contact.swf. Nous allons utiliser la mme technique afin de charger dynamiquement les polices de notre menu. Pour cela, nous intgrons au sein de la librairie une police au sein de la bibliothque. Nous obtenons donc deux lments dans notre librairie :
Une image bitmap reprsentant notre logo
18 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Une police utilise par notre menu

La figure 21-8 illustre la bibliothque :

Figure 21-8. Bibliothque de la librairie partage. Comme nous lavons vu lors du chapitre 16 intitul Le texte, les polices peuvent tre embarques lexcution. De la mme manire que notre image bitmap, nous allons extraire la dfinition de classe de police et ajouter celle-ci dans la liste des polices disponibles.

19 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

La figure 21-9 illustre lide :

Figure 21-9. Chargement dune police laide dune librairie partage. Nous modifions la classe Application afin dextraire la police et lajouter :
package org.bytearray.document { import import import import import import import flash.display.BitmapData; flash.display.Bitmap; flash.system.ApplicationDomain; flash.text.Font; flash.text.TextField; flash.text.TextFormat; org.bytearray.abstrait.ApplicationDefaut;

public class Application extends ApplicationDefaut { private private private private var logo:BitmapData; var police:Font; const NOM_LOGO:String = "Logo"; const NOM_POLICE:String = "PoliceMenu";

public function Application () {

20 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

if ( ApplicationDomain.currentDomain.hasDefinition ( NOM_LOGO ) ) { var defLogo:Class = Class ( ApplicationDomain.currentDomain.getDefinition ( NOM_LOGO ) ); logo = new defLogo (0,0); addChild ( new Bitmap ( logo ) ); } )) if ( ApplicationDomain.currentDomain.hasDefinition ( NOM_POLICE { var defPolice:Class = Class ( ApplicationDomain.currentDomain.getDefinition ( NOM_POLICE ) ); police = new defPolice(); Font.registerFont ( defPolice ); } } } }

Afin dutiliser la police extraite, nous crons un menu dynamique. Nous avons donc besoin de charger un fichier XML contenant les chemins daccs au XML. Pour cela, nous allons diffuser un vnement depuis le SWF de prchargement, cela va nous permettre de passer au module application.swf les chemins daccs aux fichiers. Nous dfinissons un fichier XML nomm donnees.xml :
<MENU> <BOUTON legende="Accueil" url="accueil.swf"/> <BOUTON legende="Photos" url="photos.swf"/> <BOUTON legende="Blog" url="blog.swf"/> </MENU>

Puis nous crons une classe vnementielle EvenementInfos contenant les diffrentes proprits indiquant les chemins :
package org.bytearray.evenements { import flash.events.Event; public class EvenementInfos extends Event {

21 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

public static const CHEMINS:String = "chemins"; public public public public var var var var images:String; polices:String; swf:String; xml:String;

public function EvenementInfos ( pType:String, pCheminImages:String, pCheminPolices:String, pCheminSWF:String, pCheminXML:String ) { super ( pType ); images = pCheminImages; polices = pCheminPolices; swf = pCheminSWF; xml = pCheminXML; } } }

Le SWF de prchargement doit donc indiquer au SWF principal les chemins daccs aux fichiers, pour cela nous diffusons un vnement au module application.swf une fois les fichiers chargs :
private function chargementTermine ( pEvt:Event ):void { stage.removeChild ( jauge ); pEvt.target.contentLoaderInfo.sharedEvents.dispatchEvent ( new EvenementInfos ( EvenementInfos.CHEMINS , cheminImages, cheminPolices, cheminSWF, cheminXML ) ); }

Notre SWF application.swf na plus qu couter cet vnement et enregistrer les chemins puis charger le XML afin de crer le menu :
package org.bytearray.document { import import import import import import import import import import import import import flash.display.BitmapData; flash.display.Bitmap; flash.display.Loader; flash.display.Sprite; flash.events.Event; flash.events.IOErrorEvent; flash.events.MouseEvent; flash.events.SecurityErrorEvent; flash.net.URLRequest; flash.system.ApplicationDomain; flash.text.Font; flash.text.TextFormat; org.bytearray.evenements.EvenementInfos;

22 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

import org.bytearray.xml.ChargeurXML; import org.bytearray.abstrait.ApplicationDefaut; public class Application extends ApplicationDefaut { private private private private private private var var var var var var logo:BitmapData; police:Font; donneesXML:ChargeurXML; domaineEnCours:ApplicationDomain; conteneurMenu:Sprite; chargeur:Loader;

private const NOM_LOGO:String = "Logo"; private const NOM_POLICE:String = "PoliceMenu"; private const MENU_XML:String = "donnees.xml"; private private private private var var var var cheminXML:String; cheminImages:String; cheminSWF:String; cheminPolices:String;

public function Application () { loaderInfo.sharedEvents.addEventListener( EvenementInfos.CHEMINS, initialisation ); chargeur = new Loader(); addChild ( chargeur ); conteneurMenu = new Sprite(); true ); conteneurMenu.addEventListener ( MouseEvent.CLICK, clicBouton, conteneurMenu.x = 110; conteneurMenu.y = 25; addChildAt ( conteneurMenu, 0 ); donneesXML = new ChargeurXML ( true ); ); donneesXML.addEventListener ( Event.COMPLETE, chargementTermine

donneesXML.addEventListener ( IOErrorEvent.IO_ERROR, erreurChargement ); donneesXML.addEventListener ( SecurityErrorEvent.SECURITY_ERROR, erreurChargement ); domaineEnCours = ApplicationDomain.currentDomain; if ( domaineEnCours.hasDefinition ( NOM_LOGO ) ) { NOM_LOGO ) ); var defLogo:Class = Class ( domaineEnCours.getDefinition (

23 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

logo = new defLogo (0,0); addChild ( new Bitmap ( logo ) ); } } private function initialisation ( pEvt:EvenementInfos ):void { cheminXML = pEvt.xml; cheminImages = pEvt.images; cheminSWF = pEvt.swf; cheminPolices = pEvt.polices; if ( domaineEnCours.hasDefinition ( NOM_POLICE )) { NOM_POLICE ) ); var defPolice:Class = Class ( domaineEnCours.getDefinition ( police = new defPolice(); Font.registerFont ( defPolice ); ); } } private function chargementTermine ( pEvt:Event ):void { var donnees:XML = pEvt.target.donnees; var boutonMenu:Bouton; var i:int = 0; var formatage:TextFormat = new TextFormat ( police.fontName, 8 ); for each ( var noeud:XML in donnees.* ) { boutonMenu = new Bouton(); boutonMenu.buttonMode = true; boutonMenu.mouseChildren = false; boutonMenu.lien = noeud.@url; boutonMenu.legende.embedFonts = true; boutonMenu.legende.defaultTextFormat = formatage; boutonMenu.legende.text = noeud.@legende; donneesXML.charge ( new URLRequest ( cheminXML + MENU_XML )

24 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

boutonMenu.x = (boutonMenu.width + 5) * i; conteneurMenu.addChild ( boutonMenu ); i++; } } private function clicBouton ( pEvt:MouseEvent ):void { ); } private function erreurChargement ( pEvt:Event ):void { trace(pEvt); } } } chargeur.load ( new URLRequest ( cheminSWF + pEvt.target.lien )

En utilisant un nom de police gnrique, nous pouvons ainsi remplacer plus tard la police utilise pour le menu par une autre sans crer des erreurs de logique Ainsi une police Verdana, Times ou autre pourra tre utilise de manire transparente pour dsigner la police du menu. Lorsque nous testons le module application.swf, nous ne voyons pas le menu affich. A linverse, en testant le SWF par le module principal nous obtenons le rsultat suivant :

25 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Figure 21-10. Utilisation de la police au sein du menu dynamique. La police est ainsi intgre au sein du site et charge une seule et unique fois. Nous conomisons bande passante et gagnons en facilit de mise jour. Afin de mettre jour la police, nous modifions la police prsente au sein de la bibliothque et rgnrons le fichier librairie.swf. La figure 21-11 illustre lapplication relance en ayant modifi la police prsente au sein de la librairie partage :

26 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Figure 21-11. Remplacement de la police travers la librairie partage. La police extraite est dsormais disponible au sein de tous les SWF de lapplication. En cliquant sur chacun des boutons nous chargeons de nouveaux SWF ayant recours cette police. Nous ajoutons au sein de la librairie partage une nouvelle police destine tre utilise par le contenu. Celle-ci est associe une classe PoliceContenu. La figure 21-12 illustre la bibliothque de la librairie partage :

27 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Figure 21-12. Bibliothque de la librairie partage. Chaque bouton du menu charge un SWF reprsentant une partie du site, ici encore chaque SWF va utiliser les dfinitions de polices charges le SWF de prchargement. Nous vitons ainsi dintgrer des polices au sein de chaque SWF et optimisons le poids du site.
accueil.swf.

En cliquant sur le bouton Accueil nous chargeons le SWF

Nous ajoutons au sein de ce dernier le code suivant afin de crer un champ texte et dutiliser la dfinition de police PoliceContenu :
var domaineEnCours:ApplicationDomain = ApplicationDomain.currentDomain; var policeContenu:String = "PoliceContenu"; if ( domaineEnCours.hasDefinition ( policeContenu ) ) { var defPolice:Class = Class ( domaineEnCours.getDefinition ( policeContenu ) ); var police:Font = new defPolice(); var formatage:TextFormat = new TextFormat ( police.fontName, 8 );

28 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Font.registerFont ( defPolice ); var monContenu:TextField = new TextField() addChild ( monContenu ); monContenu.x = 40; monContenu.y = 110; monContenu.embedFonts = true; monContenu.defaultTextFormat = formatage; monContenu.autoSize = TextFieldAutoSize.LEFT; monContenu.wordWrap = true; monContenu.width = 450; monContenu.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis consectetuer enim fringilla ligula. Donec facilisis scelerisque sem. Nunc lobortis ante eget turpis. Donec auctor ullamcorper diam. Phasellus sed enim non urna aliquam consectetuer. Morbi sit amet purus. Suspendisse eros leo, volutpat eu, eleifend sit amet, bibendum ac, lacus. Suspendisse elit. In egestas lorem in nisi. Integer imperdiet felis et ligula. Quisque semper dapibus pede. Mauris ac neque vel erat porttitor hendrerit. Duis justo augue, scelerisque a, rutrum nec, rutrum ut, justo. Maecenas luctus. Aenean a leo et eros blandit sollicitudin. Sed bibendum placerat ligula. Integer varius. Aliquam in felis in felis convallis laoreet. Vivamus nulla. Quisque rutrum massa id nunc." }

Nous extrayons dynamiquement la police PoliceContenu, la figure 21-13 illustre le rsultat :

Figure 21-13. Utilisation de la police au sein du texte de prsentation.


29 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Chapitre 21 Application finale version 0.1

Afin de tester notre mcanisme, nous modifions le fichier de police puis nous relanons notre site sans recompiler les autres SWF constituant lapplication. Nous obtenons le rsultat illustr par la figure suivante :

Figure 21-14. Remplacement de la police travers la librairie partage. De cette manire nous centralisons nos images ainsi que les polices par le biais de la librairie partage. La mise jour de lapplication est ainsi simplifie et les temps de chargement optimiss.

A retenir
Afin dextraire dynamiquement une classe nous utilisons la mthode getDefinition de lobjet ApplicationDomain.

Ainsi sachve notre aventure au cur de lActionScript 3. Rendezvous sur le forum ddi louvrage pour en discuter et ainsi rpondre dventuelles questions. A trs vite !

30 / 30 Thibault Imbert pratiqueactionscript3.bytearray.org

Vous aimerez peut-être aussi