xiaohuli 2012-04-26
自己做Flex换肤器
1人收藏此文章,我要收藏
发表于1年前,已有748次阅读共0个评论
今天周末,昨晚看电影睡得很迟,早上直接起床吃中饭。本来和朋友约好一起吃饭的,可是老天下雨,上网打了几把魔兽,把把被虐。这下火大了,打开资料看,忽然看见自己以前做的Ext的换肤器,当时时根据EXT论坛上别人写的一些代码,把它整理了下,最后跑成功了。
最近我们在搞Flex,我就想我可以做个Flex换肤器啊。
OK,说干就干。首先把以前Ext那个看了下,大致知道需要很多个CSS,这些CSS用于皮肤切换,一个下拉列表框,供用户选择皮肤,最后用户选择后的信息保存到客户端cookies里面,程序启动加载cookies里面的东西,根据它来决定加载哪个CSS效果。
那就开始仿照吧。
要下拉列表框是吧?这个不难,咱用Flex做一个。如下:
[Bindable]
privatevarcssSource:Array=[
{label:'默认',data:''},
{label:'样式1',data:'BlueTan.css},
{label:'样式2',data:'Brauwny.css'},
{label:'样式3',data:'kingnarestyle.css},
{label:'样式4',data:'shadow.css'},
{label:'样式5',data:'SunNight.css'}
];
<mx:ComboBoxid="cssCB"dataProvider="{cssSource}"/>
OK第一步搞定。
页面需要加载CSS,那开始写吧,于是加入如下:
<mx:Stylesource="”/>
这里的source指定要加载的CSS路径。我一开始想我给这个对象一个id,然后我选择下拉列表里面那个值,让这个控件的source换成哪个值不就好了嘛。可是问题来了,我写个id,它就编译不了,原来这玩意不支持id属性,我晕死。
怎么办?怎么办?
对了,Flex不是有绑定变量的功能嘛,我定义个绑定变量,然后让这里的source绑定到它不就成了?
于是我写了如下代码:
[Bindable]
privatevarcssName:String;
<mx:Stylesource="{cssName}”/>
我晕,再次看见红叉叉,说明是不支持编译期间绑定。我晕,这个也不支持,那个也不支持,你个破Flex这么垃圾啊。
到此我已经没有头绪了,只好找来Flex3cookbook开始看第9章:皮肤和样式。
看着看着忽然眼前一亮,看到了对我有用的一句代码。
StyleManager.loadStyleDeclarations(cssName,true);//可以运行时动态加载css效果
有了这个就好办啊,页面加载完成我就调个函数,动态加载用户保存的CSS啊,在运行期间用户通过下拉列表框切换
了皮肤,我再用这个来动态加载啊。
思路对了,下面就要按照要求来实现了。原来这家伙不支持加载CSS,只加载swf文件,教程中说了要用
mxmlcmain.css
这样的命令来把CSS文件转换为swf文件。OK照着干,把用到的CSS文件转换,再修改代码。
修改后代码为:
[Bindable]
privatevarcssSource:Array=[
{label:'默认',data:''},
{label:'样式1',data:'BlueTan.swf'},
{label:'样式2',data:'Brauwny.swf'},
{label:'样式3',data:'kingnarestyle.swf'},
{label:'样式4',data:'shadow.swf'},
{label:'样式5',data:'SunNight.swf'}
];
下面的问题是得给下拉列表框一个位置和一个选择后的函数,于是又写了个函数:
<mx:ComboBoxid="cssCB"x="20"y="30"dataProvider="{cssSource}"close="closeHandler(event);"/>
privatefunctioncloseHandler(event:Event):void{
varresult:String=ComboBox(event.target).selectedItem.data;
trace(result);
if(result!=""){//为空的情况不用加载任何swf文件,空是默认皮肤
StyleManager.loadStyleDeclarations(result,true);
}
这下问题简单多了,用户通过下拉列表选择了皮肤,我动态加载,貌似已经实现了。但是问题又来了,
你不可能让用户每次进来都要选一次吧?老大我要是就喜欢红色,我希望每次进来都红色,能行嘛?
是的,这个问题也比较严重,要考虑到与用户使用和交互的友好性。OK,改吧。
现在用户已经选择了他喜欢的皮肤,我得保存起来,存哪里?服务端?客户端?以前Ext人家是放入客户端cookies里面的,那我们Flex没有必要放到数据库的表里面吧!好的,我们也放入客户端系统中。
难道要存入文件中?用Flex/ActionScript来操作文件?晕,不会吧,这可怎么写啊。
于是乎,偶又打开了ActionScript3.0cookbook这本书,我就看目录,果然在第17章发现了个很好的目录名:
存储持久化数据
仔细一看就是讲如何把数据保存在客户端的,看来这个就是我要找的了。花了点心思看玩了数据写入和读出,好啦,
万事俱备,开始最后收尾了。用户选择后我要写入到本地共享对象中。代码加入:
privatefunctioncloseHandler(event:Event):void{
varresult:String=ComboBox(event.target).selectedItem.data;
trace(result);
if(result!=""){
StyleManager.loadStyleDeclarations(result,true);
}
//写入客户端共享对象
varcssData:SharedObject=SharedObject.getLocal("UserCss");
cssData.data.cssName=result;
cssData.flush();
}
既然数据保存了,那么初次启动肯定要用到这些数据啊,来吧,收工吧,加入最后代码:
<mx:Applicationxmlns:mx="http://www.adobe.com/2006/mxml"layout="absolute"initialize="initCollections()"
creati>
privatefunctionloadStyle():void{
//从客户端共享对象读取保存的CSS数据
varcssData:SharedObject=SharedObject.getLocal("UserCss");
if(cssData.data.cssName){//css名字存在就加载相应的CSS
trace(cssData.data.cssName);
switch(cssData.data.cssName){
case'BlueTan.swf':
cssCB.selectedIndex=1;
break;
case'Brauwny.swf':
cssCB.selectedIndex=2;
break;
case'kingnarestyle.swf':
cssCB.selectedIndex=3;
break;
case'shadow.swf':
cssCB.selectedIndex=4;
break;
case'SunNight.swf':
cssCB.selectedIndex=5;
break;
default:cssCB.selectedIndex=0;
}
if(cssData.data.cssName!=""){
StyleManager.loadStyleDeclarations(cssData.data.cssName,true);
}
}
else{
cssCB.selectedIndex=0;
}
}
大功告成!
每天进步一点点就好,其实在实际扩展过程中会有很多问题,还会报很多异常。
如果大家开发前台最好装个debug模式的flashpalyer,主要可以断点调试,通过
trace();打印信息。自己扩展成功了发现代码也不过寥寥数行,但是整个过程差不多花了
我一个半小时。但是这1个半小时却带给了我很多惊喜和快乐。
其实Flex和ActionScript还是很强大的,呵呵。
/////////////////////////////////////////////////////////////////////////
刚刚写完这个帖子,我又仔细的运行了下,又发现一个BUG。
原来你每次调用:
StyleManager.loadStyleDeclarations(result,true);//这里的true表示加载完成后立马更新页面效果
来加载新的css时并没有去掉先前的css因此整合页面不稳定,有时候显示正确,有时候却是2种css效果的叠加。
修改后的代码为:
[Bindable]
privatevarlastCssName:String;//上次加载的CSS名称
[Bindable]
privatevarcssSource:Array=[
{label:'默认',data:''},
{label:'样式1',data:'BlueTan.swf'},
{label:'样式2',data:'Brauwny.swf'},
{label:'样式3',data:'kingnarestyle.swf'},
{label:'样式4',data:'shadow.swf'},
{label:'样式5',data:'SunNight.swf'}
];
privatefunctionloadStyle():void{
//从客户端共享对象读取保存的CSS数据
varcssData:SharedObject=SharedObject.getLocal("UserCss");
lastCssName=cssData.data.cssName;
if(lastCssName){//css名字存在就加载相应的CSS
trace(lastCssName);
switch(lastCssName){
case'BlueTan.swf':
cssCB.selectedIndex=1;
break;
case'Brauwny.swf':
cssCB.selectedIndex=2;
break;
case'kingnarestyle.swf':
cssCB.selectedIndex=3;
break;
case'shadow.swf':
cssCB.selectedIndex=4;
break;
case'SunNight.swf':
cssCB.selectedIndex=5;
break;
default:cssCB.selectedIndex=0;
}
if(lastCssName!=""){
StyleManager.loadStyleDeclarations(lastCssName,true);
}
}
else{
cssCB.selectedIndex=0;
}
}
privatefunctioncloseHandler(event:Event):void{
varresult:String=ComboBox(event.target).selectedItem.data;
trace(result);
if(result!=""){
//先除去旧的再加载新的
StyleManager.unloadStyleDeclarations(lastCssName,false);//这里false先不更新,等下面加载完了再更新
StyleManager.loadStyleDeclarations(result,true);
lastCssName=result;
}
else{//如果选择的是默认,则去除旧的就OK了
StyleManager.unloadStyleDeclarations(lastCssName,true);
lastCssName=result;
}
//写入客户端共享对象
varcssData:SharedObject=SharedObject.getLocal("UserCss");
cssData.data.cssName=result;
cssData.flush();
}
红色代码是更改后加的,主要是加载当前皮肤时先除去之前的皮肤效果,
并且把之前皮肤效果的名称换成当前皮肤效果名称。
background-color: blue;background-color: yellow;<input type="button" value="变蓝" @click="changeColorT