Lesson 1 Start
?
Flex 4 默认为Spark主题,Spark组件的外观设计与之前相比也变得更简单高效。
Spark组件都不包含可视外观属性,所有这类信息都包含在外观文件SkinClass中。
SkinClass借助 FXG和状态语法完成组件的交互式设计,其使用MXML编写新的外观文件。
在组件中则通过skinClass属性来应用相关外观文件,如:<s:Button label="myButton"?skinClass="myButtonSkin"/>。
以下是一个简单的ButtonSkinClass:
<?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"
alpha.disabled="0.7">
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:filters>
<s:DropShadowFilter alpha="0.5" distance="1.2" quality="2" excludeFrom="down"/>
</s:filters>
<s:Rect id="myMain" radiusX="4" radiusY="4" top="0" right="0" bottom="0" left="0">
<s:fill>
<s:SolidColor color="0x5bc1e9" color.over="0xcfc851" color.down="0xb19600"/>
</s:fill>
<s:stroke>
<s:SolidColorStroke color="0x001fa4" weight="1"/>
</s:stroke>
</s:Rect>
<s:Rect id="myTop" radiusX="4" radiusY="4" top="1" right="1" left="1" height="50%">
<s:LinearGradient rotation="90">
<s:GradientEntry color="0xFFFFFF" alpha="0.7"/>
<s:GradientEntry color="0xFFFFFF" alpha="0.2"/>
</s:LinearGradient>
<s:Label text="myButton" verticalCenter="2" horizontalCenter="0" ?textAlign="center" fontWeight="bold"?color="0x131313" color.over="0x0150ad" color.down="0x0150ad"/>
</s:Skin>
效果如下图:

Lesson 2
SkinClass借助 FXG和状态语法完成组件的交互式设计。
FXG也有类似提取公共方法的功能,其实现依靠Library标签。
比如设计中用到好多处外观相同的“竖线”,不过摆放的位置不同,当然不用每用到一处就写一遍相似度达99%的代码,使用FXG的Library标签就可以将其提取简化。
直接把“竖线”代码放在<fx:Library/>中,设好其id,下面用到处使用<fx:id x="..." y="..."/>进行调用便可。
以下是一个简单实例:
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Library>
<fx:Definition name="fg">
<s:Group alpha="0.5">
<s:Line x="1" xFrom="0.5" xTo="0.5" yTo="34">
<s:SolidColorStroke color="#ffffff" caps="none" weight="1" joints="miter" miterLimit="4"/>
</s:Line>
<s:Line xFrom="0.5" xTo="0.5" yTo="34">
<s:SolidColorStroke color="#000000" caps="none" weight="1" joints="miter" miterLimit="4"/>
</s:Group>
</fx:Definition>
</fx:Library>
<s:State name="normal"/>
<s:DropShadowFilter alpha="0.5" angle="90" distance="1.7" quality="2"/>
<s:Path winding="nonZero" data="M0.5 35.5L0.5 4.5C0.5 2.29102 2.29102 0.5 4.5 0.5L730.5 0.5C732.709 0.5 734.5 2.29102 734.5 4.5L734.5 35.5" >
<s:LinearGradient x="367.5" y="35.5" scaleX="35" rotation="-90">
<s:GradientEntry color="#00738c" ratio="0"/>
<s:GradientEntry color="#49b7d3" ratio="0.04"/>
<s:GradientEntry color="#366cad" ratio="0.96"/>
<s:GradientEntry color="#bfeffb" ratio="1"/>
<s:SolidColorStroke color="#001fa4" caps="none" weight="1" joints="miter" miterLimit="4"/>
</s:Path>
<fx:fg x="465" y="1"/>
<fx:fg x="600" y="1"/>

