Creating programmatic skins (Part I)

During and after my Creating a Visual Experience with Flex 2 presentation with Juan Sanchez, I received a ton of great feedback and great questions. Several people expressed interest in learning more about programmatic skinning. So, my intention here is to create a small series of posts to dive deeper into that topic.

As a quick recap on skinning, paraphrased from the Flex documentation: The term “skinning” refers to the process of changing a component’s appearance by changing or replacing its visual elements. In contrast, “styling” is the process of changing a component’s appearance by editing a predefined set of attributes. This gets a little messy because you actually specify skins using styles. But, the point here is that you can only get so far with styling. If you want a truly unique-looking Flex app, you’re going to have to do some skinning.

There are two methods of skinning: graphical and programmatic. With graphical skinning you embed pre-made artwork from a program like Fireworks or Flash, whereas with programmatic you write a class to dynamically draw artwork. Programmatic skinning is not unlike custom component development (on a small scale).

Understanding how skins are applied to Flex components.

To really understand skinning, graphical or programmatic, it is useful to know how skins are created and displayed for the standard Flex 2 components. This speaks really loudly to a point that Doug McCune made in his presentation, and that is READ THE FRAMEWORK CODE! This will be the topic of this week’s post as we look under the hood of a couple of common components.

Containers and Controls

Generally speaking, Flex components fall into two categories: containers and controls. Containers have a skin to represent the border; controls have a series of skins to represent various phases (up, over, down, etc). Let’s have a look at what’s going on inside of mx.core.Container – the base class of Panel, Canvas, VBox, HBox, etc; and inside of mx.controls.Button.

Container

This method is called when a Container instantiates or when a border-related style (such as borderSkin, borderStyle, etc) changes:

mx.core.Container [4033]

protected function createBorder():void
{
if (!border && isBorderNeeded())
{
var borderClass:Class = getStyle("borderSkin");

border = new borderClass();
border.name = "border";

...

// Add the border behind all the children.
 rawChildren.addChildAt(DisplayObject(border), 0);
...
}
}

Basically it is just creating an instance of a class (whatever you set for the borderSkin style-attribute), and adding it behind everything else in the component. Later, in updateDisplayList() the border’s size is set to the component’s overall size.

Button

Button introduces the notion of a “phase”. A phase is either up, down, over, or disabled; or a selected variation (e.g., selectedOver). Current phase is determined by the value of the selected and enabled properties as well as the user’s interaction with the control. A change in phase will trigger the following method:

mx.controls.Button [1488]

/**
* @private
* Displays one of the several possible skins,
* depending on the skinName and creating
* it if it doesn't already exist.
*/
mx_internal function viewSkinForPhase(skinName:String):void

{
// Has this skin already been created?
 var newSkin:IFlexDisplayObject =
IFlexDisplayObject(getChildByName(skinName));

// If not, create it.
 if (!newSkin)
{
var newSkinClass:Class = Class(getStyle(skinName));

if (newSkinClass)
{
newSkin = IFlexDisplayObject(new newSkinClass());

newSkin.name = skinName;

...

addChild(DisplayObject(newSkin));

newSkin.setActualSize(unscaledWidth, unscaledHeight)
...
}
}

// Hide the old skin.
 if (currentSkin)

currentSkin.visible = false;

// Keep track of which skin is current.
 currentSkin = newSkin;

// Show the new skin.
 if (currentSkin)
currentSkin.visible = true;
...
}

Again, an instance of a class is created and then added as a child. This method sizes the skin to match the component’s size, and later on in a method called layoutContents, the various elements (skin, label, icon) are stacked in the proper z-order.

mx.controls.Button [1947]

...
if (currentSkin)
setChildIndex(DisplayObject(currentSkin), numChildren - 1);

if (currentIcon)
setChildIndex(DisplayObject(currentIcon), numChildren - 1);

if (textField)
setChildIndex(textField, numChildren - 1);
...

Conclusion

In a nutshell, that’s basically how skins are applied to the out-of-the-box Flex components. Each component may vary slightly, but the pattern is the same:

  • Create an instance of a class, specified via CSS (either a class or a embedded image)
  • Add the instances as a child of the compoent being skinned
  • Make sure the instance is behind everythign else in the component

As I mentioned earlier, programmatic skinning is kind of like writing custom components. It’s useful to understand the UIComponent lifecycle, including initialization, invalidation, and measurement. Describing that in detail is out of scope for this post, but we’ll see how skins can affect measurement in the next post. Thanks for reading. Check back soon.

This entry was posted in Flex, Skinning. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

2 Comments

  1. Posted August 29, 2007 at 3:42 pm | Permalink

    Andy,

    I saw you out at 360 flex conference and found your seminar quit useful. I am still fairly new to this Flex thing, but i love. I was hoping you could answer me a quick question:

    I am creating a simple mxml component, to use with the eBay API. The component is a representation of an ebay item’s shipping information. Into the mxml component (which extends the mx:Panel), i am passing an eBay API type ‘SimpleItemType’; here is the code for the component (imports removed):

    Pricing Information

    Alright,
    so the problem is that when compiled, the label (’Test2′) which is bound to the myItem var does display the correct information. The problem is that in the onInit() function, the reference to the myItem var does not display the correct value. It is reset! How can i fix this? thank you for your help!!!

    Mike Gearhardt
    Mister Money Holdings
    michael.gearhardt@mistermoney.com

  2. Posted September 10, 2007 at 8:25 pm | Permalink

    Great post Andy, very helpful

One Trackback

  1. [...] McIntosh has written a great overview & tutorial on programmtic skinning in Flex. It was exactly the type of information I was searching for as I attempted to create my first [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">