Spark Skinning

You create Spark skins by either editing an existing skin class or creating a new skin class for a Spark component. For information about skinning MX components, see Skinning MX components.

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">

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 Apache Flex, 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 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 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 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 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 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 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>

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.

Creating skins from source files

When creating a custom Spark skin, the easiest way to get started is to use the source of an existing skin of a similar type as your base class. You can use the default Spark skins as a good starting point for creating skins, or the skins in the Wireframe theme. The Wireframe theme defines a set of simple skins that provide a "prototype" look to the application. They are lighter weight than the default skins but have less functionality.

The default Spark skins are more heavyweight than the Wireframe skins, but they let you maintain the default Spark look and feel. For example, if you are creating a custom Button skin, you can open the spark.skins.spark.ButtonSkin.mxml file. Save this file in a different location with a different name. You can then edit the contents of this file for your new custom skin.

The default skins have names that are similar to the component class name (for example, ToggleButton uses the ToggleButtonSkin class). If you are unsure of the skin class name is used by a component, look in the defaults.css file. This style sheet defines all the default skin classes for the components that have them.

Skins commonly contain graphic elements such as rectangles, lines, and ellipses, as well as visual effects such as blend modes, transforms, and filters. Using these graphic elements, sometimes referred to as MXML graphics, is simple and integrates well with most skins. For more information, see MXML graphics.

Another way to create a spark skin is to generate its graphical elements from a tool that outputs FXG. FXG is a graphics interchange format that defines vector graphic elements. You can create a graphic in a design tool that can then export the instructions for drawing that graphic in the FXG format. You can either use the exported FXG as a component in your skin class, or you can convert the FXG syntax to use in your Spark skin class to define the appearance of your skins. Using FXG output as a custom component in your skin class is more efficient because the compiler optimizes FXG custom component source code. For more information about using FXG and converting it to MXML graphics, see FXG.

Skinning Spark components

All Spark components and subcomponents that are subclasses of SkinnableComponent can be reskinned. Common tasks when reskinning Spark components include adding borders, drop shadows, and transitions to a skin class.

Setting minimum sizes of a skin

A common task when creating Spark skins is to set minimum sizes of the skin. This causes the Flex layout to not reduce the size of a component below a pre-defined width or height. You do this with the minWidth and minHeight properties on the skin class's top-level element.

In general, you should not change the values of the minimum height and minimum width properties once they are set.

The following example creates a custom Button skin that has a minimum width of 100 pixels and a minimum height of 100 pixels.
<?xml version="1.0"?> 
<!-- SparkSkinning/SquareButtonExample.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.SquareButtonSkin" label="Click Me"/> 
 
</s:Application>
The following is the custom skin class used for this example:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/SquareButtonSkin.mxml --> 
<s:SparkSkin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="100" minHeight="100" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.Button")] 
    </fx:Metadata> 
    
    <fx:Script> 
        <![CDATA[         
            static private const exclusions:Array = ["labelDisplay"]; 
            override public function get colorizeExclusions():Array {return exclusions;} 
        ]]>        
    </fx:Script> 
    
    <s:states> 
        <s:State name="up" /> 
        <s:State name="over" /> 
        <s:State name="down" /> 
        <s:State name="disabled" /> 
    </s:states> 
    
    <!-- layer 1: shadow --> 
    <s:Rect left="-1" right="-1" top="-1" bottom="-1" radiusX="2" radiusY="2"> 
        <s:fill> 
            <s:LinearGradient rotation="90"> 
                    <s:GradientEntry color="0x000000" 
                                   color.down="0xFFFFFF" 
                                   alpha="0.01" 
                                   alpha.down="0" /> 
                    <s:GradientEntry color="0x000000" 
                                   color.down="0xFFFFFF" 
                                   alpha="0.07" 
                                   alpha.down="0.5" /> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Rect> 
    
    <!-- layer 2: fill --> 
    <s:Rect left="1" right="1" top="1" bottom="1" radiusX="2" radiusY="2"> 
        <s:fill> 
            <s:LinearGradient rotation="90"> 
                <s:GradientEntry color="0xFFFFFF" 
                               color.over="0xBBBDBD" 
                               color.down="0xAAAAAA" 
                               alpha="0.85" /> 
                <s:GradientEntry color="0xD8D8D8" 
                               color.over="0x9FA0A1" 
                               color.down="0x929496" 
                               alpha="0.85" /> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Rect> 
    
    <!-- layer 2: border --> 
    <s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2"> 
        <s:stroke> 
            <s:LinearGradientStroke rotation="90" weight="1"> 
                <s:GradientEntry color="0x000000" 
                               alpha="0.5625" 
                               alpha.down="0.6375" /> 
                <s:GradientEntry color="0x000000" 
                               alpha="0.75" 
                               alpha.down="0.85" /> 
            </s:LinearGradientStroke> 
        </s:stroke> 
    </s:Rect> 
 
    <s:Label id="labelDisplay" 
             textAlign="center" 
             verticalAlign="middle" 
             lineBreak="toFit" 
             horizontalCenter="0" verticalCenter="1" 
             left="10" right="10" top="2" bottom="2"> 
    </s:Label> 
    
</s:SparkSkin>
The default Button skin's minimum size is 21 pixels wide by 21 pixels high. These minimums are set on the root tag of the skin class, as the following example shows:
<s:SparkSkin 
	xmlns:fx="http://ns.adobe.com/mxml/2009" 
	xmlns:s="library://ns.adobe.com/flex/spark" 
	minWidth="21" minHeight="21" 
	alpha.disabled="0.5"> 
	... 
</s:SparkSkin>
You might notice that in the default button skin's class, though, the Rect border sets its width to a value that is larger than the minimum width on the skin:
 <s:Rect id="border" left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2">

The width property on the Rect border is used for buttons whose size is unset. The minWidth property on the SparkSkin is used when the button's width is variable, meaning that it hasn't been explicitly set. The previous example sets a default width and height of 69 and 20. However, it is pinned to (left=0, right=0, top=0, bottom=0) so that if the label is too large or the button is explicitly sized, then this rectangle will resize.

Accessing application properties

In addition to accessing the host component's properties, you can access properties of the application. This is useful if there are global settings that you want to access, or runtime information that is passed into the application that you want available in the skin class.

To access global variables in a custom skin, you use the FlexGlobals.topLevelApplication property. Using this property gives you access to all global variables, including variables that were passed into the application as flashVars variables.

The following example accesses a String that is a global variable on the application:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/GlobalVariableAccessorExample.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> 
        public var myLabelString:String = "Hello World"; 
    </fx:Script> 
 
    <s:Button skinClass="mySkins.GlobalVariableAccessorSkin"/> 
 
