Flex 4 Components Exposed

March 25, 2011

Adobe Flex

«»

Skinning with the SparkSkin object

The decoupling of display logic is one of the most valuable architectural enhancements in the Spark library.

SparkSkin basics

Imagine you just built an application with a set of five custom components. If your five components each extend one of the classes listed earlier from the Spark library, you can create an unlimited number of skins for each of your five custom Spark-based components and keep them organized in an entirely separate package or library. You can then declare a different default skin for each instance of the same component, or even swap skins on the fly by triggering an event during runtime from a user initiated behavior or sequence. Isn’t that cool? Code listing 3 is an example of a Spark skin component.

Listing 3 Example of a skin for a Spark Button

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="utf-8"?>
	<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" #A
		xmlns:mx="library://ns.adobe.com/flex/halo"
		xmlns:s="library://ns.adobe.com/flex/spark"
		xmlns:ai="http://ns.adobe.com/ai/2008" #B
		xmlns:d="http://ns.adobe.com/fxg/2008/dt" #C
		minWidth="21" minHeight="21">
		<fx:Metadata>
			[HostComponent("spark.components.Button")]
		</fx:Metadata>
		<s:states>
			<s:State name="up"/>
			<s:State name="over"/>
			<s:State name="down"/>
			<s:State name="disabled"/>
		</s:states>
		<!-- FXG exported from Adobe Illustrator. -->
		<s:Graphic version="1.0" viewHeight="30"
			viewWidth="100" ai:appVersion="14.0.0.367" d:id="mainContainer">
			<s:Group x="-0.296875" y="-0.5" d:id="firstGroup"
				d:type="layer" d:userLabel="Group One">
				<s:Group d:id="secondGroup"> #D
					<s:Rect x="0.5" y="0.5" width="100"
						height="30" ai:knockout="0">
						<s:fill>
							<s:LinearGradient x="0.5" y="15.5" scaleX="100" rotation="-0">
								<s:GradientEntry color="#ffffff" ratio="0"/>
								<s:GradientEntry ratio="1"/>
							</s:LinearGradient>
						</s:fill>
						<s:stroke>
							<s:SolidColorStroke color="#0000ff" caps="none"
								weight="1" joints="miter" miterLimit="4"/>
						</s:stroke>
					</s:Rect>
				</s:Group>
			</s:Group>
		</s:Graphic>
		<s:Label id="labelElement" horizontalCenter="0" #E
			verticalCenter="1" left="10" right="10"
			top="15" bottom="2">
		</s:Label>
	</s:SparkSkin>
 
#A Skins ALWAYS extend SparkSkin in Flex 4
#B Namespace declaration for Illustrator graphics
#C Namespace declaration for FXG graphics
#D Group object followed by design
#E Label declaration followed by style information

When you create Spark components, you will usually create two classes for every component. The component class holds behavioral logic, such as events that are dispatched by the component, the component’s data model, skin parts that are implemented by the skin class, and view states that the skin class supports.

The skin class on the other hand, is responsible for managing visual appearance of the component and visual subcomponents, including how everything is laid out and sized. It must also define the supported view states, graphics, and the data representation.

1
2
3
4
5
6
7
8
Migration Tip
	FXG stands for "Flash XML Graphics", and in essence, does for MXML what CSS did for HTML. However, FXG has
	quite a bit more power under the hood, so while its relationship to Flash and Flex components can be loosely
	compared to the relationship between CSS and HTML, it does not make logical sense to compare FXG directly to
	CSS. Many of the limitations imposed on CSS do not exist with FXG. Although it may seem strange to compare
	FXG to CSS, FXG theoretically accomplishes the same thing that CSS does: It separates layout structure and
	behavioral code from design and graphics code. This makes it easier to create components that can be quickly
	and easily integrated into any application without even having to look at the component's code base.

Using Metadata to Bind Component Skins

The component and skin classes must both contain certain metadata in order for them to work properly together.

The component class must:

  • Define the skin(s) that correspond to it
  • Identify skin parts with the [SkinPart] metadata tag
  • Identify view states that are supported by the component using the [SkinState] tag

