Home >
One of the new features in Flex 4 is Spark skins. The spark.skins package is part of the Spark namespace and, as noted in Adobe's Flex 4 LiveDocs, "Custom Spark skins are MXML files that define the logic, graphic elements, and other objects that make up a skin for a Spark component." So what does that mean to you? I hope to get you started in learning the answer to that question in this post.
Please note:
This example was developed using the Flex 4 SDK nightly build 4.0.0.8702 which can be found here.
While I was thinking about skinning something like a Spark DropDownList for this example, I decided I wanted to go with a component that wasn't quite as deep so I've chosen the Spark Button instead. The Button is simple enough that it can be covered quickly, but still interactive so we can see the results of user interaction. With that said, let's get started.
The first thing you'll need to do is create a new MXML component which is based on spark.components.supportClasses.Skin and name it myButtonSkin. I'm creating mine in my com.fincanon.skins package, but you can obviously create yours in your appropriate package. Also, as a personal preference, I remove the default width and height of 400x300. We'll leave the Layout choice set to the default of spark.layouts.BasicLayout.
The resulting skin "shell" simply establishes the ff:, s:, and mx: namespaces and sets the layout to BasicLayout. For more information on these namespaces, read Differences between Flex 3 and Flex 4 beta on Adobe Developer Connection.
Your new Spark skin shell:
<?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">
<s:layout>
<s:BasicLayout/>
</s:layout>
</s:Skin>
Now that we have an our empty shell, we need to start filling it in by defining the host component and the states using the <fx:Metadata> tag and the <s:states> tag respectively. When you define a host component, you have access to all of the public properties and styles that are a part of that host component from within your skin. Since we're skinning a Button here, it should come as no surprise that the host component needs to be... yep, you guessed it... a Button.
Define the host component and skin states:
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<s:states>
<mx:State name="up"/>
<mx:State name="down"/>
<mx:State name="over"/>
<mx:State name="disabled"/>
</s:states>
Now let's start adding something visual to our skin. How about if our button has a drop shadow? And how about if that drop shadow is altered depending on the current state? We'll change the alpha, the distance, the blurX and the blurY in the over state while we change the entire drop shadow to be an inner shadow in the down state. As you can see in the following code, you can change the properties from state to state by simply adding the state name to the property in question. For example, the alpha property of our drop shadow is set to 0.5 for all states. However, by adding alpha.over="0.3", we've changed that property in that individual state.
Adding the drop shadow:
<s:filters>
<s:DropShadowFilter quality="3"
alpha="0.5" alpha.over="0.3"
distance="5" distance.over="15"
blurX.over="15" blurY.over="15"
inner.down="true"/>
</s:filters>
Next up, let's fill in the background of the skin. I'm going with a gradient green background with slightly rounded corners, but feel free to play around with this. To create the background, we'll use the Spark Rect component with a Spark fill and a Halo LinearGradient. That's right, we're mixing and matching between the mx: namespace (Halo) and the s: namespace (Spark). Why? Because we can.
Use a Spark Rect for the background:
<s:Rect left="0" right="0" top="0" bottom="0" radiusX="5" radiusY="5">
<s:fill>
<mx:LinearGradient rotation="90">
<mx:entries>
<mx:GradientEntry color="#0a5c00" ratio="0.00"/>
<mx:GradientEntry color="#84a381" ratio="0.40" ratio.over="0.25"/>
<mx:GradientEntry color="#0a5c00" ratio="0.80"/>
</mx:entries>
</mx:LinearGradient>
</s:fill>
</s:Rect>
So let's take a quick look at the code for our background, shall we? The first thing you'll see is that the left, right, top, and bottom styles are all set to zero. This tells the background Rect that it needs to stretch to all four sides of the Button. The next items in the Rect are the radiusX and radiusY (you may have previously known these two items as cornerRadius). I've set them both to 5 here, but at the end of this example, I'll show you a couple of different things you can do with them.
Within the Rect is the Spark fill which we've fitted with the Halo LinearGradient. In order to get the colors of the gradient to go the direction we want, let's set the rotation property to 90 (the default of 0 runs left to right). Inside the LinearGradient is the entries property which is an array of GradientEntry classes. The color property of GradientEntry is the color which will be set at that point in the LinearGradient and the ratio property is a percentage (from 0.0 to 0.1) at which the transition to this color will start. You'll notice that we've treated the middle GradientEntry just as we did the alpha property of the drop shadow by changing its ratio in the over state (ratio.over="0.25").
Last but not least, let's add the label to our Button. This step is just about straight forward as it gets because all we need to do is drop in a Spark SimpleText control and set a couple of properties and styles. The only piece of this that really needs to match this example is the id and it must be "labelDisplay" because that's the name that the Spark Button will look for when it tries to locate its label. Other properties and styles I've used in this example are to set the color and location of the label and the padding around it. To make the button wider or taller, change the left, right, top, and bottom styles.
Add the label:
<s:SimpleText id="labelDisplay" color="#ffffff"
horizontalCenter="0" verticalCenter="0"
left="10" right="10" top="5" bottom="5"/>
Now to skin a Button in your app you simply need to point that button's skinClass to your custom skin.
Define the skinClass for your Button:
<s:Button label="Spark Skins!" skinClass="com.fincanon.skins.myButtonSkin"/>
And finally, let's take a look at all of this code together as well as the resulting Button (below the code).
<?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">
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<s:states>
<mx:State name="up"/>
<mx:State name="down"/>
<mx:State name="over"/>
<mx:State name="disabled"/>
</s:states>
<s:filters>
<s:DropShadowFilter quality="3"
alpha="0.5" alpha.over="0.3"
distance="5" distance.over="15"
blurX.over="15" blurY.over="15"
inner.down="true"/>
</s:filters>
<s:Rect left="0" right="0" top="0" bottom="0" radiusX="5" radiusY="5">
<s:fill>
<mx:LinearGradient rotation="90">
<mx:entries>
<mx:GradientEntry color="#0a5c00" ratio="0.00"/>
<mx:GradientEntry color="#84a381" ratio="0.40" ratio.over="0.25"/>
<mx:GradientEntry color="#0a5c00" ratio="0.80"/>
</mx:entries>
</mx:LinearGradient>
</s:fill>
</s:Rect>
<s:SimpleText id="labelDisplay" color="#ffffff"
horizontalCenter="0" verticalCenter="0"
left="10" right="10" top="5" bottom="5"/>
<s:layout>
<s:BasicLayout/>
</s:layout>
</s:Skin>
Your newly skinned Button:
So now, as I mentioned earlier, let's play around with the radiusX and radiusY real quick. First, let's see what happens when we make them both negative numbers:
Change the radiusX and radiusY to -10
<s:Rect left="0" right="0" top="0" bottom="0" radiusX="-10" radiusY="-10">
<s:fill>
<mx:LinearGradient rotation="90">
<mx:entries>
<mx:GradientEntry color="#0a5c00" ratio="0.00"/>
<mx:GradientEntry color="#84a381" ratio="0.40" ratio.over="0.25"/>
<mx:GradientEntry color="#0a5c00" ratio="0.80"/>
</mx:entries>
</mx:LinearGradient>
</s:fill>
</s:Rect>
Negative radiusX and radiusY:
Next, let's make radiusX positive and radiusY negative:
<s:Rect left="0" right="0" top="0" bottom="0" radiusX="20" radiusY="-10">
<s:fill>
<mx:LinearGradient rotation="90">
<mx:entries>
<mx:GradientEntry color="#0a5c00" ratio="0.00"/>
<mx:GradientEntry color="#84a381" ratio="0.40" ratio.over="0.25"/>
<mx:GradientEntry color="#0a5c00" ratio="0.80"/>
</mx:entries>
</mx:LinearGradient>
</s:fill>
</s:Rect>
Positive radiusX and negative radiusY:
And finally, let's switch them around so we have a negative radiusX and a positive radiusY:
<s:Rect left="0" right="0" top="0" bottom="0" radiusX="-10" radiusY="20">
<s:fill>
<mx:LinearGradient rotation="90">
<mx:entries>
<mx:GradientEntry color="#0a5c00" ratio="0.00"/>
<mx:GradientEntry color="#84a381" ratio="0.40" ratio.over="0.25"/>
<mx:GradientEntry color="#0a5c00" ratio="0.80"/>
</mx:entries>
</mx:LinearGradient>
</s:fill>
</s:Rect>
Negative radiusX and positive radiusY:
So that's it for this posting. I encourage you to get in there and play around with all kinds of different Spark skinning in Flex 4. Experiment and have a little fun with it so you'll know what to expect when you use it on a project. I plan to write a follow up post about adding transitions to your skins, but in the mean time you can check it out here in the Adobe LiveDocs.





