Posts in Category "Flex"

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).

 

Architecting a Flex App

Web developers who take Flex for a spin may initially be confused by the user interface model.  Although a typical, servlet-style, request-response model will work in Flex, There Is A Better Way.  Thanks to the "[Binding]" tag in the Actionscript language, you can bind your view to your model data so that changes to the model are automatically reflected in the view.  The Cairngorm microarchitecture formalizes this approach, and is a great starting point for developers who want to figure out how to "make it all work together".  In this post I’ll describe how variable binding, feature-driven development, and Cairngorm all work together for NoteTag.

Here’s how a typical, non-trivial Flex app might be architected:

Domain

  • The classes that make up the domain model.  In NoteTag, this includes Notes, Tasks, and Subscriptions.  (A Subscription is a collection of related Notes or Tasks.)

Model

  • A Singleton that holds bindable instances of the domain model.  In NoteTag, the ModelLocator Singleton holds the user’s list of Subscriptions, the user’s Connections, the current Subscription, the current Note, etc.

View

  • The UI components (generally, though not always, MXML files).  The UI components that are state-dependent are bound to instance variables in the ModelLocator.  Any changes to data in the ModelLocator will cause the UI to automatically update, provided that that data is marked as "[Bindable]".  An example in NoteTag is the NoteListView, which presents the list of Notes in the current Subscription.  If the current Subscription or any of its Notes change then the NoteListView will automatically update to reflect the changes.

Controller

  • Infrastructure for implementing features as event-driven Commands.  Examples in NoteTag include GetSubscriptionCommand, GetNoteCommand, etc.

Business

  • Business logic classes perform operations on Domain objects, often making calls to remote services and returning the results asynchronously.  The SubscriptionManager class is the entry point for most of NoteTag’s business logic.

Service

  • The services layer, which holds all classes that are used to make remote service (HTTPService, RemoteService, and WebService) calls.  NoteTag uses a set of service factory classes, which decouple the configuration of specific HTTP services from the components that make calls to HTTP services.

Most application features touch on some or all of these components.  Here’s the workflow for a typical feature:

  1. The View broadcasts an event.
  2. The singleton Controller receives the event, maps it to its corresponding Command, and executes the Command.
  3. The Command delegates to the appropriate Business object to perform the business logic.
  4. The Business object performs the business logic, possibly making one or more asynchronous calls to various Services, and returns the result by dispatching a new event to the Command.
  5. The Command assigns the result to the singleton Model.
  6. Any Views that are bound to the data in the singleton Model are automatically updated.

So how would this work for a specific feature?  When the user selects a Note from the list of Notes (at the top of the screen, below), the Note is loaded from the appropriate repository (Blogger or TypePad) and displayed in the editor (at the bottom of the screen, below):

 

1. Broadcasting the Event

When the user clicks on the first Note in the list, NoteListView dispatches an event, as follows:

// NoteListView.mxml
private function getSelectedEntry(event:ListEvent) : void
{
  var selectedEntry:TagBasedEntry =
    TagBasedEntry(currentFeed.entries[event.rowIndex-1]);

  Application.application.dispatchEvent(
    new GetNoteEvent(selectedEntry.metadata,true));
}

2. Responding to the Event

Because NoteTag’s Front Controller has registered itself to listen for all Command Events, it will be notified when GetNoteEvent is dispatched:
// NoteTagController.as
public class NoteTagController extends FrontController
{
  public function NoteTagController()
  {
    addCommand(LoginEvent.EVENT_LOGIN, LoginCommand);
    addCommand(GetNoteEvent.EVENT_GET_NOTE, GetNoteCommand);
    addCommand(GetTaskEvent.EVENT_GET_TASK, GetTaskCommand);
    addCommand(PostNoteEvent.EVENT_POST_NOTE, PostNoteCommand);
    // more commands...
  }
}

Cairngorm’s FrontController provides the infrastructure for listening for events and responding to them by executing the appropriate command.

3. Executing the Command

To retrieve the Note, NoteTag needs to make a call to the blog server that stores the user’s notes.  The GetNoteCommand, which implements Cairngorm’s Command interface, takes care of this:

// GetNoteCommand.as
internal class GetNoteCommand extends ChainedCommand
{
 
public override function execute(event:CairngormEvent):void
 
{
    var initialEvent:GetNoteEvent = GetNoteEvent(event);

   
var subscriptionManager:SubscriptionManager =
     
ModelLocator.getInstance().subscriptionManager;

   
setNextEventHandler(subscriptionManager,
     
handleLoadNote,
     
LoadNoteEvent.EVENT_LOAD_NOTE,
     
onSubscriptionFault,
     
SubscriptionFaultEvent.EVENT_SUBSCRIPTION_FAULT);

    subscriptionManager.loadNote(initialEvent.metadata);
  }

  private function handleLoadNote(event:LoadNoteEvent):void
  {
    // handle result here...
  }

  // ...
}

(You may have noticed that GetNoteCommand extends ChainedCommand — this class chains asynchronous calls together using the setNextEventHandler method.  In a future post, I’ll go into greater detail on ChainedCommand, and asynchronous responders in general.)

4. Performing the Business Logic

The SubscriptionManager handles the loading of the Note by executing a series of HTTP service calls to the tag server and the blog server.  When the note has been loaded, the SubscriptionManager will dispatch a LoadNoteEvent, which will be handled by GetNoteCommand.handleLoadNote (see the next item).

5. Updating the Model

GetNoteCommand responds to the event by assigning the loaded Note to the appropriate data member on the ModelLocator:

// GetNoteCommand.as
internal class GetNoteCommand extends ChainedCommand
{
 
// ...

  private function handleLoadNote(event:LoadNoteEvent):void
  {
    ModelLocator.getInstance().currentNote = event.note;
  }

  // ...
}

6. Updating the View

Any views that are bound to the ModelLocator’s currentNote member will automatically update themselves to reflect the new data.  NoteView, the component that’s responsible for displaying the Note in an editor, is one such view:

// NoteView.mxml
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
   
xmlns:view="com.adobe.kiwi.notetag.view.*"
 
 
xmlns="*">
 
<mx:Script>
  <![CDATA[
    import com.adobe.kiwi.notetag.model.*;

    [Bindable] private var model:ModelLocator = ModelLocator.getInstance();
  ]]>
  </mx:Script>

  <view:NoteEdit id="noteEditor" width="100%" height="100%"
   
note="{model.currentNote}" />

</mx:VBox>

 

Every other feature — publishing a Note, fetching a Subscription, updating a Task — is implemented with the same Event-to-Command-to-Model-to-View approach.  Command-specific Events can be dispatched from multiple contexts, without knowing which Views will be affected.  Views can bind to Model changes without knowing where the originating Event was dispatched from.  Loose coupling leads to cleaner, more maintainable code.

AS3 language 101 for C/C++ coders

This article covers aspects of ActionScript 3 that would be helpful to C/C++ application engineers transitioning to application development in Flex and Flash.

I’ve used C/C++ through most of my educational and professional career. I’ve also done a respectable share of Javascript and Perl. ActionScript 3 could be viewed as an interesting blend of features from all of these languages. Actionscript 3 conforms to the ECMAscript 4 spec. So, it is a standard languange. In addition to the standard language syntax, AS3 contains a custom class framework for working with Flex/Flash.

The following are areas in the AS3 language that I personally found interesting.

Continue reading…