自个儿做Flex换肤器

自己做Flex换肤器
自己做Flex换肤器
1人收藏此文章, 我要收藏
发表于1年前 , 已有748次阅读 共0个评论

今天周末,昨晚看电影睡得很迟,早上直接起床吃中饭。本来和朋友约好一起吃饭的,可是老天下雨,上网打了几把魔兽,把把被虐。这下火大了,打开资料看,忽然看见自己以前做的Ext的换肤器,当时时根据EXT论坛上别人写的一些代码,把它整理了下,最后跑成功了。
最近我们在搞Flex ,我就想我可以做个Flex 换肤器啊。
OK,说干就干。首先把以前Ext 那个看了下,大致知道需要很多个CSS,这些CSS用于皮肤切换,一个下拉列表框,供用户选择皮肤,最后用户选择后的信息保存到客户端cookies 里面,程序启动加载cookies里面的东西,根据它来决定加载哪个CSS效果。


那就开始仿照吧。
要下拉列表框是吧?这个不难,咱用Flex做一个。如下:

            [Bindable]
            private var cssSource: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:ComboBox  id="cssCB" dataProvider="{cssSource}"  />

OK第一步搞定。

页面需要加载CSS,那开始写吧,于是加入如下:
<mx:Style source=" ”/>

这里的source指定要加载的CSS路径。我一开始想我给这个对象一个id,然后我选择下拉列表里面那个值,让这个控件的source换成哪个值不就好了嘛。可是问题来了,我写个id,它就编译不了,原来这玩意不支持id属性,我晕死。

怎么办?怎么办?
对了,Flex不是有绑定变量的功能嘛,我定义个绑定变量,然后让这里的source绑定到它不就成了?
于是我写了如下代码:
  [Bindable]
            private var cssName:String;
<mx:Style source="{cssName} ”/>   

我晕,再次看见红叉叉,说明是不支持编译期间绑定。我晕,这个也不支持,那个也不支持,你个破Flex这么垃圾啊。

到此我已经没有头绪了,只好找来Flex3 cook book开始看第9章:皮肤和样式。
看着看着忽然眼前一亮,看到了对我有用的一句代码。
StyleManager.loadStyleDeclarations(cssName,true);//可以运行时动态加载css 效果
有了这个就好办啊,页面加载完成我就调个函数,动态加载用户保存的CSS啊,在运行期间用户通过下拉列表框切换
了皮肤,我再用这个来动态加载啊。
思路对了,下面就要按照要求来实现了。原来这家伙不支持加载CSS,只加载swf文件,教程中说了要用
mxmlc main.css
这样的命令来把CSS文件转换为swf文件。OK照着干,把用到的CSS文件转换,再修改代码。

修改后代码为:

[Bindable]
            private var cssSource: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:ComboBox  id="cssCB"  x="20" y="30" dataProvider="{cssSource}"  close="closeHandler(event);"/>

  private function closeHandler(event:Event):void {
            
               var result:String= ComboBox(event.target).selectedItem.data;
               trace(result);
               if(result!=""){//为空的情况不用加载任何swf 文件,空是默认皮肤
                StyleManager.loadStyleDeclarations(result,true);
                }
这下问题简单多了,用户通过下拉列表选择了皮肤,我动态加载,貌似已经实现了。但是问题又来了,
你不可能让用户每次进来都要选一次吧? 老大我要是就喜欢红色,我希望每次进来都红色,能行嘛?
是的,这个问题也比较严重,要考虑到与用户使用和交互的友好性。OK,改吧。

现在用户已经选择了他喜欢的皮肤,我得保存起来,存哪里?服务端?客户端?以前Ext人家是放入客户端cookies里面的,那我们Flex没有必要放到数据库的表里面吧!好的,我们也放入客户端系统中。
难道要存入文件中?用Flex/ActionScript 来操作文件?晕,不会吧,这可怎么写啊。

于是乎,偶又打开了ActionScript3.0 cookbook 这本书,我就看目录,果然在第17章发现了个很好的目录名:
存储持久化数据

仔细一看就是讲如何把数据保存在客户端的,看来这个就是我要找的了。花了点心思看玩了数据写入和读出,好啦,
万事俱备,开始最后收尾了。用户选择后我要写入到本地共享对象中。代码加入:
  private function closeHandler(event:Event):void {
            
               var result:String= ComboBox(event.target).selectedItem.data;
               trace(result);
               if(result!=""){
                StyleManager.loadStyleDeclarations(result,true);
                }
               //写入客户端共享对象
                var cssData:SharedObject =SharedObject.getLocal("UserCss");
                cssData.data.cssName=result;
                cssData.flush();
               
            }     


既然数据保存了,那么初次启动肯定要用到这些数据啊,来吧,收工吧,加入最后代码:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="initCollections()"
  creati >


private function loadStyle():void{
            
               //从客户端共享对象读取保存的CSS数据
               var cssData: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]
             private var lastCssName:String;//上次加载的CSS名称
            
            [Bindable]
            private var cssSource: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'}
            ];
            private function loadStyle():void{
            
               //从客户端共享对象读取保存的CSS数据
               var cssData: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;
               }
            
            }
            
            private function closeHandler(event:Event):void {
            
               var result: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;
                }
               //写入客户端共享对象
                var cssData:SharedObject =SharedObject.getLocal("UserCss");
                cssData.data.cssName=result;
                cssData.flush();
               
            }     

红色代码是更改后加的,主要是加载当前皮肤时先除去之前的皮肤效果,
并且把之前皮肤效果的名称换成当前皮肤效果名称。