Skinning Spark componentsAll Spark components and subcomponents that are subclasses of SkinnableComponent can be reskinned. Common tasks when reskinning Spark components include adding borders, drop shadows, and transitions to a skin class. Setting minimum sizes of a skinA common task when creating Spark skins is to set minimum sizes of the skin. This causes the Flex layout to not reduce the size of a component below a pre-defined width or height. You do this with the minWidth and minHeight properties on the skin class’s top-level element. In general, you should not change the values of the minimum height and minimum width properties once they are set. The following example creates a custom Button skin that has a
minimum width of 100 pixels and a minimum height of 100 pixels.
<?xml version="1.0"?>
<!-- SparkSkinning/SquareButtonExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:Button id="myButton" skinClass="mySkins.SquareButtonSkin" label="Click Me"/>
</s:Application>
The following is the custom skin class used for this example:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/mySkins/SquareButtonSkin.mxml -->
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="100" minHeight="100"
alpha.disabled="0.5">
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<fx:Script>
<![CDATA[
static private const exclusions:Array = ["labelDisplay"];
override public function get colorizeExclusions():Array {return exclusions;}
]]>
</fx:Script>
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<!-- layer 1: shadow -->
<s:Rect left="-1" right="-1" top="-1" bottom="-1" radiusX="2" radiusY="2">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0x000000"
color.down="0xFFFFFF"
alpha="0.01"
alpha.down="0" />
<s:GradientEntry color="0x000000"
color.down="0xFFFFFF"
alpha="0.07"
alpha.down="0.5" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<!-- layer 2: fill -->
<s:Rect left="1" right="1" top="1" bottom="1" radiusX="2" radiusY="2">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF"
color.over="0xBBBDBD"
color.down="0xAAAAAA"
alpha="0.85" />
<s:GradientEntry color="0xD8D8D8"
color.over="0x9FA0A1"
color.down="0x929496"
alpha="0.85" />
</s:LinearGradient>
</s:fill>
</s:Rect>
<!-- layer 2: border -->
<s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2">
<s:stroke>
<s:LinearGradientStroke rotation="90" weight="1">
<s:GradientEntry color="0x000000"
alpha="0.5625"
alpha.down="0.6375" />
<s:GradientEntry color="0x000000"
alpha="0.75"
alpha.down="0.85" />
</s:LinearGradientStroke>
</s:stroke>
</s:Rect>
<s:Label id="labelDisplay"
textAlign="center"
verticalAlign="middle"
lineBreak="toFit"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="2" bottom="2">
</s:Label>
</s:SparkSkin>
The default Button skin’s minimum size is 21 pixels wide by 21
pixels high. These minimums are set on the root tag of the skin
class, as the following example shows:
<s:SparkSkin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21"
alpha.disabled="0.5">
...
</s:SparkSkin>
You might notice that in the default button skin’s class, though,
the Rect border sets its width to a value that is larger than the
minimum width on the skin:
<s:Rect id="border" left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2"> The width property on the Rect border is used for buttons whose size is unset. The minWidth property on the SparkSkin is used when the button’s width is variable, meaning that it hasn’t been explicitly set. The previous example sets a default width and height of 69 and 20. However, it is pinned to (left=0, right=0, top=0, bottom=0) so that if the label is too large or the button is explicitly sized, then this rectangle will resize. Accessing application propertiesIn addition to accessing the host component’s properties, you can access properties of the application. This is useful if there are global settings that you want to access, or runtime information that is passed into the application that you want available in the skin class. To access global variables in a custom skin, you use the FlexGlobals.topLevelApplication property. Using this property gives you access to all global variables, including variables that were passed into the application as flashVars variables. The following example accesses a String that is a global variable
on the application:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/GlobalVariableAccessorExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
public var myLabelString:String = "Hello World";
</fx:Script>
<s:Button skinClass="mySkins.GlobalVariableAccessorSkin"/>
</s:Application>
The following is the custom skin class for this example:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\GlobalVariableAccessorSkin.mxml -->
<s:Skin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21">
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<fx:Script>
import mx.core.FlexGlobals;
[Bindable]
private var localString:String = FlexGlobals.topLevelApplication.myLabelString;
</fx:Script>
<!-- Specify one state for each SkinState metadata in the host component's class -->
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:Rect
left="0" right="0"
top="0" bottom="0"
width="69" height="20"
radiusX="2" radiusY="2">
<s:stroke>
<s:SolidColorStroke color="0x000000" weight="1"/>
</s:stroke>
</s:Rect>
<s:Label id="labelDisplay"
text="{localString}"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="2" bottom="2">
</s:Label>
</s:Skin>
Using style properties in custom skinsWithin the skin, you can get the value of certain style properties on the host component. The component declares what styles can be set so that they can be set inline in MXML. For example, you can set the chromeColor property on all skins that extend the SparkSkin class. On other skins, such as container skins, you can set the backgroundColor. The following example sets the color of the border inside the
skin to the same value as the Button’s color style
property:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/BorderColorButtonExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:Button id="myButton"
label="Basic Button"
skinClass="mySkins.BorderColorButtonSkin"
color="green"/>
</s:Application>
The following is the skin class for this example: <?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\BorderColorButtonSkin.mxml -->
<s:Skin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21"
creationComplete="initSkin()">
<fx:Script>
private function initSkin():void {
/* Note that because color can change, you could override the
updateDisplayList() method and set it there so that the
color is updated in the skin when it is updated on the
host component. */
outline.color = getStyle('color');
}
</fx:Script>
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<!-- Specify one state for each SkinState metadata in the host component's class -->
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2">
<s:stroke>
<s:SolidColorStroke id="outline" weight="1"/>
</s:stroke>
</s:Rect>
<s:Label id="labelDisplay"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="2" bottom="2">
</s:Label>
</s:Skin>
You can use a binding expression to set properties in the skin
based on values in the host component. The following example binds
the value of the stroke’s color property to the
host component’s color style property:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/StyleWatcherExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:Button id="myButton"
label="Basic Button"
skinClass="mySkins.StyleWatcherSkin"
click="myButton.setStyle('color','red')"
color="green"/>
</s:Application>
The following is the skin class for this example:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\StyleWatcherSkin.mxml -->
<s:Skin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21">
<fx:Metadata>
[HostComponent("spark.components.Button")]
</fx:Metadata>
<!-- Specify one state for each SkinState metadata in the host component's class -->
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:Rect left="0" right="0" top="0" bottom="0" width="69" height="20" radiusX="2" radiusY="2">
<s:stroke>
<!-- Match the border color to the button label's color. -->
<s:SolidColorStroke color="{getStyle('color')}" weight="1"/>
</s:stroke>
</s:Rect>
<s:Label id="labelDisplay"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="2" bottom="2">
</s:Label>
</s:Skin>
This example sets the color of the stroke when the application starts. When you click the Button, the color property later changes while the application is running because style properties are bindable. For custom components, you must declare the style metadata on the component itself so that inline MXML styling will work. To expose a property as a style, you can also define a getter and setter for the property. You then override the styleChanged() method to dispatch the binding change event. In the application, you call the setStyle() method on the instance’s skin property, which sets the value of the style property on the skin. The following example exposes a custom style property called borderColor on
the custom skin:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/mySkins/StyleableBorderSkin.mxml -->
<s:Skin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
alpha.disabled="0.5">
<fx:Script>
[Bindable("borderColorChange")]
public function get borderColor():uint {
return getStyle("borderColor");
}
public function set borderColor(value:uint):void {
setStyle("borderColor", value);
}
override public function styleChanged(styleProp:String):void {
super.styleChanged(styleProp);
if (styleProp == "borderColor" || styleProp == null)
dispatchEvent(new Event("borderColorChange"));
}
</fx:Script>
<fx:Metadata>
[HostComponent("spark.components.SkinnableContainer")]
</fx:Metadata>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<s:Group id="contentGroup" left="0" right="0" top="0" bottom="0">
<s:layout>
<s:VerticalLayout/>
</s:layout>
</s:Group>
<s:Rect left="0" right="0" top="0" bottom="0">
<s:stroke>
<s:SolidColorStroke color="{borderColor}" weight="1"/>
</s:stroke>
</s:Rect>
</s:Skin>
In the following application, you set the color of the border
by using the ColorPicker control. This control uses the setStyle() method
to change the color of the container’s border.
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/StyleableBorderExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark" height="250" width="450">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<fx:Script>
private function setSkinStyles(e:Event):void {
myContainer.setStyle("borderColor",e.currentTarget.selectedColor);
}
</fx:Script>
<s:SkinnableContainer id="myContainer"
height="200" width="200"
skinClass="mySkins.StyleableBorderSkin">
</s:SkinnableContainer>
<mx:ColorPicker id="myColorPicker" change="setSkinStyles(event)"/>
</s:Application>
Another way to expose style properties to the skin is to add
code to the updateDisplayList() method to manually
push style values into the skin’s graphics. This is the more efficient
method of pushing style properties to skins because it does not
rely on binding. The following example overrides the updateDisplayList() method
to push the background color’s style property:
<fx:Script>
override protected function updateDisplayList(unscaleWidth:Number, unscaledHeight:Number):void {
// Push style values into the graphics properties before calling super.updateDisplayList
backgroundFill.color = getStyle("backgroundColor");
// Call super.updateDisplayList to do the rest of the work
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
</fx:Script>
<s:Rect left="0" right="0" top="0" bottom="0">
<s:SolidColor id="backgroundFill" />
</s:Rect>
Using events in custom skinsIn general, you should use states to react to user interaction. Some skins, such as the Button control’s skin, block user interaction to the skin. This means that you cannot trigger events defined on InteractiveObject on the skin. These include user interaction events such as mouseDown, mouseOut, and click. You can trigger events defined on UIComponent on the skin, however. These events include creationComplete and other lifecycle events. A Button control’s default skin defines several Rect elements and a Label control. These simple classes do not support any events. To add lifecycle event listeners, you can wrap a simple element or series of elements in a Group tag. This lets you register lifecycle events that are defined on UIComponent. The following example shows a Group inside the skin that triggers
a creationComplete event:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning/mySkins/LifecycleEventExample.mxml -->
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
height="200" width="200">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<s:Group width="150" height="100">
<s:Button label="Click It or Ticket" skinClass="mySkins.LifecycleEventSkin"/>
</s:Group>
</s:Application>
The custom skin class for this example is as follows:
<?xml version="1.0" encoding="utf-8"?>
<!-- SparkSkinning\mySkins\LifecycleEventSkin.mxml -->
<s:Skin
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
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>
<s:Rect id="buttonBorder" width="100%" height="20" radiusX="2" radiusY="2">
<s:stroke>
<mx:SolidColorStroke color="0x000000" weight="1" weight.over="2"/>
</s:stroke>
</s:Rect>
<s:Group creationComplete="trace('creationComplete')">
<s:Label id="labelDisplay"
horizontalCenter="0" verticalCenter="1"
left="10" right="10" top="6" bottom="2">
</s:Label>
</s:Group>
</s:Skin>
|
|