在上一篇文章中,为大家介绍了Flex中皮肤的简单且基本也是最常用的用法,就是先在外部程序中做好皮肤样式,之后再在Flex中加载这些皮肤资源,比如是PNG图片,或者是SWF文件等,这些方法都是比较直接且方便简单的。不过灵活性还是不太好,而现在这篇文章中要讲的,就是从程序里AS编写的皮肤,你可以更灵活自由地控制皮肤的样式与表达方式等,而不用依赖于外部的图片或SWF资源,不过这样还是有些缺点的,就是扩展或是改变时就比较不灵活,像图片资源的皮肤的话,可以很轻易的改变不同的图片的路径资源等就可以修改了不同的皮肤模式,而用AS Graphics写的皮肤的话,要修改一下那些代码,当然,你也可以在AS代码里写上动态加载其它图片资源又或者作一些更高级的效果等等。
      先在这里解析一下,要以代码方式自定义一个皮肤的话,需要自已编写一个皮肤子类,继承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 
}
上面的代码就是皮肤的代码,之后你还要做的,就是将该皮肤应用到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: Cla***eference( "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的源码,会得到很多知识!