Another little post about skinning Flex components…

If you need to access the component that a skin belongs to, you need to know that the skin’s parent is the component that you’re skinning. Which means you can access properties of the component through the skin’s parent (N.B. Don’t do this in the constructor – the skin isn’t added to the component’s display list until after the constructor has run).

For example, you can find a Button’s label as the label property of the button skin’s parent. Using this, you can create skins that vary based on the parent’s label. For example, a set of buttons for controlling video playback would all have different icons on them. Rather than creating different skins for each button, you can create one skin and draw a different icon based on the skin’s label (play, pause, etc).

Hiding a Button’s label

If, as above, you use a button’s label as an indicator for modifying a button skin, you may also want to hide the label graphic. The label graphic is the only child of the Button that is a UITextField. And the Button is the skin’s parent. So, to hide the label you look through the parent’s children, find the UITextField, and set it’s visible property to false.

Example

Putting these two ideas together produces something like this

package
{
  import mx.controls.Button;
  import mx.core.UITextField;
  import mx.skins.ProgrammaticSkin;
  
  public class MediaButtonSkin extends ProgrammaticSkin
  {
    public function MediaButtonSkin()
    {
      super();
    }

    override protected function updateDisplayList(
                                         w:Number, h:Number ):void
    {
      // check we're on a Button
      if( ! parent ) return;
      if( ! parent is Button )
        throw( new Error( "MediaButtonSkin may only be 
                                         used on Button objects" ) );

      super.updateDisplayList( w, h );
      
      // find the label display UITextField and hide it
      for( var i:int = 0; i < parent.numChildren; i++ )
      {
        if( parent.getChildAt( i ) is UITextField )
        {
          parent.getChildAt( i ).visible = false;
        }
      }
      
      // choose a fill color based on the skin state
      var fillColor:uint;
      switch( name )
      {
        case "upSkin":
        case "selectedUpSkin":
          fillColor = 0xCC0000;
          break;
        case "overSkin":
        case "selectedOverSkin":
          fillColor = 0xFF3300;
          break;
        case "downSkin":
        case "selectedDownSkin":
          fillColor = 0xFF9900;
          break;
        case "disabledSkin":
        case "selectedDisabledSkin":
          fillColor = 0x7F0000;
          break;
      }
      
      // set the circle properties
      var radius:Number = Math.min( w, h ) / 2;
      var centreX:Number = w / 2;
      var centreY:Number = h / 2;
      
      // draw the circle
      graphics.clear();
      graphics.beginFill( fillColor, 1 );
      graphics.drawCircle( centreX, centreY, radius );
      graphics.drawCircle( centreX, centreY, radius-3 );
      graphics.endFill();
      
      graphics.beginFill( fillColor, 0 );
      graphics.drawCircle( centreX, centreY, radius-3 );
      graphics.endFill();
      
      // get the button's label text
      var label:String = Button( parent ).label.toLowerCase();
      
      // draw the graphics based on the label text
      switch( label )
      {
        case "play":
          var tipX:Number = centreX + radius * 0.5;
          var backX:Number = centreX - radius * 0.3;
          var tipYOffset:Number = radius * 0.45;
    
          graphics.beginFill( fillColor, alpha );
          graphics.moveTo( tipX, centreY );
          graphics.lineTo( backX, centreY + tipYOffset );
          graphics.lineTo( backX, centreY - tipYOffset );
          graphics.lineTo( tipX, centreY );
          graphics.endFill();
          break;
          
        case "pause":
          var outX:Number = radius * 0.4;
          var inX:Number = radius * 0.15;
          var tipY:Number = radius * 0.45;
    
          graphics.beginFill( fillColor, alpha );
          graphics.drawRect( centreX + inX, centreY - tipY, 
                                         outX - inX, tipY * 2 );
          graphics.drawRect( centreX - outX, centreY - tipY, 
                                         outX - inX, tipY * 2 );
          graphics.endFill();
          break;
      }
    }
  }
}

Add this little bit of MXML

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
  layout="absolute" width="160" height="40" backgroundColor="0">
  <mx:HBox x="45" y="5">
    <mx:Button skin="MediaButtonSkin" 
                             width="30" height="30" label="play"/>
    <mx:Button skin="MediaButtonSkin" 
                             width="30" height="30" label="pause"/>
  </mx:HBox>
</mx:Application>