更新日期:2010.8.20
1.本例目的 使用Parsley,进行模块化
2.本例开发环境 parsley2.2.2, flex_sdk_4.1.0.16076,flash buider 4
3.Application加载模块后,模块之间互相通信功能:
通过ModuleLoader加载两个module,并且在两个module之间发送消息
这个程序只对界面实体类托管,而不对界面类托管,有利于减少界面类反射花费的时间,值得推荐
(1)创建一个消息类,用于两个module之间的消息通信TestEvent.as
public class TestEvent extends Event
{
public static const TEST:String = "testEvent";
private var _message:String;
private var _sender:String;
public function TestEvent( message:String, sender:String=null ) {
super( TEST );
_message = message;
_sender = sender;
}
public function get message():String {
return _message;
}
public function get sender():String {
return _sender;
}
override public function clone() : Event {
return new TestEvent( message, sender );
}
}
(2)创建一个全局类,保存全局信息GlobalData.as
[Bindable]
public class GlobalData
{
/**
* 保存模块间通信的信息
*/
public var items:ArrayCollection = new ArrayCollection();
/**
* 在parsley的Context中初始化 count
*/
public var count:int = 0;
public function incrementedCount():int {
return ++count;
}
}
(3)创建主界面主界面只是用于显示两个module,并初始化上下文
ParsleyModuleExam1.mxml
creationComplete="creationCompleteHandler(event)"
import mx.events.FlexEvent;
protected function creationCompleteHandler(event:FlexEvent):void {
module1Loader.loadModule( "module1/Module1.swf" );
module2Loader.loadModule( "module2/Module2.swf" );
}
]]>
(4)创建上下文配置文件 xmlns:fx=""
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:spicefactory="">
import shell.ShellPanelPM;
import common.GlobalData;
]]>
5
说明
这里只初始化了两个类ShellPanelPM和GlobalData
ShellPanelPM是module中界面实体类,这样一来两个module共用一个ShellPanelPM
这里对GlobalData的count变量进行了初始化,该值用于表示消息发送的序号
模块的contex由加载module时再初始化,有利于减少内存,提高速度。
模块1和模块2代码相同,这里只给出一个模块的代码
(5)创建module界面类Module1.mxml
这个module,只包含了一个自定义视图组件Module1Panel,有利于代码的复用
xmlns:fx=""
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:parsley=""
xmlns:module1="module1.*"
layout="vertical" width="100%"
preinitialize="FlexContextBuilder.build(Module1Context,this)">
import org.spicefactory.parsley.flex.FlexContextBuilder;
]]>
说明
本人曾想把FlexContextBuilder.build(Module1Context,this),改成
但是,报
1172: 找不到定义 Module1Context
无语
(6)创建模块1的ContextModule1Context.mxml
xmlns:mx=""
xmlns=""
xmlns:mod1="module1.*">
(7)创建自定义组件类Module1PanelModule1Panel.mxml
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
addedToStage="dispatchEvent(new Event('configureIOC', true))"
title="Module #1 Panel"
width="400" height="300">
[Inject]
[Bindable]
public var model:Module1PanelPM;
]]>
text="{model.textReceive}"
/>
change="model.textSend=messageText.text"/>
界面如下
注意:
语句addedToStage="dispatchEvent(new Event('configureIOC', true))"不能删,否则module将无法使用parsley中的上下文
即使是binding,当textArea的内容发生改变时,也必须使用change="model.textSend=messageText.text",将值传递给model
(8)创建Module1Panel对应的实体类Module1PanelPM.as
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import common.GlobalData;
import common.TestEvent;
[Event( name="testEvent", type="common.TestEvent" )] [ManagedEvents( "testEvent" )] public class Module1PanelPM extends EventDispatcher
{
[Bindable]
public var textReceive:String = "Initial string";
[Bindable]
public var textSend:String = "";
[Inject]
[Bindable]
public var globalData:
GlobalData;
public function Module1PanelPM(target:IEventDispatcher=null) {
super(target);
}
public function sendMessage() : void {
globalData.incrementedCount();
dispatchEvent( new TestEvent( textSend, "Module1" ) );
}
[MessageHandler]
public function onMessage( event : TestEvent ) : void {
textReceive = "[" + globalData.count + "] ";
if( event.sender != null )
textReceive += event.sender + ": ";
textReceive += event.message;
}
}
说明
[ManagedEvents( "testEvent" )] :表示这个类对托管消息进行操作
GlobalData:这个类来自Application
程序运行界面
说明
当从module1发消息TestEvent时,所有的module都接收到这个消息,并通过 [MessageHandler]进行处理,通过textReceive += event.message;将消息内容显示在界面上。
4.Application与模块之间通信功能
本例子用于演示Application与模块之间的通信
有个Parsley框架之后,Application与消息之间的通信都是通过Parsley框架完成的,其实也就很简单了,呵呵
本例绝大部分代码来自上例
(1)主应用程序ParsleyModuleExam2.mxml
与上例的ParsleyModuleExam1.mxml相比,添加一个消息发送与接收组件ShellPanel和监控组件DataMonitor。
ShellPanel与Module1Panel相同,DataMonitor组件用于显示所有模块和应用程序派发的消息。
(2)组件DataMonitorDataMonitor.mxml
该组件用于显示所有模块和主程序派发的消息
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
title="Message Monitor Panel"
addedToStage="dispatchEvent(new Event('configureIOC', true))">
import org.spicefactory.lib.logging.LogContext;
import org.spicefactory.lib.logging.Logger;
import common.GlobalData;
import common.TestEvent;
private static const log:Logger = LogContext.getLogger(DataMonitor);
[Inject]
[Bindable]
public var model:GlobalData;
[MessageHandler]
public function onMessage( event : TestEvent) : void {
var text:String = "";
if( event.sender != null )
text += event.sender + ": ";
text += event.message;
log.debug( "Message handler, message = '{0}'", text );
model.items.addItem( text );
}
]]>
界面显示如下
5.利用程序打开module(1)主程序ParsleyModuleExam3.mxml
主程序有二个相同的module,一个通过程序创建,一个通过moduleLoader创建
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:parsley=""
xmlns:app="shell.*"
creationComplete="creationCompleteHandler(event)"
addedToStage="dispatchEvent(new Event('configureView', true));"
minWidth="955" minHeight="600">
import mx.events.FlexEvent;
import shell.Module3Window;
import org.spicefactory.parsley.core.context.Context;
[Inject]
public var context:Context;
private function onModule1Button():void {
//创建组件Module3Window ,里面包含ModuleLoader
var window:Module3Window = new Module3Window();
//将组件的上下文添加到根视图上
context.viewManager.addViewRoot(window);
this.addElement(window);
}
protected function creationCompleteHandler(event:FlexEvent):void {
module2Loader.loadModule( "module1/Module1.swf" );
}
]]>
注意
这里不能将url直接写在ModuleLoader内,否则提示无法在主程序初始化时加载模块,必须写在creationComplete事件内
(2)组件Module3Window Module3Window.mxml
这个窗体包含了ModuleLoader,当加载模块时,显示Loading,加载完成后,显示模块
creationComplete="init()"
import mx.events.ModuleEvent;
private function init():void {
currentState = "loading";
module3Module.addEventListener(ModuleEvent.READY,onModuleReady);
module3Module.loadModule("module1/Module1.swf");
}
private function onModuleReady(event:ModuleEvent):void {
module3Module.removeEventListener(ModuleEvent.READY,onModuleReady);
currentState = "loaded";
}
]]>
注意
移除监听器是个好习惯,否则将影响内存回收
其它程序参见上例。
程序运行界面如下
6.ViewStack与Module相结合使用(1)主程序ParsleyModuleExam4.mxml
ViewStack中视图的切换通过自定义事件ViewChangeEvent进行
[Event(name="view.inbox",type="shell.ViewChangeEvent")]
[Event(name="view.provider",type="shell.ViewChangeEvent")]
[Event(name="view.debug",type="shell.ViewChangeEvent")]
[ManagedEvents("view.inbox,view.provider,view.debug")]
说明:定义了三个托管事件,当点击按钮时派发
说明:这里本可以不要上下文配置,但是去除后,则程序出错,不知为什么?
说明:定义了三个按钮和一个组件ContentRegion ,该组件中包含了一个ViewStack
(2)组件ContentRegionContentRegion.mxml
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:shell="shell.*"
paddingBottom="5" paddingLeft="10" paddingRight="10" paddingTop="10">
(3)组件ContentViewStackContentViewStack.mxml
ViewStack包含了四个视图,通过Parsley的消息框架接收消息并控制显示哪一个视图
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:shell="shell.*"
addedToStage="dispatchEvent( new Event( 'configureIOC', true ) ); this.selectedIndex=0">
[MessageHandler]
public function handleViewChange(event:ViewChangeEvent):void {
var newIndex:int = 0;
if( event.type == ViewChangeEvent.INBOX ) {
newIndex = 1;
} else if( event.type == ViewChangeEvent.PROVIDER ) {
newIndex = 2;
} else if( event.type == ViewChangeEvent.DEBUG ) {
newIndex = 3;
}
this.selectedIndex = newIndex;
}
]]>
width="100%" height="100%" />
width="100%" height="100%" />
说明
InboxLoader 和ProviderLoader 里包含有ModuleLoader
InboxLoader 和ProviderLoader 代码结构相同,这里只列出一个模块代码
(4)组件InboxLoader
InboxLoader.mxml
这是一个模块调用组件
creationComplete="init()">
import mx.events.ModuleEvent;
private function init():void {
if(inboxModule.url==null)
inboxModule.loadModule("inbox/InboxModule.swf");
}
]]>
(5)InboxModuleInboxModule.mxml
这是一个模块,里面有一个自定义视图组件
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo"
width="100%" height="100%"
addedToStage="dispatchEvent(new Event('configureIOC',true))">
[Inject]
[Bindable]
public var model:InboxContainerPM;
]]>
(6)界面实体类
InboxContainerPM
这是InboxContainer视图对应的实体类,需要在Context中声明一下,这里就不列出了。
public class InboxContainerPM extends EventDispatcher
{
private var _items:ArrayCollection = new ArrayCollection();
public function InboxContainerPM(target:IEventDispatcher=null) {
super(target);
}
[Bindable] public function get items():ArrayCollection {
return _items;
}
public function set items(items:ArrayCollection):void {
_items = items;
}
[Init] public function init():void {
var array:Array = [ "Line 1", "Line 2", "Line 3" ];
_items.source = array;
}
}
说明
Init标签是Parsley的,表示在这个类初始化时执行。
界面运行如下
总结1.代码详见:ParsleyModuleProject_2010820.zip
还有一个项目ParsleyModuleProject2_2010820.zip的代码没有在这列出,功能与此类似,可以参考
2.这次只是初步能使用模块,但还有一些问题值得深入研究
主程序的Context和模块的Context如何加载和卸载。
3.Cairngorm3中有基于Parsley 模块化的扩展,值得一看
参考文献1. %20module%20mxml&d=0