About Spark skins

In the Flex 4 skinning model, the skin controls all visual elements of a component, including layout. The new architecture gives developers greater control over what their components look like a structured and tool-friendly way. Previously, MX components that used the Halo theme for their skins defined their look and feel primarily through style properties.

Spark skins can contain multiple elements, such as graphic elements, text, images, and transitions. Skins support states, so that when the state of a component changes, the skin changes as well. Skin states integrate well with transitions so that you can apply effects to one or more parts of the skins without adding much code.

You typically write Spark skin classes in MXML. You do this with MXML graphics tags (or FXG components) to draw the graphic elements, and specify child components (or subcomponents) using MXML or ActionScript.

The base class for Flex 4 skins is the spark.components.supportClasses.Skin class. The default Spark skins are based on the SparkSkin class, which subclasses the Skin class.

In general, you should try to put all visual elements of a component in the skin class. This helps maintain a necessary separation between the model (the logic and declarative structure of the application) and the view (the appearance of the application). Properties that are used by skins (for example, the placement of the thumb in a slider control) should be defined in the component so that they can be shared by more than one skin.

Most skins use the BasicLayout layout scheme within the skin class. This type of layout uses constraints, which means that you specify the distances that each element is from another with properties such as left, right, top, and bottom. You can also specify absolute positions such as the x and y coordinates of each element in the skin.

When creating skins, you generally do not subclass existing skin classes. Instead, it is often easier to copy the source of an existing skin class and create another class from that. Use this method especially if you are going to reuse the skin for multiple instances of a component or multiple components. If you want to change the appearance of a single instance of a component, you can use MXML graphics syntax or apply styles inline.

When creating a Spark skin, you can use MXML, ActionScript, FXG, embedded images, or any combination of the above. You do not generally use run-time loaded assets such as images in custom skins.

Applying skins

You usually apply Spark skins to components by using CSS or MXML. With CSS, you use the skinClass style property to apply a skin to a component, as the following example shows:

s|Button { 
    skinClass: ClassReference("com.mycompany.skins.MyButtonSkin"); 
}

When applying skins with MXML, you specify the name of the skin as the value of the component’s skinClass property, as the following example shows:

<s:Button skinClass="com.mycompany.skins.MyButtonSkin" />

You can also apply a skin to a component in ActionScript. You call the setStyle() method on the target component and specify the value of the skinClass style property, as the following example shows:

myButton.setStyle("skinClass", Class(MyButtonSkin));

Anatomy of a skin class

Custom Spark skins are MXML files that define the logic, graphic elements, subcomponents, states, and other objects that make up a skin for a Spark component.

The structure of Spark skin classes is similar to other custom MXML components. They include the following elements:
  • Skin root tag, or a subclass of Skin (required)

  • Host component metadata (optional, but recommended)

  • States declarations (required if defined on the host component)

  • Skin parts (required if defined on the host component)

  • Script block (optional)

  • Graphic elements and other controls (optional)

In addition to these elements, Spark skins can contain MXML language tags such as Declarations and Library.

Root tags

Skin classes use the Skin class, or a subclass of Skin such as SparkSkin, as their root tag. The root tag contains the namespace declarations for all namespaces used in the skin class. The following commonly appears at the top of each skin class file:

<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

You can set additional properties on the <s:Skin> tag, such as minWidth or scaleX. You can also set style properties such as color and fontWeight. In addition, you can specify values based on the state of the control on the root tag. For example, color.down="0xFFFFFF". You cannot set the includeIn or excludeFrom properties on the root tag of the skin class.

If you create a custom theme for your application, or if you do not need support for global Spark styles in your custom skin class, you can use Skin rather than SparkSkin as your custom skin’s root tag. The SparkSkin class adds support for the colorization styles (such as chromeColor and symbolColor) and supports excluding specific skin parts from colorization, or for specifying symbols to colorize.

Host components

Spark skin classes typically specify the host component on them. The host component is the component that uses the skin. By specifying the host component, Spark skins can gain a reference to the component instance that uses the skin by using the hostComponent property.

The following example from an MXML-based skin defines the Spark Button as the host component:

<fx:Metadata> 
    [HostComponent("spark.components.Button")] 
</fx:Metadata>

Adding the [HostComponent] metadata is optional, but it lets Flex perform compile-time checking for skin states and required skin parts. Without this metadata, no compile-time checking can be done.