</s:Application>
The following is the custom skin class for this example:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning\mySkins\GlobalVariableAccessorSkin.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> 
    <fx:Script> 
        import mx.core.FlexGlobals;        
        
        [Bindable] 
        private var localString:String = FlexGlobals.topLevelApplication.myLabelString; 
    </fx:Script> 
    
    <!-- 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" 
        text="{localString}" 
        horizontalCenter="0" verticalCenter="1" 
        left="10" right="10" top="2" bottom="2"> 
    </s:Label> 
</s:Skin>

Using style properties in custom skins

Within the skin, you can get the value of certain style properties on the host component. The component declares what styles can be set so that they can be set inline in MXML. For example, you can set the chromeColor property on all skins that extend the SparkSkin class. On other skins, such as container skins, you can set the backgroundColor.

The following example sets the color of the border inside the skin to the same value as the Button's color style property:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/BorderColorButtonExample.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.BorderColorButtonSkin" 
        color="green"/> 
 
</s:Application>

The following is the skin class for this example:

<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning\mySkins\BorderColorButtonSkin.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" 
    creationComplete="initSkin()"> 
    
    <fx:Script> 
        private function initSkin():void { 
            /* Note that because color can change, you could override the 
                updateDisplayList() method and set it there so that the 
                color is updated in the skin when it is updated on the 
                host component. */ 
            outline.color = getStyle('color'); 
        } 
    </fx:Script> 
    
    <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 id="outline" 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>
You can use a binding expression to set properties in the skin based on values in the host component. The following example binds the value of the stroke's color property to the host component's color style property:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/StyleWatcherExample.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.StyleWatcherSkin" 
        click="myButton.setStyle('color','red')" 
        color="green"/> 
 
</s:Application>
The following is the skin class for this example:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning\mySkins\StyleWatcherSkin.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> 
            <!-- Match the border color to the button label's color. --> 
            <s:SolidColorStroke color="{getStyle('color')}" 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>

This example sets the color of the stroke when the application starts. When you click the Button, the color property later changes while the application is running because style properties are bindable.

For custom components, you must declare the style metadata on the component itself so that inline MXML styling will work.

To expose a property as a style, you can also define a getter and setter for the property. You then override the styleChanged() method to dispatch the binding change event. In the application, you call the setStyle() method on the instance's skin property, which sets the value of the style property on the skin.

The following example exposes a custom style property called borderColor on the custom skin:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/StyleableBorderSkin.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" 
    alpha.disabled="0.5"> 
    
    <fx:Script>        
        [Bindable("borderColorChange")] 
        
        public function get borderColor():uint { 
            return getStyle("borderColor"); 
        } 
        
        public function set borderColor(value:uint):void { 
            setStyle("borderColor", value); 
        } 
 
        override public function styleChanged(styleProp:String):void { 
            super.styleChanged(styleProp); 
 
            if (styleProp == "borderColor" || styleProp == null) 
                dispatchEvent(new Event("borderColorChange")); 
        } 
    </fx:Script>    
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata>     
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states>    
    <s:Group id="contentGroup" left="0" right="0" top="0" bottom="0"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="{borderColor}" weight="1"/> 
        </s:stroke> 
    </s:Rect> 
</s:Skin>
In the following application, you set the color of the border by using the ColorPicker control. This control uses the setStyle() method to change the color of the container's border.
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/StyleableBorderExample.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" height="250" width="450"> 
 
    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 
 
    <fx:Script> 
        private function setSkinStyles(e:Event):void { 
            myContainer.setStyle("borderColor",e.currentTarget.selectedColor); 
        } 
    </fx:Script> 
 
    <s:SkinnableContainer id="myContainer" 
        height="200" width="200" 
        skinClass="mySkins.StyleableBorderSkin">        
    </s:SkinnableContainer> 
        
    <mx:ColorPicker id="myColorPicker" change="setSkinStyles(event)"/> 
 
</s:Application>
Another way to expose style properties to the skin is to add code to the updateDisplayList() method to manually push style values into the skin's graphics. This is the more efficient method of pushing style properties to skins because it does not rely on binding. The following example overrides the updateDisplayList() method to push the background color's style property:
<fx:Script> 
	override protected function updateDisplayList(unscaleWidth:Number, unscaledHeight:Number):void { 
		// Push style values into the graphics properties before calling super.updateDisplayList 
		backgroundFill.color = getStyle("backgroundColor"); 
		// Call super.updateDisplayList to do the rest of the work 
		super.updateDisplayList(unscaledWidth, unscaledHeight); 
	} 
</fx:Script> 
<s:Rect left="0" right="0" top="0" bottom="0"> 
	<s:SolidColor id="backgroundFill" /> 
</s:Rect>

Using events in custom skins

In general, you should use states to react to user interaction. If a skin part is removed from display list whenever the mouse hovers over a control, then that part cannot receive mouseDown events. Some skins, such as the Button control's skin, block user interaction to the skin so that the underlying control only can dispatch events. This means that you cannot trigger events defined on InteractiveObject on the skin. These include user interaction events such as mouseDown, mouseOut, and click.

You can trigger events defined on UIComponent on the skin, however. These events include creationComplete and other lifecycle events.

A Button control's default skin defines several Rect elements and a Label control. These simple classes do not support any events. To add lifecycle event listeners, you can wrap a simple element or series of elements in a Group tag. This lets you register lifecycle events that are defined on UIComponent.

The following example shows a Group inside the skin that triggers a creationComplete event:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/LifecycleEventExample.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" 
    height="200" width="200"> 
    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 
 
    <s:Group width="150" height="100"> 
        <s:Button label="Click It or Ticket" skinClass="mySkins.LifecycleEventSkin"/> 
    </s:Group> 
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning\mySkins\LifecycleEventSkin.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> 
    
    <s:states> 
        <s:State name="up"/> 
        <s:State name="over"/> 
        <s:State name="down"/> 
        <s:State name="disabled"/> 
    </s:states> 
 
    <s:Rect id="buttonBorder" width="100%" height="20" radiusX="2" radiusY="2"> 
        <s:stroke> 
            <mx:SolidColorStroke color="0x000000" weight="1" weight.over="2"/> 
        </s:stroke> 
    </s:Rect> 
    
    <s:Group creationComplete="trace('creationComplete')"> 
        <s:Label id="labelDisplay" 
            horizontalCenter="0" verticalCenter="1" 
            left="10" right="10" top="6" bottom="2"> 
        </s:Label> 
    </s:Group> 
</s:Skin>

Skinning Spark containers

Spark defines two types of containers:
  • Skinnable containers

  • Non-skinnable containers

Both types of containers are in the spark.components.* package. Most Spark containers are skinnable. For example, the SkinnableContainer, Panel, and TitleWindow containers are skinnable. Skinnable containers subclass the SkinnableContainer class.

