Flex 4 皮膚功能介紹

原文: http://www.adobe.com/devnet/flex/articles/flex4_skinning.html

[原創翻譯鏈接:  http://www.smithfox.com/?e=34 轉載請保留此聲明]

Flex 4(代號:Gumbo)的主要主題之一是"Design in mind", 皮膚則是這個主題的重要組成部分。Flash Player是迄今爲止最具創意的web工作機制。然而,Flex應用程序卻有了一個不太好的名聲:大部分程序看上去都很相似,那是因爲許多開發人員選擇了Flex默認的外觀和體驗(比如Halo)而不是應用豐富的樣式和皮膚。

Flex 4中可以更容易地完全改變應用程序的外觀和體驗。新的皮膚架構建立在Flex 4的一些架構改動以及邏輯和組件視覺元素的清晰分離的基礎上。正因爲如此,在Flex 4的組件沒有直接包含任何有關他們的視覺外觀的信息。所有這些信息包含在皮膚文件中,這要感謝FXG和新的states語法,使新的皮膚文件完全可以用MXML編寫,這樣它們就更容易地被閱讀和編寫,同時也更易於工具訪問。

在這篇文章中,您將瞭解在Flex 4對皮膚架構的改進。 通過編寫一個按鈕的基本皮膚,你會學到一點關於FXG和新states的語法。 接下來,您將通過製作一個slider皮膚的過程瞭解怎麼用契約在組件和皮膚進行交互。 最後,您將通過創建一個新組件的皮膚來深入學習可變換皮膚的組件。

注:在本文檔中,Halo組件是指Flex 3已經有的組件。 Spark組件指的是在Flex 4中一套新組件。


編寫一個簡單的按鈕皮膚

FXG是利用Flash Player作矢量圖形的聲明標記語言。 用他你可以很容易地創建一個自定義按鈕。 這個按鈕,只是簡單地在一個矩形框裏面放些文字(見圖1)。

Sample1.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">
    <s:Group verticalCenter="0" horizontalCenter="0">
        <s:Rect id="rect" radiusX="4" radiusY="4" top="0" 
           right="0" bottom="0" left="0">
           <s:fill>
               <s:SolidColor color="0x77CC22" />
           </s:fill>
           <s:stroke>
               <s:SolidColorStroke color="0x131313" weight="2"/>
           </s:stroke>
        </s:Rect>
        
        <s:Label text="Button!" color="0x131313" 
                textAlign="center" verticalAlign="middle"
                horizontalCenter="0" verticalCenter="1"
                left="12" right="12" top="6" bottom="6"
        />
    </s:Group>
</s:Application>

The sample1 button

圖1: sample1 按鈕

如果你熟悉Flex 3,你一定熟悉上面的語法,雖然你可能不熟悉所使用的有些特別的組件。 Goup容器是Spark中基本的沒有樣式的容器。 Rect是一個新的FXG圖元,沒錯,一個矩形。 在文檔中的最後標籤Lable是Spark中的新的文本組件。 讀MXML時就像在描述一個組件,它是一個用1像素深灰色畫出圓角的長方形,中間是一些綠色的文字。
FXG好處之一是,它不僅是讓我們更容易理解繪畫語句,而且因爲FXG使用XML結構所以使得他可以用工具創作。 如需有關FXG信息,請參閱FXG規範 。


轉換你的按鈕圖形爲一個按鈕皮膚

到目前爲止,MXML文件還只是一個不能交互的靜態的作品。 它還沒采取Flex 4新的皮膚功能。 爲此,你需要把它應用到Button組件並使用它作爲一個皮膚。 要創建Spark皮膚文件,用Skin作爲新的MXML文件的根標籤。 然後,將上面的圖形代碼copy過來: 
ButtonSkin1.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" alpha.disabled=".5">
 
    <!-- states -->
    <s:states>
        <s:State name="up" />
        <s:State name="over" />
        <s:State name="down" />
        <s:State name="disabled" />
    </s:states>
    
    <!-- border and fill -->
    <s:Rect id="rect" radiusX="4" radiusY="4" top="0" right="0" bottom="0" left="0">
        <s:fill>
           <s:SolidColor color="0x77CC22" />
        </s:fill>
        <s:stroke>
           <s:SolidColorStroke color="0x131313" weight="2"/>
        </s:stroke>
    </s:Rect>
    
    <!-- text -->
    <s:Label text="Button!" color="0x131313" 
            textAlign="center" verticalAlign="middle"
            horizontalCenter="0" verticalCenter="1"
            left="12" right="12" top="6" bottom="6"
    />
</s:Skin>

你會發現還多了一些states。 我將稍後討論這個。
皮膚文件完成後,你需要將它關聯到一個按鈕組件。 Spark架構中,每一個可變換皮膚組件是通過skinClass CSS樣式來和皮膚關聯起來,這個CSS樣式可以用樣式表設置或者直接寫在MXML內。 當前例子中,稍後再使用: 
Sample2.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">
    <s:Button verticalCenter="0" horizontalCenter="0" skinClass="ButtonSkin1" 
             click="trace('I\'ve been clicked!')" focusIn="trace('focus...on me?')" />
</s:Application>

The sample2 button

圖2: sample2 按鈕

現在,您已經將一個新的皮膚文件應用到這個按鈕了。 按鈕組件包含所有按鈕的行爲邏輯。 它添加事件監聽器,發送新的事件,指示組件所處state,等等。 皮膚無需處理組件中定義的所有可視元素。
但是,這個按鈕現在看起來和之前創建的靜態圖形沒有什麼不同。 按鈕應該是互動的,但還不是這樣。這是因爲你還沒有定義在不同states下的按鈕外觀。 


介紹皮膚契約(contract)

一個靜態的皮膚很無聊。 爲了有點趣,皮膚必須能與組件交互,反之亦然。 這兩個因素通過皮膚契約進行交互。 有三個要點是:皮膚states,data和parts(見圖3)。 一方面,組件定義了這三種不同要點,另一方面,皮膚則處理這三個要點。

The skinning contract comprises data, parts, and states.

圖3: 皮膚契約包含 data, parts, 和 states. 

定義皮膚states

在Spark中的每個可變換皮膚組件都有一組皮膚states。 你可以依據組件所處皮膚state來改變你的皮膚外觀。對於一個按鈕有四種基本皮膚states: up,over,down和disabled。 您可以爲這些皮膚狀態定義不同的外觀(見圖4)。
ButtonSkin2.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" alpha.disabled=".5">
 
    <!-- states -->
    <s:states>
        <s:State name="up" />
        <s:State name="over" />
        <s:State name="down" />
        <s:State name="disabled" />
    </s:states>
    
    <!-- dropshadow for the down state only -->
    <s:Rect radiusX="4" radiusY="4" top="0" right="0" bottom="0" 
        left="0" includeIn="down">
        <s:fill>
           <s:SolidColor color="0"/>
        </s:fill>
        <s:filters>
           <s:DropShadowFilter knockout="true" blurX="5" blurY="5" 
           alpha="0.32" distance="2" />   
        </s:filters>
    </s:Rect>
    
    <!-- border and fill -->
    <s:Rect id="rect" radiusX="4" radiusY="4" top="0" right="0" 
         bottom="0" left="0">
        <s:fill>
           <s:SolidColor color="0x77CC22" color.over="0x92D64E" 
           color.down="0x67A41D"/>
        </s:fill>
        <s:stroke>
           <s:SolidColorStroke color="0x131313" weight="2"/>
        </s:stroke>
    </s:Rect>
    
    <!-- highlight on top -->
    <s:Rect radiusX="4" radiusY="4" top="2" right="2" left="2" 
        height="50%">
        <s:fill>
           <s:LinearGradient rotation="90">
               <s:GradientEntry color="0xFFFFFF" alpha=".5"/>
               <s:GradientEntry color="0xFFFFFF" alpha=".1"/>
           </s:LinearGradient>
        </s:fill>
    </s:Rect>
    
    <!-- text -->
    <s:Label text="Button!" color="0x131313" 
            textAlign="center"
            verticalAlign="middle"
            horizontalCenter="0" verticalCenter="1"
            left="12" right="12" top="6" bottom="6" 
     />
</s:Skin>

ButtonSkin2.mxml<h4>ButtonSkin2.mxml</h4>

<h4>ButtonSkin2.mxml</h4>

圖4. 按鈕的四個皮膚states

不同皮膚狀態,組件看起來不同是因爲你皮膚定義的不同。 這個皮膚文件採用了新的states語法。 這是Flex 4新功能,這使得編寫state更加清晰和簡潔。 語法是property.stateName="property所處狀態的值值"。 例如, alpha.disabled=".5"是指當按鈕進入disabled皮膚state,皮膚會改變alpha爲50%。over和down狀態,我定義了不同的填充色,color.over="0x92D64E" color.down="0x67A41D"。
新的state語法爲MXML組件增加了includeIn和excludeFrom屬性。 按鈕陰影皮膚僅包含在down state,這使按鈕按下時很好看。此外,爲了更加地生動,所有states下我都添加了另一個矩形使按鈕頂部突出。
注:更多Flex 4中語法增強信息,請查看 新的states語法規範

基於皮膚state而改變按鈕外觀,使得操作按鈕有一種交互的體驗。但你會發現,該按鈕組件的文本是硬編碼爲"Button!"。 在下一節中,你將看到皮膚如何顯示組件的數據,當前例子中,數據就是label屬性。

從組件獲取數據

我建議你總是把HostComponent元數據聲明在你的皮膚。HostComponent元數據指向你皮膚的組件,通過它可以在皮膚中訪問組件。在按鈕皮膚,你可以使用這個hostComponent屬性綁定到按鈕label屬性。 
ButtonSkin3.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" alpha.disabled=".5">
 
    <fx:Metadata>
       [HostComponent("spark.components.Button")]
    </fx:Metadata>
 
    ...
    
    <!-- text -->
    <s:Label text="{hostComponent.label}" color="0x131313" 
            textAlign="center"
            verticalAlign="middle"
            horizontalCenter="0" verticalCenter="1"
            left="12" right="12" top="6" bottom="6"
     /> 
</s:Skin>

當聲明按鈕後, 皮膚中的文字會顯示label屬性值.
Sample4.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">
    
    <fx:Style>
        @namespace s "library://ns.adobe.com/flex/spark";
                s|Button {
           skinClass: ClassReference("ButtonSkin3");
        }
    </fx:Style>
 
    <s:layout>
        <s:VerticalLayout />
    </s:layout>
    <s:Button label="Button #1" />
    <s:Button label="Button #2" />
    <s:Button label="Button #3" />
</s:Application>

主應用程序聲明瞭三個按鈕。 每個按鈕都使用相同的皮膚文件ButtonSkin3,這是由CSS類型選擇器定義的。但是,每個按鈕都有不同的標籤。 因爲現在的皮膚拉(pulls)組件的label屬性來顯示文本,按鈕看起來像你期盼的那樣,顯示不同的文字(見圖5)。

The buttons now display their own labels.

圖5: 按鈕顯示他自己的文字 

你已經看到了皮膚契約三個要點中的兩個,states和data。 皮膚state是一種組件進行交互的方式,而皮膚則定義了這些states的外觀和體驗。數據,這些用戶可設置的組件屬性,通過使用HostComponent元數據和hostComponent屬性能被拉到(原文:can be pulled into)皮膚中。 在上面的例子,皮膚從按鈕組件中拉數據(按鈕組件的label屬性)。另一種方法是用皮膚parts(第三個要點)將數據推(push)到皮膚中。

繼續談皮膚契約: 皮膚parts

皮膚parts是皮膚契約的第三部分。在Spark中,每個可變換皮膚的組件都有一組皮膚parts用來幫助定義組件。 以scrollbar爲例,有四個皮膚parts:增加按鈕,減少按鈕,軌跡帶和滾動條。 再以按鈕爲例,他僅有一個皮膚parts,labelDisplay。這是按鈕組件要求提供的一部分。在上面的按鈕皮膚例子中,與其綁定文本爲{hostComponent.label} ,還不如你提供一個文本組件的id labelDisplay,按鈕會識別這個皮膚part,從面將label屬性推送到皮膚中。
ButtonSkin4.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" alpha.disabled=".5">
 
    <fx:Metadata>
       [HostComponent("spark.components.Button")]
    </fx:Metadata>
 
    <!-- states -->
    <s:states> 
        <s:State name="up" />
        <s:State name="over" />
        <s:State name="down" />
        <s:State name="disabled" />
    </s:states>
    
    <!-- dropshadow for the down state only -->
    <s:Rect radiusX="4" radiusY="4" top="0" right="0" bottom="0" 
        left="0" includeIn="down">
        <s:fill>
           <s:SolidColor color="0"/>
        </s:fill>
        <s:filters>
           <s:DropShadowFilter knockout="true" blurX="5" blurY="5" 
           alpha="0.32" distance="2" />   
        </s:filters>
    </s:Rect>
    
    <!-- border and fill -->
    <s:Rect id="rect" radiusX="4" radiusY="4" top="0" right="0" 
        bottom="0" left="0">
        <s:fill>
           <s:SolidColor color="0x77CC22" color.over="0x92D64E" 
           color.down="0x67A41D"/>
        </s:fill>
        <s:stroke>
           <s:SolidColorStroke color="0x131313" weight="2"/>
        </s:stroke>
    </s:Rect>
    
    <!-- highlight on top -->
    <s:Rect radiusX="4" radiusY="4" top="2" right="2" left="2" height="50%">
        <s:fill>
           <s:LinearGradient rotation="90">
               <s:GradientEntry color="0xFFFFFF" alpha=".5"/>
               <s:GradientEntry color="0xFFFFFF" alpha=".1"/>
           </s:LinearGradient>
        </s:fill>
    </s:Rect>
    
    <!-- text -->
    <s:Label id="labelDisplay" color="0x131313" textAlign="center"
            verticalAlign="middle"
            horizontalCenter="0" verticalCenter="1"
            left="12" right="12" top="6" bottom="6"
    />
    
    <!-- transitions -->
    <s:transitions>
        <s:Transition>
          <s:CrossFade target="{rect}" />
        </s:Transition>
    </s:transitions>
</s:Skin>

Label不再綁定到hostComponent。相反,我給它一個id labelDisplay ,這是一個按鈕組件所需的皮膚part。按鈕組件自動處理數據,將它的label屬性推送到labelDisplay。
除了分配一個label皮膚part,我還在皮膚中添加了一個簡單的CrossFade transition。皮膚文件是定義組件的所有可視化方面的地方,包括transition。當前例子中,隨時改變按鈕狀態,你會看到不同state之間切換時的漸變效果。 

製作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

<?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"
      minWidth="11" minHeight="100" alpha.disabled="0.5">
     <fx:Metadata>
       [HostComponent("spark.components.VSlider")]
    </fx:Metadata> 
    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
    </s:states>
    
    <fx:Declarations>
        <fx:Component id="dataTip">
           <s:DataRenderer minHeight="24" minWidth="40" x="20"> 
               <s:Rect top="0" left="0" right="0" bottom="0">
                   <s:fill>
                       <s:SolidColor color="0xFFF46B" alpha=".9"/>
                   </s:fill>
                   <s:filters>
                       <s:DropShadowFilter angle="90" color="0x999999" distance="3"/>                   
                   </s:filters>
               </s:Rect>
                      
               <s:Label id="labelField" text="{data}"
                        horizontalCenter="0" verticalCenter="1"
                        left="5" right="5" top="5" bottom="5"
                        textAlign="center" verticalAlign="middle" color="0x555555" />
           </s:DataRenderer>
        </fx:Component>
    </fx:Declarations>
    
    <s:Button id="track" left="5" right="5" top="0" bottom="0" skinClass="MyTrackSkin" />
    <s:Button id="thumb" left="0" right="0" width="18" height="8" skinClass="MyThumbSkin" />
 
</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:

package
{
 
[SkinState("normal")]
[SkinState("disabled")]
public class NoteCard extends SkinnableComponent
{
    public function NoteCard()
    {
        super();
    }
    
    [SkinPart(required="true")]
    public var labelDisplay:TextBase;
    
    [SkinPart(required="false")]
    public var closeButton:Button;
    
    private var _text:String;
 
    public function get text():String
    {
        return _text;
    }
 
    public function set text(value:String):void
    {
        if (_text == value)
           return;
        _text = value;
    }
 
    ...
}
}

此組件聲明數據屬性,皮膚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

package
{
[SkinState("normal")]
[SkinState("disabled")]
public class NoteCard extends SkinnableComponent
{
    ...   
    override public function set enabled(value:Boolean) : void
    {
        if (enabled != value)
           invalidateSkinState();
        super.enabled = value;
    }
    
    override protected function getCurrentSkinState() : String
    {
        if (!enabled)
           return "disabled";
        return "normal"
    }
    
    ...
}
}

當設置enabled屬性時,enabled setter調用invalidateSkinState()以通知皮膚,組件的state需要改變,這樣getCurrentSkinState()隨即會被調用。


處理皮膚parts

處理皮膚parts,有兩種主要方法應該要重寫,partAdded()和partRemoved()。這些方法會告訴你一個特定的皮膚part被添加了或被刪除了。當裝載一個皮膚時Parts將被加入或是刪除。皮膚是在運行時交換的,並且延遲加載的,所以只有在某種states情況下或者是一個動態part剛剛被創建時part纔會被加入。在partAdded()方法你可以設置你想要的任何數據到part,而且也可以attach一些事件偵聽到part上。當part被刪除時,你應該在partRemoved()方法中做相反的事情。
NoteCard.as

package
{
public class NoteCard extends SkinnableComponent
{
    [SkinPart(required="true")]
    public var labelDisplay:TextBase;
    
    [SkinPart(required="false")]
    public var closeButton:Button;
    
    public function set text(value:String):void
    {
        if (_text == value)
           return;
        _text = value;
        
        if (labelDisplay)
           labelDisplay.text = value;
    }
 
    override protected function partAdded(partName:String, instance:Object) : void
    {
       super.partAdded(partName, instance);
       
       if (instance == labelDisplay)
           labelDisplay.text = _text;
       if (instance == closeButton)
           closeButton.addEventListener(MouseEvent.CLICK, closeButton_clickHandler);
    }
    
    override protected function partRemoved(partName:String, instance:Object) : void
    {
       super.partRemoved(partName, instance);
        
        if (instance == closeButton)
           closeButton.removeEventListener(MouseEvent.CLICK, closeButton_clickHandler);
    }
    
    protected function closeButton_clickHandler(event:MouseEvent) : void
    {
       event.stopPropagation();
       
       IVisualElementContainer(parent).removeElement(this);
    }
 }
}

在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組件架構白皮書。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章