You can specify a host component on an ActionScript-based skin, too. To do this, you declare a bindable public property called hostComponent. The hostComponent property must of the correct type. For example:
import spark.components.Button; 
... 
[Bindable] 
public var hostComponent:Button;

The value of the hostComponent property is set by the component when the skin is loaded.

Skin states

Skin states are skin elements that are associated with component states. They are not the same as component states. For example, when a Button is down, the Button’s skin displays elements that are associated with the down skin state. When the button is up, the button displays elements that are associated with the up skin state.

Skins must declare skin states that are defined on the host component. At runtime, the component sets the appropriate state on the skin. Skin states are referenced by using the dot-notation syntax, property.state (for example, alpha.down defines the value of the alpha property in the down state).

The following example shows the skin states for the Spark Button skin:
 <s:states> 
    <s:State name="up" /> 
    <s:State name="over" /> 
    <s:State name="down" /> 
    <s:State name="disabled" /> 
</s:states>

Skin parts

Skin parts are components defined in the skin class and the host component. They often provide a way for a host component to push data into the skin. The component also uses skin parts to hook up behaviors.

Spark container skins include a content group that defines the group where the content children are pushed into and laid out in. This element has an ID of contentGroup. All skinnable containers have a contentGroup. The content group is a static skin part.

Layouts

Both the Skin and SparkSkin classes use BasicLayout as their default layout scheme. This is the equivalent of having the following defined in the skin class:
<s:layout> 
    <s:BasicLayout/> 
</s:layout>

The layout scheme is important when there is more than one graphical element or subcomponent used in the skin. BasicLayout relies on constraints and/or absolute positioning to determine where to place components.

Subcomponents

The Spark skin classes typically include graphic elements and other components that make up the appearance of the skin.

Script blocks

Optionally, the Spark skin class can include a Script block for skin-specific logic.

Most Spark skins have a special <fx:Script> block at the top of the skin class. This block typically defines style properties that the skin class respects, including the exclusions that the skin uses. The tag includes a special attribute, fb:purpose="styling":
<fx:Script fb:purpose="styling">

This attribute is used by Flash Builder. When you create a copy of a skin class in Flash Builder, you can opt to make the skin styleable. If you choose to make it styleable, Flash Builder includes this section of the skin class. If you choose not to make the skin styleable, Flash Builder excludes this section.

Language tags

Like any MXML-based class, you can use the Library tag inside the root tag to declare repeatable element definitions. If you want to use non-visual objects in your skin class, you must wrap them in a Declarations tag.

Versions of included skin classes

While the new Spark skinning architecture makes creating your own skins easy, Flex 4 includes several sets of skins.

The following table describes the skinning packages that ship with Flex 4:

Package

Description

spark.skins.spark.*

Default skins for Spark components.

spark.skins.wireframe.*

A simplified theme for developing applications with a “prototype” look to them. To use wireframe skins, you can apply the wireframe theme or apply the skins on a per-component basis.

For information about applying themes, see Using themes.

mx.skins.halo.*

MX skins available for MX components that do not conform to the Spark skinning architecture. You can use these skins in your application instead of the Spark skins by overriding the styles, loading the Halo theme, or by setting the compatibility-version compiler option to 3.0.0 when compiling your application.

For information about these skins, see Skinning MX components.

mx.skins.spark.*

The default skins for MX components when using the default Spark theme.

These skins are used by the MX components in Flex 4 applications. These skins give the MX components a similar appearance to the Spark components in Flex 4 applications.

mx.skins.wireframe.*

Wireframe skins for MX components.

Skins typically follow the naming convention componentNameSkin.mxml. In the ActionScript 3.0 Reference for the Adobe Flash Platform, most skins have several versions. For example, there are four classes named ButtonSkin. The default skins for the Spark components are in the spark.skins.spark.* package.

Flex 4 also ships with several themes that use some of the skinning packages. For more information, see About the included theme files.

Skinning contract

The skinning contract between a skin class and a component class defines the rules that each member must follow so that they can communicate with one another.

The skin class must declare skin states and define the appearance of skin parts. Skin classes also usually specify the host component, and sometimes bind to data defined on the host component.

The component class must identify skin states and skin parts with metadata. If the skin class binds to data on the host component, the host component must define that data.

