Archive for July, 2006

Loading Flex Skins at Runtime

,,

There are lots of great resources out there on how to skin your Flex applications.  Quick recap: Flex supports two approaches to skinning:  graphical and programmatic.  Graphical skinning involves creating graphical assets in Flash, Photoshop, Fireworks, etc. and embedding them in your Flex application.  Programmatic skinning involves creating an ActionScript class which defines the skin for a control.  As you might guess, graphical skinning is easier, programmatic skinning more powerful.

One drawback to both approaches is that the skinned resources (SWF/PNG/GIF/etc. for graphical skins, the AS class file for programmatic skins) must be available at compile-time for the skins to be applied.  Or do they?  In this post I’ll describe a neat trick for pulling in graphical skins at run-time (using a live demo with source code).

To make this example as simple as possible, I’m going to create a Flex app that allows the Button control to be dynamically skinned.  This app will pull down a skinning SWF at runtime, load the skins, and apply them to the Button control.  Again, to keep it simple I’m going to use the skin template file provided in NJ’s skinning article, and apply the RadioButton skins to the Button control.  (A tip of the cap to Roger Gonzalez and NJ for basically coming up with this solution.)

Step #1: Create a wrapper SWF for the skin assets

The skin assets are in the aforementioned skin template file.  I want to create a wrapper SWF that my Flex app can load at runtime and from which it can extract the appropriate assets, in this case the four symbols for the RadioButton control.  Here’s the source for wrapper SWF:

package
{
  import flash.display.Sprite;

  public class Wrapper extends Sprite
  {
    [Embed(source="flex_skins.swf",symbol="RadioButton_upIcon")]
    public var rbUpSkin: Class;
    [Embed(source="flex_skins.swf",symbol="RadioButton_downIcon")]
    public var rbDownSkin: Class;
    [Embed(source="flex_skins.swf",symbol="RadioButton_disabledIcon")]
    public var rbDisabledSkin: Class;
    [Embed(source="flex_skins.swf",symbol="RadioButton_overIcon")]
    public var rbOverSkin: Class;
  }
}

Step #2: Put the wrapper SWF on your web server

The Flex app needs to load the wrapper SWF from somewhere!

Step #3: In your Flex app, use a Loader to load the wrapper SWF

I created a utility class called ClassLoader to wrap up functionality related to loading the SWF and extracting the class.  Here are the key lines:

loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
...

request = new URLRequest(swfLib);
var context:LoaderContext = new LoaderContext();
context.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
loader.load(request, context);

Notice that the Loader loads the SWF into an ApplicationDomain that has the current domain as its parent.  This is the key to ensuring that the app can access the loaded class and its assets.

Step #4: Get the class from the loaded SWF and instantiate it

Here we load the Class using an agreed-upon className (in this case, "Wrapper"):

var wrapperClass:Class = loader.contentLoaderInfo.applicationDomain.getDefinition(className) as Class;
var wrapper:Object = new wrapperClass();

Step #5: Apply the skins using setStyle

You can apply the skins to a particular instance of Button or to all instances of Button.  Here’s the latter approach:

StyleManager.getStyleDeclaration("Button").setStyle("upSkin", wrapper.rbUpSkin);
StyleManager.getStyleDeclaration("Button").setStyle("downSkin", wrapper.rbDownSkin);
StyleManager.getStyleDeclaration("Button").setStyle("disabledSkin", wrapper.rbDisabledSkin);
StyleManager.getStyleDeclaration("Button").setStyle("overSkin", wrapper.rbOverSkin);

Step #6: Run the app

My sample app (running live here, with "View Source" enabled) displays a text field where you can enter the URL of the wrapper SWF (also live here).  Enter the wrapper SWF’s URL, click the "Apply" button, and you’ll see that the button now looks like an oversized radio button.  As you mouse over or click on the button, you’ll notice that its appearance changes to display the appropriate skin.  If you create your own wrapper SWF which exposes a different set of symbols with the same class names (rbUpSkin, etc.), you could point the sample app at it and have it load and display a different set of skins.

 

So why would you want to do this?  Dynamic skinning gives you one incredibly powerful benefit:  you can let your users put their skins on your app.  Imagine an MP3 player like Winamp built in Flex.  The developer doesn’t need to create a library of skins for the app, he/she can just expose a UI for setting the skin SWF (perhaps in the Preferences dialog) and let the user pick and choose from whatever skins the community comes up with. Equally important, the developer has fine-grained control over which controls can be skinned and which should retain their original skins (by only calling setStyle on the desired controls).  And last but not least, keeping the skins outside of the application SWF will keep the application file size from exploding as the skins proliferate (with a minor but certainly-acceptable-to-the-user performance hit when the skins are loaded).

 

Notetag in Depth

File under: ,,,,,

Brian Riggs has penned an in-depth article entitled "NoteTag: Anatomy of a Flex mashup", now available on Flex DevNet site, that unifies many of the blog entries here to describe the architecture behind Notetag.

Making HTTP calls in ActionScript 3

The lingua franca of the web is HTTP, and no programming language can claim to be a web-oriented language if it doesn’t fully support it. Custom APIs and modern protocols are increasingly being built on top of HTTP. Just as it’s rarely a good idea to invent a new XML language, it’s increasingly apparent that HTTP, and more broadly REST, is almost always the most appropriate foundation upon which to build new web APIs.

So how does ActionScript 3 stack up in the area of HTTP support? I’ll (arbitrarily) give a score to AS3’s support for the core elements of a minimal HTTP implementation.

Methods

AS3’s HTTPService class supports the basic HTTP methods (GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE), but doesn’t allow custom methods. The WebDAV protocol uses a handful of custom HTTP methods (PROPFIND, PROPPATCH, MKCOL, etc.) which HTTPService would be unable to invoke, so it may be a while before we see an AS3-based WebDAV implementation.

Score: 4/5 (Support for custom methods is missing, but few APIs use them anyhow)

Request headers

HTTPService has a headers property which can hold an arbitrary Array of name-value pairs.

Score: 5/5

Response headers

Unfortunately, there’s no way to extract the headers from an HTTP response. (If you’re interested in verifying this, just make an HTTPService call to a bogus page and set a breakpoint in the fault handler. When execution stops there, examine the FaultEvent in Flex Builder’s Variables window — there’s no evidence anywhere that the error is indeed a 404.) I had a conversation with Matt Chotin of the Flex team about this some months ago, and he indicated that not all browsers expose the headers.

This is a pretty significant limitation, especially since REST principles are motivating the designers of more recent protocols and APIs to leverage the HTTP status code vocabulary to define the semantics of method results, rather than invent new vocabularies in the form of custom responses.

Score: 1/5 (I’ll give it one point, since at least it’s possible to distinguish between successful calls and unsuccessful calls.)

Authentication

HTTPService supports authentication against a remote endpoint via the setRemoteCredentials method. Alternatively, the user’s credentials can be specified in Flex Data Server’s proxy-service.xml file.

Score: 5/5

Overall: 15/20

With the glaring exception of support for response headers, AS3’s support for HTTP is full-featured enough to implement just about any REST-style protocol or API. Let’s hope the Flex and Apollo teams can fill this one remaining hole in a future release.