Spark groups are examples of containers that are not skinnable. They provide a lightweight mechanism that you use to perform layout. However, Spark groups do not support skinning to ensure that they add minimal overhead to your application. To modify the visual appearance of a Spark group, you can use the corresponding Spark container, which is skinnable. By contrast, all MX containers are skinnable.

Skinnable container skins are just like other component skins. They define a group that defines where the content children are laid out. This element has an ID of contentGroup. All skinnable containers have a content group. All visual children of a container are pushed into the content group and laid out using that group's layout rules.

For a list of which containers are skinnable and/or scrollable, see About Spark containers.

Sizing Spark container skins

A common reason to skin a Spark container is to add a graphic element, such as a rectangle, to be the background of the container. You typically set its size by using the constraint properties or the percent size properties by using one of the following methods:
  • Constraints (setting top=0, bottom=0, left=0, right=0)

  • Percent sizes (setting height=100%, width=100%)

When the components are first drawn, constraint properties take precedence (so if you set them, then the dimensional properties are ignored). However, if you set the value of the constraint properties, but then later explicitly set the value of the percent size properties at runtime (for example, by setting the percentHeight property to 100), Flex honors the new settings.

Using constraint or percent size properties depends on the use case. In general, a skin resizes when the host component resizes. The choice of percent or constraint sizing is based on the resizing scenario. For example, if you want a label element to be always half as wide as the skin, then set the width property to 50%. If you want a label element to be always inset by 5 pixels, set the left and right properties to 5.

The resizable skin elements in the default Spark skins usually set the left and right properties. To specify their default size, they also set the width property. As a result, if the component size is not set in the application, the element's width sets the default size of the skin and the component.

The following example from the ButtonSkin.mxml class shows that the default size of the Button control is the default size of its skin. This is calculated from the rectangle in the skin, and is 69 pixels wide and 20 pixels high:
 <s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2"> 
	<s:stroke> 
		<s:LinearGradientStroke rotation="90" weight="1"> 
			<s:GradientEntry color="0x000000" alpha="0.5625" alpha.down="0.6375" /> 
			<s:GradientEntry color="0x000000" alpha="0.75" alpha.down="0.85" /> 
		</s:LinearGradientStroke> 
	</s:stroke> 
</s:Rect>

Another technique for designing resizable skins is to specify a minimum size for the skin. For more information, see Setting minimum sizes of a skin.

Adding borders to Spark containers

One common use of a custom Spark skin is to add a borders to a container. Borders can be simple boxes around the perimeter of a container, or they can define drop shadows, line styles, corner radii, and other properties of a border.

To add a simple rectangular border to a container's skin, you add a Rect object. You can set the height and width of the Rect to 100% so that the skin sizes itself to the size of the container automatically, or you can set the offsets to 0.

The following example adds a simple 1-point, black line as a border around the container:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/SimpleContainerBorderExample.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:SkinnableContainer id="myContainer" height="200" width="200" skinClass="mySkins.ContainerBorderSkin"> 
        <s:Button label="Click Me"/> 
    </s:SkinnableContainer> 
 
</s:Application>
The contentGroup container has left, right, top, and bottom offsets of 1. This lets the border display outside the content instead of sitting directly on top of the outside edges of container children. The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/ContainerBorderSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states> 
        
    <s:Group id="contentGroup" left="1" right="1" top="1" bottom="1"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0x000000" weight="1"/> 
        </s:stroke> 
    </s:Rect> 
 
</s:Skin>

The container's skin sets its constraint properties (top, bottom, left, and right) to 0, so the Rect object is set to the same size as the container.

To create a border with rounded corners in a skin, you set the values of the radiusX and radiusY properties of the Rect object. These properties define the number of pixels for the corners on the x and y axes of the Rect. The following example sets the radiusX and radiusY properties to 10 to round the corners of the container's skin:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/RoundedContainerBorderExample.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:SkinnableContainer id="myContainer" 
        height="200" width="200" 
        skinClass="mySkins.ContainerRoundedBorderSkin"> 
        <s:Button label="Click Me"/> 
    </s:SkinnableContainer> 
 
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/ContainerRoundedBorderSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states> 
    
    <s:Group id="contentGroup" left="10" right="10" top="10" bottom="10"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
 
    <s:Rect left="0" right="0" top="0" bottom="0" radiusX="10" radiusY="10"> 
        <s:stroke> 
            <s:SolidColorStroke color="0x000000" weight="1"/> 
        </s:stroke> 
    </s:Rect> 
 
</s:Skin>

You can add a border to a container by using the BorderContainer class. The BorderContainer class is a subclass of the SkinnableContainer class. Because you use CSS styles and class properties to control the appearance of the BorderContainer class, you typically do not create a custom skin for it.

The following example shows that the BorderContainer class takes the cornerRadius, borderColor, borderVisible, and borderAlpha style properties in addition to layout properties, which let you define a border similar to that shown in the previous example without skinning:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/BorderContainerExample.mxml --> 
<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"> 
    
    <s:BorderContainer width="200" height="200" 
        borderColor="0x000000" 
        borderAlpha="1" 
        cornerRadius="10" 
        borderWeight="1"> 
 
 
<s:layout> 
 
 
 
<s:HorizontalLayout 
paddingLeft="15" paddingRight="15" 
paddingTop="15" paddingBottom="15"/> 
 
 
</s:layout> 
 
        <s:Button label="Click Me"/> 
 
    </s:BorderContainer> 
    
</s:Application>
Because the border-related properties are styles, you can use CSS to define a consistent border across all instances of the BorderContainer class; for example:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/BorderContainerStyleExample.mxml --> 
<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"> 
    
    <fx:Style> 
        @namespace s "library://ns.adobe.com/flex/spark"; 
        
        s|BorderContainer { 
            borderColor:#000000; 
            borderAlpha:1; 
            cornerRadius:10; 
            borderWeight:1; 
        }       
    </fx:Style> 
    <s:BorderContainer width="200" height="200"> 
 
 
<s:layout> 
 
 
 
<s:HorizontalLayout 
paddingLeft="15" paddingRight="15" 
paddingTop="15" paddingBottom="15"/> 
 
 
</s:layout> 
        <s:Button label="Click Me"/> 
    </s:BorderContainer> 
    
</s:Application>

To add a border to a single instance of a container or group, you are not required to create a custom skin for the container. You can instead add a Rect graphic in the application itself. This is a process known as composition, where you are adding graphical elements to the application that change the appearance of controls in the application. Sometimes skinning gives a better abstraction because it distinguishes between visual children and content children.

For example, you can define MXML graphic tags fragment that draws the graphic elements that make up the border. These graphic elements are typically a stroke or fill (or both).

