[译] Flex 4 皮肤功能介绍 -续
英文原文:?http://www.adobe.com/devnet/flex/articles/flex4_skinning.html
本文翻译原创链接: http://www.smithfox.com/?e=34 转载请注明
翻译:?smithfox
?
?
上接:
http://smithfox.iteye.com/admin/blogs/847465
?
制作slider皮肤皮肤parts不仅可以推送组件数据到皮肤中,组件也可以用它们来注册行为。为了讲得更明白,以slider组件为例。slider两个主要parts是轨迹条和滑动块。在这个例子中,该组件没有把任何数据推送到皮肤来显示,但它添加了事件监听器到parts中并且会根据组件的value属性执行滑动块的布局。例如,当点击轨迹条,组件会更新其value属性并且定位滑动块到适当位置。此外,还有动态皮肤parts,数据提示,这是用来拖动滑块时显示弹出的提示信息。 在图6所示是一个简单slider和一个修改后的slider。
图6: 修改后的slider(左边) 和原来的slider (右边)
为构建这个, 你的皮肤文件必须声明三个皮肤parts: thumb(滑块), track(轨迹条), and dataTip(数据提示)。
MySliderSkin.mxml
图7: NoteCard 组件例子
主应用程序仅创建一个有点旋转的语录NoteCard。 有趣的部分是NoteCard类,它扩展了spark.components.supportClasses.SkinnableComponent类并且在生命周期方法中添加代码。
NoteCard.as:
01
package
02
{
03
?
?04
[SkinState(
"normal"
)]
05
[SkinState(
"disabled"
)]
06
public
class
NoteCard
extends
SkinnableComponent
07
{
08
????
public
function
NoteCard()
09
????
{
10
????????
super
();
11
????
}
12
????
?13
????
[SkinPart(required=
"true"
)]
14
????
public
var
labelDisplay:TextBase;
15
????
?16
????
[SkinPart(required=
"false"
)]
17
????
public
var
closeButton:Button;
18
????
?19
????
private
var
_text:
String
;
20
?
?21
????
public
function
get
text():
String
22
????
{
23
????????
return
_text;
24
????
}
25
?
?26
????
public
function
set
text(value:
String
):
void
27
????
{
28
????????
if
(_text == value)
29
???????????
return
;
30
????????
_text = value;
31
????
}
32
?
?33
????
...
34
}
35
}
此组件声明数据属性,皮肤states,皮肤parts。 对于数据,NoteCard有一个公共的text属性。 此外,NoteCard有两个皮肤states,normal和disabled,用SkinStates元数据声明在类声明代码的上面。 这就告诉皮肤,它需要实现这两个states。
NoteCard还有两个皮肤parts,是通过SkinPart元数据声明的。 SkinPart元数就直接声明在皮肤part名称之上。 当前例子,labelDisplay是必需的TextBase类皮肤part,closeButton是一个可选的Button类皮肤part。
由于皮肤是运行时载入,当组件第一次启动时,你不能保证有一定有皮肤。 你也不能保证已经有了全部的皮肤parts,尤其是他们是可选的。 框架负责了这些事情: 衔接part声明和组件属性定义,并且通过皮肤生命周期方法通知组件parts已经准备好了。?
为实现皮肤states,你需重写getCurrentSkinState()方法以返回皮肤当前所处状态,当前例子中,它会返回"normal"或"disabled"。当一些事件导致皮肤state变得无效时,组件应该调用invalidateSkinState()方法。NoteCard.as
01
package
02
{
03
[SkinState(
"normal"
)]
04
[SkinState(
"disabled"
)]
05
public
class
NoteCard
extends
SkinnableComponent
06
{
07
????
...??
08
????
override
public
function
set
enabled(value:
Boolean
) :
void
09
????
{
10
????????
if
(enabled != value)
11
???????????
invalidateSkinState();
12
????????
super
.enabled = value;
13
????
}
14
????
?15
????
override
protected
function
getCurrentSkinState() :
String
16
????
{
17
????????
if
(!enabled)
18
???????????
return
"disabled"
;
19
????????
return
"normal"
20
????
}
21
????
?22
????
...
23
}
24
}
当设置enabled属性时,enabled setter调用invalidateSkinState()以通知皮肤,组件的state需要改变,这样getCurrentSkinState()随即会被调用。
处理皮肤parts,有两种主要方法应该要重写,partAdded()和partRemoved()。这些方法会告诉你一个特定的皮肤part被添加了或被删除了。当装载一个皮肤时Parts将被加入或是删除。皮肤是在运行时交换的,并且延迟加载的,所以只有在某种states情况下或者是一个动态part刚刚被创建时part才会被加入。在partAdded()方法你可以设置你想要的任何数据到part,而且也可以attach一些事件侦听到part上。当part被删除时,你应该在partRemoved()方法中做相反的事情。
NoteCard.as
01
package
02
{
03
public
class
NoteCard
extends
SkinnableComponent
04
{
05
????
[SkinPart(required=
"true"
)]
06
????
public
var
labelDisplay:TextBase;
07
????
?08
????
[SkinPart(required=
"false"
)]
09
????
public
var
closeButton:Button;
10
????
?11
????
public
function
set
text(value:
String
):
void
12
????
{
13
????????
if
(_text == value)
14
???????????
return
;
15
????????
_text = value;
16
????????
?17
????????
if
(labelDisplay)
18
???????????
labelDisplay.text = value;
19
????
}
20
?
?21
????
override
protected
function
partAdded(partName:
String
, instance:
Object
) :
void
22
????
{
23
???????
super
.partAdded(partName, instance);
24
???????
?25
???????
if
(instance == labelDisplay)
26
???????????
labelDisplay.text = _text;
27
???????
if
(instance == closeButton)
28
???????????
closeButton.addEventListener(MouseEvent.CLICK, closeButton_clickHandler);
29
????
}
30
????
?31
????
override
protected
function
partRemoved(partName:
String
, instance:
Object
) :
void
32
????
{
33
???????
super
.partRemoved(partName, instance);
34
????????
?35
????????
if
(instance == closeButton)
36
???????????
closeButton.removeEventListener(MouseEvent.CLICK, closeButton_clickHandler);
37
????
}
38
????
?39
????
protected
function
closeButton_clickHandler(event:MouseEvent) :
void
40
????
{
41
???????
event.stopPropagation();
42
???????
?43
???????
IVisualElementContainer(parent).removeElement(
this
);
44
????
}
45
?
}
46
}
在partAdded()方法中,当labelDisplay part加入时,我设置text到这个part。此外,在text属性的setter方法中,我检查,看看是否已经加入labelDisplay,如果是的话,我重新设置labelDisplay.text为组件的_text值以保证和组件text属性同步。在partAdded()方法中,我添加一个click事件监听器到closeButton皮肤part。在partRemoved()我一定要删除这个click事件监听器。
作为一个SkinnableComponent ,你需要做的就是利用这个强大的换肤机制。 当有人创造了某个组件的皮肤,他们必须实现皮肤states和皮肤parts以得到期望的组件行为。 在图6所示的皮肤在样例源代码中可以找到,即使这是一个简单的组件定义,你依然可以用不同的皮肤完全改变它的外观和体验。这就是皮肤真正的力量。
注:当创建可变换皮肤组件,您可能要决定某些行为是属于皮肤的还是组件。 没有一个明确的硬性的规则。只要能让你的工作更容易就行了。 作为一般指导,一切外观和感观的定义应在皮肤MXML文件中声明。 另一方面,如果有多个皮肤想要某个特殊行为,那么将这个行为放在组件可能是一个好主意。例如,slider中滑块的定位是做在VSlider和HSlider,没有在皮肤上。
Flex 4皮肤发生了重大修改。 明确分开了组件和皮肤。该组件包含了数据,行为和核心逻辑,而皮肤定义了组件的外观和体验。组件由ActionScript编写而皮肤写在MXML中,这是托FXG和新states语法的福。 组件和皮肤通过皮肤契约进行交互。 又因为它们是各自独立的文件,所以新的皮肤很容易应用到组件上从而完全改变他们的外观。
欲了解更多的Flex 4皮肤信息,请查看 皮肤架构规范 以及 Gumbo组件架构白皮书。