Overview
Adobe have signficantly changed the skinning system in Flash Builder 4 to give the developer greater abilities to create custom skins for existing and custom components without having to dive into Flash or any other graphics application.
In Flex 3, to create a panel container with rounded corners and a gradient background involved Flash, 9-slice scaling, SWC exports and some ugly CSS work. Now we can draw all this natively in MXML with minimal effort. It also uses Vector drawing so one size fits all.
The core of this new skinning system lies in the Spark components. Each component allows you to specify a skin file using the skinClass style attribute. In this tutorial we'll create a custom skin for the SkinnableContainer component.
The source files to the project can be downloaded via the link at the bottom of this post.
SkinnableContainer
This is the base class for all Spark container components. A container is basically a normal component that has an area that the developer can use to add sub-components such as buttons, labels etc. The nearest thing in Flex 3 is the Canvas, which no longer exists in Flex 4.
The default skin for SkinnableContainer is minimal - in fact it only contains the content area required for adding subcomponents. We'll start by adding a SkinnableContainer to our test application by either dragging it over from the Layout section of the components tree or typing the MXML tag directly in Flash Builder:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768"> <s:SkinnableContainer x="10" y="10" width="300" height="250" /> </s:Application>
If you run this app now you'll be presented with a totally blank screen because the default skin has no graphical elements at all.
Creating the skin class & hooking it up to SkinnableContainer
Now we need to create our skin MXML class. Create a new folder named "skins" and add a new MXML component in there called SimplePanelSkin.mxml. Make sure that the new component is based on spark.components.supportClasses.Skin:
We now need to tell our SkinnableContainer in our application file to point to this skin. To do this we simply add the skinClass attribute:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768"> <s:SkinnableContainer x="10" y="10" width="300" height="250" skinClass="skins.SimplePanelSkin"/> </s:Application>
Adding the contentGroup
If you run the app now, you'll receive an error stating that required skin part "contentGroup" can not be found. If we look at the documentation for SkinnableContainer (http://livedocs.adobe.com/flex/gumbo/langref/spark/components/SkinnableC...), we'll see that contentGroup is a required skin part. This is easily added to our SimplePanelSkin.mxml file:
<?xml version="1.0" encoding="utf-8"?> <s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" width="400" height="300"> <!-- Content area --> <s:Group id="contentGroup" left="0" right="0" top="0" bottom="0"> <s:layout> <s:BasicLayout/> </s:layout> </s:Group> </s:Skin>
Note the id given to the Group element is "contentGroup" - the Spark skinning system looks for this so it knows where to add sub-components. We also specify it should use the BasicLayout layout system. This allows the developer to drop sub components at any absolute x,y position. We could alternatively use Tile, Horizontal or Vertical layout types if we wish to automate the positioning of sub components.
Defining the states
Run the app now and you'll receive a new error stating that "normal" is an undefined state. Another look at the documentation shows us that there are two states for a SkinnableContainer - normal and disabled. Whilst we don't specifically need to provide different skins for each state, we do need to define the states in our skin file. This is easily done by adding the state tag into SimplePanelSkin.mxml:
<s:states> <s:State name="normal" /> <s:State name="disabled" /> </s:states>
If we run the app now we get... nothing. This is expected as we have yet to add any graphical elements to our skin. However, the good news is that we get no errors, so we know that the states and required skin parts have been setup correctly.
Adding graphical elements
OK, now onto the fun stuff. Let's add a grey rectangle around the component with rounded corners. Make sure this code appears above the contentGroup:
<!-- Background --> <s:Rect left="0" right="0" top="0" bottom="0" radiusX="5" radiusY="5" > <s:stroke> <s:SolidColorStroke color="#555555" /> </s:stroke> </s:Rect>
Run the app, and behold we have a border! Now let's add a grey gradient inside the border:
<!-- Background --> <s:Rect left="0" right="0" top="0" bottom="0" radiusX="5" radiusY="5" > <s:stroke> <s:SolidColorStroke color="#555555" /> </s:stroke> <s:fill> <s:LinearGradient rotation="90" > <s:GradientEntry color="0xeeeeee" /> <s:GradientEntry color="0xfdfdfd" /> </s:LinearGradient> </s:fill> </s:Rect>
And a nice drop shadow underneath the contentGroup:
<!-- Drop shadow --> <s:filters> <s:DropShadowFilter blurX="4" blurY="4" alpha="0.32" distance="3" angle="45" knockout="false" /> </s:filters>
Pretty easy, huh? Hopefully you'll now have something that looks like this:
Intellisense will give you hints as to what you can use within a Spark skin. Basically you have the ability to draw primitive shapes with edges, fills and filters as well as the majority of Spark components such as Images, Buttons, SimpleText areas etc. Go for it, throw some stuff in and enjoy!
Adding sub components
Remember a while back we created a contentGroup? As you know, this is used for adding sub components. We can do this as we do with any other Flex component. Here I'll add a button and a label to our main application file:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768"> <s:SkinnableContainer x="10" y="10" width="300" height="250" skinClass="skins.SimplePanelSkin"> <mx:Label y="70" text="This is a label" horizontalCenter="0"/> <s:Button y="99" label="Clicky" horizontalCenter="0"/> </s:SkinnableContainer> </s:Application>
This will produce the following:
Conclusion
Here I've shown you how to create a basic MXML skin file for a SkinnableContainer. As you can see there's very little to it. In the following parts of this tutorial we'll cover states, CSS and finally create a custom Spark component.
| Attachment | Size |
|---|---|
| SimplePanelDemo.zip | 19.82 KB |



Comments
application height
Sir,
I have defined 5 states for an application. I am unable to change the height of the application for each state. I have tried like this
State1.height="908" State2.height="1200"
But its not changing for state 2.. its remaining constant...
Super..
Nice one, thanks a lot, just helped me a lot with a app that I am working on..
Thanks
Very informative post......and easy to follow. Thanks :)
Advanced Flashbuilder Custom Components
I recently posted on how to create default skins for your components when shipping with swc files thought that you might like to know
http://www.betadesigns.co.uk/Blog/2010/05/14/default-skin-for-custom-fla...
Good explanation Thank
Good explanation
Thank
Awesome...thanks
Explained so simply. All the adobe docs fail to accomplish what you did so easily.
Thanks
Thank you Matt.
I managed to bang out a skinned component in a few minutes thanks to this (and a little experience with Degrafa).
Cheers,
Geoffrey
Nice and clean
Thanks matt, well explained.
Do the height and width attributes ever play any role in the skin or are they just noise?
That's a really great
That's a really great question! From what I can see they don't do anything at all. Under the hood, the skinning code just pulls the bits out of the class it needs, and I don't think the dimensions make any difference.
Wonder why they're even there in the Skin class? Maybe this will get cleared up by Adobe as we near release (in early 2010, boo!)
thx
awsome, lookforward to more from you