Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Créer un preloader Flex personnalisé

Compatible Flex 4. Cliquer pour en savoir plus sur les compatibilités.Compatible Flash CS5. Cliquer pour en savoir plus sur les compatibilités.Par Mickael Chaize traduit par autourdeflash (Gilles Doucen - http://autourdeflash.wordpress.com/), le 30 mars 2011

Cet article est une traduction de l'article "Building a custom Flex preloader" écrit par Mickael Chaize.

Pré-requis

Pour profiter pleinement de cet article, vous aurez besoin des logiciels
  • Flash CS5
  • Flash Builder 4

Présentation

Récemment, j'en ai eu un peu assez de la barre de progression standard de Flex4 et j'ai donc décidé de créer un preloader personnalisé en m'appuyant sur les logiciels Flash CS5 et Flash builder 4.

L'exemple qui sert de base pour cet article m'a été inspiré par Seth Duffey et l'excellent tutoriel qu'il a écrit sur le sujet. C'est le seul exemple que j'ai trouvé utilisant une animation flash créée avec flash CS5 en vue d'améliorer l'expérience utilisateur.

Dans cet article, j'expliquerai comment étendre la classe de base SparkDownloadProgressBar pour réaliser son propre preloader Flex4 à partir d'une animation créée depuis le logiciel Flash Professionnel CS5.

Comprendre la manière dont le Player Flash charge une application Flex 4

J'ai trouvé que la partie la plus délicate à comprendre concernait la gestion du chargement d'une application flex4 par le Player Flash. Le Player charge d'abord le fichier contenant l'application principale (par exemple main.swf). Lorsqu'il détecte qu'il s'agit d'une application flex 4, il charge les librairies partagées (RSL) de la version de framework utilisée.

Les RSLs contenant le Framework Flex peuvent être signées ou non signées. Si vous publiez une application avec des RSLs signées, elles seront mises en cache par le Player Flash et le navigateur. Si vous effacez le cache du navigateur, elles seront toujours disponibles depuis le cache du Player. Si vous publiez une application avec des RSLs non signées, elles seront uniquement mises en cache par le navigateur et seulement réutilisables par des applications provenant du même domaine.

Opter pour des RSLs non signées peut profondément impacter l'expérience utilisateur pendant la phase de chargement de l'application Flex. J'ai publié une application basique à partir de RSLs non signées. Le Player Flash commence par charger l'application principale contenue dans le fichier fichier main.swf puis les librairies partagées (OSMF, TLF, Text Layout Framework, framework, spark, rpc data services et sparkskins). Les RSLs peuvent se charger en parallèle. Le Player peut commencer le chargement des RSLs non signées avant même la fin du chargement de l'application principale. La barre de progression standard peut alors montrer des comportements inattendus. Par exemple, de temps en temps l'état d'avancement de la barre de progression peut décroître soudainement ou la barre se mettre à clignoter.

C'est directement due à l'implémentation de la classe SparkDownloadProgressBar qui gère cette barre de progression. Dans sa version standard, la barre de progression est composée de deux barres. Une première barre formée par un rectangle gris situé en arrière plan suit la progression du chargement. Une seconde suit la progression de la phase d'initialisation. J'ai voulu étendre cette classe pour plusieurs raisons. Premièrement, parce que la barre de progression affiche plusieurs informations à la fois à savoir le chargement de l'application principale et celui des RSLs. La bonne nouvelle est que vous pouvez suivre les deux procédures séparément en surchargeant respectivement les méthodes progressHandler et rslProgressHandler et ainsi permettre à l'utilisateur de suivre ces deux phases séparément. Deuxièmement, parce que la classe SparkDownloadProgressBar n'affiche aucune information textuelle contrairement à sa version Flex3.

Avant de regarder le code de plus près, jetons un œil sur le résultat final. Videz le cache de votre navigateur et cliquez consécutivement sur ces 2 liens:

  • lien vers l'application utilisant des RSLs non signées: Le player charge l'application principale et les 6 librairies partagées.
  • Lien vers l'application avec les RSLs signées: Le player charge uniquement l'application principale et charge les RSLs directement depuis son cache réduisant considérablement les délais de chargement.

Comme vous pouvez le voir, la barre de progression standard est remplacée par le logo animé créé avec Flash CS5. Le logo blanc apparaît une fois l'application principale chargée. Son animation est contrôlée par la méthode setMainProgress. Les contours du logo apparaissent une fois les RSLs chargées. Son animation est contrôlée par la méthode setDownloadRSLProgress. Pour finir; le rectangle rouge disparaît progressivement avec un effet de fondu au cours de la phase d'initialisation gérée par la méthode setInitAppProgress. Les différentes étapes sont montrées sur la figure 1.

Figure 1. étapes de l'animation pour chacunes des phases traversées

L'animation créée sous Flash CS5 est publiée sous la forme d'un swc (librairie flash compilée) qui peut ensuite être importée directement depuis mon projet flash builder 4. Le source est disponible sous le dossier nommé FLAsource.

Fig2: logo animé dans Flash CS5

Etendre la classe SparkDownloadProgressBar

Pour utiliser cette animation, vous devez étendre la classe SparkDownloadProgressBar. Vous pouvez ensuite librement modifier les comportements standard en surchargeant les méthodes setDownloadprogress, rslProgressHandler et progressHandler. Rappelez-vous que lorsque la méthode rslProgressHandler est appelée, vous pouvez lire le nombre totale de RSLs à charger par l'application.

Pour afficher la progression moyenne, vous devez utiliser l'expression:

Math.round(i*100/t + a/t)

ou i est l'index de la RSL en cours de chargement, t est le nombre totale de RSLs et a le degré d'avancement.

Dans mon exemple, preloaderDisplay est le nom de la classe contenant le logo animé.

Fig3: Assignation de la classe PreloaderDisplay à l'animation dans Flash CS5

Voici le code de ma barre de progression personnalisée:

/*
* Class blogged on RIAgora.com
* Inspired by http://www.leavethatthingalone.com/blog/index.cfm/2009/11/11/Flex4CustomPreloader
*/
 
package com.riagora.loader
{
   import flash.events.Event;
   import flash.events.ProgressEvent;
   import flash.utils.getTimer;
 
   import mx.events.RSLEvent;
   import mx.preloaders.SparkDownloadProgressBar;
 
   public class Preloader extends SparkDownloadProgressBar
   {
 
      private var _displayStartCount:uint = 0;
      private var _initProgressCount:uint = 0;
      private var _downloadComplete:Boolean = false;
      private var _showingDisplay:Boolean = false;
      private var _startTime:int;
      private var preloaderDisplay:PreloaderDisplay;
      private var rslBaseText:String = "Loading app: ";
      private var numberRslTotal:Number = 1;
      private var numberRslCurrent:Number = 1;
 
      public function Preloader()
      {
         super();
      }
 
      /**
       *  Event listener for the FlexEvent.INIT_COMPLETE event.
       *  NOTE: This event can be commented out to stop preloader from completing during testing
       */
      override protected function initCompleteHandler(event:Event):void
      {
         dispatchEvent(new Event(Event.COMPLETE));
      }
 
      /**
       *  Creates the subcomponents of the display.
       */
      override protected function createChildren():void
      {
         if (!preloaderDisplay) {
            preloaderDisplay = new PreloaderDisplay();
 
            var startX:Number = Math.round((stageWidth - preloaderDisplay.width) / 2);
            var startY:Number = Math.round((stageHeight - preloaderDisplay.height) / 2);
 
            preloaderDisplay.x = startX;
            preloaderDisplay.y = startY;
            addChild(preloaderDisplay);
         }
      }
 
      /**
       * Event listener for the ProgressEvent.PROGRESS event event.
       * Download of the first SWF app
       **/
      override protected function progressHandler(evt:ProgressEvent):void {
         if (preloaderDisplay) {
            var progressApp:Number = Math.round((evt.bytesLoaded/evt.bytesLoaded)*100);
 
            //Main Progress displays the shape of the logo
            preloaderDisplay.setMainProgress(progressApp);
 
            setPreloaderLoadingText(rslBaseText + Math.round((evt.bytesLoaded/evt.bytesLoaded)*100).toString() + "%");
         }else{
            show();
         }
      }
 
      /**
       * Event listener for the RSLEvent.RSL_PROGRESS event.
      **/
      override protected function rslProgressHandler(evt:RSLEvent):void {
         if (evt.rslIndex && evt.rslTotal) {
 
            numberRslTotal = evt.rslTotal;
            numberRslCurrent = evt.rslIndex;
            rslBaseText = "loading RSLs (" + evt.rslIndex + " of " + evt.rslTotal + ") ";
 
            var progressRsl:Number = Math.round((evt.bytesLoaded/evt.bytesTotal)*100);
 
            preloaderDisplay.setDownloadRSLProgress(Math.round( (numberRslCurrent-1)*100/numberRslTotal + progressRsl/numberRslTotal));
 
            setPreloaderLoadingText(rslBaseText + Math.round((evt.bytesLoaded/evt.bytesTotal)*100).toString() + "%");
         }
      }
 
      /**
       *  indicate download progress.
       */
      override protected function setDownloadProgress(completed:Number, total:Number):void {
         if (preloaderDisplay) {
            //useless class in my case. I manage the display changes directly in the Progress handlers
         }
      }
 
      /**
       *  Updates the inner portion of the download progress bar to
       *  indicate initialization progress.
       */
      override protected function setInitProgress(completed:Number, total:Number):void {
         if (preloaderDisplay) {
            //set the initialization progress : red square fades out
            preloaderDisplay.setInitAppProgress(Math.round((completed/total)*100));
 
            //set loading text
            if (completed > total) {
               setPreloaderLoadingText("ready for action");
            } else {
               setPreloaderLoadingText("initializing " + completed + " of " + total);
            }
         }
      } 
 
      /**
       *  Event listener for the FlexEvent.INIT_PROGRESS event.
       *  This implementation updates the progress bar
       *  each time the event is dispatched.
       */
      override protected function initProgressHandler(event:Event):void {
         var elapsedTime:int = getTimer() - _startTime;
         _initProgressCount++;
 
         if (!_showingDisplay &&   showDisplayForInit(elapsedTime, _initProgressCount)) {
            _displayStartCount = _initProgressCount;
            show();
            // If we are showing the progress for the first time here, we need to call setDownloadProgress() once to set the progress bar background.
            setDownloadProgress(100, 100);
         }
 
         if (_showingDisplay) {
            // if show() did not actually show because of SWFObject bug then we may need to set the download bar background here
            if (!_downloadComplete) {
               setDownloadProgress(100, 100);
            }
            setInitProgress(_initProgressCount, initProgressTotal);
         }
      }
 
      private function show():void
      {
         // swfobject reports 0 sometimes at startup
         // if we get zero, wait and try on next attempt
         if (stageWidth == 0 && stageHeight == 0)
         {
            try
            {
               stageWidth = stage.stageWidth;
               stageHeight = stage.stageHeight
            }
            catch (e:Error)
            {
               stageWidth = loaderInfo.width;
               stageHeight = loaderInfo.height;
            }
            if (stageWidth == 0 && stageHeight == 0)
               return;
         }
 
         _showingDisplay = true;
         createChildren();
      }
 
      private function setPreloaderLoadingText(value:String):void {
         //set the text display in the flash SWC
         preloaderDisplay.loading_txt.text = value;
      }
 
   }
}

Pour utiliser ce preloader dans votre application, assigner la classe à la propriété preloader de l'application principale comme ci-dessous:

<s:Application preloader=”com.riagora.loader.Preloader>.

Ou aller à partir d'ici

Si vous souhaitez regarder le code source d'un peu plus près, téléchargez le zip du projet ici ou directement depuis l'article original.

Depuis Flex Builder, sélectionnez File > Import > Flash Builder Project pour importer le projet dans l'environnement de travail.

Une vidéo est disponible sur mon blog ou je reprends le contenu de cet article.

En savoir plus