The following example draws a border around a VGroup that contains Button controls without using a custom skin class:
<?xml version="1.0"?> 
<!-- SparkSkinning/SimpleBorderExampleNoSkin.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:Group> 
        <!-- border/background graphics --> 
        <s:Rect width="100%" height="100%"> 
               <s:stroke> 
                   <s:SolidColorStroke color="0x000000" weight="2"/> 
               </s:stroke> 
        </s:Rect> 
 
        <!-- content of container --> 
        <s:VGroup left="10" top="10" right="10" bottom="10"> 
            <s:Button label="Click Me"/> 
            <s:Button label="Click Me"/> 
        </s:VGroup> 
    </s:Group> 
 
</s:Application>

Adding drop shadows to Spark containers

To add a drop shadow to a Spark container, you use the DropShadowFilter class. You add the filter to one of the graphic elements defined on that container's skin class. The DropShadowFilter class supports the alpha, angle, distance, and other properties that let you customize the appearance of the drop shadow. In addition, you add a fill to the graphic element. The fill defines the color of the drop shadow. These settings are all in the custom skin class and not in the main application.

The following example defines a drop shadow on the container's skin:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/DropShadowBorderExample.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:SkinnableContainer id="myContainer" 
        height="200" width="200" 
        skinClass="mySkins.DropShadowBorderSkin"> 
        <s:Button label="Click Me"/> 
    </s:SkinnableContainer> 
 
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/DropShadowBorderSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal"/> 
        <s:State name="disabled"/> 
    </s:states> 
 
    <!-- drop shadow --> 
    <s:Rect left="0" top="0" right="0" bottom="0"> 
        <s:filters> 
            <s:DropShadowFilter 
                blurX="20" blurY="20" 
                alpha="0.32" 
                distance="11" 
                angle="90"                 
                knockout="true"/> 
        </s:filters> 
        <s:fill> 
            <s:SolidColor color="0x000000"/> 
        </s:fill> 
    </s:Rect> 
    
    <!-- layer 1: border --> 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0x000000" alpha="0.50" weight="1"/> 
        </s:stroke> 
    </s:Rect> 
 
    <s:Group id="contentGroup" left="0" right="0" top="0" bottom="0"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
</s:Skin>

In this example, the drop shadow is the first element on the skin. You can use the depth property to change the ordering of the elements. This property determines the order in which items inside of containers are rendered. Spark containers order their items based on their depth property, with the lowest depth in the back, and the higher in the front. Items with the same depth value appear in the order they are added to the container.

A more efficient alternative to the DropShadowFilter class is to use the dropShadowEnabled property to add a drop shadow. For more information, see Using the dropShadowEnabled property

You can also add a drop shadow to any individual container in the application itself through composition. The downside to doing it this way is that the drop shadow is applied to a single instance of the container, and not all containers. It also removes the benefits of abstraction that skins provide.

Adding padding to Spark containers

Another common use of Spark skins is modify the padding of Spark containers.

To add padding, you can set the offset properties of the container's content group. These properties are top, bottom, left, and right.

The following example sets the offsets of the contentGroup to 10 so that the contents of the container are padded by 10 pixels in all directions:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/PaddedContainerBorderExample.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:SkinnableContainer id="myContainer" 
        height="200" width="200" 
        skinClass="mySkins.ContainerPaddedBorderSkin" 
    > 
        <s:Button label="Click Me"/> 
    </s:SkinnableContainer> 
 
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/ContainerPaddedBorderSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states> 
    
    <s:Group id="contentGroup" left="10" right="10" top="10" bottom="10"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0x000000" weight="1"/> 
        </s:stroke> 
    </s:Rect> 
 
</s:Skin>

You can also add padding to the layout object. For example, the VerticalLayout object takes paddingLeft, paddingRight, paddingBottom, and paddingTop properties.

Specifying the layout in the skin changes the default layout for this container. However, you can set it on the container instance itself to override the default layout that you set in the skin.

Related information

Adding scroll bars to Spark containers

Spark containers do not have scroll bars by default. To add scroll bars to a Spark container, use the Scroller class.

To add scroll bars to a container's skin class, you wrap the content group or other element in a Scroller tag. The child of a Scroller tag must implement the IViewport interface. This interface is implemented by Group and DataGroup, and some components such as the RichEditableText control.

To ensure that scroll bars appear, do not set explicit sizes on the group. If you set the size of the group explicitly, then that size becomes the size of the Group and no scroll bars will be shown.

The following example defines the size of the application, and sizes the container to a percent of the overall height and width. The scroller is constrained to the size of the viewport (in this case, the content group). Because the image is larger than the size of its parent, Flex displays scroll bars.
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/ScrollbarContainerExample.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" height="200" width="200"> 
        
        <s:SkinnableContainer id="myContainer" 
            height="50%" width="50%" 
            skinClass="mySkins.ScrollBarContainerSkin"> 
            <s:Image source="@Embed(source='../assets/myImage.jpg')"/> 
        </s:SkinnableContainer> 
        
 </s:Application>
This example uses the following custom skin class:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/ScrollBarContainerSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal"/> 
        <s:State name="disabled"/> 
    </s:states> 
 
    <s:Scroller height="100%" width="100%">    
        <s:Group id="contentGroup"> 
            <s:layout> 
                <s:BasicLayout/> 
            </s:layout> 
        </s:Group> 
    </s:Scroller> 
</s:Skin>

The Scroller layout does not support the left/right/top/bottom constraints on the content group of the container. And in general, you should not explicitly set the height and width properties of the Scroller's content group. Instead, you should let the content group size naturally.

You can use the minViewportInset property on the Scroller class to inset the viewport relative to its Scroller along the edges where a scroll bar does not already keep the viewport away from the edge. For example, if you set the minViewportInset property to 10, then the right edge of the viewport would be 10 pixels from the right edge of its scroller, unless the vertical scroll bar was visible. When a scroll bar is visible, the viewport and the scroll bar would not have any space between them.

Related information

Adding background fills and images to Spark containers

To add background fills and background images to a Spark container's skin, you add a Rect or other graphic element and add the fill to that element.

To add a color fill, you add a graphic element that is a subclass of FilledElement and define a child fill tag. Subclasses of FilledElement include Rect, Ellipse, and Path. The fill must implement the IFill interface, which includes the BitmapFill, LinearGradient, RadialGradient, or SolidColor classes.

The following example defines three containers with three different fills:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/BackgroundFillExample.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" 
    width="700"> 
    
    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 
 
    <s:SkinnableContainer id="myContainer" 
        height="200" width="200" 
        skinClass="mySkins.BackgroundFillSkin"> 
        <s:Button label="Basic Fill"/> 
    </s:SkinnableContainer> 
 
    <s:SkinnableContainer id="myContainer2" 
        height="200" width="200" 
        skinClass="mySkins.RadialBackgroundFillSkin"> 
        <s:Button label="Radial Gradient Fill"/> 
    </s:SkinnableContainer> 
    <s:SkinnableContainer id="myContainer3" 
        height="200" width="200" 
        skinClass="mySkins.LinearBackgroundFillSkin"> 
        <s:Button label="Linear Gradient Fill"/> 
    </s:SkinnableContainer> 
 