The following table shows these rules of the skinning contract:
 

Skin Class

Host Component

Host component

<fx:Metadata> 
     [HostComponent("spark.components.Button")] 
</fx:Metadata> 

n/a

Skin states

<s:states> 
     <s:State name="up"/> 
</s:states>
[SkinState("up")]; 
public class Button { 
... 
}

Skin parts

<s:Button id="upButton"/>
[SkinPart(required="false")] 
public var upButton:Button;

Data

text="{hostComponent.title}"
[Bindable] 
public var title:String;

The compiler validates the [HostComponent], [SkinPart], and [SkinState] metadata (as long as the [HostComponent] metadata is defined on the skin). This means that skin states and skin parts that are identified on the host component must be declared in the skin class.

For each [SkinPart] metadata in the host component, the compiler checks that a public variable or property exists in the skin. For each [SkinState] metadata in the host component, the compiler checks that a state exists in the skin. For skins with [HostComponent] metadata, the compiler tries to resolve the host component class, so it must be fully qualified.

After you have a valid contract between a component and its skin class, you can apply the skin to the component.

Accessing host components

Spark skins optionally specify a host component. This is not a reference to an instance of a component, but rather, to a component class. You define the host component by using a [HostComponent] metadata tag with the following syntax:

<fx:Metadata> 
    [HostComponent(component_class)] 
</fx:Metadata>

For example:

<fx:Metadata> 
    [HostComponent("spark.components.Button")] 
</fx:Metadata>

When a skin defines this metadata, Flex creates the typed property hostComponent on the skin class. You can then use this property to access members of the skin’s host component instance from within the skin. For example, in a Button skin, you can access the Button’s style properties or its data (such as the label).

You can access public properties of the skin’s host component by using the strongly typed hostComponent property as a reference to the component.
<s:SolidColor color="{hostComponent.someColor as uint}" />

This only works with public properties that are declared directly on the host component. You cannot use this to access the host component’s private or protected properties.

To access the values of style properties on a host component from within a skin, you are not required to specify the host component. You can use the getStyle() method as the following example shows:
<s:SolidColorStroke color="{getStyle('color')}" weight="1"/>

You can also access the root application’s properties and methods by using the FlexGlobals.topLevelApplication property. For more information, see Accessing application properties.

Defining skin states

Each skinnable component has a set of visual skin states. For example, when a Button is down, the Button’s skin displays elements that are associated with the down skin state. When the button is up, the button displays elements that are associated with the up skin state.

To have a valid contract between a skinnable Spark component and its skin, you identify the skin states in the component. Then, define the state’s appearance in the component’s skin class.

Skin states are declared in the skin class and identify the different states that the component can assume visually. You can define how the visual appearance changes as the skin's state changes in the skin class.

Subclasses inherit the skin states of their parent. For example, the Button class defines the skin states up, down, over, and disabled. The ToggleButton class, which is a subclass of Button, declares the upAndSelected, overAndSelected, downAndSelected, and disabledAndSelected skin states, in addition to those states defined by the Button control.

Identifying skin states in a component

Part of the contract between a Spark skin and its host component is that the host component must identify the skin states that it supports. To identify a skin state in the component’s class, you use the [SkinState] metadata tag. This tag has the following syntax:

[SkinState("state")]

You specify the metadata before the class definition. The following example defines four skin states for the Button control:

[SkinState("up")] 
[SkinState("over")] 
[SkinState("down")] 
[SkinState("disabled")] 
public class Button extends Component { .. } 

Defining the skin states in the skin class

The Spark skinning contract requires that you declare supported skin states in the skin class. You can also optionally define the appearance of the skin state in the skin class. Even if you declare a skin state in the skin class, you are not required to define its appearance.

To define a skin state in a skin class:
  1. Declare the skin state in a <states> tag.

  2. Set the value of properties based on the state of the component. This step is optional, but if you don’t define the skin state’s appearance, then the skin does not change when the component enters that state.

To declare skin states in the skin class, you populate the top-level <s:states> tag with an array of State objects. Each State object corresponds to one skin state.

The following example defines four states supported by the Button skin class:

<s:Skin ...> 
    <s:states> 
        <s:State name="up"/> 
        <s:State name="over"/> 
        <s:State name="down"/> 
        <s:State name="disabled"/> 
    </s:states> 
    ... 
