Many Flex components, such as Button, Slider, and NumericStepper, support stateful skins. A stateful skin uses view states to specify the skins for the different states of the component. For more information on view states, see Using View States.
You can determine whether a skin property supports stateful skins from its description in the Adobe Flex Language Reference. For example, all stateful skin properties contain a sentence in the form shown below for the TitleWindow.closeButtonSkin property:
You can use the closeButtonSkin style to assign the skin for the following skin states: disabled, down, over, up.
To function as a stateful skin, the skin must implement the IStateClient interface. Since that interface is implemented by the UIComponent class, you can use any subclass of UIComponent to define a stateful skin. You then assign the stateful skin class to a stateful skin property of the component.
For example, a Button control has eight possible states, and eight associated skins. To create a single skin class that defines the skins for all eight states, you create a skin based on the UIComponent control. You then define eight view states within your skin where the name of each view state corresponds to a state of the Button control, as the following example shows:
<?xml version="1.0"?>
<mx:UIComponent xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="down">
</mx:State>
<mx:State name="over">
</mx:State>
...
<mx:State name="selectedUp">
</mx:State>
</mx:states>
<mx:Script>
<![CDATA[
<!-- Define the skin by using the Flash drawing API. -->
]]>
</mx:Script>
</mx:UIComponent>
After defining your stateful skin, you assign it to the skin style property of the control. You can assign the stateful control by using CSS, the setStyle() method, by using inline styles, or by using the StyleManager class. The following example sets it by using CSS:
<?xml version="1.0"?>
<!-- skins/ApplyButtonStatefulSkinAll.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Button {
skin: ClassReference("myComponents.MyButtonStatefulSkinAll");
}
</mx:Style>
<mx:Button label="Hello" id="b" />
</mx:Application>
The executing SWF file for the previous example is shown below:
For more information, see Applying skins.
You do not have to define all eight skins; you only define the skins that you want to create. For all others, you use the default skins supplied with Flex. The following stateful skin component only defines skins for the up and over states of the Button control:
<?xml version="1.0"?>
<mx:UIComponent xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:states>
<mx:State name="up">
</mx:State>
<mx:State name="over">
</mx:State>
</mx:states>
...
</mx:UIComponent>
You then specify this component as the value of the skin style property, and specify the default skins for the remaining six properties, as the following example shows:
<?xml version="1.0"?>
<!-- skins/SimpleButtonStatefulSkin.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Button {
skin: ClassReference("ButtonUpStatefulSkin");
downSkin: ClassReference("mx.skins.halo.ButtonSkin");
disabledSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedUpSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedOverSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedDownSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedDisabledSkin: ClassReference("mx.skins.halo.ButtonSkin");
}
</mx:Style>
<mx:Button id="b1" label="Click Me"/>
</mx:Application>
The executing SWF file for the previous example is shown below:
A stateful skin is a programmatic skin, so you have to define it using the rules defined in the section Programmatic skins recipe. That means you have to define an override of the updateDisplayList() method, and for an ActionScript class, you also define a constructor.
To create a view state, you define a base view state, and then define a set of changes, or overrides, that modify the base view state to define each new view state. Each new view state can modify the base state by adding or removing child components, by setting style and property values, or by defining state-specific event handlers.
One of the most common ways to define stateful skins is to define the skin with several properties or styles that can be modified by each view state. For example, the following stateful skin defines a property to control the line weight, fill color, and drop shadow for a skin used by the Button control:
<?xml version="1.0"?>
<!-- skins/myComponents/MyButtonStatefulSkin.mxml -->
<mx:UIComponent xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import flash.filters.DropShadowFilter;
// Define a drop shadow for the over and down states.
[Bindable]
private var myFilter:DropShadowFilter = new DropShadowFilter(0);
// Define a private var for line weight.
private var _lineWeight:Number = 1;
// Define public setter and getter for line weight.
public function get lineWeight():Number
{
return _lineWeight;
}
public function set lineWeight(value:Number):void
{
_lineWeight = value;
invalidateDisplayList();
}
// Define a private var for the fill color.
private var _rectFill:uint = 0x00FF00;
// Define public setter and getter for fill color.
public function get rectFill():uint
{
return _rectFill;
}
public function set rectFill(value:uint):void
{
_rectFill = value;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
graphics.lineStyle(lineWeight, 0x0066FF);
graphics.beginFill(rectFill, 0.50);
graphics.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, 10, 10);
filters = [myFilter];
}
]]>
</mx:Script>
<mx:states>
<mx:State name="up">
</mx:State>
<mx:State name="over">
<mx:SetProperty target="{this}"
name="rectFill" value="0x00CC33"/>
<mx:SetProperty target="{myFilter}"
name="distance" value="4"/>
</mx:State>
<mx:State name="down">
<mx:SetProperty target="{this}"
name="rectFill" value="0x00CC33"/>
<mx:SetProperty target="{myFilter}"
name="inner" value="true"/>
<mx:SetProperty target="{myFilter}"
name="distance" value="2"/>
</mx:State>
</mx:states>
</mx:UIComponent>
This examples defines skins the for following states:
up
Does not define any changes; therefore, the base view state defines the skin for the up state.
over
Changes the fill color to 0x00CC33, sets the line width to 2 pixels, and creates a 2-pixel wide drop shadow.
down
Changes the fill color to 0x00CC33, sets the drop shadow type to inner, and creates a 2-pixel wide drop shadow.
The following application uses this skin:
<?xml version="1.0"?>
<!-- skins/ApplyButtonStatefulSkin.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Style>
Button {
skin: ClassReference("myComponents.MyButtonStatefulSkin");
disabledSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedUpSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedOverSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedDownSkin: ClassReference("mx.skins.halo.ButtonSkin");
selectedDisabledSkin: ClassReference("mx.skins.halo.ButtonSkin");
}
</mx:Style>
<mx:Button label="Hello" id="b" />
</mx:Application>
The executing SWF file for the previous example is shown below:
You can use images in a stateful skin where a change of state causes the skin to display a different image. One issue when using images is that the base view state must contain the image so that when the skin is first created it has values for the measuredWidth and measuredHeight properties, otherwise Flex sets the height and width of the skin to 0.
In the following example, you embed images for the up, over, down, and disabled states of the Button control:
<?xml version="1.0" encoding="utf-8"?>
<!-- skins/myComponents/MyButtonStatefulSkinImages.mxml -->
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
// Embed the skin images.
[Bindable]
[Embed(source="../../assets/orb_up_skin.gif")]
private var buttonUp:Class;
[Bindable]
[Embed(source="../../assets/orb_over_skin.gif")]
private var buttonOver:Class;
[Bindable]
[Embed(source="../../assets/orb_down_skin.gif")]
private var buttonDown:Class;
[Bindable]
[Embed(source="../../assets/orb_disabled_skin.gif")]
private var buttonDisabled:Class;
]]>
</mx:Script>
<mx:states>
<mx:State name="up"/>
<mx:State name="notBase">
<mx:RemoveChild target="{baseButton}"/>
</mx:State>
<mx:State name="over" basedOn="notBase">
<mx:AddChild creationPolicy="all">
<mx:Image source="{buttonOver}"
maintainAspectRatio="false"
width="100%" height="100%"/>
</mx:AddChild>
</mx:State>
<mx:State name="down" basedOn="notBase">
<mx:AddChild creationPolicy="all">
<mx:Image source="{buttonDown}"
maintainAspectRatio="false"
width="100%" height="100%"/>
</mx:AddChild>
</mx:State>
<mx:State name="disabled" basedOn="notBase">
<mx:AddChild creationPolicy="all">
<mx:Image source="{buttonDisabled}"
maintainAspectRatio="false"
width="100%" height="100%"/>
</mx:AddChild>
</mx:State>
</mx:states>
<mx:Image id="baseButton"
width="100%" height="100%"
source="{buttonUp}"
maintainAspectRatio="false"/>
</mx:Canvas>
In this example the skin performs the following actions:
View states let you change appearance of a component, typically in response to a user action. Transitions define how a change of view state looks as it occurs on the screen. You define a transition by using the effect classes, in combination with several effects designed explicitly for handling transitions. For more information on transitions, see Using Transitions.
In the following example, you add a transition to the stateful skin definition from the previous section. In this example, the transition defines a 100 ms animation to occur when changing the fill color of the skin:
<?xml version="1.0"?>
<!-- skins/myComponents/MyButtonStatefulSkinTrans.mxml -->
<mx:UIComponent xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import flash.filters.DropShadowFilter;
// Define a drop shadow for the over and down states.
[Bindable]
private var myFilter:DropShadowFilter = new DropShadowFilter(0);
// Define a private var for line weight.
private var _lineWeight:Number = 1;
// Define public setter and getter for line weight.
public function get lineWeight():Number
{
return _lineWeight;
}
public function set lineWeight(value:Number):void
{
_lineWeight = value;
invalidateDisplayList();
}
// Define a private var for the fill color.
private var _rectFill:uint = 0x00FF00;
// Define public setter and getter for fill color.
public function get rectFill():uint
{
return _rectFill;
}
public function set rectFill(value:uint):void
{
_rectFill = value;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
graphics.lineStyle(lineWeight, 0x0066FF);
graphics.beginFill(rectFill, 0.50);
graphics.drawRoundRect(0, 0, unscaledWidth, unscaledHeight, 10, 10);
filters = [myFilter];
}
]]>
</mx:Script>
<mx:states>
<mx:State name="up">
</mx:State>
<mx:State name="over">
<mx:SetProperty target="{this}"
name="rectFill" value="0x00CC33"/>
<mx:SetProperty target="{myFilter}"
name="distance" value="4"/>
</mx:State>
<mx:State name="down">
<mx:SetProperty target="{this}"
name="rectFill" value="0x00CC33"/>
<mx:SetProperty target="{myFilter}"
name="inner" value="true"/>
<mx:SetProperty target="{myFilter}"
name="distance" value="2"/>
</mx:State>
</mx:states>
<mx:transitions>
<mx:Transition>
<mx:AnimateProperty target="{this}"
property="rectFill" duration="100"/>
</mx:Transition>
</mx:transitions>
</mx:UIComponent>