Flex中的皮肤(一)
这里是第一篇,将讲述一下Flex中如何应用UI的皮肤,其实应用UI皮肤不难,你们在使用Flex的过程中是否觉得Flex中自带的皮肤样式不太好看?或者是想自已做个比较有特色的?下面就我们来说说皮肤吧,先来个简单的,你们在做网页时,做导航按钮什么的很多人都是用一个图片来作为一个按钮吧?之后做几个不同的颜色,之后就在CSS或者JS里设置一下当鼠标Over和Down和Out等等动作时,就切换不同颜色的图片,这样实现动态效果。在Flex里也可以如此简单的做皮肤。你可以先画好一个UI的皮肤,之就就将该图片应用到Flex里面。
先来看看效果:
之后我们来看看代码:
1 <?
xml version="1.0" encoding="utf-8"
?>
2
<
mx:Application
xmlns:mx
="http://www.adobe.com/2006/mxml"
layout
="absolute"
creationComplete
="init()"
width
="257"
height
="182"
backgroundGradientColors
="[#ffffff, #ffffff]"
>
3
4
<
mx:Script
>
5
<![CDATA[
6
7
//Embed标签是用于将一些外部资源加入到Flex中,随Flex的编译成SWF文件,
8
//这里是加入一张PNG图片,即是做好的Skin图片
9
[Embed(source="images/buttonskin.png",
10
scaleGridTop="26",
11
scaleGridBottom="64",
12
scaleGridLeft="30",
13
scaleGridRight="106")]
14
//上面的Embed标签下面要紧跟着这个Class,意思就是将上面的资源加入到Flex
15
//后变为这个Class的内容,即调用这个Class时,就是调用那些资源
16
private var MyBtnSkin:Class;
17
18
//在程序创建完成时会调用该函数,在函数里面设置Button的样式(Style)
19
//这里就是设置按钮的up,over,down三个鼠标状态时的皮肤,就是上面加入的图片资源
20
private function init():void{
21
btn.setStyle("upSkin",MyBtnSkin);
22
btn.setStyle("overSkin",MyBtnSkin);
23
btn.setStyle("downSkin",MyBtnSkin);
24
}
25
]]>
26
</
mx:Script
>
27
<
mx:Button
id
="btn"
label
="Hello World"
width
="190"
height
="90"
/>
28
</
mx:Application
>
怎么样?很简单吧?大家需要注意一下的是在Embed标签里,我定义了一些scaleGridTop之类的属性,这是跟皮肤的缩放有关的,如果不定义那些属性的话,那么图片是多大的,就按多大来进行缩放,当你的按钮很大时,那些皮肤图片就会被拉大,出现马赛克与变形等,这都是不好看的。加入了9格缩放模式后,当你缩放按钮时,九个格中的四个角的区域不会被缩放,保持原样,中间格会宽高同时缩放,中间上下格会仅是宽度缩放,中间左右格只会高度缩放,这样,那个皮肤的边框无论你如何缩放,都是原来的大小比例,而不会将整个图片一起拉申。
现在的按钮太单调了,只有一个外观,现在大家可以再加多两个不同颜色或者其它图案的图片作为不同状态的皮肤就可以了,比如将overSkin改成红色边框的图片等。
其实大家有没有发现,上面代码的写法感觉比较麻烦的。我们可以用CSS来实现,我们可以直接点,将皮肤直接写在Button上,如下:
1 <
mx:Button
label
="Hello World"
2
upSkin
="@Embed('images/buttonskin.png')"
3
overSkin
="@Embed('images/buttonskin.png')"
4
downSkin
="@Embed('images/buttonskin.png')"
5
/>
这样也是同样的效果。省事好多了吧。或者我们用CSS来写:
1 <?
xml version="1.0" encoding="utf-8"
?>
2
<
mx:Application
xmlns:mx
="http://www.adobe.com/2006/mxml"
layout
="absolute"
width
="257"
height
="182"
backgroundGradientColors
="[#ffffff, #ffffff]"
>
3
<
mx:Style
>
4
Button{
5
up-skin:Embed(source="images/buttonskin.png",
6
scaleGridTop="26",
7
scaleGridBottom="64",
8
scaleGridLeft="30",
9
scaleGridRight="106");
10
over-skin:Embed(source="images/buttonskin.png",
11
scaleGridTop="26",
12
scaleGridBottom="64",
13
scaleGridLeft="30",
14
scaleGridRight="106");
15
}
16
</
mx:Style
>
17
<
mx:Button
id
="btn"
label
="Hello World"
width
="190"
height
="90"
/>
18
</
mx:Application
>
如果觉得加上CSS代码会令程序代码混乱的话,就将CSS代码写在CSS文件里去,在程序里导入CSS文件就可以了。(至于CSS的用法,我就不说了,反正Flex里的CSS方式与Html里的用法用样。只是要注意一下CSS里面设置的属性的名字就可以了)
<mx:Style source="styles/styles.css" />
但有人可能会问,这样做的话,如果一个程序有很多不同的UI,并有不同的皮肤,那不就是要生成很多的图片?这个问得好,确实,如果以这种方式的话,就像一个网站里的images文件夹一样,有很多的小图片,这样太麻烦了,而且也不好维护。既然有这样的问题,我们就将皮肤干脆做成在一个文件里面算了,方便快捷,维护又方便,而这个文件,就是SWF文件。我们如果有Flash基础的话,基本对MC都不会陌生,对,这次我们的主角就是SWF里面的MC,我们可以将一个皮肤做成一个MC,在Flash里将所有用到的皮肤都做在一个SWF里,一个图片就像是一个MC,之后发布该SWF文件,在Flex里加载这个SWF文件,再在需要的皮肤里调用SWF里面相应皮肤的MC的名字就可以了。如下:
这个是在Flash里做好的皮肤SWF文件,里面有三个不同颜色的皮肤模式
[swf]attachments/month_0805/p2008518233635.swf[/swf]
这里要注意一下,在Flash里做这些皮肤时,要将MC加上链接,链接的名称,就是你在Flex里调用该皮肤的名称,图片如下:
在做好皮肤的SWF后,让我们回到Flex 里面,在Flex里写如下代码:
1 <? xml version="1.0" encoding="utf-8"
?>
2 <
mx:Application
xmlns:mx
="http://www.adobe.com/2006/mxml"
layout
="absolute"
width
="257"
height
="182"
backgroundGradientColors
="[#ffffff, #ffffff]"
>
3
<
mx:Style
>
4 Button{
5 up-skin:Embed(source="images/btnSkin.swf",symbol="btnUP");
6 over-skin:Embed(source="images/btnSkin.swf",symbol="btnOVER");
7 down-skin:Embed(source="images/btnSkin.swf",symbol="btnDOWN");
8 }
9
</
mx:Style
>
10
<
mx:Button
id
="btn"
label
="Hello World"
width
="100"
height
="60"
/>
11 </
mx:Application
>
注意一下的就是,在Embed标签里,要导入的资源文件不是PNG了,而是一个SWF,就是我们刚才在Flash里做好的皮肤文件,注意看,后面还跟着一个symbol属性,该属性就是指明你要调用哪一个MC,就是SWF里面的MC,记得,都要为每个MC做链接,并链接名字要与symbol里的名字致。至此,我们的皮肤就完成了。一个SWF文件就搞掂。这里是最终效果:
好了,这篇教程就到些结束,上面介绍的都是Skin的比较简单快捷的用法,不过灵活性就不是很大,但也是皮肤技术的最基础的,大家也可以再扩展一下其它用法等等的,在下篇文章,也就是Flex皮肤系列文章的(二)中,我会介绍一下用程序代码来编写皮肤,这就是不依赖于外部的资源文件,直接用AS3代码用Graphics来自已画皮肤。下篇将会用到AS3的Draw API方面的知识,请大家做好准备。
Flex中的皮肤(二)
在上一篇文章中,为大家介绍了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: 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的源码,会得到很多知识!
//其它
在flash中画好皮肤,转换成symbol给一个name如bulletCheck,用command->convert symbol to component,file->publish导出后,将生成的a.swf文件拷贝到flex与css file同路径下,创建css file,写如:
Button
{
upSkin: Embed('a.swf',symbol='bulletCheck');
}
右键点css file,compile css to swf, 会生成与css同名的swf文件,之前的swf也会编在其中。
<mx:Style source="abc.css"/>
每次改css时都要重新编译生成swf!!!