</s:Skin>
After you declare the skin states in the skin class, you can then define the appearance of the skin states. To do this, you specify the values of the properties based on the skin state by using the dot-notation syntax (property_name.state_name). To set the value of the weight property of a SolidColorStroke object in the over state, you specify the value on the weight.over property, as the following example shows:
<s:SolidColorStroke color="0x000000" weight="1" weight.over="2"/>

You can also specify the stateful values of properties by using the includeIn and excludeFrom properties.

Most commonly, you specify values of style properties based on the state. Flex applies the style based on the current state of the component. The skin is notified when the component’s currentState property changes, so the skin can update the appearance at the right time.

A common use of this in the Spark skins is to set the alpha property of a component when the component is in its disabled state. This is often set on the top-level tag in the skin class, as the following example from the ButtonSkin class shows:
<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21" 
    alpha.disabled="0.5">
Another example is when a Button label’s alpha property changes based on other states. The skin sets the value of the labelDisplay’s alpha property. When the button is in its up state, the label has an alpha of 1. When the button is in its over state, the label has an alpha of .25, as the following example shows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/StatesButtonExample.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:Button label="Alpha Changes" skinClass="mySkins.MyAlphaButtonSkin"/>

</s:Application>

The executing SWF file for the previous example is shown below:

The skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\MyAlphaButtonSkin.mxml -->
<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21">
    
    <fx:Metadata>
        [HostComponent("spark.components.Button")]
    </fx:Metadata> 
    
    <!-- Specify one state for each SkinState metadata in the host component's class -->
    <s:states>
        <s:State name="up"/>
        <s:State name="over"/>
        <s:State name="down"/>
        <s:State name="disabled"/>
    </s:states>

    <s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2">
        <s:stroke>
            <s:SolidColorStroke color="0x000000" weight="1"/>
        </s:stroke>
    </s:Rect>
    
    <s:Label id="labelDisplay" 
        alpha.up="1"
        alpha.down=".1"
        alpha.over=".25"
        horizontalCenter="0" verticalCenter="1"
        left="10" right="10" top="2" bottom="2">
    </s:Label>
</s:Skin>
You can set any property on a component based on the state. You are not limited to style properties. For example, you can change the label of a Button control in its skin based on its state, as the following example shows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/ChangeLabelBasedOnStateExample.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:Button id="myButton" label="Basic Button" skinClass="mySkins.ChangeLabelBasedOnState"/>

</s:Application>

The executing SWF file for the previous example is shown below:

The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\ChangeLabelBasedOnState.mxml -->
<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21">

    <fx:Metadata>
        [HostComponent("spark.components.Button")]
    </fx:Metadata> 
    
    <!-- Specify one state for each SkinState metadata in the host component's class -->
    <s:states>
        <s:State name="up"/>
        <s:State name="over"/>
        <s:State name="down"/>
        <s:State name="disabled"/>
    </s:states>

    <s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2">
        <s:stroke>
            <s:SolidColorStroke color="0x000000" weight="1"/>
        </s:stroke>
       <!-- Add this fill to make the "hit area" cover the entire button. -->
       <s:fill>
            <s:SolidColor color="0xFFFFFF"/>
       </s:fill>
    </s:Rect>
    
    <s:Label id="labelDisplay"
         text.up="UP" text.over="OVER" text.down="DOWN"
         horizontalCenter="0" verticalCenter="1"
         left="10" right="10" top="2" bottom="2">
    </s:Label>
</s:Skin>

If you identify a skin state on the component, but do not declare it on the skin class, Flex throws a compiler error if the [HostComponent] metadata is set. You are not required to define the appearance of the skin state in the skin class, but you are required to declare it in the skin class in this case.

If you try to set the value of a style property in a skin class on a state that is not identified by the host component’s class, Flex throws a compiler error. This error checking helps to enforce the contract between the component and the skin.

In addition to conditionalizing the values of properties, you can also include or exclude graphic elements from the skin based on the component’s state. To do this, you use the includeIn and excludeFrom properties to define which states a graphic element applies to.

The following example displays a partially transparent gray box when the Button control is in the down state:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/IncludeButtonExample.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:Button label="Click Me In the Button Class" skinClass="mySkins.MyIncludeButtonSkin" color="0xCCC333"/>

</s:Application>

