Adobe Catalyst Beta is out and although it allows manipulating and creating some great skins, programmers are not off the hook just yet. Sometimes you need that extra knowledge on how to skin Spark component to get your component to look and behave exactly as you need. In this tutorial I will show you how to skin the ButtonBar component so it has a label text and an icon.
The final results will look and behave as shown in the figure below:

Create VerticalButtonBarSkin skin
First create a skin for the ButtonBar and call it VerticalButtonBarSkin.mxml. VerticalButtonBarSkin includes the states:
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
Each Flex 4 Beta holds an instance to the HostComponent metadata so the component can identify the skin you are creating.
[HostComponent("spark.components.ButtonBar")]
The skin also includes a declaration tag and you can include the button skin. In our case the button skin will hold the label and icon image as well as code to handle the different states.
<fx:Declarations>
<fx:Component id="middleButton" >
<s:ButtonBarButton skinClass="components.IconButtonSkin"
left="0" right="0">
</s:ButtonBarButton>
</fx:Component>
</fx:Declarations>
Lastly, we would need to draw a rectangle around the component so I decided to that here. Notice that I am getting a reference to the hostComponent component, so we can set the color of the button bar component from the implementation. Also notice that we have the data group to hold the data we will be passing to the button skin.
<!-- layer: fill -->
<s:Rect width="100%" height="100%" x="0" y="0">
<s:fill>
<mx:SolidColor color="{hostComponent.getStyle('color')}" />
</s:fill>
</s:Rect>
<!---
@copy spark.components.SkinnableDataContainer#dataGroup
-->
<s:DataGroup id="dataGroup" width="100%" height="100%">
<s:layout>
<s:ButtonBarHorizontalLayout gap="-1"/>
</s:layout>
</s:DataGroup>
Create IconButtonSkin skin
Next step is to create the following skin: components/IconButtonSkin.mxml. The IconButtonSkin skin will hold the label a rectangle to be placed on the selected item and an icon. I am overridden the updateDisplayList method and picking up the data that will be set in the implementation in order to set the icon image and the label text name. Additionally, I am setting the width of the button to fit the 100% size of the host component so once the user re-size the component the buttons will get re-sized automatically.
<fx:Script>
<![CDATA[
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList((this.hostComponent as Object).outerDocument.hostComponent.width, unscaledHeight);
var image:Class = Class((this as Object).hostComponent.data.imageIcon);
var label:String = (this as Object).hostComponent.data.label;
labelElement.text = label;
icon.source = image;
this.width = this.hostComponent.width;
this.minWidth = this.hostComponent.width;
}
]]>
</fx:Script>
We also need to set the states we will be using. In our case we will be using some more advanced states other than the traditional: up, down and over states, since we need to customize the component to fit our exact need.
<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" stateGroups="overStates" />
<s:State name="down" stateGroups="downStates" />
<s:State name="disabled" stateGroups="disabledStates" />
<s:State name="upAndSelected" stateGroups="selectedStates, selectedUpStates" />
<s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
<s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
<s:State name="disabledAndSelected" stateGroups="selectedUpStates, disabledStates, selectedStates" />
</s:states>
The code below create the square we will be drawing using the FP10 drawing API utilizing the FXG code. GradientEntry sets the color to match our button bar component and include the color gradient once we select and item.
<!-- layer: fill -->
<s:Rect left="1" right="1" top="1" bottom="1" width="100%">
<s:fill>
<s:LinearGradient x="114" y="18.5" scaleX="15.5724" rotation="-90">
<s:GradientEntry color="{hostComponent.getStyle('color')}" ratio="0"
color.selectedUpStates="#2a2a2a"
color.overAndSelected="#2a2a2a"/>
<s:GradientEntry color="{hostComponent.getStyle('color')}" ratio="1"
color.selectedUpStates = "#444444"
color.overAndSelected = "#444444"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
Lastly, we define the SimpleText and BitmapImage components:
<s:SimpleText id="labelElement"
color.selectedUpStates="{hostComponent.getStyle('color')}"
color.overAndSelected="{hostComponent.getStyle('color')}"
textAlign="left"
verticalAlign="left"
color="0x000000"
truncation="1"
horizontalCenter="0" verticalCenter="1"
left="50" right="10">
</s:SimpleText>
<s:BitmapImage left="5" id="icon" width="20" height="20" resizeMode="scale" />
Implementation
Now that we created all the skins all we have to do is to implement. Take a look at the implementation below which includes the ButtonBar tag pointing to our VerticalButtonBarSkin skin and a click event once the user selected an item. Notice that we define the data we will be passing to the skin, which includes the label text and the icon we will be using to create the button bar image icon.
<s:Panel title="Panel" width="400">
<s:ButtonBar requiresSelection="true"
width="100%" color="#ece7e7"
skinClass="components.VerticalButtonBarSkin"
click="buttonClickHandler(event)">
<s:layout>
<s:VerticalLayout />
</s:layout>
<s:dataProvider>
<s:ArrayCollection>
<s:source>
<fx:Object label="Download files" imageIcon="{icon1}" />
<fx:Object label="Home" imageIcon="{icon2}" />
<fx:Object label="My Computer" imageIcon="{icon3}" />
<fx:Object label="Documents" imageIcon="{icon4}" />
<fx:Object label="Download images" imageIcon="{icon1}" />
</s:source>
</s:ArrayCollection>
</s:dataProvider>
</s:ButtonBar>
</s:Panel>
The event handler for the user click event is listed below. It take the selectedIndex property from the component and uses the data provider to find the item.
protected function buttonClickHandler(event:MouseEvent):void
{
var index:int = event.currentTarget.selectedIndex;
var item:Object = (event.currentTarget.dataProvider as ArrayCollection).getItemAt(index);
trace(item.label+" label was clicked");
}
Where to go from here?
To read more about skinning Flex Spark components follow this link:
http://livedocs.adobe.com/flex/gumbo/html/WSA95C9644-B650-4783-B5C0-D2C7F95A23E3.html