Some major changes have been implemented with the introduction of Flex 4. The new framework introduces new methods for skinning, states, layouts and declaring non-visual elements in MXML. There's also a new component set called Spark, which will change the way you think about developing modular components. For this article, we will be designing one of the simplest Flex components: a Button. Through this, I'll be implementing some of the major changes so that you can see how it's applied.
Beginning the Button
To start, pop open Flash Builder 4 and create a new Application, making sure you're using the new Flex 4.0 framework. I named my project SparkButtonDemo. Once the project has been created, you'll be presented with your Application's MXML file. You may already notice some differences; Your main Application tag is no longer mx:Application, but s:Application, and there is a new child tag called fx:Declarations. The simple change in namespace for the Application tag is to indicate that we're no longer using the mx component set, while the fx:Declarations tag is used for any non-visual children (data providers, FileReference objects, services...anything you cannot see).
To start creating our new Button, let's create a new MXML Component called SparkButton. The component should extend spark.components.Button.
Skinning the Button
The new Spark component set was designed from the ground up to make skinning components easier and more modular. To start skinning your button, you must create another MXML Component that extends from the spark Skin component (spark.skins.SparkSkin). Once this class is created, you can start adding some skin elements to it. The first thing that needs to be added to your skin is a metadata declaration describing the type of component we are skinning. This looks a little something like this:
View CodeXML | |
<fx:Metadata> [HostComponent("spark.components.Button")] </fx:Metadata> | |
After adding this code, you will see 4 errors pop up in your Problems window. Flash Builder is trying to tell you that you haven't defined the required states for a Button. So let's add 4 states: up, over, down, and disabled. You should have something that looks like this so far:
View CodeXML | |
<?xml version="1.0" encoding="utf-8"?> <s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Metadata> [HostComponent("spark.components.Button")] </fx:Metadata> <s:states> <s:State name="up"/> <s:State name="over"/> <s:State name="down"/> <s:State name="disabled"/> </s:states> </s:SparkSkin> | |
Next, we will add some graphics to our Button. For this example we're going to keep it simple, and add a rectangle with a 2 pixel border, and a label control for the button's text. First, let's add the rectangle:
View CodeXML | |
<s:Rect width="100%" height="100%"> <s:stroke> <s:SolidColorStroke color="0x000000" weight="2"/> </s:stroke> <s:fill> <s:SolidColor color="0xCCCCCC"/> </s:fill> </s:Rect> | |
Graphics are defined using the new FXG format. It is similar to SVG, in that it is defined entirely in XML. This new format is what allows the interchange between Flash Catalyst and Flash Builder 4 (and presumably Flash CS5).
Next, let's add the label control to display the Button's text. The label must have an id of "labelDisplay" for this to work properly. This is because the Button classes default skin expects a label control called labelDisplay (http://help.adobe.com/en_US/FlashPlatform//reference/actionscript/3/spark/skins/spark/ButtonSkin.html). It should be noted that every skinnable component within the Spark framework has a default skin. To replace this skin, you must implement all of the required properties. Leaving the label control out, or not naming the label control correctly, does not cause any errors. You simply won't see the text on the Button. With that said, here's our label control:
View CodeXML | |
<s:Label id="labelDisplay" width="100%" height="100%" textAlign="center" verticalAlign="middle" fontSize="12"/> | |
Assigning the label's width and height to 100%, while setting textAlign and verticalAlign both to their center values, will make sure the label is centered within our button.
Viewing the Button
In your Button's MXML code, you must define the skinClass it is going to use. This looks something like this:
View CodeXML | |
<?xml version="1.0" encoding="utf-8"?> <s:Button xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" skinClass="com.ninem.SparkButtonSkin"> </s:Button> | |
Place your newly created button on your main application, set the width/height and label properties, and hit go. Your code should look something like this:
View CodeXML | |
<?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/mx" minWidth="955" minHeight="600" xmlns:ninem="com.ninem.*"> <ninem:SparkButton width="100" height="50" label="Hello"/> </s:Application> | |
You should see your label control in the upper left hand corner of your screen. It works, but it has no states at the moment. Let's work on that:
Reacting to Mouse Events
Let's define some simple styling to react to the mouse events a common button expects. When we roll over, we'll make the text get a little larger. And when the button's enabled property is set to false, let's make the whole button's alpha property drop to 0.5.
To make the text get a little larger when we rollover, it's as easy as adding one more property to our label in the skin.
View CodeXML | |
<s:Label id="labelDisplay" width="100%" height="100%" textAlign="center" verticalAlign="middle" fontSize="12" fontSize.over="16"/> | |
You'll see that I added fontSize.over="16." This will override the fontSize whenever the button is in it's over state.
To set the button's alpha to 0.5 when it's disabled, we add a property to the root of the skin tag. "alpha.disabled" will set the alpha of the whole button when it enter it's disabled state.
At this point, your skin should look something like this:
View CodeXML | |
<?xml version="1.0" encoding="utf-8"?> <s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="60" minHeight="20" alpha.disabled="0.5"> <fx:Metadata> [HostComponent("spark.components.Button")] </fx:Metadata> <s:states> <s:State name="up"/> <s:State name="over"/> <s:State name="down"/> <s:State name="disabled"/> </s:states> <s:Rect width="100%" height="100%"> <s:stroke> <s:SolidColorStroke color="0x000000" weight="2"/> </s:stroke> <s:fill> <s:SolidColor color="0xCCCCCC"/> </s:fill> </s:Rect> <s:Label id="labelDisplay" width="100%" height="100%" textAlign="center" verticalAlign="middle" fontSize="12" fontSize.over="16"/> </s:SparkSkin> | |
So as you can see, skinning has drastically changed in Flex 4. It may take some time getting used to, but it appears that the changes are for the better. Your skins are now more modular, and more separated from your "business logic."
To get all of the code associated with this article, point your svn client here: https://code.9mmedia.com/svn/public/SparkButtonDemo/trunk/
3 Comments
Thanks for sharing knowledge. However, I believe, some day, flex should come up with option like CSS… an external file to manage skinning.
Thanks so much for this demo!!! My buttons look awesome!!
It’s been a while since I’ve had to skin Flash components. This is leaps and bounds better than the old Flash Component Framework 2.0 way. Props to Adobe for making this simple and intuitive.
And props to you for posting this article. Thanks.