The executing SWF file for the previous example is shown below:

The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\MyIncludeButtonSkin.mxml -->
<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21">

    <fx:Metadata>
        [HostComponent("spark.components.Button")]
    </fx:Metadata> 
    
    <!-- Specify one state for each SkinState metadata in the host component's class -->
    <s:states>
        <s:State name="up"/>
        <s:State name="over"/>
        <s:State name="down"/>
        <s:State name="disabled"/>
    </s:states>

    <s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2">
        <s:stroke>
            <s:SolidColorStroke color="0x000000" weight="1"/>
        </s:stroke>
    </s:Rect>
    
    <s:Label id="labelDisplay"
        horizontalCenter="0" verticalCenter="1"
        left="10" right="10" top="2" bottom="2">
    </s:Label>

    <!-- Highlight (down state only) -->
    <!-- Note that you might need to adjust the radiusX and radiusY properties. -->
    <s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" includeIn="down">
        <s:fill>
            <s:SolidColor color="0x000000" alpha="0.25"/>
        </s:fill>
    </s:Rect>
</s:Skin>

When you exclude a graphic element with the excludeFrom property, Flex removes it from its parent’s child list. The element can no longer be referenced.

If you do not specify either the includeIn or excludeFrom property for a graphic element in a skin class, the graphic element is used in all states of the control by default.

You can specify multiple states to be included or excluded by using a comma-separated list of states for the includeIn or excludeFrom properties. The following example excludes the Rect graphic element from the host component’s over and down states:

<s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" excludeFrom="over, down">

The excludeFrom and includeIn properties can be set only in MXML. You cannot set the values of these properties in ActionScript.

All classes support the includeIn and excludeFrom properties when they are used in the skin class, as long as they are a visual child of a visual parent. This means that you cannot set these properties on the root tag of the skin file, nor can you set them on scalar properties. For example, the following shows a valid and an invalid use of the includeIn property:
<s:states> 
    <s:State name="StateA" /> 
</s:states> 
 
<!-- This is a valid use of the includeIn property: --> 
<s:Button> 
    <s:label.StateA> 
        <fx:String>My Label</fx:String> 
    </s:label.StateA> 
</s:Button> 
 
<!-- This is an invalid use of the includeIn property: --> 
<s:Button> 
    <s:label> 
        <fx:String includeIn="StateA">My Label</fx:String> 
    </s:label> 
</s:Button>
If you exclude some graphic elements from a skin, the component sometimes resizes itself or its appearance fluctuates as it cycles through its states. For example, if you set the value of the excludeFrom property to down on a button skin’s labelDisplay, the label disappears when the user clicks the button. As a result, the button resizes itself in the down state. This behavior is expected and is shown in the following example:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/HideLabelOnDownExample.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:Button id="myButton" 
        skinClass="mySkins.HideLabelOnDownSkin" 
        label="This Is A Long Button Label"/>

</s:Application>

The executing SWF file for the previous example is shown below:

The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\HideLabelOnDownSkin.mxml -->
<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21">

    <fx:Metadata>
        [HostComponent("spark.components.Button")]
    </fx:Metadata> 
    
    <!-- Specify one state for each SkinState metadata in the host component's class -->
    <s:states>
        <s:State name="up"/>
        <s:State name="over"/>
        <s:State name="down"/>
        <s:State name="disabled"/>
    </s:states>

    <s:Rect 
        left="0" right="0" 
        top="0" bottom="0" 
        width="69" height="20" 
        radiusX="2" radiusY="2">
        <s:stroke>
            <s:SolidColorStroke color="0x000000" weight="1"/>
        </s:stroke>
    </s:Rect>
    
    <s:Label id="labelDisplay"
         excludeFrom="down"
         horizontalCenter="0" verticalCenter="1"
         left="10" right="10" top="2" bottom="2">
    </s:Label>
</s:Skin>

The button resizes because its size is dynamically determined by the contents of the label by default. Excluding the label results in the label being removed from its parent.

To prevent the Button from resizing itself, you can set the alpha.down property to 0 or the visible.down property to false rather than excluding the component altogether. The label then disappears but the button does not resize itself because the label is not removed from its parent, as the following example shows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/HideLabelOnDownExample2.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:Button id="myButton" 
        skinClass="mySkins.HideLabelOnDownSkin2" 
        label="This Is A Long Button Label"/>

