About Spark skinsIn 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 skinsYou 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 classCustom 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:
In addition to these elements, Spark skins can contain MXML language tags such as Declarations and Library. Root tagsSkin 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 componentsSpark 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 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. Skin statesSkin 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 partsSkin 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. LayoutsBoth 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. SubcomponentsThe Spark skin classes typically include graphic elements and other components that make up the appearance of the skin. Script blocksOptionally, the Spark skin class can include a Script block for skin-specific logic. Most
Spark skins have a special <fx:Script> block
at the top of the skin class. This block typically defines style
properties that the skin class respects, including the exclusions
that the skin uses. The tag includes a special attribute, fb:purpose="styling":
<fx:Script fb:purpose="styling"> This attribute is used by Flash Builder. When you create a copy of a skin class in Flash Builder, you can opt to make the skin styleable. If you choose to make it styleable, Flash Builder includes this section of the skin class. If you choose not to make the skin styleable, Flash Builder excludes this section. Language tagsLike 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 classesWhile 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:
Skins typically follow the naming convention componentNameSkin.mxml. In the ActionScript 3.0 Reference for the Adobe Flash Platform, most skins have several versions. For example, there are four classes named ButtonSkin. The default skins for the Spark components are in the spark.skins.spark.* package. Flex 4 also ships with several themes that use some of the skinning packages. For more information, see About the included theme files. Skinning contractThe 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:
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 componentsSpark 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 statesEach 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 componentPart 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 classThe 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:
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 partsSome 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 labelDisplay:TextBase; 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 componentAfter you have defined a skin, you can apply it to a component
in one of the following ways:
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 partsThe 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. |
|