Lesson 3
SkinClass借助FXG和状态语法完成组件的交互式设计。
FXG对于复杂的图形会生成多而繁琐的代码,这时还不如使用一张透明的PNG图片代替来得“划算”。
当然SkinClass中也有插入图片的标签,就是<s:BitmapImage/>。其插入图片非常方便,也可随意缩放、旋转等。
?
<?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"
alpha.disabled="0.7">
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<s:BitmapImage scaleX="0.35" scaleY="0.35" width="64" height="64" source="@Embed('search.png')"/>
</s:Skin>
Lesson 4
FXG的基本形状有三种,矩形(圆角矩形)、椭圆(圆形)、直线。
Rect矩形(圆角矩形),以下是基本的一些属性:
width <length>: 矩形的宽度。
height <length>: 矩形的高度。
radiusX <length>: 圆角矩形,圆角在X轴椭圆半径的弯道,此值四舍五入。
radiusY <length>: 圆角矩形,圆角在y轴椭圆半径的弯道,此值四舍五入。
rotation <Number>: 旋转角度,正数为顺时针,负数为逆时针。
scaleX <Number>: x轴缩放,1为100%。
scaleY <Number>: y轴缩放,1为100%。
visible <Boolean>: 可见性。
Ellipse椭圆(圆形),以下是基本的一些属性:
width <length>: 椭圆的宽度。
height <length>: 椭圆的高度。
rotation <Number>: 旋转角度,正数为顺时针,负数为逆时针。
scaleX <Number>: x轴缩放,1为100%。
scaleY <Number>: y轴缩放,1为100%。
visible <Boolean>: 可见性。
Line直线,以下是基本的一些属性:
xFrom <Number>: X轴起点,默认为0。
yFrom <Number>: y轴起点,默认为0。
xTo <Number>: X轴终点,默认为0。
yTo <Number>: y轴终点,默认为0。
rotation <Number>: 旋转角度,正数为顺时针,负数为逆时针。
scaleX <Number>: x轴缩放,1为100%。
scaleY <Number>: y轴缩放,1为100%。
alpha <Number>: 不透明度,1为100%。
visible <Boolean>: 可见性。
FXG的复杂图形使用Path来绘制。
以下是一个尖角向下的红色三角形的绘制(其主要通过节点坐标来绘制):
<Path data="M 0 0 L 100 0 L 50 100 Z">
??? <fill>
??????? <SolidColor color="#FF0000"/>
??? </fill>
</Path>
P.S. data中M表示起始位置,Z表示返回到起始位置。
Lesson 5
FXG使用fill对图形本体进行上色,使用stroke对图形边框进行上色,而fill(填充)和stroke(画笔)有三种上色方法:
fill — SolidColor(色块),RadialGradient(径向渐变),LinearGradient(线性渐变)。
stroke — SolidColorStroke(实线),RadialGradientStroke(径向渐变),LinearGradientStroke(线性渐变)。
以下是一个简单的例子:
<?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">
??? <s:VGroup horizontalCenter="0" verticalCenter="0" gap="21">
??? ??? <s:HGroup gap="50">
??? ??? ??? <s:Rect width="100" height="100">
??? ??? ??? ??? <s:fill>
??? ??? ??? ??? ??? <s:LinearGradient>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#00cc00"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#ff4400"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#004488"/>
??? ??? ??? ??? ??? </s:LinearGradient>
??? ??? ??? ??? </s:fill>
??? ??? ??? </s:Rect>
??? ??? ??? <s:Ellipse width="100" height="100">
??? ??? ??? ??? <s:fill>
??? ??? ??? ??? ??? <s:RadialGradient>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#00cc00"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#ff4400"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#004488"/>
??? ??? ??? ??? ??? </s:RadialGradient>
??? ??? ??? ??? </s:fill>
??? ??? ??? </s:Ellipse>
??? ??? ??? <s:Rect width="100" height="100">
??? ??? ??? ??? <s:fill>
??? ??? ??? ??? ??? <s:SolidColor color="#bffaa00"/>
??? ??? ??? ??? </s:fill>
??? ??? ??? </s:Rect>
??? ??? </s:HGroup>
??? ??? <s:HGroup gap="21">
??? ??? ??? <s:Ellipse width="100" height="100">
??? ??? ??? ??? <s:stroke>
??? ??? ??? ??? ??? <s:LinearGradientStroke weight="20">
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#00cc00"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#ff4400"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#004488"/>
??? ??? ??? ??? ??? </s:LinearGradientStroke>
??? ??? ??? ??? </s:stroke>
??? ??? ??? </s:Ellipse>
??? ??? ??? <s:Rect width="100" height="100">
??? ??? ??? ??? <s:stroke>
??? ??? ??? ??? ??? <s:RadialGradientStroke weight="20">
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#00cc00"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#ff4400"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#004488"/>
??? ??? ??? ??? ??? </s:RadialGradientStroke>
??? ??? ??? ??? </s:stroke>
??? ??? ??? </s:Rect>
??? ??? ??? <s:Ellipse width="100" height="100">
??? ??? ??? ??? <s:stroke>
??? ??? ??? ??? ??? <s:SolidColorStroke color="#ffaa00" weight="20"/>
??? ??? ??? ??? </s:stroke>
??? ??? ??? </s:Ellipse>
??? ??? </s:HGroup>
??? </s:VGroup>
</s:Skin>
效果如下:

