23 Jun in 4, builder, flash, flex, gumbo, skinning, tutorial

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:

part1_newcomp.jpg

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:

part1_skinned.jpg

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:

part1_subcomps.jpg

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.

AttachmentSize
SimplePanelDemo.zip19.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

All content © 2009-2011 Matthew Butt. All views expressed herein are my own and do not represent the views of my employer, AREA203 Digital.