加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Flex4 Skinning 2: 皮肤协议

发布时间:2020-12-15 01:25:47 所属栏目:百科 来源:网络整理
导读:?上一篇随笔中笔者介绍了如何为按钮制作一个简单的自定义皮肤,接下来分析一下皮肤文件的组成部分,并对皮肤协议(skinning contract)中各个部分进行详细介绍。 首先可以看到根标签为Skin,接下来是HostComponent元数据,这个可以在我们新建文件的时候指定,
?上一篇随笔中笔者介绍了如何为按钮制作一个简单的自定义皮肤,接下来分析一下皮肤文件的组成部分,并对皮肤协议(skinning contract)中各个部分进行详细介绍。

  首先可以看到根标签为Skin,接下来是HostComponent元数据,这个可以在我们新建文件的时候指定,可以参照上一篇随笔的图1。

view source
print ?
1 <fx:Metadata>
2   [HostComponent("spark.components.Button")]
3 </fx:Metadata>

  接下来就是states。

view source
print ?
1 <!-- states -->
2 <s:states>
3 ???<s:State name="disabled" />
4 ???<s:State name="down" />
5 ???<s:State name="over" />
6 ???<s:State name="up" />
7 </s:states>

  来查看一下按钮(spark.components.Button)组件的源代码,准确说应该是按钮(spark.components.Button)类的父类按钮基类(spark.components.supportClasses.ButtonBase),找到元数据中带有SkinState元数据的部分,可以看到该类具有四个skin state: up,over,down disabled,这与在皮肤文件中定义的states部分相对应。代码如下:

view source
print ?
01 //--------------------------------------
02 //? Skin states
03 //--------------------------------------
04 ??
05 /**
06 ?*? Up State of the Button
07 ?*??
08 ?*? @langversion 3.0
09 ?*? @playerversion Flash 10
10 ?*? @playerversion AIR 1.5
11 ?*? @productversion Flex 4
12 ?*/
13 [SkinState("up")]
14 ??
15 /**
16 ?*? Over State of the Button
17 ?*??
18 ?*? @langversion 3.0
19 ?*? @playerversion Flash 10
20 ?*? @playerversion AIR 1.5
21 ?*? @productversion Flex 4
22 ?*/
23 [SkinState("over")]
24 ??
25 /**
26 ?*? Down State of the Button
27 ?*??
28 ?*? @langversion 3.0
29 ?*? @playerversion Flash 10
30 ?*? @playerversion AIR 1.5
31 ?*? @productversion Flex 4
32 ?*/
33 [SkinState("down")]
34 ??
35 /**
36 ?*? Disabled State of the Button
37 ?*??
38 ?*? @langversion 3.0
39 ?*? @playerversion Flash 10
40 ?*? @playerversion AIR 1.5
41 ?*? @productversion Flex 4
42 ?*/
43 [SkinState("disabled")]

  States是Flex4中skinning contract的重要组成部分。皮肤(skin)文件和相对应的控件组件(component)相互交互用到的正是skinning contract. 如同Button有四个state一样,每个skinning component都有一系列相对应的state,可以通过component当前所处的state来对外观进行不同的设置。

  组件的includeIn和excludeFrom属性为不同state的外观定义提供了很大便利。通过这两个属性可以在某特定的state下设置该state需要的外观。另外值得注意的是也可以通过“属性名.state=属性值”来进行更进一步的设置。如:

