[译] 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。

The modified slider (left) and original slider (right).

图6: 修改后的slider(左边) 和原来的slider (右边)

为构建这个, 你的皮肤文件必须声明三个皮肤parts: thumb(滑块), track(轨迹条), and dataTip(数据提示)。
MySliderSkin.mxml

01 <? xml version = "1.0" encoding = "utf-8" ?>
02 < s:Skin xmlns:fx = "http://ns.adobe.com/mxml/2009 "
03        xmlns:s = "library://ns.adobe.com/flex/spark "
04        minWidth = "11" minHeight = "100" alpha.disabled = "0.5" >
05       < fx:Metadata >
06         [HostComponent("spark.components.VSlider")]
07      </ fx:Metadata >
08      < s:states >
09          < s:State name = "normal" />
10          < s:State name = "disabled" />
11      </ s:states >
12       
13      < fx:Declarations >
14          < fx:Component id = "dataTip" >
15             < s:DataRenderer minHeight = "24" minWidth = "40" x = "20" >
16                 < s:Rect top = "0" left = "0" right = "0" bottom = "0" >
17                     < s:fill >
18                         < s:SolidColor color = "0xFFF46B" alpha = ".9" />
19                     </ s:fill >
20                     < s:filters >
21                         < s:DropShadowFilter angle = "90" color = "0x999999" distance = "3" />                  
22                     </ s:filters >
23                 </ s:Rect >
24                         
25                 < s:Label id = "labelField" text = "{data}"
26                          horizontalCenter = "0" verticalCenter = "1"
27                          left = "5" right = "5" top = "5" bottom = "5"
28                          textAlign = "center" verticalAlign = "middle" color = "0x555555" />
29             </ s:DataRenderer >
30          </ fx:Component >
31      </ fx:Declarations >
32       
33      < s:Button id = "track" left = "5" right = "5" top = "0" bottom = "0" skinClass = "MyTrackSkin" />
34      < s:Button id = "thumb" left = "0" right = "0" width = "18" height = "8" skinClass = "MyThumbSkin" />
35    
36 </ s:Skin >

在皮肤中定义了这些皮肤parts这后,组件负责处理他们。它添加事件监听器到滑块,让你可以在轨迹条中拖动滑块。 它还根据相应的value值来定位滑块。请看一下上面示例代码中的MyTrackSkin和MyThumbSkin。你会看到许多FXG的例子。请注意, 自定义的滑块皮肤相比默认Spark滑块皮肤有着完全不同的形状。
"数据提示"皮肤part是动态的 -- 它负责生成和布局。当前的例子中,当你拖动滑块,会在滑块右边弹出数据提示。有了皮肤契约,皮肤可以只管定义皮肤part,和所有可视化方面的内容,而不必担心有什么副作用。 所有的衔接都由组件来处理。
注:一些Flex 4内置组件不仅附加行为到皮肤parts上,同时也会推送数据到皮肤parts。另一种得到皮肤中数据的方法是通过hostComponent属性来拉他们。
当一个组件创建了皮肤时,并非所有的皮肤parts是必需的。 例如,VSlider的数据提示皮肤part并不是必需的。 如果它不存在,就不显示数据提示。 


创建可变换皮肤组件

Spark可变换皮肤组件没有在幕后做什么特别的事情。. They have data properties and advertise the skin parts and skin states they need through metadata。 他们还有少许关键的方法来用管理皮肤和皮肤parts的生命周期。您也可以和它一样轻松地创建一个新的换肤组件。
为了演示一下,你可以创建一个简单NoteCard组件,它可以用来在屏幕上显示笔记。在图7所示的例子中,应用程序随机创建多个语录。

NoteCard component instances.

图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

为实现皮肤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

处理皮肤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组件架构白皮书。

你可能感兴趣的:(框架,Flex,Adobe,actionscript)