</s:Application>

The executing SWF file for the previous example is shown below:

The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\HideLabelOnDownSkin2.mxml -->
<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21">

    <fx:Metadata>
        [HostComponent("spark.components.Button")]
    </fx:Metadata> 
    
    <!-- Specify one state for each SkinState metadata in the host component's class -->
    <s:states>
        <s:State name="up"/>
        <s:State name="over"/>
        <s:State name="down"/>
        <s:State name="disabled"/>
    </s:states>

    <s:Rect 
        left="0" right="0" 
        top="0" bottom="0" 
        width="69" height="20" 
        radiusX="2" radiusY="2">
        <s:stroke>
            <s:SolidColorStroke color="0x000000" weight="1"/>
        </s:stroke>
    </s:Rect>
    
    <s:Label id="labelDisplay"
         visible.down="false"
         horizontalCenter="0" verticalCenter="1"
         left="10" right="10" top="2" bottom="2">
    </s:Label>
</s:Skin>

Skin parts

Some components have multiple skin parts. For example, a NumericStepper contains an up button, a down button, and text. These parts are declared by the component. The skin defines their appearance. Parts defined by the component can be optional or required in the skin.

Skin parts are an important part of the skinning contract. They let the component instance interact with the skin and can be used to push data down into a skin or to define behaviors.

Skin parts do not have to be top-level tags in the Spark skin class. They can be anywhere in the MXML file.

To declare a skin part on a component, you use the [SkinPart] metadata. For example, the Button class defines a skin part for the label in the ButtonBase class:

[SkinPart(required="false")] 
public var labelDisplaIDisplayText;

The ButtonSkin class defines a Label component as the labelDisplay part:

 <s:Label id="labelDisplay" 
    textAlign="center" 
    verticalAlign="middle" 
     maxDisplayedLines="1" 
    horizontalCenter="0" verticalCenter="1" 
    left="10" right="10" top="2" bottom="2"> 
</s:Label>

In this example, the labelDisplay skin part is technically not required (required="false"), but without it, Flex would not draw a label on the Button control. The default value for the required property is false. The contract between the Button class and its skin dictates that when you set the label property on a button, the value is pushed down into the skin and modifies the value of the labelDisplay skin part’s text, if the labelDisplay skin part exists.

Parts are identified by their id attributes. The id attribute of the skin part in the skin class must match the property name of the skin part in the host component. This helps enforce the contract between the skin and the host component. For example, if you have the following declaration in your component:

[SkinPart(required="true")] 
public var textInput:TextInput;

The skin class sets the id attribute of a TextInput instance to “textInput”, as the following example shows:

<s:TextInput id="textInput" ... />

There are two primary types of skin parts: static and dynamic. Static parts are instantiated automatically by the skin. There can be only one instance of a static part. For example, the VScrollBar class has four static parts: track, thumb, incrementButton, and descrementButton. The Button class has one static part: label.

Dynamic parts are instantiated when needed. There can be more than one instance of a dynamic part. Dynamic parts are defined in the Declarations block of a skin so that one or more instances of the part can be instantiated and used by the skin. The Slider class’s DataTip, for example, is a dynamic skin part. In the VSliderSkin and HSliderSkin, the dataTip skin part is defined in a Declarations block.

In your host component, you specify the type for a dynamic skin part as an IFactory. For example, in the Slider class, the dataTip is defined as:
[SkinPart(required="false", type="mx.core.IDataRenderer")] 
public var dataTip:IFactory; 

At runtime, when a component skin is loaded, the SkinnableComponent base class finds all the skin parts, assigns parts that were found, and throws a runtime error if any required parts are not defined by the skin. Deferred parts are found (because they are defined properties of the skin), but have a value of null when the component is first instantiated.

Applying skin classes to a component

After you have defined a skin, you can apply it to a component in one of the following ways:
  • CSS

  • MXML

  • ActionScript

To associate a skin with a component in CSS, you set the value of the skinClass style property in the style sheet. You can use either type or class selectors to apply a skin class to a component.

The following example applies the mySkins.NewPanelSkin class with all Panel containers by using a type selector:

@namespace s "library://ns.adobe.com/flex/spark"; 
s|Panel { 
    skinClass:ClassReference("mySkins.NewPanelSkin"); 
}