view source
print ?
1 <s:SolidColor color="0x77CC22" color.over="0x92D64E" color.down="0x67A41D" />

  以上代码段表示当按钮处于over state的时候,颜色为"0x92D64E",其它state时颜色为"0x77CC22"。

  SkinState元数据为皮肤文件中定义哪些state做了直接说明,如果皮肤中没有定义相关的state,而且这个state被设置为required=true,那么编译就会报错。

  子类会从父类中集成state,比如前面提到的四个skin state: up,down,disabled的定义并不存在于spark.components.Button中,而是在ButtonBase中。不过我们可以通过Exclude元数据对其进行过滤。

  在开发过程中,如果改变state,需要用到component的getCurrentSkinState()和invalidateSkinState()方法。getCurrentSkinState()用来追踪component的各个属性,并确定skin应该处于的state. invalidateSkinState()方法会使改变前的state失效并且根据getCurrentSkinState()方法返回值来设置当前的state.

  有时候skin需要引用相关component的内容。如果设置了HostComponent元数据,那么在skin里面就可以直接调用。在skin和component之间交互数据有两种方式。推式(push method)是指component把数据推入到skin中。相反的,拉式(pull method)是指skin向component索要属性值数据。关于这两种数据传输方式并没有固定的规定,但是一般情况下,如果component中定义SkinPart为required=true,则采用推式(push method)。

  接下来我们可以在spark.components.supportClass.ButtonBase类定义中可以找到标有SkinPart的成员变量labelDisplay,代码如下:

view source
print ?
01 //--------------------------------------------------------------------------
02 //
03 //? Skin parts
04 //
05 //--------------------------------------------------------------------------
06 ??
07 [SkinPart(required="false")]
08 ??
09 /**
10 ?*? A skin part that defines the label of the button.?
11 ?*??
12 ?*? @langversion 3.0
13 ?*? @playerversion Flash 10
14 ?*? @playerversion AIR 1.5
15 ?*? @productversion Flex 4
16 ?*/
17 public var labelDisplay:TextBase;

  Skin part也是skin contract的重要组成部分。任何复杂的component都是由简单的组合而成。比如前面提到的按钮,就是由文本和矩形组成。文本就是按钮的skin part. 它由component声明,可以是可选的。定义的方式是使用SkinPart元数据,可以把这个元数据标记在任何公共的属性上,而属性的名称就成了这个part的名称。比如在按钮当中,labelDisplay就是一个SkinPart,它被定义为可选的,类型为TextBase.

  在component中定义了skin part后,需要在skin文件中进行对应的实现,比如我们可以将上一篇随笔中定义的label的id属性设置为labelDisplay,这样Flex就会通过id属性自动将这个label与Button的labelDisplay相匹配。

  将皮肤文件的Label部分改成如下代码所示:

view source
print ?
1 <!-- text -->
2 <s:Label id="labelDisplay" text="{hostComponent.label}" color="0x131313"?
3 ?????????textAlign="center" verticalAlign="middle"?
4 ?????????horizontalCenter="0" verticalCenter="1"?
5 ?????????left="12" right="12" top="6" bottom="6" />

  可以发现,此时skin文件要想引用对应的component中的数据只需要使用hostComponent属性就可以了,这是因为我们在最开始就已经对元数据HostComponent进行了设置。修改相应的主应用程序,代码如下所示:

view source
print ?
01 <?xml version="1.0" encoding="utf-8"?>
02 <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"?
03 ???????????????xmlns:s="library://ns.adobe.com/flex/spark"?
04 ???????????????xmlns:mx="library://ns.adobe.com/flex/mx"?
05 ???????????????minWidth="955" minHeight="600">
06 ??
07 ????<fx:Declarations>
08 ????????<!-- Place non-visual elements (e.g.,services,value objects) here -->
09 ????</fx:Declarations>
10 ??????
11 ????<fx:Style>
12 ????????@namespace s "library://ns.adobe.com/flex/spark";
13 ????????@namespace mx "library://ns.adobe.com/flex/mx";
14 ????????s|Button {
15 ????????????skinClass:ClassReference("com.guyue.skins.SimpleButtonSkin");
16 ????????}
17 ????</fx:Style>
18 ??????
19 ????<s:layout>
20 ????????<s:VerticalLayout />
21 ????</s:layout>
22 ??????
23 ????<s:Button label="Simple Button" />
24 ??????
25 ????<s:Button label="I used to be a button without customerized skin. " />
26 ??????
27 </s:Application>

  再次运行程序,可以发现现在两个按钮公用同样的皮肤,而且skin的定义在主应用程序端是完全透明的,这样外观和逻辑就实现了分离。如图3.

