Adobe Flex 3 Help

Creating stateful skins

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>

Note: You can create a stateful skin in either ActionScript or MXML. The examples in this section use MXML because it requires fewer lines of code to define view states.

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:

Example: Creating a stateful skin

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:

Creating stateful skin using images

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:

  • Defines no changes from the base view state to create the up view state.
  • Defines the notBase view state to remove the image defined by the base view state. All other view states, except for the up view state, are based on the notBase view state.
  • Sets the creationPolicy property to all for each AddChild tag. This property specifies to create the child instance at application startup, rather than on the first change to the view state. This prevents flickering when viewing a view state for the first time. For more information, see Using View States.
  • Sets the width and height properties to 100% for the Image tags because the Canvas container is what is being resized by the skin parent, not the individual Image controls. This setting configures the Image control to size itself to its parent.
  • Sets the maintainAspectRatio property to false on each Image tag so that the image stretches to fill the full size of the Canvas container.

Using transitions with a stateful skin

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>