27 June 2011
Download the Test Drive solution files (ZIP, 14 MB)
Download the assets files for this tutorial (ZIP, 2 KB)
TestDrive.mxml
@namespace s "library://ns.adobe.com/flex/spark";
(...)
s|TextInput.searchInput
{
skinClass: ClassReference("skins.SearchInputSkin");
}
s|Button.searchButton
{
skinClass: ClassReference("skins.SearchButtonSkin");
}
FlexWebTestDrive.mxml
<s:Application ...>
(...)
<s:TextInput id="searchTxt" prompt="Last Name"
styleName="searchInput" .../>
<s:Button id="searchBtn" styleName="searchButton" .../>
</s:Application>
skins/SearchInputSkin.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
alpha.disabledStates="0.5" blendMode="normal" minHeight="30">
<fx:Metadata>
[HostComponent("spark.components.TextInput")]
</fx:Metadata>
<fx:Script>
<![CDATA[
private static const focusExclusions:Array = ["textDisplay"];
override public function get focusSkinExclusions():Array {
return focusExclusions;
};
]]>
</fx:Script>
<s:states>
<s:State name="normal"/>
<s:State name="disabled" stateGroups="disabledStates"/>
<s:State name="normalWithPrompt"/>
<s:State name="disabledWithPrompt" stateGroups="disabledStates"/>
</s:states>
<s:Rect id="border" left="0" right="0" top="0" bottom="0"
radiusX="3" radiusY="3">
<s:stroke>
<s:SolidColorStroke id="borderStroke" weight="1" />
</s:stroke>
</s:Rect>
<s:Rect id="background" left="1" right="1" top="1" bottom="1"
radiusX="3" radiusY="3">
<s:fill>
<s:SolidColor id="bgFill" color="0xFFFFFF" />
</s:fill>
</s:Rect>
<s:Rect id="shadow" left="1" top="1" right="1" height="1"
radiusX="3" radiusY="3">
<s:fill>
<s:SolidColor color="0x000000" alpha="0.12" />
</s:fill>
</s:Rect>
<s:RichEditableText id="textDisplay" verticalAlign="middle"
widthInChars="10" left="10" right="10" top="10" bottom="7" />
<s:Label id="promptDisplay" maxDisplayedLines="1" verticalAlign="middle"
mouseEnabled="false" mouseChildren="false"
includeIn="normalWithPrompt,disabledWithPrompt"
includeInLayout="true" fontStyle="normal"
left="10" right="10" top="10" bottom="7"/>
</s:SparkSkin>
skins/SearchButtonSkin.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:assets="assets.*"
minHeight="30" minWidth="30">
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<s:states>
<s:State name="disabled" />
<s:State name="down" />
<s:State name="over" />
<s:State name="up" />
</s:states>
<s:Rect left="0" right="0" top="0" bottom="0"
topRightRadiusX="3" topRightRadiusY="3" bottomRightRadiusX="3"
bottomRightRadiusY="3">
<s:stroke>
<s:SolidColorStroke color="#1A1A1A" weight="1"/>
</s:stroke>
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0x999999" color.up="0x666666"/>
<s:GradientEntry color="0x4D4D4D" color.up="0x333333"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
<assets:search_icon horizontalCenter="0" verticalCenter="0"/>
<!-- SkinParts
name=iconDisplay, type=spark.primitives.BitmapImage, required=false
name=labelDisplay, type=spark.core.IDisplayText, required=false-->
</s:Skin>
In this tutorial, you learn to change the appearance of components by creating and using skins. You will skin the TextInput and Button controls used to search the employees. To customize the look and layout of the TextInput control, you create a new skin based on its default skin and then modify it. For the search button, you create a completely new skin and then add graphics to it.
Return to Design mode of FlexWebTestDrive.mxml. Select the searchTxtTextInput control. In the Properties view, click the button next to the Skin field and select Create Skin (see Figure 1). In the New MXML Skin dialog box, set the package to skins and the name to SearchInputSkin; leave the host component set to spark.components.TextInput, create it as a copy of spark.skins.spark.TextInputSkin, and remove the ActionScript styling code (see Figure 2).
In FlexWebTestDrive.mxml, switch to Source mode and look at the TextInput tag. It now has a skinClass style set to the name of the new skin SearchInputSkin—which currently is the same as the default skin.
<s:TextInput id="searchTxt" prompt="Last Name" skinClass="skins.SearchInputSkin" .../>
Switch back to Design mode and with the TextInput selected, click Convert to CSS. In the New Style Rule dialog box, select Specific component with style name and name it searchInput (see Figure 3). Make this the style used by the search TextInput control in all the application states.
The new selector should appear as shown here:
s|TextInput.searchInput
{
skinClass: ClassReference("skins.SearchInputSkin");
}
This class selector can only be applied to TextInput controls.
Return to FlexWebTestDrive.mxml and note that styleName has only been set for one of the application states. To set it for all states, modify the source code or select the TextInput control and select Apply Current Properties to All States.
Note that instead of creating a class selector and applying it to the one TextInput instance as you did here, you could also have created and used an ID selector.
Review the code for SearchInputSkin.mxml.
The skin class extends the SparkSkin base class and defines its host component, the component this skin can be applied to:
[HostComponent("spark.components.TextInput")]
In the Script block is some code to specify what subcomponents should be excluded when rendering a focus ring for the component.
Next, the skin states are defined:
<s:states>
<s:State name="normal"/>
<s:State name="disabled" stateGroups="disabledStates"/>
<s:State name="normalWithPrompt"/>
<s:State name="disabledWithPrompt" stateGroups="disabledStates"/>
</s:states>
You can look up the names of the skin states the skin should contain in the component's API (see Figure 4). Remember you can open a component's API by selecting Help > Dynamic Help, clicking a tag in MXML, and then clicking the API link in the Help view.
Next are graphics tags that provide a way to define graphics with MXML tags (see Figure 5). There are multiple Rect tags, which draw rectangles for a border, fill, and shadow for TextInput controls that use this skin. The order in which the elements are defined defines their depth, so the fill rectangle is drawn on top of the border rectangle and the shadow rectangle on top of the fill rectangle.
At the end of the code are RichEditableText and Label controls. The RichEditableText control is the part of the TextInput component that holds and displays the input value. It is called a skin part and is a subcomponent of the TextInput control. The text property of the RichEditableText skin part gets set (in the TextInput component source code) to the text property of the TextInput control, which can be set programatically or by user input. The various attributes of the RichEditableText control specify where the RichEditableText should appear in the TextInput control; in this case, it is vertically aligned, with at least 1 pixel from the top, bottom, right, and left edges of the TextInput control. You modify these values to change where the RichEditableText control is displayed within the TextInput control.
<s:RichEditableText id="textDisplay"
verticalAlign="middle"
widthInChars="10"
left="1" right="1" top="1" bottom="1" />
The Label control is the skin part of the TextInput component that holds and displays the optional prompt value. The text property of the Label skin part is set (in the TextInput component source code) to the prompt property of the TextInput control. Just as for the RichEditableText skin part, you can set various attributes of the Label control to specify where the prompt Label should appear in the TextInput control.
<s:Label id="promptDisplay" maxDisplayedLines="1"
verticalAlign="middle"
mouseEnabled="false" mouseChildren="false"
includeIn="normalWithPrompt,disabledWithPrompt"
includeInLayout="false"/>
Note, the id values assigned to the two controls:textDisplay and promptDisplay. These are the names assigned to the subcomponents (the skin parts) defined and referenced in the host component's source code, in this case, the TextInput control. In order for you to specify the look and layout of the skin parts, you must use the id's assigned and used by the host component. You can look up the names and types of the skin parts you can reference in a component skin in the component's API (see Figure 6).
In the SparkSkin tag, set minHeight to 30. In the RichEditableText tag, change the left, right, and top properties to 10 pixels and its bottom property to 7. Add these same values to the Label control and also set the includeInLayout property to true and set its fontStyle to normal.
Your code should appear as shown here:
<s:SparkSkin minHeight="30" ...>
(...)
<s:RichEditableText id="textDisplay"
verticalAlign="middle"
widthInChars="10"
left="10" right="10" top="10" bottom="7" />
<s:Label id="promptDisplay" maxDisplayedLines="1"
verticalAlign="middle"
mouseEnabled="false" mouseChildren="false"
includeIn="normalWithPrompt,disabledWithPrompt"
includeInLayout="true"
left="10" right="10" top="10" bottom="7"
fontStyle="normal"/>
</s:SparkSkin>
To each of the three Rect tags, add radiusX and radiusY properties set to 3 pixels.
Example code for one of the rectangles is shown here:
<s:Rect left="0" right="0" top="0" bottom="0" id="border" radiusX="3" radiusY="3">
<s:stroke>
<s:SolidColorStroke id="borderStroke" weight="1" />
</s:stroke>
</s:Rect>
Run the application and/or switch to Design mode for FlexWebTestDrive.mxml. The search TextInput control should now be larger, have rounded corners, and the prompt should no longer be italic (see Figure 7).
Next, skin the search button.
Return to Design mode of FlexWebTestDrive.mxml.Select the Search button and in the Properties view, click the button next to the Skin field and select Create Skin. In the New MXML Skin dialog box, create a skin called SearchButtonSkin in the skins package, leave the host component as spark.components.ButtonSkin, and uncheck Create as copy of (see Figure 8).
In SearchButtonSkin.mxml, switch to Source mode and review the code.
The skin class extends the Skin base class and because you specified a host component in the dialog box to create it, the host component is also set:
[HostComponent("spark.components.Button")]
The skin states for a Button are also defined:
<s:states>
<s:State name="disabled" />
<s:State name="down" />
<s:State name="over" />
<s:State name="up" />
</s:states>
Lastly, there is a comment listing the names and types of all the required and optional skin parts so you don’t have to look them up in the API (see Figure 9):
<!-- SkinParts
name=iconDisplay, type=spark.primitives.BitmapImage, required=false
name=labelDisplay, type=spark.core.IDisplayText, required=false
-->
Download and unzip testdrive_assets.zip and add search_icon.fxg to the FlextWebTestDrive project's assets folder. Open assets/search_icon.fxg and review the code (see Figure 10).
FXG is a declarative XML syntax for defining vector graphics that closely follows the Flash Player 10 rendering model. This FXG file was created by drawing in Fireworks and then selecting Commands > Export to FXG. You can also create FXG files using Illustrator or Photoshop.
In general, you should use an FXG file as a standalone component in your applications. This gives you the greatest amount of memory optimization, and lets you reuse your FXG files in other parts of the same application, or in other applications. In addition, by keeping the FXG file separate from the application, you can edit and export the FXG again from a graphics tool.
Although you can resize and animate the entire FXG component, you cannot modify objects within it at runtime. If you want to reference any of its subobjects, you need to convert the FXG syntax to MXML graphics and use those tags in your application instead.
After the states in SearchButtonSkin.mxml, use Code Assist to select the search_icon FXG class (see Figure 11).
By using Code Assist, the namespace assignment was written for you in the Skin root tag: xmlns:assets="assets.*".
This tells the compiler where to find this class. You put the FXG file in the assets folder so assets.* says look for files with the name of the tag in the assets folder.
Your skin class should appear as shown here:
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:assets="assets.*">
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<s:states>
<s:State name="disabled" />
<s:State name="down" />
<s:State name="over" />
<s:State name="up" />
</s:states>
<assets:search_icon/>
</s:Skin>
Save the file and then return to FlexWebTestDrive.mxml in Design mode. You should see the search button with the new skin (see Figure 12).
Return to SparkButtonSkin and set the minHeight and minWidth properties of the Skin class to 30. In the search_icon tag, set the horizontalCenter and verticalCenter properties to 0.
<s:Skin xmlns:assets="assets.*" minHeight="30" minWidth="30" ...>
(...)
<assets:search_icon horizontalCenter="0" verticalCenter="0"/>
Above the search_icon tag, add a Rect tag and set its left, right, top, and bottom properties to 0. Set its topRightRadiusX, topRightRadiusY, bottomRightRadiusX, and bottomRightRadiusY properties to 3. Set its stroke property to an instance of the SolidColorStroke class with a color of #1A1A1A.
<s:Rect left="0" right="0" top="0" bottom="0" topRightRadiusX="3" topRightRadiusY="3" bottomRightRadiusX="3" bottomRightRadiusY="3">
<s:stroke>
<s:SolidColorStroke color="#1A1A1A" weight="1"/>
</s:stroke>
</s:Rect>
The size of the rectangle is set by setting the left, right, top, and bottom properties to 0. The rectangle will fill the entire area of the button, leaving 0 pixels on each side.
The corner curvature is set by the radius properties. The corners on the right side will be rounded, the corners on the left side will be square (because no radii were specified).
The rectangle will be outlined with a solid dark line with a thickness of 1 pixel.
Set the rectangle's fill property to an instance of the LinearGradient class with a rotation of 90. Add a GradientEntry with a color of 0x999999 and a color in the up state of 0x666666. Add a second GradientEntry with a color of 0x4D4D4D and a color in the up state of 0x333333.
<s:Rect left="0" right="0" top="0" bottom="0" topRightRadiusX="3" topRightRadiusY="3" bottomRightRadiusX="3" bottomRightRadiusY="3">
<s:stroke>
<s:SolidColorStroke color="#1A1A1A" weight="1"/>
</s:stroke>
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0x999999" color.up="0x666666"/>
<s:GradientEntry color="0x4D4D4D" color.up="0x333333"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
Save the file.
Return to Design mode of FlexWebTestDrive.mxml and reposition the search TextInput and Button controls (see Figure 13). Make these the properties for all states.
To set the positions for all states, modify the source code or select each control and select Apply Current Properties to all Components.
With the Search button selected, click Convert to CSS. In the New Style Rule dialog box, select Specific component with style name and name it searchButton (see Figure 14). Return to Design mode of FlexWebTestDrive.mxml and make this the style for all states by right-clicking the search button and selecting Apply Current Properties to All States.
The new selector should appear as shown here:
s|Button.searchButton
{
skinClass: ClassReference("skins.SearchButtonSkin");
}
Run the application (see Figure 15). Enter a last name and click the search button; the controls should work exactly as before—they just have a new look.
You have now successfully skinned the TextInput and Button controls used to search the employees in the application.
In this tutorial, you learned to use skins to change the appearance of components. You learned to create and modify a skin based on a default component skin and to create and add graphics to a new skin. In the next tutorial you skin the other controls in the application.
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.