4 May 2011

Styling and skinning an application



This sample project shows you how to customize the appearance of an application using styling and skinning. To look at the code, right-click on the SWF in the browser and select View Source or download the sample files and view the source files in a text editor or follow the instructions to import the Flash Builder FXP. Multiple application versions are provided: an XML version that does not require a server as well as versions using Flash Remoting with PHP, Java, and ColdFusion servers.
Styling versus skinning
With styling, you set component styles inline in MXML (as you have already seen):
<s:Label x="20" y="40" text="XYZ Corporation Directory" fontSize="20" fontWeight="bold" color="#1239E3"/>
... or preferably, in a style sheet using selectors (style rules). Each component has a limited number of styles defined for it. For example, for a Label you can set styles including font-size, font-family, and color. For a Button, you can also set a corner-radius style. If you want to change the appearance of a component more drastically then possible with a component's styles, you need to create or edit the associated component skin—the file specifying what the component should look like.
There are two families of components in the Flex framework: Spark and MX. The tags that start with s, such as <s:Button> are Flex 4 or later Spark components. The tags that start with mx, such as <mx:PieChart> are the older Flex components. You set the appearance of MX components primarily using styling. The newer Spark components have been re-architected to primarily use a skinning (rather than styling) model in which each component's associated skin file manages everything related to a component's appearance, including its graphics, its layout, and its states.
To view the styles available for a component, look at the styles section of its API in ASDocs or look for the members with the blocks symbol in front of them in Flash Builder code-hinting.
Defining a style sheet and a CSS global selector
To include a style sheet, you use the Style tag.
<fx:Style source="TestDrive.css"/>
Look at the code inside this style sheet. You will see various CSS selectors defined. The first is the global selector whose styles will be applied to all components.
global { font-family: Verdana; fontSize: 10; chrome-color: #7F7364; selection-color: #BFB59F; roll-over-color: #E5DFC3; focus-color: #7F7364; }
In a style sheet, you can specify styles using camel case (fontSize) or the more standard CSS hyphenated version (font-size). In ActionScript and MXML, you have to use the camel case version because ActionScript variables cannot contain hyphens.
Styles set more specifically for a component (either inline or using other CSS selectors we'll take a look at next) apply over values specified in the global selector. For example, the XYZ Label still uses a font-size of 20 that is set inline in its MXML tag instead of the value of 10 set here. Closer, or more specific style values take precedence.
Using CSS type selectors
To apply style rules to only certain types of components, you use CSS type selectors. Here is a type selector for the Button component.
@namespace s "library://"; @namespace mx "library://"; s|Button { cornerRadius: 5; color:#FFFFFF; font-weight:bold; }
Because there are two types of Button components, Spark and MX, a namespace must be declared and the selector must specify a namespace. These styles will automatically be applied to all Spark Button components and they will now all have rounded corners and bold, white text.
Using CSS descendant selectors
To apply style rules to only components located in a particular relationship to other components, use CSS descendant selectors. Although not included in the style sheet for this application, here is an example for a style applied to Spark Buttons located within Spark HGroups.
s|HGroup s|Button { cornerRadius: 5; color:#FFFFFF; font-weight:bold; }
Using CSS pseudo selectors
Spark components have multiple states. For example, a Spark Button component has disabled, down, over, and up states. A component's states are listed in the Skin States section of its API. You also saw in the previous samples how to define states for your own custom components.
If you want a component to have different styles in different states, you use CSS pseudo selectors. Pseudo selectors are defined by using a colon after the selector name to reference the name of a state. Here is a pseudo selector that sets the label text to black (instead of white) for Button components in their disabled state.
s|Button:disabled{ color:#000000; }
Using CSS class selectors
To apply style rules selectively to only certain components, you use CSS class selectors. To define class selectors, you use a period in front of a custom name. Here is a class selector called navButton.
In the code, deptPieCht_itemClickHandler() is called when the user clicks on a pie wedge.
.navButton { fontSize: 12; }
To apply this style to a component, you set its styleName property to the name of the class selector. This style can be applied to any component.
<s:Button id="empBtn" label="Employees" styleName="navButton" .../>
Multiple class selectors can be applied by setting styleName to a list of names separated by spaces.
<s:Button styleName="redText smallText" />
If you only want this custom style to be applied to certain types of components, you can preface the selector with the component type. Here is a class selector called bigNavButton that can only be applied to Spark Button components.
s|Button.bigNavButton{ fontSize: 14; }
Using CSS ID selectors
To apply a style rule to a single component, you use CSS ID selectors. To create an ID selector, you name the selector using a # in front of a component's id. Here is an ID selector:
#xyz { color: #403029; fontSize: 20; fontWeight: bold; }
for the xyz Label control.
<s:Label id="xyz" x="36" y="36" text="XYZ Corporation Directory"/>
Note that the Label no longer has any inline styles set in the MXML tag as it did before. It is a best practice to set all your styles in a style sheet.
Change styles at runtime
This application has a toggle button to increase or decrease the size of text at runtime.
<s:ToggleButton id="biggerBtn" label="Bigger" click="biggerBtn_clickHandler(event)" .../>
The event handler contains code to increase or decrease the text size used in various parts of the application.
You can’t change a style at runtime the same way you do a property because styles can be inherited by their children; changing a style's value may affect other components. These style relationships are managed by a StyleManager class. If you look at the Styles section of a component's API, you will see that each style lists its CSS Inheritance as either true or false.
There are several ways to change styles dynamically at runtime. You can use a component's setStyle() method:
... or you can set its styleName property to the name of a different CSS class selector.
TIf you want to make changes to a style selector instead of making changes to a specific component, you can use the getStyleDeclaration() method of the StyleManager class and the setStyle() method. Every Flex component has a styleManager property you can use to get a reference to the StyleManager class. In this code, the fontSize style of the global selector is increased to 12 pixels.
Here is the complete code for the ToggleButton click handler that uses these techniques to increase and decrease font sizes.
protected function biggerBtn_clickHandler(event:MouseEvent):void{ if(biggerBtn.selected==true){ xyz.setStyle("fontSize",24); empBtn.styleName="bigNavButton"; this.styleManager.getStyleDeclaration("global").setStyle("fontSize",12); } else{ xyz.setStyle("fontSize",20); empBtn.styleName="navButton"; this.styleManager.getStyleDeclaration("global").setStyle("fontSize",10); } }
The ToggleButton also has custom graphics that change to display a plus or minus sign when the button is clicked. We'll look at the code to accomplish this next.
Creating skins using graphics generated with Creative Suite 4 or later
As mentioned previously, the new Spark components have been re-architected to primarily use a skinning (rather than styling) model in which each component's associated skin file manages everything related to a component's appearance, including its graphics, its layout, and its states.
When you create a new skin class in Flash Builder (File > New > MXML Skin), you get a dialog box to choose a host component (what component the skin will be applied to) and the option to base the skin on an existing skin class. This makes it easy to create new skin classes. Within the skin class, you use tags for graphics classes located in the spark.primitives package (Rect, Line, Ellipse, Path, BitmapImage, and RectangularDropShadow ) to declaratively create the graphics.
Instead of writing these tags, you can create the custom graphics using Fireworks CS4 or later, Illustrator CS4 or later, or Photoshop CS4 or later. You use the drawing tools in these products to create the graphics and then select a menu option to save or export the file as FXG, an XML-based graphics interchange format for the Flash Platform. You can then use the generated FXG file as a component in the skin class.
The graphics for the ToggleButton in the application were created with Fireworks CS4 or later. The exported FXG files were saved as BiggerButton.fxg and SmallerButton.fxg. Here is the generated code for BiggerButton.fxg, which defines a filled circle and two crossed lines.
<?xml version="1.0" encoding="UTF-8"?> <Graphic version="1.0" xmlns="" xmlns:fw="" viewHeight= "25" viewWidth= "25"> <Group id="Page_1" fw:type="page"> <Group id="State_1" fw:type="state"> <Group id="Layer_1" fw:type="layer"> <Path winding="evenOdd" data="M 2 13 C 2 7 7 2 13 2 C 18 2 23 7 23 13 C 23 18 18 23 13 23 C 7 23 2 18 2 13 Z "> <fill> <SolidColor color="#bfb59f" alpha="1"/> </fill> <stroke> <SolidColorStroke color="#403029" weight="3"/> </stroke> </Path> <Path winding="evenOdd" data="M 13 6 L 13 18"> <stroke> <SolidColorStroke color="#403029" weight="2"/> </stroke> </Path> <Path winding="evenOdd" data="M 7 12 L 19 12"> <stroke> <SolidColorStroke color="#403029" weight="2"/> </stroke> </Path> </Group> </Group> </Group> </Graphic>
Next, take a look at BiggerButtonSkin.mxml. The base code that specifies the host component and the states was automatically generated from options in the New MXML Skin wizard. The only code added was to create instances of the two classes generated by Fireworks, BigggerButton and SmallerButton located in the skins folder. The includeIn property is used to specify in which states each graphics should be used.
<?xml version="1.0" encoding="utf-8"?> <s:Skin xmlns:fx="" xmlns:s="library://" xmlns:mx="library://" xmlns:skins="skins.*"> <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:State name="disabledAndSelected" /> <s:State name="downAndSelected" /> <s:State name="overAndSelected" /> <s:State name="upAndSelected" /> </s:states> <skins:BiggerButton includeIn="disabled,down,over,up"/> <skins:SmallerButton includeIn="disabledAndSelected,downAndSelected,overAndSelected,upAndSelected"/> </s:Skin>
To apply a skin class to a component, you set its skinClass style. In MXML, you can just set skinClass to the fully qualified class name of the class.
<s:ToggleButton skinClass="skins.BiggerButtonSkin" .../>
In a style sheet, you need to use the ClassReference keyword to denote you are setting this style to an ActionScript class.
s|ToggleButton{ skinClass: ClassReference("skins.BiggerButtonSkin"); }
The ToggleButton now shows the plus graphics when it is in one of its normal states and the minus graphics when it has been clicked and is in one of its selected states.
Creating a custom skin with graphics and skin parts
This application uses a second skin class to skin the Application container, XYZSkin.mxml. In this skin class, the graphics for the skin are defined using spark.primitives graphics tags. The Rect class is used to create a white rectangle to fill the entire application by setting the top, bottom, left, and right constraint properties to 0.
<s:Rect id="backgroundRect" left="0" right="0" top="0" bottom="0" > <s:fill> <s:SolidColor id="bgRectFill" color="#FFFFFF"/> </s:fill> </s:Rect>
A second Rect instance is used to create a smaller rectangle with rounded corners inside it with a brown stroke and a tan fill. This rectangle's stroke property is set equal to an instance of the SolidColorStroke class and the fill property to an instance of the SolidColor class. Additional stroke and fill classes can be found in the package. Constraint properties are set so the rectangle resizes with the browser window size—if the Flex application is set to resize, which it does by default unless you specify a height and width. It is drawn so that it is 30 pixels from three edges and 35 pixels from the top.
<s:Rect id="border" left="30" right="30" top="35" bottom="30" radiusX="10"> <s:stroke> <s:SolidColorStroke color="#403029" weight="5"/> </s:stroke> <s:fill> <s:SolidColor color="#F9F8E9"/> </s:fill> </s:Rect>
When adding visual objects, the order of declaration in the MXML file determines the order the objects are added to the display list and hence to the drawing surface. In this case, the second rectangle appears on the top of the first because it is defined later in the code; it is added to the display list after the first rectangle.
The next tag in the skin class is BitmapImage, another Spark primitive. It is used to fill a rectangular region with bitmap data drawn from a source file.
<s:BitmapImage right="50" top="15" source="@Embed('employee.gif')"/>
@Embed is a compiler directive used to tell the compiler to embed this graphics file into the application at compile time, not to load it at runtime. This code adds an employee icon in the upper right corner of the application on top of the rectangles.
If you associated the skin with the Application and ran the application now, you would see the skin but nothing else, not the Label or the DataGrid—none of the contents of the application. This is because we have not yet specified in the skin where to add and position the contents that get added to the container.
Many components are composites of other components. In the API, these subcomponents are listed under Skin Parts. For the Application class, two skin parts are listed: contentGroup and controlBarGroup, both of type Group. This means two Group containers with ID's of contentGroup and controlBarGroup are defined in the Application class as skin parts, and you can set their visual appearance and layout in a skin class. Our application does not have a control bar, so we just need to add the contentGroup, the Group object that the main content children get added to.
The last and critical piece of code in the skin is to add this Group container with the name contentGroup and to specify where it should appear. This sets the position of the content within the skinned Application. You could also set the layout by changing and/or modifying the associated layout class and properties for this Group.
<s:Group id="contentGroup" top="50" bottom="50" right="70" left="70" />
Lastly, the skin sets a minimum size for the skin so the Flex layout manager does not reduce it below this size.
<s:Skin xmlns:fx="" xmlns:s="library://" xmlns:fb="" minWidth="750" minHeight="560">
The complete code for the skin class is included here:
<?xml version="1.0" encoding="utf-8"?> <s:Skin xmlns:fx="" xmlns:s="library://" xmlns:fb= minWidth="750" minHeight="560"> <fx:Metadata> [HostComponent("spark.components.Application")] </fx:Metadata> <s:states> <s:State name="normal" /> <s:State name="disabled" /> <s:State name="normalWithControlBar" /> <s:State name="disabledWithControlBar" /> </s:states> <!--white background--> <s:Rect id="backgroundRect" left="0" right="0" top="0" bottom="0" > <s:fill> <s:SolidColor id="bgRectFill" color="#FFFFFF"/> </s:fill> </s:Rect> <!--brown border and tan fill--> <s:Rect id="border" left="30" right="30" top="35" bottom="30" radiusX="10"> <s:stroke> <s:SolidColorStroke color="#403029" weight="5"/> </s:stroke> <s:fill> <s:SolidColor color="#F9F8E9"/> </s:fill> </s:Rect> <s:BitmapImage right="50" top="15" source="@Embed('employee.gif')"/> <s:Group id="contentGroup" top="50" bottom="50" right="70" left="70" /> </s:Skin>
The skin is applied to the Application container in a style sheet or in the Appliation tag as shown here:
<s:Application xmlns:fx="" skinClass="skins.XYZSkin" ...>