by Michaël Chaize

Michaël Chaize

Table of contents

Created

31 January 2011

Requirements

   
Prerequisite knowledge
Some knowledge of ActionScript is needed to understand the example source code. Knowledge of Flash Builder and Flash CS5 will also be helpful.
 
User level
Intermediate
 
Required 
Flash Builder 4 (Download trial)
Sample files
Recently I became bored with the standard Flex 4 progress bars and decided to create a custom Flex 4 preloader using Flash Professional CS5 and Flash Builder 4.
 
The example that serves as the basis for this article was inspired by Seth Duffey and the excellent tutorial he wrote on this topic.  His was the only example I found that uses an animation created in Flash to customize the user experience.
 
In this article I’ll explain how to extend the classic SparkDownloadProgressBar class to create a custom Flex 4 preloader using a custom animation created in Flash Professional  CS5.
 

 
Understanding how Flash Player loads Flex 4 applications

I found that the trickiest part of this effort was correctly understanding the behavior of Flash Player when it is loading a Flex 4 application. First, Flash Player loads the SWF application file for example, main.swf. When it discovers that the SWF is a Flex 4 application, it needs to download the Runtime Shared Libraries RSLs of the appropriate framework.
 
Framework RSLs come in two versions: signed and unsigned. If you publish an application with signed RSLs, then they can be cached in a special Flash Player cache and in the browser cache. If you clear the browser’s cache, then the RSLs still persist in the special Flash Player cache. If you publish a Flex 4 application with unsigned framework RSLs, then they are cached only in the browser cache and can only be used by applications coming from the same domain.
 
Using unsigned RSLs instead of signed RSLs can dramatically change the end users’ experience while loading your Flex 4 application. I’ve developed a classic Flex 4 sample and published it with unsigned RSLs. Flash Player starts loading the main.swf, and then it loads the RSLs: OSMF, TLF Text Layout Framework, framework, Spark, rpc data services, and sparkskins. The RSLs can be downloaded in parallel. Flash Player can start downloading the unsigned RSLs even if the download of the main application is not complete. As a result the progress bars of the classic Flex 4 preloader might display unexpected behaviors. For example, sometimes the progress bar advances and then goes back or flickers.
 
The default implementation of the SparkDownloadProgressBar uses two tracks. The first track a grey rectangle in the background of the progress bar shows the download progress. The second one shows the initialization progress. I wanted to extend this class for several reasons. First, the download progress track displays mixed information; it shows the download progress of the main application as well as the RSL download progress. The good news is that you can override two separate progress handlers progressHandler for the main app and rslProgressHandler for the RSLs so that you users can see and understand these two steps separately. Second, the new SparkDownloadProgressBar doesn’t display any text information although the Flex 3 version did.
 
Before exploring the code, take a look at the final result. Clear your browser’s cache and click on the following links in order:
 
As you can see, the application does not using the classic progress bar. Instead it uses a custom animation I created in Flash Professional CS5. The white logo appears when the main application is loaded. The ActionScript  function that controls the white logo is setMainProgress. The function setDownloadRSLProgress controls the stroke of the logo plus a glow effect while the RSLs are loaded. Finally, setInitAppProgress is used to fade out the red square during the initialization step see Figure 1.
 
After I created the Flash Professional CS5 animation I published it to obtain a SWC component, which can be imported into a Flash Builder 4 project. The FLA file is located in the FLAsource folder of the project.
 

 
Extending SparkDownloadProgressBar

To use the download progress animation, you need to extend the SparkDownloadProgressBar class. Then you’re free to override the default behavior of methods such as setDownloadprogress, rslProgressHandler, and progressHandler. When the rslProgressHandler method is called, you can get the total number of RSLs to load for your application. To display the global download progress of the RSLs, you can use this function: Math.roundi*100/t + a/t where i is the current index of the loaded RSL, t is the total number of RSLs, and a is the progress download status of the RSL. In the example below, preloaderDisplay is the name of the Flash component.
 
Here is the code for the custom SparkDownloadProgressBar component:
 
/* * 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 <code>FlexEvent.INIT_COMPLETE</code> 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 <code>ProgressEvent.PROGRESS event</code> 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 <code>RSLEvent.RSL_PROGRESS</code> 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 <code>FlexEvent.INIT_PROGRESS</code> 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; } } }
To use this preloader class in your main application, just use the preloader property of the main application tag; for example:
 
<s:Application preloader=”com.riagora.loader.Preloader”>

 
Where to go from here

If you want to explore the source code of the sample application, download the sample ZIP file for this article, start Flash Builder, and then choose File > Import > Flash Builder Project to import the project.
 
You can also watch this video in which I summarize how I built the custom Flex 4 preloader for this article and show a demonstration of it running.