Facebook Application Development
It might seem sexy for a programmer to do all this fancy stuff by code, but why should I write 43 lines of code when I can just use the illustrator file that the designer sent me?
nice article, thanks for sharing ...
i dont get the same result with the flex4 sdk (build 8702) the text doesn't display and the button's with is approx. 20 pixels ... I also had to change mx:entries to s:entries any ideas?
Ok, I solved my problem, seems that to display the text on the button like it should, the simpletext instance's id must be set to "labelDisplay" and it works just as expected
Thanks for the comments guys.
julien, glad you got the instance id and I'll double check on the issue with the entries namespace.
kolt, you make a very good point that also speaks to the fact that there is more than one way to do almost anything, but remember that this is just a simple example. Once you start getting into skinning components and controls that are more deep in their construction than a Button, the Illustrator file will only get you so far. You also have to consider things that the Illustrator file won't be able to address like state transitions in your skins.
While Spark skinning is a bit of an extra step, it is one in which we gain a LOT more control over the look and feel of our components, controls and apps in general.
julien,
Thanks again for catching the labelDisplay instance id. It turns out I hadn't updated my SDK. Such are the perils of developing with Beta, right? ;)
I've updated the post and the code above. I've also made note of the build number at the top of the post. As for changing the namespace for the entries (mx:entries to s:entries), I get an error that says, "Could not resolve s:entries to a component implementation." but it still works fine when I use mx:entries.
Hi Jason,
Thanks for the reply, and the quick update. I agree with you about the "risks" of developing with Beta, but, as you mentionned the "skinning" features in gumbo are really awesome ... And as a developer I agree that these new "skinning" possibilities are a huge step ahead, I'm really happy about all these new gumbo features ...
The cool thing is since we have "access" to the source, it's still possible to figure out those little changes (like the label's id) ... Thanks again for the article.
Awesome article. I like show you show how to tweak the given example more then once, instead of just giving one example.
Hi Jason,
Did you ever found any good way to reuse skin code?
For instance, I wanna a icon button skin, instead of creating a button from scratch I wish to extends default ButtonSkin and just add my icon.
Basically is the problem discussed here:
http://www.flexjunk.com/2009/08/03/flex-4-skinning-ignores-developer-needs/
VELO
Nathan,
Thanks! I like to play around with things like this to see the different outcomes. I think playing around and experimenting to get unexpected results is the best way to get to a point where you can easily get expected results.
Velo,
I actually skimmed over that article on flexjunk the other day, but I haven't taken any time to actually dig into it. It looks like there are several new comments so I'll catch up on it as soon as I can.
why can not show the label font ?
Hi coco,
You should be able to alter the font of the label by using the "fontFamily" style in the labelDisplay. For example:
fontFamily="Arial"
or
fontFamily=""Times New Roman"
Hi Jason. Thanks for the post. Just curious, if you are using one of the stable SDK builds, how does the button set to the simpleText in the skin. labelDisplay is not working for 4.0.0.6898 and I am having a hard time finding any documentation on how to proceed. As you said, these are the perils of using something in Beta. I'd rather not use a nightly build that isn't necessarily stable even for experimentation.
Nevermind, I figured it out, it's labelElement instead of labelDisplay