The following example associates the mySkins.CustomButtonSkin class with all components that use the myButtonStyle class selector:

.myButtonStyle { 
    skinClass:ClassReference("mySkins.CustomButtonSkin"); 
} 
... 
<s:Button label="Spark Button" className="myButtonStyle"/>

To associate a skin with a component in MXML, you set the value of the skinClass property inline. The following example applies the mySkins.NewPanelSkin class to this instance of the Panel container:

<s:Panel skinClass="mySkins.NewPanelSkin"/>

The advantage to applying skins with CSS is that with CSS you can use type and class selectors to apply the skin to all components of a particular type (such as all Buttons) or all classes that are in a particular class (such as all components with the style name “myButtonStyle”).

CSS supports inheritance. If you apply a skin class to a Button control in the Button type selector, the skin applies to all Button controls and subclasses of Button controls, such as the ToggleButton control. This can have unexpected results in the case of subcomponents. If you apply a new skin to all Label controls, components that have Label subcomponents will also use that skin. As a result, you should generally use class selectors for applying skins to basic components.

Setting a skin class in MXML lets you only apply the skin to the instance of the component.

You can also apply a skin class to a component in ActionScript. Call the setStyle() method on the component, and set the value of the skinClass style property to the skin class. You must cast the skin class as a Class in the setStyle() method.

The following example applies the custom skin class to the Button when you click the Button:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/SimpleLoadExample.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <fx:Script>
        import mySkins.*;
        
        private function changeSkinClickHandler():void {
            myButton.setStyle("skinClass", Class(MyButtonSkin));
        }
    </fx:Script>

    <s:Button id="myButton" 
        label="Click Me" 
        color="0xFFFFF" 
        click="changeSkinClickHandler()"/>

</s:Application>

The executing SWF file for the previous example is shown below:

The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\MyButtonSkin.mxml -->
<s:Skin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21">

    <fx:Metadata>
        [HostComponent("spark.components.Button")]
    </fx:Metadata> 
    
    <!-- Specify one state for each SkinState metadata in the host component's class -->
    <s:states>
        <s:State name="up"/>
        <s:State name="over"/>
        <s:State name="down"/>
        <s:State name="disabled"/>
    </s:states>

    <s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2">
        <s:stroke>
            <s:SolidColorStroke color="0x000000" weight="1"/>
        </s:stroke>
    </s:Rect>
    
    <s:Label id="labelDisplay"
             horizontalCenter="0" verticalCenter="1"
             left="10" right="10" top="2" bottom="2">
    </s:Label>
</s:Skin>

Be sure to import the appropriate skin class package before you can use the class in your application.

As with CSS, you can use ActionScript to apply a skin class to all instances of a particular component type. Call the setStyle() method on the component’s style declaration. The following example applies the custom Button skin to all buttons in the application:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/ASTypeSelectorLoadExample.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

    <fx:Script>
        import mySkins.*;
        
        private function changeSkinClickHandler():void {
            styleManager.getStyleDeclaration("spark.components.Button").setStyle("skinClass", Class(MyButtonSkin));
        }
    </fx:Script>

    <s:Button id="myButton1" label="Click Me" color="0xFFFFF" 
        click="changeSkinClickHandler()"/>
    <s:Button id="myButton2" label="Click Me" color="0xFFFFF" 
        click="changeSkinClickHandler()"/>
    <s:Button id="myButton3" label="Click Me" color="0xFFFFF" 
        click="changeSkinClickHandler()"/>

</s:Application>

The executing SWF file for the previous example is shown below:

Documenting skin states and skin parts

The ASDoc utility supports documenting the [SkinState] and [SkinPart] metadata tags.

You can document the [SkinState] metadata tag by adding comments above the tag in the component’s class file, as the following example shows:

/** 
* Up state of the button. 
*/ 
[SkinState("up")]

To document a [SkinPart] metadata tag, the ASDoc utility uses the description of the variable from the component’s class file. You do not add a comment on the actual metadata tag, as the following example shows:

[SkinPart(required="false")] 
/** 
* A skin part that defines the  label of the button. 
*/ 
public var labelDisplay:TextGraphicElement;

The ASDoc utility adds skin state documentation to the “Skin States” section of the class’s documentation. It adds skin part documentation to the “Skin Parts” section of the class’s documentation.

For more information about the ASDoc utility, see ASDoc.