先在這里解析一下,要以代碼方式自定義一個皮膚的話,需要自已編寫一個皮膚子類,繼承ProgrammaticSkin這個類,這個是所有編寫自定義皮膚的基類,該類也派生了另外兩個類:RectangularBorder 與 Border 類,都是差不多的,如果你是寫Icon之類的小皮膚的話,比如CheckBox或者RadioButton這類皮膚不需要太復雜的畫圖邏輯,而且大小固定,就像是一個小Icon吧,只是有幾個狀態而已,這類小皮膚的話,繼承ProgrammaticSkin就可以了,而寫一些復合的控件,背景大小可以調節之類的皮膚(其實就是大部分皮膚)就用Border或者RectangularBorder類。但都有一個相同點,就是繼承了那些類之后,都必需覆蓋 updateDisplayList 這個方法,這個方法是由程序自動調用,當需要用到控件時,需要控件的皮膚進行表現時,就會調用那個方法,所以你必需覆蓋它,并將你的畫圖邏輯代碼寫在那個方法里面。還有要注意的是,這個皮膚類會與你應用這皮膚的控件的Style設置共享,也就是說你可以在編寫這個皮膚類代碼里面,使用getStyle()等等方法獲得設置在目標控件中的風格屬性,比如說是<mx:Panel backgroundColor="0xffffff" borderSkin="MySkin"> 那么你可以在MySkin代碼里獲取這個顏色值來進行畫出該顏色的圖片或其它操作,而直接將顏色值寫死在代碼里是不規范的,就如我下面貼出來的的代碼,不過出于自已懶,快速代個示例代碼,所以犯這個錯了。說多了,下面看看代碼先。
好了,我們先看看看代碼,這份代碼是寫了一個Panel的皮膚:
1 package com.jiangzone
2 {
3 import mx.skins.Border
4 import mx.core.EdgeMetrics;
5 import mx.core.Container;
6 import mx.graphics.RectangularDropShadow;
7
8 public class MyPanelBorderSkin extends Border {
9
10 public function MyPanelBorderSkin():void {
11 }
12
13 /**
14 * 該方法必需要覆蓋,如果你要自定義自已的皮膚的話,
15 * 該方法當在控件更新外觀時將會被自動調用
16 * 會傳入兩個參數數,第一個是Width,第二個是Height,即是該控件的寬與高
17 * */
18 override protected function updateDisplayList(w:Number,h:Number):void {
19 super.updateDisplayList(w,h);
20
21 var ba:uint = 1; //backgroundAlpha 背景透明度
22 var bg:uint = 0xffffff; //backgroundColor 背景顏色
23 graphics.clear(); //graphics這個屬性是父類里已經提供了的
24 var p:Container = parent as Container; //獲取該皮膚所應用在的父容器,這里為Panel
25
26 //這里需要注意,一定要判斷父容器是否已被設置,在文章里作解釋
27 if(p){
28 //獲取容器定義的區域邊界信息對象
29 var vm:EdgeMetrics = p.viewMetrics;
30 //設置四個角的圓度
31 var radiusContent:Object = {tl:vm.top,tr:0,bl:0,br:vm.top};
32 //標題欄圓度
33 var radiusTitle:Object = {tl:vm.top,tr:0,bl:0,br:0};
34 //畫一個圓角矩形,整個背景
35 this.drawRoundRect(0,0,w,h,radiusContent,bg,ba);
36 //畫一個圓角矩形,標題欄
37 this.drawRoundRect(0,0,w,vm.top,radiusTitle,0xff0000,.7);
38 //畫一個圓角矩形,標題欄的那個高光水晶條
39 this.drawRoundRect(0,0,w,vm.top / 2,radiusTitle,0xffffff,.3);
40
41 //下面是畫陰影的。
42 var dropShadow:RectangularDropShadow = new RectangularDropShadow();
43 dropShadow.distance = 8;
44 dropShadow.angle = 60;
45 dropShadow.color = 0x000000;
46 dropShadow.alpha = 0.4;
47
48 dropShadow.tlRadius = radiusContent.tl;
49 dropShadow.trRadius = radiusContent.tr;
50 dropShadow.blRadius = radiusContent.bl;
51 dropShadow.brRadius = radiusContent.br;
52
53 dropShadow.drawShadow(graphics, 0, 0, w, h);
54 }
55 }
56 }
57 }
2 {
3 import mx.skins.Border
4 import mx.core.EdgeMetrics;
5 import mx.core.Container;
6 import mx.graphics.RectangularDropShadow;
7
8 public class MyPanelBorderSkin extends Border {
9
10 public function MyPanelBorderSkin():void {
11 }
12
13 /**
14 * 該方法必需要覆蓋,如果你要自定義自已的皮膚的話,
15 * 該方法當在控件更新外觀時將會被自動調用
16 * 會傳入兩個參數數,第一個是Width,第二個是Height,即是該控件的寬與高
17 * */
18 override protected function updateDisplayList(w:Number,h:Number):void {
19 super.updateDisplayList(w,h);
20
21 var ba:uint = 1; //backgroundAlpha 背景透明度
22 var bg:uint = 0xffffff; //backgroundColor 背景顏色
23 graphics.clear(); //graphics這個屬性是父類里已經提供了的
24 var p:Container = parent as Container; //獲取該皮膚所應用在的父容器,這里為Panel
25
26 //這里需要注意,一定要判斷父容器是否已被設置,在文章里作解釋
27 if(p){
28 //獲取容器定義的區域邊界信息對象
29 var vm:EdgeMetrics = p.viewMetrics;
30 //設置四個角的圓度
31 var radiusContent:Object = {tl:vm.top,tr:0,bl:0,br:vm.top};
32 //標題欄圓度
33 var radiusTitle:Object = {tl:vm.top,tr:0,bl:0,br:0};
34 //畫一個圓角矩形,整個背景
35 this.drawRoundRect(0,0,w,h,radiusContent,bg,ba);
36 //畫一個圓角矩形,標題欄
37 this.drawRoundRect(0,0,w,vm.top,radiusTitle,0xff0000,.7);
38 //畫一個圓角矩形,標題欄的那個高光水晶條
39 this.drawRoundRect(0,0,w,vm.top / 2,radiusTitle,0xffffff,.3);
40
41 //下面是畫陰影的。
42 var dropShadow:RectangularDropShadow = new RectangularDropShadow();
43 dropShadow.distance = 8;
44 dropShadow.angle = 60;
45 dropShadow.color = 0x000000;
46 dropShadow.alpha = 0.4;
47
48 dropShadow.tlRadius = radiusContent.tl;
49 dropShadow.trRadius = radiusContent.tr;
50 dropShadow.blRadius = radiusContent.bl;
51 dropShadow.brRadius = radiusContent.br;
52
53 dropShadow.drawShadow(graphics, 0, 0, w, h);
54 }
55 }
56 }
57 }
上面的代碼就是皮膚的代碼,之后你還要做的,就是將該皮膚應用到Panel這個容器里:
1 <mx:Application
2 xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()"
3 layout="absolute">
4
5 <mx:Style>
6 .myPanelSkin {
7 borderSkin: ClassReference( "com.jiangzone.MyPanelBorderSkin" );
8 }
9 </mx:Style>
10
11 <mx:Panel borderSkin="com.jiangzone.MyPanelBorderSkin"
12 width="200" height="150" x="24" y="23"/>
13
14 </mx:Application>
2 xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()"
3 layout="absolute">
4
5 <mx:Style>
6 .myPanelSkin {
7 borderSkin: ClassReference( "com.jiangzone.MyPanelBorderSkin" );
8 }
9 </mx:Style>
10
11 <mx:Panel borderSkin="com.jiangzone.MyPanelBorderSkin"
12 width="200" height="150" x="24" y="23"/>
13
14 </mx:Application>
代碼很簡單,這里要說一下,viewMetrics 這個屬性是Container控件所獨有的屬性,是一個只讀屬性,編寫Container子類的時候都要覆蓋它,是用于定義這個容器正文區與邊界值的,比如Canvas的四周都是0,所以沒有標題欄與邊條,而Panel就有四周的邊界,而Top邊界比較大,用作顯示title的,所以如果你要做容器的皮膚的話,注意一下這個值。還有就是,為什么獲取了皮膚應用的控件引用(parent)后還要判斷它是否為空?因為當程序加載到這個控件時,是先加載那個皮膚的,所以parent的值未被設置,是空的,如果你不作判斷的話,將會出現空引用的錯誤(parent.viewMetrics),當加載完皮膚后,再加載控件并設置控件的屬性和設置皮膚,這時將會再次調用updateDisplayList的方法,這時parent才有值,就是那個控件的引用。當改變了style或一些屬性后,又會自動觸發調用updateDisplayList方法。
我們來看看最終運行效果:
補充一下:
在第一篇文章里,說了將皮膚做在SWF文件里再加載,想一下,可以將該皮膚做成動畫MC的,而不單單只是一個畫面,可以做成一些動畫作為皮膚,之后在Flex引用該SWF的Symbol,這樣皮膚就有了動畫效果了,不只只是單純的不動的平面圖!
Flex中的皮膚教程就說到這里,皮膚還有很多可探索的,只要大家有求知欲,多點看看英文文檔,看看別人的例子程序代碼,現在Flex也開源了,也可以多看看Flex的源碼,會得到很多知識!