The skin class must:

  • Use the [HostComponent] metadata tag to specify the corresponding component
  • Declare view states and define the appearance of each state
  • Define the way skin parts should appear on the stage

Let’s take a look at the three basic essentials of a skin component in further detail, starting with the SkinState
metadata tag.

1
2
3
	Migration Tip:
	Note that skin parts must have the same name in both the skin class and the corresponding component class or
	your application will show compile errors or throw runtime errors.

CUSTOM COMPONENT VIEW STATES

The view states that are supported by a component and its corresponding skin must be defined by placing a [SkinState] tag for every view state. These tags are placed directly above the class declaration, as seen in Listing 4.

Listing 4 View states are defined directly above the class statement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package
	{
		import spark.components.supportClasses.SkinnableComponent;
		[SkinState("default")] #A
		[SkinState("hover")]
		[SkinState("selected")]
		[SkinState("disabled")]
		public class SkinStateExample extends SkinnableComponent
		{
			public function SkinStateExample()
			{
				super();
			}
		}
	}
	#A SkinState tags preceed the class declaration

You can now control the state of the component by setting the currentState property with the component
declaration in MXML, as seen in Listing 5.

Listing 5 Controlling custom component state

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
	<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
		xmlns:mx="library://ns.adobe.com/flex/halo"
		xmlns:s="library://ns.adobe.com/flex/spark"
		xmlns:MyComp="myComponents.*">
		<s:layout>
			<s:VerticalLayout/>
		</s:layout>
		<s:Label text="Login or Register"
			fontSize="14" fontWeight="bold"/>
		<MyComp:SkinStateExample currentState="default"/> #A
	</s:Application>
	#A Use currentState to set the component's state

While states are defined in a component’s implementation class and controlled in the skin, skin parts are defined in the MXML skin and controlled by the implementation class.

DEFINING SKIN PARTS

The importance of the [SkinPart] metadata tag is another critical facet of understanding the relationship between component classes and skin classes in the Spark architecture. As you will see in a moment, this metadata tag is especially useful for creating custom components.

The implementation of the SkinPart metadata is simple. First, define skin parts in the same way as you would declare objects in a standard MXML file. For example, if one of your skin parts was a Spark Button with an id property of myButton, you would define it with the code:

1
<s:Button id="myButton" width="100" height="20" x="0" y="0"/>

Next, declare the skin parts in the component using the [SkinPart] metadata tag. Make sure that the variable is typed the same as what you just defined in the MXML skin, and that the id of the object in the MXML skin matches the name of the variable in the component. The component code for the myButton skin part that you just saw is illustrated in Listing 6.

Listing 6 The SkinPart tag binds component variables to MXML skin part definitions

1
2
3
4
5
6
7
8
9
10
public class CustomComponent extends SkinnableComponent
	{
		public function CustomComponent()
		{
			super();
		}
		[SkinPart(required="true")] #A
		public var myButton:Button;
	}
	#A SkinPart metadata binding on the myButton variable

Notice the use of the required property after the SkinPart metadata tag is declared. Make sure you declare this property because required is – pardon the pun – required.

You have now learned two of the three essentials to Spark skinning. The third essential element is tying the component implementation to the skin using the HostComponent metadata.

1
2
3
4
	WARNING
	In order for your skin part bindings to work, you must be sure to type the respective variable the same as the
	MXML object definition and the variable must be named the same as the id attribute that is set on the respective
	MXML object definition.

DECLARING THE HOST

The last of the three basic essentials to using skins with custom Spark components is the declaration of the host component in the skin. To accomplish this, you use the [HostComponent] metadata tag. An excellent example of this was provided in the example skin at the beginning of this article, in Listing 3. The code from listing 3 that is used for the declaration of the host component is provided again in Listing 7.

Listing 7 Use HostComponent metadata to bind a skin to a component class

1
2
3
<fx:Metadata>
		[HostComponent("spark.components.Button")]
	</fx:Metadata>
email

«»

Comments

comments