Flex生成的swf是个2帧的MovieClip,文档类继承SystemManager。
2帧结构是为创建加载应用程序的默认加载器。
加载器的外观是通过代码绘制的,SystemManager使用工厂模式来创建应用程序。
当Flex工程和需要的共享库下载完毕后,SystemManager跳到第二帧,从应用程序域中找出Application的定义创建实例。
SystemManager:
SystemManager是Flex发布swf的文档类,是应用程序的入口,SystemManager继承MovieClip,不仅控制swf的加载和启动,还划分Flex工程显示层级,保存嵌入字体和样式,监视swf画布大小等顶级功能,某些功能被映射到Application或全局管理器中,你可以选择访问的方式。
popUpChildren:IChildList,toolTipChildren:IChildList,cursorChildren:IChildList并不是具体的显示层,而是记录顶级层的集合,SystemManager自动维护这些集合。
SystemManager重写了DisplayObjectContainer的接口,它与Container一样忽略辅助性的元素,通过SystemManager获取的子级实际上不包含浮动窗口、工具提示和光标这样的辅助层,也不包括内部的鼠标捕捉层,只包含默认的应用程序层和使用addChild()等方法添加进来的应用层,这也是SystemManager提供cursorChildren等这样的集合访问而不是通过getChildAt()访问顶级显示层的原因。
通过cursorChildren这样的集合也并不能完全暴露SystemManager的所有子级,要访问原始子级,可以使用rawChildren:IChildList属性进行遍历,但不要通过此手段破坏SystemManager的结构。
载入Flex项目
SystemManager实现IFlexModuleFactory接口,但并不表示它可以作为一个Module载入,扩展IFlexModuleFactory是为了利用工厂模式分离preloader和Application的关联。整个Flex项目确实可以载入到其它项目中的,但不是以Module方式载入而是通过普通方法加载,例如使用SWFLoader或Loader加载,SystemManager作为顶级容器加入到项目的显示列表。
SystemManager的application:IUIComponent属性引用载入Flex项目的application实例,getDefinitionByName(name:String):Object方法获取载入swf中的定义,例如Object(SystemManager(mySwfLoader.content).application.testButton获取载入项目中的testButton按钮实例,SystemManager(mySwfLoader.content).getDefinitionByName("TestButton")获取载入项目中的TestButtond定义。
将Flex项目载入到另一个Flex项目中时后,会使用新项目的SystemManager管理顶级层,因为全局管理器只能有一个,例如载入项目中有弹出窗口,这个弹出窗口的父级为新项目的SystemManager,原项目的SystemManager仅作为一个父级存在,它的功能只限定于引用,原项目中可以使用topLevelSystemManager:ISystemManager属性引用新项目的SystemManager。让一个Flex项目载入另外一个Flex项目没有什么优势,不能很好的体现项目和模块的关系,代码组织不明确且无法避免重定义,我们应该使用Module来分割应用程序。一个例外是你可以让Flash项目载入Flex工程构建UI来代替Flash的v3组件框架,但你需要斟酌加载swf给Flash项目带来的等待时间,Flex项目通常体积比较大。
Application
Application是应用层的顶级容器,Application的某些属性关系到Flex如何编译项目或生成的网页,这些属性不能用as设置,只能通过mxml标签设置,如下:
获取swf地址和flashVars
Application的url:String可以获取swf地址,但这个属性依然不能获取网页传递的值对,因为Application是通过发布swf的loaderInfo获取地址,parameters:Object属性映射了loadInfo的flashVars,访问flashVars参数比较方便。
预加载器
Flex的预加载器为mx.preloaders.Preloader,Preloader加载应用程序和SystemManager传入的RSL共享库以及资源模块,Preloader并不包含加载器的界面,它是一个位置为(0,0)的空Sprite,采用mx.preloaders.DownloadProgressBar作为默认的皮肤。
Preloader在加载过程中发送大量的事件,DownloadProgressBar和SystemManger通过监听Preloader的事件显示加载进度和跳帧。Application中preloader属性可以更改Preloader使用的加载界面,默认路径为mx.preloaders.DownloadProgressBar,如果你指定其他类,这些类将代替DownloadProgressBar被编译到项目中。
修改加载界面可以分为两种方式,一是修改默认预加载器样式,即设置DownloadProgressBar的属性,二是通过制定类名自定义预加载器皮肤,Preloader作为功能类无需更改其逻辑。Application的usePreloader:Boolean属性可以禁用预加载器,但并不会改变Flex发布swf的结构,swf依然被编译为2帧并且使用Prelaoder加载,只是不创建加载外观而已。
修改预加载器样式
DownLoadProgressBar提供了一些属性用于设置加载器样式,背景样式被声明为public,自定义加载界面后仍然可以设置这些属性,绘制的矢量界面和文本标签被声明为protected,自定义加载界面后将无法访问。
背景样式
DownloadProgressBar并不是只有中间显示的那个加载框,还拥有一个和舞台一样大小的背景色块,backgroundColor:uint,backgroundAlpha:Number属性可以设置加载器的背景颜色和透明度,backgroundImage:String和backgroundSize:String属性可以设置背景图片和大小,backgroundImage可以是文件名或类名,设置为文件名后会自动使用内部的Loader加载背景图片,但加载背景图片会增加加载swf加载时间,backgroundSize设置背景图片的缩放方式,如果为百分比,则”100%”表示拉伸到背景图片到完整大小,为”auto”表示图片保持大小不变。
Preloader在创建DownLoadProgressBar时会将背景设置为Application的背景颜色,然后调用DownLoadProgressBar的initialize()方法,我们可以通过覆盖initiliaze()方法更改背景设置,例如:
override public function initialize():void
{
super.initialize();
backgroundImage="chuyin30.jpg";
backgroundAlpha=0.2;
}
加载框和加载条
borderRect:RoundedRectangle可以修改加载框的大小,barFrameRect:RoundedRectangle和barRect:RoundedRectangle可以修改加载槽和加载条的大小,如图:
这两个属性都是只读的,修改需要覆盖get属性。没有修改加载框和加载条颜色的属性,加载框默认为透明的灰色,要自定义加载框和加载条,需要自定义加载界面。
修改加载标签
默认情况下DownloadProgressBar只显示了提示标签而没有显示进度标签,我们可以通过showPercentage:Boolean属性启用进度标签,showLabel:Boolean用于扩展显示自定义标签,labelRect:Retangle,labelFormat:TextFormat修改提示标签的大小和格式,percentRect:Rectangel和percentFormat属性设置进度文本的大小和格式。label:String属性为提示标签文本内容,加载应用程序时显示downloadingLabel:String属性的值,加载共享库时显示共享库的加载个数。
下图为修改过的DownloadProgressBar样式:
修改代码如下:
package { import mx.graphics.RoundedRectangle; import mx.preloaders.DownloadProgressBar;
public class DownloadProgressBar1 extends DownloadProgressBar {
public function DownloadProgressBar1() { super(); }
override public function initialize():void { super.initialize(); backgroundColor=0xAAAAAA; downloadingLabel=""; showPercentage=true; }
override protected function get borderRect():RoundedRectangle { return new RoundedRectangle(0, 0, 282, 80, 4); }
override protected function get barFrameRect():RoundedRectangle { return new RoundedRectangle(14, 40, 254, 24); }
override protected function get barRect():RoundedRectangle { return new RoundedRectangle(14, 39, 254, 26, 0); } } }
|
将DownloadProgressBar1指定给Application的preload属性即可看到效果。
自定义加载界面
修改DownloadProgressBarPreloader的样式往往不能满足项目高级需求,例如需要将应用程序和资源下载进行分段显示,自定义加载界面需必须实现IPreloaderDisplay接口,这个接口包括外观的背景设置,所处的舞台大小等属性,但Flex的框架决定了IPreloaderDisplay与Preloader的耦合度很高,重新编写界面不容易,更多的时候我们选择继承DownLoadProgressBar类来简化操作,DownLoadProgressBar不仅实现IPreloaderDisplay接口,而且考虑的非常细致,例如加载所需的时间小于700m或启动进度超过总数的一半不显示加载界面。此外DownLoadProgressBar更是为我们扩展加载界面提供了有力的支持,某些方法定义了但内容却为空,这就是供我们扩展的提示。
DownloadProgressBar中的加载框、加载条都是通过代码绘制的,这使得默认加载器的体积很小,绘制的代码在createChildren()方法中,而更改进度显示的代码在setProgress()方法中,这两个方法实现了显示和逻辑的分离,通过覆盖这两个方法定制自己的加载界面可以在不改变加载逻辑的方式下进行,下面我们让Flex使用使用flash绘制的界面作为加载器的皮肤:
1. 在flash中新建影片剪辑,绘制所需的界面,如图:
progressBar |
progressText |
rslBar |
rslText |
将加载条命名为progressBar,进度文本命名为progressText,共享库进度条为rslBar,共享库文本为rslText,然后将元件导出为ActionScript,类名为DownloadProgressBarDisplay,将fla发布为swc导入Flex后便可以使用flash制作的素材了。
2. 覆盖定义
覆盖定义之前我们要了解DownloadProgressBar内部一些实现,为了避免加载速度过快导致加载界面闪动一下就消失,createChildren()方法在700ms后才被调用,如果你想一开始就显示加载界面可以在构造函数中创建界面,但创建的界面不会自动居中,需要自己设置,此时可以利用基类提供的stageWidth:Number和stageHeight:Number属性访问舞台尺寸,但这个两个属性只有在执行createChildren()方法时才变得准确。默认绘制的加载条把显示进度分割为两个阶段,第一个阶段是加载阶段,应用程序将和rsl共享库同时加载,第二个阶段是Application初始化阶段,DownloadProgressBar使用属性DOWNLOAD_PERCENTAGE:uint进行百分比分割,你可以修改DOWNLOAD_PERCENTAGE更改默认进度条分割的百分比,也可以选择不进行分割、,下表为DownloadProgressBar对Preload添加的侦听器。
侦听器 |
说明 |
progressHandler(event:ProgressEvent):void |
应用程序和共享库加载进度 |
rslProgressHandler(event:RSLEvent):void |
rsl加载进度 |
rslCompleteHandler(event:RSLEvent):void |
rsl加载完毕 |
rslErrorHandler(event:RSLEvent):void |
rsl加载出错 |
completeHandler(event:Event):void |
应用程序和共享库加载完毕 |
initProgressHandler(event:Event):void |
应用程序初始化进度 |
progressHandler()和initProgressHandler()都调用setProgress()方法设置进度,setProgress()反映加载总数。RSLEvent既可以反映rsl共享库的加载进度又可以反映加载数量,通过覆盖rsl的侦听器可以单独为rsl创建进度条。
通过对DownloadProgressBar的观察使我们了解到,要定制加载界面,起码要覆盖createChildren()和setProgress()这两个方法,其它方法可以自己选择覆盖,MyDownloadProgressBar通过覆盖createChildren()和setProgress()方法引入flash制作的界面并且更新进度,如下:
package { import flash.events.Event;
import mx.events.RSLEvent; import mx.preloaders.DownloadProgressBar;
public class MyDownloadProgressBar extends DownloadProgressBar { private var downloadDisplay:DownloadProgressBarDisplay;
public function MyDownloadProgressBar() { super(); downloadDisplay=new DownloadProgressBarDisplay(); }
override protected function createChildren():void { downloadDisplay.x=(stageWidth-downloadDisplay.width)/2; downloadDisplay.y=(stageHeight-downloadDisplay.height)/2; addChild(downloadDisplay); }
override protected function rslProgressHandler(event:RSLEvent):void { downloadDisplay.rslText.text="共享库"+"("+event.rslIndex+"/"+event.rslTotal+")"; downloadDisplay.rslBar.scaleX=event.bytesLoaded/event.bytesTotal; }
override protected function setProgress(completed:Number, total:Number):void { var per:Number=completed/total; downloadDisplay.progressBar.scaleX=per; downloadDisplay.progressText.text=(100*per).toFixed(0)+"%"; } } } |
我们在构造函数中创建了flash绘制的加载界面是避免createChildren()方法70ms后才执行而导致rslProgressHandler中无法访问downloadDisplay报错,由于stageWidth和stageHeight在createChildren()才能反映准确的舞台尺寸,因此在createChildren()中创建sownloadDisplay并且设置为舞台居中,rslProgressHandler使用了独立的进度条反映rsl加载进度,setProgress()反映总的加载进度。
3. 测试加载效果
将Flex框架发布为共享库,然后再导入几个共享库一同进行测试,如下图:
总加载进度会在加载过程中有跳跃的现象,这是因为同时加载应用程序swf和rsl时,无法一时间获取所有rsl的大小,而在Application初始化时发生跳动是因为我们没有进行百分比分割。自定义加载界面后,DownloadProgressBar默认加载器的加载框和加载标签等样式变得没有意义,但加载背景仍然有效,我们仍然可以通过覆盖initiliaze()方法设置加载器的背景。
封装预加载器
如果能够将预加载器界面和代码封装到一个swc中,就可以让任何Flex工程使用,通过上面的例子修改如下:
1. 新建影片剪辑,放置DownloadProgressBarDisplay的实例,命名为preloadBox
2. 在flash路径库中导入Flex的framwork.swc
3. 取消DownloadProgressBarDisplay的导出
4. 将新影片剪辑导出为MyDownloadProgressBar
MyDownloadProgressBar元件绑定的代码MyDownloadProgressBar.as如下:
package { import flash.events.Event;
import mx.events.RSLEvent; import mx.preloaders.DownloadProgressBar;
public class MyDownloadProgressBar extends DownloadProgressBar { public function MyDownloadProgressBar() { super(); preloadBox.visible=false; }
override protected function createChildren():void { x=(stageWidth-width)/2; y=(stageHeight-height)/2; preloadBox.visible=true; }
override protected function rslProgressHandler(event:RSLEvent):void { preloadBox.rslText.text="共享库"+"("+event.rslIndex+"/"+event.rslTotal+")"; preloadBox.rslBar.scaleX=event.bytesLoaded/event.bytesTotal; }
override protected function setProgress(completed:Number, total:Number):void { var per:Number=completed/total; preloadBox.progressBar.scaleX=per; preloadBox.progressText.text=(100*per).toFixed(0)+"%"; } } } |
在createChildren()方法中显示加载界面一来符合基类70ms的逻辑,再者舞台大小在createChildren()才具备准确的数值。