</s:Application>

The example uses the following custom skin class for the radial gradient fill:

<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/RadialBackgroundFillSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states> 
 
    <!-- layer 1: border --> 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0" alpha="0.50" weight="1" /> 
        </s:stroke> 
    </s:Rect> 
 
    <!-- background fill --> 
    <s:Rect id="background" left="1" top="1" right="1" bottom="1"> 
        <s:fill> 
            <s:RadialGradient> 
                <s:entries> 
                    <s:GradientEntry color="0xFFAABB" ratio="0" alpha="1"/> 
                    <s:GradientEntry color="0xFFCCDD" ratio=".33" alpha="1"/> 
                    <s:GradientEntry color="0xFFEEFF" ratio=".66" alpha="1"/> 
                </s:entries> 
            </s:RadialGradient>            
        </s:fill> 
    </s:Rect> 
    
    <s:Group id="contentGroup" left="10" right="10" top="10" bottom="10"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
</s:Skin>

The example uses the following custom skin class for the basic background fill:

<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/BackgroundFillSkin.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" alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states> 
 
    <!-- layer 1: border --> 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0" alpha="0.50" weight="1" /> 
        </s:stroke> 
    </s:Rect> 
 
    <!-- background fill --> 
    <s:Rect id="background" left="1" top="1" right="1" bottom="1"> 
        <s:fill> 
            <s:SolidColor id="bgFill" color="0xFF0000"/> 
        </s:fill> 
    </s:Rect> 
    
    <s:Group id="contentGroup" left="10" right="10" top="10" bottom="10"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
</s:Skin>

The example uses the following custom skin class for the linear background fill:

<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/LinearBackgroundFillSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states> 
 
    <!-- layer 1: border --> 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <mx:SolidColorStroke color="0" alpha="0.50" weight="1"/> 
        </s:stroke> 
    </s:Rect> 
 
    <!-- background fill --> 
    <s:Rect id="background" left="1" top="1" right="1" bottom="1"> 
        <s:fill> 
            <s:LinearGradient> 
                <s:entries> 
                    <s:GradientEntry color="0xFFAABB" ratio="0" alpha="1"/> 
                    <s:GradientEntry color="0xFFCCDD" ratio=".33" alpha="1"/> 
                    <s:GradientEntry color="0xFFFFFF" ratio=".66" alpha="1"/> 
                </s:entries> 
            </s:LinearGradient>            
        </s:fill> 
    </s:Rect> 
    
    <s:Group id="contentGroup" left="10" right="10" top="10" bottom="10"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
</s:Skin>

You can also add a background fill to a content group, although the content group often defines an offset or padding properties. These constraints are applied to the background in addition to the content. As a result, you typically add the background fill to a graphic element that is set to the entire height and width of the container.

To add a background image, you can use the BitmapFill class or a BitmapImage fill. The following example adds a background with a BitmapFill object:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/BackgroundFillExample.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:HorizontalLayout/> 
    </s:layout> 
 
    <s:SkinnableContainer id="myContainer" 
        height="256" width="206" 
        skinClass="mySkins.BitmapFillBackgroundSkin" 
    > 
        <s:Button label="Bitmap Fill"/> 
    </s:SkinnableContainer> 
</s:Application>
This example uses the following custom skin class:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/BitmapFillBackgroundSkin.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" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.SkinnableContainer")] 
    </fx:Metadata> 
    
    <s:states> 
        <s:State name="normal" /> 
        <s:State name="disabled" /> 
    </s:states> 
 
    <!-- layer 1: border --> 
    <s:Rect left="0" right="0" top="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0" alpha="0.50" weight="3" /> 
        </s:stroke> 
    </s:Rect> 
 
    <!-- background fill --> 
    <s:Rect id="background" left="3" top="3" right="3" bottom="3" alpha=".25"> 
        <s:fill> 
            <s:BitmapFill source="@Embed(source='../../assets/myImage.jpg')"/> 
        </s:fill> 
    </s:Rect> 
    
    <s:Group id="contentGroup" left="10" right="10" top="10" bottom="10"> 
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout> 
    </s:Group> 
</s:Skin>

In this example, the container is perfectly sized to fit the bitmap background plus the width of the border. In many cases, though, you will have to set properties on the fill to avoid tiling or apply clipping. For more information, see the BitmapFill class in the ActionScript 3.0 Reference for Apache Flex.

The content group of the container's skin is typically the last element defined in the skin class. This is because the skin's layers are applied in the order in which they appear in the skin class. When a skin class has a background fill in a skin class, you add the content layer after it so that the content is not covered by the fill.

You can use the depth property, which gives you more control over the z-index. This lets you rearrange the appearance of the skin's elements so that it is not determined by the child order.

Swapping skins

You cannot unload or remove a skin class from its host component without specifying another skin to take its place. Otherwise, the component would have no definition of its visual parts (and would therefore break the skinning contract).

To swap a skin, you set the component's skinClass property to another skin class.

The following example applies a custom skin to the button when the application starts up. It then toggles the custom skin with the Button control's default skin class when you click the button:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/SimpleUnloadExample.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 spark.skins.spark.ButtonSkin; 
        import mySkins.MyButtonSkin; 
        import flash.utils.*; 
        
        private function toggleSkin():void { 
            if (getQualifiedClassName(myButton.getStyle("skinClass")) == getQualifiedClassName(MyButtonSkin)) { 
                // Load the default skin class. 
                myButton.setStyle("skinClass", Class(ButtonSkin)); 
            } else { 
                // Load the custom skin class. 
                myButton.setStyle("skinClass", Class(MyButtonSkin));          
            } 
        } 
    </fx:Script> 
 
    <s:Button id="myButton" 
        skinClass="mySkins.MyButtonSkin" 
        label="Toggle Skin" 
        color="0x0099FF" 
        click="toggleSkin()"/> 
 
</s:Application>

Transitions with Spark skins

You can use transitions to add visual appeal to your Spark skins. Transitions are triggered off of state changes, which are explicitly supported in Spark skins. All the visuals for a transition should be defined in the skin class and not on the component.

You can use transitions in Spark skins in the same way you would use them in your application. You add a <s:transitions> tag as a child tag of the skin's root tag. You then define the transitions and which states they apply to with the toState and fromState on the <s:Transition> child tags. Because the skin is notified of state changes from the host component, you do not have to add any logic to support state changes to the skin.

Typically, you specify the values for transitions in states and not in the effect itself.