?

Figure 3. 使用样式可以同时为多个按钮设置皮肤

  上面提到的TextBase是确定的类型,而且在声明周期中只会存在唯一的实例,称为静态的。当加载静态SkinPart的皮肤时,SkinnableComponent基类负责将所有的静态part从skin加载到component中。加载完毕之后,component就可以直接使用这些实例了。当part加载的时候,partAdded()会被调用,当part移除的时候partRemoved()方法会被调用。因此我们可以在这两个方法中添加相关事件处理逻辑。

view source
print ?
01 /**
02 ?*? @private
03 ?*/
04 override protected function partAdded(partName:String,instance:Object):void
05 {
06 ????super.partAdded(partName,instance);
07 ??????
08 ????if (instance == labelDisplay)
09 ????{
10 ????????labelDisplay.addEventListener("isTruncatedChanged",
11 ??????????????????????????????????????labelDisplay_isTruncatedChangedHandler);
12 ??????????
13 ????????// Push down to the part only if the label was explicitly set
14 ????????if (_content !== undefined)
15 ????????????labelDisplay.text = label;
16 ????}
17 }
18 ??
19 /**
20 ?*? @private
21 ?*/
22 override protected function partRemoved(partName:String,instance:Object):void
23 {
24 ????super.partRemoved(partName,instance);
25 ??????
26 ????if (instance == labelDisplay)
27 ????{
28 ????????labelDisplay.removeEventListener("isTruncatedChanged",
29 ?????????????????????????????????????????labelDisplay_isTruncatedChangedHandler);
30 ????}
31 }

  以上便是Button类中相关的代码,可以看出我们能够动态的为component中skin part添加或减少相关事件监听器。

  但是有些时候我们需要动态的使用多个SkinPart的实例,比如spark.components.supportClasses.SliderBase这个类的所有子类的dataTip属性。这种SkinPart称为动态的。具体的代码如下所示:

view source
print ?
01 //--------------------------------------------------------------------------
02 //
03 //? Skin parts
04 //
05 //--------------------------------------------------------------------------
06 ??
07 [SkinPart(required="false",type="mx.core.IDataRenderer")]
08 ??????
09 /**
10 ?*? A skin part that defines a dataTip that displays a formatted version of?
11 ?*? the current value. The dataTip appears while the thumb is being dragged.
12 ?*? This is a dynamic skin part and must be of type IFactory.
13 ?*??
14 ?*? @langversion 3.0
15 ?*? @playerversion Flash 10
16 ?*? @playerversion AIR 1.5
17 ?*? @productversion Flex 4
18 ?*/
19 public var dataTip:IFactory;

  可以看出dataTip属性的类型为Ifactory,这表示开发人员可以动态的在component中对其实例利用createDynamicPartInstance()方法初始化,利用removeDynamicPartInstance()方法来移除。相应的partAdded()和partRemoved()方法与静态SkinPart处理相似。在一个component可以有多个动态part实例,何时调用createDynamicPartInstance()完全由开发人员根据需要来决定。

  卸载skin基本上是自动进行的。程序运行时skin发生变化的时候就会调用detachSkin()方法。所有skin part相关的partRemoved()方法也会自动调用。开发人员可以在这两个方法里添加相应的事件监听。

  有些复杂的component 可能需要在skin创建后某个不确定的时间与skin part进行交互。Component需要知道partAdded()方法可能在skin初始化很久以后才会被调用。这样的skin part被称为延迟的(deferred part)。一个典型的例子就是TabNavigator,当用户点击了某个Tab之后,component才会知道新的part然后调用partAdded()方法。

  图4是Adobe官方给出的skin parts生命周期。

?

Figure 4.

  本篇随笔中提到的state,skin part,data构成了skin contract. 他们共同实现了component及其skin的定义和交互。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读