Wednesday, March 28, 2007

Flex is still Flash (adventures in preloading)

I sometimes forget that I'm still programming for Flash.

I'm very pleased with the Flex 2 framework. It's really enabled me to build some powerful applications that would have taken me a MUCH longer time in Flash. I've got all (most of) the benefits of many of the other languages+frameworks (java+swing comes to mind) and a host of others that really make application development a breeze. XML view declaration, while certainly not an original idea (I was using XUL before its MXML cousin came of age . . .) is very well executed and it's (mostly) clean tie in to ActionScript and the new component architecture has really made development a much more efficient process.

So it's sometimes really easy to forget that I'm still programming for Flash.

My first experiences with ActionScript 3 (like many others I'm sure) has been for Flex 2. It's easy to forget all about timelines. It's easy to forget about all of the hacks that we've seen (and used) to get things to do what we want in our AS2 and (and if you can remember that long ago or are unfortunate enough to work on those projects that still exist) AS1 projects. All those frame based commands. All that (shudder) timeline code. All those onEnterFrame functions buried in our MovieClips. We all did it. Hopefully we eventually learned better, but we all did it.

It's SO great to live in a development environment that has no visible concept of the timeline. And for a while, I just forgot that it existed.

But then I started creating a custom preloader. How did we used to do that? Well, we put as little as we could on frame 1, threw up some kind of animation, maybe a loading bar and when we were all done we went to frame 2 and launched our app.

Seems the same thing is happening in Flex 2.

It makes sense I suppose. But that very Flex looking loader bar was something that I just thought of as magic 'till I looked into it.

Obviously the flex framework is going to be a large chunk of what needs to be downloaded. (BTW there is talk that the framework will eventually be a download-once accompaniment to the Flash plugin which is gonna help make our .swfs quite a bit smaller!) And all of the graphics, skins, styles, etc are gonna need to be downloaded. Defining the custom loader is cake; just define the 'preloader' attribute in your application MXML. The code generated (which I think is always just a little magical when I look at it) has the SystemManager load the class you specify the the attribute. The mx.managers.SystemManager is the first piece of the framework that is loaded and is purposefully kept as lightweight as possible. Take a look at the code sometime (dig into the SDK/frameworks/source folder to get at it). It's good stuff.

And the preloader itself needs to be kept as lightweight as possible too. Just a sprite that implements IPreloaderDisplay. No flex framework goodness, just good 'ol fashioned primitives. Draw some shapes, maybe throw up some text. But ya got to keep is simple yo. Extending DownloadProgressBar is a nice and easy way to get all of the IPreloaderDisplay stuff taken care of for you though if you're not going for the absolutely slimmest loader possible.

Just a small bit of code to get you moving in the right direction if you want to do one of these yourself. . .

< project:projectApplication
project="project.*"
mx="http://www.adobe.com/2006/mxml"
preloader="project.loaders.ProjectSplash"
layout="absolute" >
. . .


Something you may notice about the above MXML is that instead of using an node, I've used something else. I really hate having more than an import and a couple lines of code in my MXML, so for anything else I'll extend and code behind. So I've got a class called ProjectApplication that extends mx.core.Application.

And here is my preloader:

package com.terralever.loaders
{
public class SplashScreen extends DownloadProgressBar
{
// VARIABLES //////////

private static var instance:SplashScreen;
private var image:Loader;

// CONSTRUCTION //////////

public function SplashScreen()
{
super();

//load the image
image = new Loader();

image.loadBytes(getSplashGraphic());
addChild(image);

image.addEventListener(Event.RENDER, onRender);

this.alpha = 0;
var fade:Fade = new Fade();
fade.alphaFrom = 0;
fade.alphaTo = 1;
fade.duration = 1000;
fade.play([this]);

}


// GETTERS and SETTERS //////////

override public function set preloader(preloader:Sprite):void
{
preloader.addEventListener(ProgressEvent.PROGRESS, SWFDownloadProgress);
preloader.addEventListener(Event.COMPLETE, SWFDownloadComplete);
preloader.addEventListener(FlexEvent.INIT_PROGRESS, FlexInitProgress);
preloader.addEventListener(FlexEvent.INIT_COMPLETE, FlexInitComplete);
}

// PUBLIC FUNCTIONS //////////

// PROTECTED FUNCTIONS //////////

/**
* This returns the default splash graphic.
* Override this method to define a new graphic.
*/
protected function getSplashGraphic():ByteArray
{
[ Embed(source="assets/splash.png", mimeType="application/octet-stream") ]
var graphicClass:Class;
return new graphicClass();
}

protected function SWFDownloadProgress( event:ProgressEvent ):void
{
//TODO
}

protected function SWFDownloadComplete( event:Event ):void
{
//TODO
}

protected function FlexInitProgress( event:FlexEvent ):void
{
//TODO
}

protected function FlexInitComplete( event:Event ):void
{
dispatchEvent(new Event(Event.COMPLETE));
}

// PRIVATE FUNCTIONS //////////

private function onRender(e:Event):void
{
this.x = this.stage.stageWidth/2 - this.width/2
this.y = this.stage.stageHeight/2 - this.height/2
}

}

All it does is throws up a graphic instead of the loader bar. But it won't be hard to get it play a dancing bear instead. ;)

-Jason Crist

1 comment:

shane d said...

first!

So where is the article on extending flex components? Just kidding, great to see some real grass roots Flash folks joining the Flex revolution. Or maybe they have been here all along and just not willing to admit that they hacked in AS1 :)