The following example uses the Resize transition to grow and shrink the Button control's label and border. The transitions are triggered when the user rolls the pointer over the Button control and when the user rolls the pointer off of the Button control.
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/ButtonTransitionExample.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" 
    height="200" width="200"> 
    
    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 
 
    <s:Button label="Click Me" 
        skinClass="mySkins.ButtonTransitionSkin"/> 
    
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning\mySkins\ButtonTransitionSkin.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"> 
 
    <s:transitions> 
        <s:Transition fromState="up" toState="over"> 
            <s:Resize target="{buttonBorder}"/> 
        </s:Transition> 
        <s:Transition fromState="over" toState="up"> 
            <s:Resize target="{buttonBorder}"/> 
        </s:Transition> 
    </s:transitions> 
 
    <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 id="buttonBorder" width="69" width.over="79" height="20" radiusX="2" radiusY="2"> 
        <s:stroke> 
            <s:SolidColorStroke color="0x000000" weight="1"/> 
        </s:stroke> 
    </s:Rect> 
    
    <!-- layer 8: text --> 
    <s:Label id="labelDisplay" 
        horizontalCenter="0" verticalCenter="1"> 
    </s:Label> 
</s:Skin>

You typically declare the state values in the object or skin, and the effects use these values. The previous example defines the width.over property on the Rect rather than using the widthBy property of the effect to set the target width.

When using transitions inside Spark skins, you might have to assign IDs to elements that do not by default have IDs. This is because transitions require a target when you define them, and without an ID, you cannot target a specific element.

As with standard Flex controls, you can use transitions with multiple effects that are playing in parallel on a Spark skin. The following example resizes and changes the color of the button's label when the user moves their mouse over or out of the Button:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/ButtonParallelTransitionExample.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" 
    height="200" width="200"> 
    
    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 
 
    <s:Button label="Click It or Ticket" 
        skinClass="mySkins.ButtonParallelTransitionSkin"/> 
    
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/ButtonParallelTransitionSkin.mxml --> 
<s:SparkSkin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minWidth="21" minHeight="21" 
    alpha.disabled="0.5"> 
 
    <s:transitions> 
        <s:Transition> 
            <s:Parallel target="{labelDisplay}"> 
                    <s:Animate> 
                        <s:SimpleMotionPath property="fontSize"/>                
                    </s:Animate> 
                    <s:AnimateColor/> 
            </s:Parallel> 
        </s:Transition> 
    </s:transitions> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.Button")] 
    </fx:Metadata> 
    
    <fx:Script> 
        static private const exclusions:Array = ["labelDisplay"]; 
        override public function get colorizeExclusions():Array {return exclusions;}        
    </fx:Script> 
    
    <s:states> 
        <s:State name="up" /> 
        <s:State name="over" /> 
        <s:State name="down" /> 
        <s:State name="disabled" /> 
    </s:states> 
    
    <!--  The following values are negative because they define a border or drop shadow. 
           The negative values separate them from the Button's bounds. --> 
    <s:Rect left="-1" right="-1" top="-1" bottom="-1" radiusX="2" radiusY="2"> 
        <s:fill> 
            <s:LinearGradient rotation="90"> 
                    <s:GradientEntry color="0x000000" 
                                   color.down="0xFFFFFF" 
                                   alpha="0.01" 
                                   alpha.down="0" /> 
                    <s:GradientEntry color="0x000000" 
                                   color.down="0xFFFFFF" 
                                   alpha="0.07" 
                                   alpha.down="0.5" /> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Rect> 
    
    <s:Rect left="1" right="1" top="1" bottom="1" radiusX="2" radiusY="2"> 
        <s:fill> 
            <s:LinearGradient rotation="90"> 
                <s:GradientEntry color="0xFFFFFF" 
                               color.over="0xBBBDBD" 
                               color.down="0xAAAAAA" 
                               alpha="0.85" /> 
                <s:GradientEntry color="0xD8D8D8" 
                               color.over="0x9FA0A1" 
                               color.down="0x929496" 
                               alpha="0.85" /> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Rect> 
    
    <s:Rect left="1" right="1" bottom="1" height="9" radiusX="2" radiusY="2"> 
        <s:fill> 
            <s:LinearGradient rotation="90"> 
                <s:GradientEntry color="0x000000" alpha="0.0099" /> 
                <s:GradientEntry color="0x000000" alpha="0.0627" /> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Rect> 
    
    <s:Rect left="1" right="1" top="1" height="9" radiusX="2" radiusY="2"> 
        <s:fill> 
            <s:SolidColor color="0xFFFFFF" 
                        alpha="0.33" 
                        alpha.over="0.22" 
                        alpha.down="0.12" /> 
        </s:fill> 
    </s:Rect> 
    
    <s:Rect left="1" right="1" top="1" bottom="1" 
        radiusX="2" radiusY="2" excludeFrom="down"> 
        <s:stroke> 
            <s:LinearGradientStroke rotation="90" weight="1"> 
                <s:GradientEntry color="0xFFFFFF" alpha.over="0.22" /> 
                <s:GradientEntry color="0xD8D8D8" alpha.over="0.22" /> 
            </s:LinearGradientStroke> 
        </s:stroke> 
    </s:Rect> 
    
    <s:Rect left="1" top="1" bottom="1" width="1" includeIn="down"> 
        <s:fill> 
            <s:SolidColor color="0x000000" alpha="0.07" /> 
        </s:fill> 
    </s:Rect> 
    <s:Rect right="1" top="1" bottom="1" width="1" includeIn="down"> 
        <s:fill> 
            <s:SolidColor color="0x000000" alpha="0.07" /> 
        </s:fill> 
    </s:Rect> 
    <s:Rect left="2" top="1" right="2" height="1" includeIn="down"> 
        <s:fill> 
            <s:SolidColor color="0x000000" alpha="0.25" /> 
        </s:fill> 
    </s:Rect> 
    <s:Rect left="1" top="2" right="1" height="1" includeIn="down"> 
        <s:fill> 
            <s:SolidColor color="0x000000" alpha="0.09" /> 
        </s:fill> 
    </s:Rect> 
    
    <s:Rect left="0" right="0" top="0" bottom="0" 
        width="69" height="20" 
        radiusX="2" radiusY="2"> 
        <s:stroke> 
            <s:LinearGradientStroke rotation="90" weight="1"> 
                <s:GradientEntry color="0x000000" 
                               alpha="0.5625" 
                               alpha.down="0.6375" /> 
                <s:GradientEntry color="0x000000" 
                               alpha="0.75" 
                               alpha.down="0.85" /> 
            </s:LinearGradientStroke> 
        </s:stroke> 
    </s:Rect> 
 
    <s:Label id="labelDisplay" 
             fontSize="{hostComponent.getStyle('fontSize')}" 
             fontSize.over="{hostComponent.getStyle('fontSize') + 4}" 
             color.over="0xFF0000" 
             textAlign="center" 
             verticalAlign="middle" 
             lineBreak="toFit" 
             maxDisplayedLines="1" 
             horizontalCenter="0" verticalCenter="1" 
             left="10" right="10" top="2" bottom="2"> 
    </s:Label> 