Lesson 6
FXG中常用的效果滤镜有7个:
GlowFilter(单色发光滤镜)
BlurFilter(模糊滤镜)
DropShadowFilter(阴影滤镜)
BevelFilter(斜角滤镜)
GradientGlowFilter(彩色发光滤镜)
GradientBevelFilter(彩色斜角滤镜)
ColorMatrixFilter(色彩响应矩阵滤镜)
<?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">
??? <fx:Script>
??? ??? <![CDATA[
??? ??? ??? private const myMatrix:Array = [0.3,0.59,0.11,
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? 0.3,
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? 0,1,0];
??? ??? ]]>
??? </fx:Script>
??? <s:states>
??? ??? <s:State name="disabled" />
??? ??? <s:State name="normal" />
??? </s:states>
??? <s:VGroup? fontSize="21" fontWeight="bold" color="#FFFFFF" gap="21" horizontalAlign="center">
??? ??? <s:Group>
??? ??? ??? <s:Label text="Simon_007"/>
??? ??? ??? <s:filters>
??? ??? ??? ??? <!--单色发光滤镜-->
??? ??? ??? ??? <s:GlowFilter blurX="10" blurY="10" quality="3" strength="4" color="#ff7700"/>
??? ??? ??? </s:filters>
??? ??? </s:Group>
??? ??? <s:HGroup gap="21">
??? ??? ??? <s:Group>
??? ??? ??? ??? <s:Label text="Simon_001"/>
??? ??? ??? ??? <s:filters>
??? ??? ??? ??? ??? <!--模糊滤镜-->
??? ??? ??? ??? ??? <s:BlurFilter blurX="3" blurY="3" quality="5"/>
??? ??? ??? ??? </s:filters>
??? ??? ??? </s:Group>
??? ??? ??? <s:Group>
??? ??? ??? ??? <s:Label text="Simon_002"/>
??? ??? ??? ??? <s:filters>
??? ??? ??? ??? ??? <!--阴影滤镜-->
??? ??? ??? ??? ??? <s:DropShadowFilter blurX="3" blurY="3" quality="3" strength="1" color="#000000" alpha="0.7" angle="70" distance="3"/>
??? ??? ??? ??? </s:filters>
??? ??? ??? </s:Group>
??? ??? ??? <s:Group>
??? ??? ??? ??? <s:Label text="Simon_003"/>
??? ??? ??? ??? <s:filters>
??? ??? ??? ??? ??? <!--斜角滤镜-->
??? ??? ??? ??? ??? <s:BevelFilter blurX="3" blurY="3" quality="5" angle="120" highlightColor="#00ff00" shadowColor="#00ff00"/>
??? ??? ??? ??? </s:filters>
??? ??? ??? </s:Group>
??? ??? </s:HGroup>
??? ??? <s:HGroup gap="21">
??? ??? ??? <s:Group>
??? ??? ??? ??? <s:Label text="Simon_004"/>
??? ??? ??? ??? <s:filters>
??? ??? ??? ??? ??? <!--彩色发光滤镜-->
??? ??? ??? ??? ??? <s:GradientGlowFilter blurX="10" blurY="10" distance="0" >
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#0000ff" alpha="0"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#0000ff"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#ff0000"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#00ff00"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#000000"/>
??? ??? ??? ??? ??? </s:GradientGlowFilter>
??? ??? ??? ??? </s:filters>
??? ??? ??? </s:Group>
??? ??? ??? <s:Group>
??? ??? ??? ??? <s:Label text="Simon_005"/>
??? ??? ??? ??? <s:filters>
??? ??? ??? ??? ??? <!--彩色斜角滤镜-->
??? ??? ??? ??? ??? <s:GradientBevelFilter blurX="3" blurY="3" angle="120" quality="3">
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#0000ff"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#ff7700"/>
??? ??? ??? ??? ??? ??? <s:GradientEntry color="#00ff00"/>
??? ??? ??? ??? ??? </s:GradientBevelFilter>
??? ??? ??? ??? </s:filters>
??? ??? ??? </s:Group>
??? ??? ??? <s:Group>
??? ??? ??? ??? <s:Label text="Simon_006" color="#ff0000"/>
??? ??? ??? ??? <s:filters>
??? ??? ??? ??? ??? <!--色彩响应矩阵滤镜-->
??? ??? ??? ??? ??? <!--将红色的 Simon_006 变为黑白-->
??? ??? ??? ??? ??? <s:ColorMatrixFilter matrix="{myMatrix}"/>
??? ??? ??? ??? </s:filters>
??? ??? ??? </s:Group>
??? ??? </s:HGroup>
??? </s:VGroup>
</s:Skin>