</s:SparkSkin>

The previous example uses the SparkSkin class rather than the Skin class as its root because it defines the colorExclusions() getter.

You can use transitions and states to effectively add and remove elements from the skin. For example, if you want an image to appear on a Button control during a mouse over, you can change the value of a property height from 0 to a new value in the over state.

The following example displays an image of a butterfly when you move the mouse over the Button control, and removes the image when you move the mouse out:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/ButterflySkinExample.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" 
    backgroundColor="0x999999"> 
 
    <s:Button id="myButton" 
        fontWeight="bold" 
        color="0xFFFFFF" 
        label="Bug of the Day" 
        skinClass="mySkins.ButterflySkin"/> 
 
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/ButterflySkin.mxml --> 
<s:SparkSkin 
    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" alpha.disabled="0.5"> 
    
    <!-- host component --> 
    <fx:Metadata> 
        <![CDATA[ 
        [HostComponent("spark.components.Button")] 
        ]]> 
    </fx:Metadata> 
 
    <s:transitions> 
        <mx:Transition fromState="up" toState="over"> 
            <mx:Parallel duration="350"> 
                <s:Resize target="{ myImage }"/> 
                <s:Fade targets="{ [myImage,labelDisplay] }"/> 
            </mx:Parallel> 
        </mx:Transition> 
        <mx:Transition fromState="over" toState="up"> 
            <mx:Parallel duration="200"> 
                <s:Resize target="{ myImage }"/> 
                <s:Fade targets="{ [myImage,labelDisplay] }"/> 
            </mx:Parallel> 
        </mx:Transition> 
    </s:transitions> 
 
    <fx:Script> 
        <![CDATA[         
            static private const exclusions:Array = ["labelDisplay"]; 
            
            override public function get colorizeExclusions():Array {return exclusions;} 
            override protected function initializationComplete():void  { 
                useChromeColor = true; 
                super.initializationComplete(); 
            }  
            
            override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void  { 
                var cr:Number = getStyle("cornerRadius");                
                super.updateDisplayList(unscaledWidth, unscaledHeight); 
            } 
            
            private var cornerRadius:Number = 2; 
        ]]>        
    </fx:Script> 
    
    <!-- states --> 
    <s:states> 
        <s:State name="up" /> 
        <s:State name="over" /> 
        <s:State name="down" /> 
        <s:State name="disabled" /> 
    </s:states> 
            
    <s:Rect id="blueRect" radiusX="8" radiusY="8" top="0" right="0" bottom="0" left="0" minHeight="30"> 
        <s:fill> 
            <s:LinearGradient x="0" y="0" scaleX="44" rotation="90"> 
                <s:GradientEntry color="#3399ff" ratio="0" color.over="#66CCFF"/> 
                <s:GradientEntry color="#3366cc" ratio="1" color.over="#3399CC"/> 
            </s:LinearGradient> 
        </s:fill> 
        <s:stroke> 
            <s:SolidColorStroke color="#ffffff" weight="2"/> 
        </s:stroke> 
    </s:Rect> 
    <!-- Border --> 
    <s:Rect radiusX="6" radiusY="6" 
        top="2" right="2" height="15" left="2"> 
        <s:fill> 
            <s:LinearGradient x="0" y="0" scaleX="23" rotation="90"> 
                <s:GradientEntry color="#ffffff" ratio="0" alpha=".3"/> 
                <s:GradientEntry color="#ffffff" ratio="1" alpha=".1"/> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Rect> 
 
    <s:Image id="myImage" 
        height="0" height.over="90" 
        source="@Embed(source='../../assets/butterfly.png')" 
        left="20"/> 
    <s:Label id="labelDisplay" 
        visible.over="false" 
        textAlign="center" verticalAlign="middle" 
        maxDisplayedLines="1" 
        horizontalCenter="0" verticalCenter="1" 
        left="10" right="10" top="2" bottom="2"/> 
    
</s:SparkSkin>
Related information

Subcomponent skinning

Some components are composites of other components. They use other components as part of their user interface. For example, a NumericStepper consists of a Button control with a down arrow, a Button control with an up arrow, and a TextInput control that displays the current value. The components that make up a composite component's user interface are known as subcomponents.

To skin Spark subcomponents you edit the skin parts in the skin class. Each subcomponent corresponds to a skin part that is defined on the host component. For example, to customize the button skin parts in a NumericStepperSkin class, you can set the value of their skinClass property to a custom class.

In many cases, the subcomponents that are defined in the main skin have their own skin classes. This is common if the subcomponents need to define their own appearance based on their state. For example, the buttons in a NumericStepper should react to their own up and down states rather than the up and down states of the composite component.

When skinning subcomponents, you typically use the SparkSkin class rather than the Skin class as the root. This is because Spark components usually rely on the colorize exclusions and other properties that require the SparkSkin class.

The following example defines custom subcomponent skins for the up and down buttons in a NumericStepper composite skin:
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/NumericStepperSkinPartExample.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" 
    height="200" width="200"> 
    
    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 
 
    <s:SkinnableContainer id="myContainer" 
        height="200" width="200"> 
        <s:NumericStepper skinClass="mySkins.NumericStepperSkin"/> 
    </s:SkinnableContainer> 
</s:Application>
The following composite skin class is nearly identical to the default NumericStepperSkin except that it defines custom skins for the button subcomponents. In this case, the spark.skins.spark.SpinnerDecrementButtonSkin and spark.skins.spark.SpinnerIncrementButtonSkin classes are replaced with mySkins.SpinnerDownButton and mySkins.SpinnerUpButton respectively.
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/NumericStepperSkin.mxml --> 
<s:SparkSkin 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    minHeight="24" 
    alpha.disabled="0.5"> 
 
    <fx:Metadata> 
        [HostComponent("spark.components.NumericStepper")] 
    </fx:Metadata> 
    
    <fx:Script> 
        /* 
           Define the skin elements that should not be colorized. 
           For numeric stepper, the skin itself is colorized but the individual parts are not. 
        */ 
        static private const exclusions:Array = ["textInput", "decrementButton", "incrementButton"]; 
        override public function get colorizeExclusions():Array {return exclusions;} 
    </fx:Script> 
    
    <s:states> 
        <s:State name="normal"/> 
        <s:State name="disabled"/> 
    </s:states> 
 
    <s:Button id="incrementButton" right="0" top="0" height="50%" 
              skinClass="mySkins.SpinnerUpButton"/> 
    <s:Button id="decrementButton" right="0" bottom="0" height="50%" 
              skinClass="mySkins.SpinnerDownButton"/> 
              
    <s:TextInput id="textDisplay" left="0" top="0" right="18" bottom="0" 
        skinClass="spark.skins.spark.NumericStepperTextInputSkin"/> 
        
</s:SparkSkin>
The following skin classes replace the SpinnerIncrementButtonSkin and SpinnerDecrementButtonSkin classes in the custom NumericStepperSkin example. Instead of drawing arrows, these custom skins use + and - signs for incrementing and decrementing the value of the TextInput control.
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/SpinnerDownButton.mxml --> 
<s:SparkSkin 
    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: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> 
    
    <!-- border/fill --> 
    <s:Path data="M 0 0 h 18 v 8 Q 18 9 16 10 h -16 Z" 
          left="0" top="0" right="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0x686868" weight="1"/> 
        </s:stroke> 
        <s:fill> 
            <s:LinearGradient rotation="90"> 
                <s:GradientEntry color="0xE8E8E8" 
                               color.over="0xC2C2C2" 
                               color.down="0xAEB0B1" /> 
                <s:GradientEntry color="0xDFDFDF" 
                               color.over="0xADAEAF" 
                               color.down="0xA1A3A5" /> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Path>    
    
    <!-- highlight --> 
    <s:Path data="M 0 0 h 16 v 6 Q 16 8 14 8 h -14 Z" 
          left="1" top="1" right="1" bottom="1" > 
        <s:stroke> 
            <s:LinearGradientStroke rotation="90" weight="1"> 
                <s:GradientEntry color="0xFFFFFF" 
                               color.down="0x000000" 
                               alpha="0.55" 
                               alpha.over="0.55" 
                               alpha.down="0.15" /> 
                <s:GradientEntry color="0xFFFFFF" 
                               color.down="0x000000" 
                               alpha="0.2475" 
                               alpha.over="0.2475" 
                               alpha.down="0" /> 
            </s:LinearGradientStroke> 
        </s:stroke> 
    </s:Path> 
    
    <!-- shadow --> 
    <s:Rect left="1" top="2" right="1" height="1" includeIn="down"> 
        <s:fill> 
            <s:SolidColor color="0x000000" alpha="0.07" /> 
        </s:fill> 
    </s:Rect> 
    
    <!-- Replace the down arrow with a minus sign. --> 
    <s:Label id="downArrow" 
        text="-" 
        horizontalCenter="0" 
        verticalCenter="0"> 
    </s:Label> 
 
</s:SparkSkin>
<?xml version="1.0" encoding="utf-8"?> 
<!-- SparkSkinning/mySkins/SpinnerUpButton.mxml --> 
<s:SparkSkin 
    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:Metadata> 
        [HostComponent("spark.components.Button")] 
    </fx:Metadata> 
    
    <fx:Script> 
    </fx:Script> 
    
    <s:states> 
        <s:State name="up" /> 
        <s:State name="over"/> 
        <s:State name="down" /> 
        <s:State name="disabled" /> 
    </s:states> 
    
    <!-- border/fill --> 
    <s:Path data="M 0 0 h 18 v 8 Q 18 9 16 10 h -16 Z" 
          left="0" top="0" right="0" bottom="0"> 
        <s:stroke> 
            <s:SolidColorStroke color="0x686868" weight="1"/> 
        </s:stroke> 
        <s:fill> 
            <s:LinearGradient rotation="90"> 
                <s:GradientEntry color="0xE8E8E8" 
                               color.over="0xC2C2C2" 
                               color.down="0xAEB0B1" /> 
                <s:GradientEntry color="0xDFDFDF" 
                               color.over="0xADAEAF" 
                               color.down="0xA1A3A5" /> 
            </s:LinearGradient> 
        </s:fill> 
    </s:Path>    
    
    <!-- highlight --> 
    <s:Path data="M 0 0 h 16 v 6 Q 16 8 14 8 h -14 Z" 
          left="1" top="1" right="1" bottom="1" > 
        <s:stroke> 
            <s:LinearGradientStroke rotation="90" weight="1"> 
                <s:GradientEntry color="0xFFFFFF" 
                               color.down="0x000000" 
                               alpha="0.55" 
                               alpha.over="0.55" 
                               alpha.down="0.15" /> 
                <s:GradientEntry color="0xFFFFFF" 
                               color.down="0x000000" 
                               alpha="0.2475" 
                               alpha.over="0.2475" 
                               alpha.down="0" /> 
            </s:LinearGradientStroke> 
        </s:stroke> 
    </s:Path> 
    
    <!-- shadow --> 
    <s:Rect left="1" top="2" right="1" height="1" includeIn="down"> 
        <s:fill> 
            <s:SolidColor color="0x000000" alpha="0.07" /> 
        </s:fill> 
    </s:Rect> 
    
    <!-- Replace the up arrow with a plus sign. --> 
    <s:Label id="upArrow" 
        text="+" 
        horizontalCenter="0" 
        verticalCenter="0"> 
    </s:Label> 
 
</s:SparkSkin>

Packaging skins

You can package custom Spark skins as a SWC file and distribute that SWC file to anyone interested in using your library of skins. The SWC file is also known as a theme SWC file. Theme SWC files for Spark skins typically consist of the following files:
  • defaults.css

  • One or more skin classes

The defaults.css file file can apply the Spark skin classes and any other style properties. The following sample CSS file from a theme SWC file applies a style property and custom skins to the Button and CheckBox controls in the Spark namespace:
@namespace s "library://ns.adobe.com/flex/spark"; 
s|Button { 
	color: Green; 
	skinClass: ClassReference("ButtonTransitionSkin"); 
} 
s|CheckBox { 
	color: Green; 
	skinClass: ClassReference("CheckBoxTransitionSkin"); 
}

To include classes that are compiled, such as Spark skins, in a theme SWC file, you use the include-classes compiler option. To include files that are not compiled, such as a stylesheet, you use the include-file compiler option. All files included in a theme SWC file must be in the source path. You use the output compiler option to specify the location of the resulting SWC file.

The following command line compiles a new theme SWC file. This theme SWC file includes two Spark skins and a defaults.css file:
compc -source-path c:/temp/myskins 
	-include-classes ButtonTransitionSkin CheckBoxTransitionSkin 
	-include-file defaults.css c:/temp/myskins/defaults.css 
	-output c:/temp/myskins/MySkins.swc
To use the theme SWC file in your application, you use the theme compiler option, as the following example shows:
mxmlc -theme=c:/temp/myskins/MySkins.swc c:myapps/ThemeExample.mxml

Navigation

Using Flex » Enhancing the user interface

Adobe and Adobe Flash Platform are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries and are used by permission from Adobe. No other license to the Adobe trademarks are granted.