Lesson 7 End
刚开始做skinClass设计时往往会为一个小模块设计一个skinClass并把其用到的所有组件和逻辑都放进skinClass内,外部只要传入模块所需的数据和处理模块所抛出的事件就行。这样设计看似简单快速,使用起来也比较方便,skinClass似乎能完成所有的事了。
但是如果把这个skinClass去掉,那么这个小模块就只有一个白板了,因为所有的组件和逻辑都在skinClass中,再想想下次如果要做皮肤扩展,那就要重写一个庞大的skinClass了(其中的很多组件和逻辑代码都是重复的)。
其实组件和skinClass是可以分开设计的,先设计一个默认皮肤的小模块,让其能单独运行;再写skinClas将其HostComponent指定到要应用此皮肤的小模块,可以在skinClass的creationComplete事件处理中使用hostComponent.XXXid 得到小模块下的子组件,再用setStyle("skinClass",Class(XXX))对子组件进行skinClass的设置。
这样就可以把组件和skinClass分开了,也可以很方便地进行皮肤扩展。
下面来看一个例子:
程序结构如图

程序通过skin_test中的button来更换Box的main_skin,再在main_skin中对Box里的小组件进行a_skin的设置。
包skin_1,skin_2,skin_3就是三套皮肤。
skin_test.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application 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:local="*">
??? <fx:Script>
??? ??? <![CDATA[
??? ??? ??? import skin_1.main_skin;
??? ??? ??? import skin_2.main_skin;
??? ??? ??? import skin_3.main_skin;
??? ??? ????
??? ??? ??? protected function button1_clickHandler(event:MouseEvent):void{
??? ??? ??? ??? box.setStyle("skinClass",Class(skin_1.main_skin));
??? ??? ??? }
??? ??? ??? protected function button2_clickHandler(event:MouseEvent):void{
??? ??? ??? ??? box.setStyle("skinClass",Class(skin_2.main_skin));
??? ??? ??? }
??? ??? ]]>
??? </fx:Script>
??? <s:VGroup width="100" horizontalCenter="0" verticalCenter="0" horizontalAlign="center" gap="10">
??? ??? <s:HGroup gap="21">
??? ??? ??? <s:Button label="skin1" click="button1_clickHandler(event)"/>
??? ??? ??? <s:Button label="skin2" click="button2_clickHandler(event)"/>
??? ??? </s:HGroup>
??? ??? <local:Box id="box" skinClass="skin_3.main_skin"/>
??? </s:VGroup>
</s:Application>
Box.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnableContainer xmlns:fx="http://ns.adobe.com/mxml/2009"?
??? ??? ??? ??? ??? ? xmlns:s="library://ns.adobe.com/flex/spark"?
??? ??? ??? ??? ??? ? xmlns:mx="library://ns.adobe.com/flex/mx">
??? <s:HGroup left="10" right="10" top="10" bottom="10" gap="10">
??? ??? <s:TextArea id="show" />
??? ??? <s:TextArea id="input" />
??? </s:HGroup>
</s:SkinnableContainer>
skin_1.main_skin.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"
??? ??? creationComplete="skin1_creationCompleteHandler(event)">
??? <fx:Metadata>
??? ??? [HostComponent("Box")]
??? </fx:Metadata>
??? <fx:Script>
??? ??? <![CDATA[
??? ??? ??? import mx.events.FlexEvent;
??? ??? ??? protected function skin1_creationCompleteHandler(event:FlexEvent):void{
??? ??? ??? ??? hostComponent.show.setStyle("skinClass",Class(a_skin));
??? ??? ??? ??? hostComponent.input.setStyle("skinClass",Class(a_skin));
??? ??? ??? }
??? ??? ]]>
??? </fx:Script>
??? <s:states>
??? ??? <s:State name="normal" />
??? ??? <s:State name="disabled" />
??? </s:states>
??? <s:Rect width="100%" height="100%">
??? ??? <s:fill>
??? ??? ??? <s:SolidColor color="#ff0000"/>
??? ??? </s:fill>
??? </s:Rect>
??? <s:Group id="contentGroup"/>
</s:Skin>
skin_1.a_skin.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">
??? <fx:Metadata>
??? ??? [HostComponent("spark.components.TextArea")]
??? </fx:Metadata>
??? <s:states>
??? ??? <s:State name="disabled" />
??? ??? <s:State name="normal" />
??? </s:states>
??? <s:Rect width="100" height="100">
??? ??? <s:fill>
??? ??? ??? <s:SolidColor color="#ffff00"/>
??? ??? </s:fill>
??? </s:Rect>
</s:Skin>
skin_2和skin_3包里的main_skin.mxml和a_skin.mxml只是color不同罢了。