Module是Adobe為解決Flex應用初始化時較大的下載負載問題而設計的一種折中方案。將主Application合理分割為多個Module后,配合延遲加載策略,就可以保證主Application在初始化只加載必要的資源從而減少等待時間。未被訪問的Module默認將不被加載,這樣在首次訪問它們時會需要額外的等待時間,當然我們也可以在監聽主Application加載完畢事件中將這些Module提前載入或者直接取消使用延遲加載策略以保證Module的響應時間。
Module 實際上是一個預編譯的SWF文件。雖然是SWF格式的文件,但是這個文件不能獨立運行,並且只能被ModuleLoader加載后才能顯示。邏輯上它是一個容器,可以像一般的容器一樣包含別的容器,組件,甚至是別的Module模塊。根據需要,預編譯的Module模塊可以被應用加載和卸載。不同的應用可以共享這些Module模塊。Flex應用可以被分割為若干個預編譯的Module模塊,可以在需要時分別加載這些模塊,避免在系統初始化時加載全部子容器。采用這種方式的Flex應用從設計上分隔了邏輯相對獨立的模塊,減少了系統初始化時的加載時間。
我們有兩種方式創建一個Module模塊,一是利用MXML標簽<mx:Module>創建Module類;令一種方式采用ModuleManager類在ActionScript中創建Module模塊類。模塊類創建后將被編譯成SWF文件。如前面章節所述我們可以利用mxmlc編譯器手動編譯或者在Flex Builder3集成開發環境提供的工具自動編譯Module類為SWF文件。這里介紹采用Flex Builder3集成開發環境提供的方式創建一個Module類。
在FlexBuilder3中創建基於MXML標簽的Module模塊
創建基於MXML標簽的Module模塊,需要擴展mx.modules.Module.
1. 第一步,在Flex Builder3集成開發環境中創建一個Flex項目Moduler。
2. 第二步,選擇File--New--MXML Module
3. 第三步,輸入Module文件名字為MXMLDemoModule,設置Module的高度和寬度,選擇Module容器的布局方式為absolute.,選擇默認的預編譯后優化的SWF選項,單擊Finish按鈕。
4. Module文件被創建,編輯Module文件,在此例中我們可以假定是一個登陸Module界面,
其源代碼為:
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="300">
<mx:Panel width="300" height="220" layout="absolute" horizontalCenter="0"
verticalCenter="0" backgroundAlpha="0.5">
<mx:Form horizontalCenter="0" verticalCenter="0" paddingBottom="0" paddingLeft="0" paddingRight="0"
paddingTop="0">
<mx:FormItem label="Account" indicatorGap="5">
<mx:TextInput borderStyle="solid" id="account" text="admin"/>
</mx:FormItem>
<mx:FormItem label="Password" indicatorGap="5">
<mx:TextInput borderStyle="solid" id="password" text="admin" displayAsPassword="true"/>
</mx:FormItem>
<mx:FormItem direction="horizontal" >
<mx:Button id="logined" label="Login" styleName="blueButton"/>
<mx:Button id="reset" label="Reset" styleName="greenButton"/>
</mx:FormItem>
</mx:Form>
</mx:Panel>
<mx:Label text="CopyRight 2008 demo Solutions" horizontalCenter="0" verticalCenter="252.5"/>
</mx:Module>
集成登陸Module模塊到主應用文件ModulerApp.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:ModuleLoader url="MXMLDemoModule.swf"/>
</mx:Application>
創建基於ActionScript的Module模塊
在ActionScript中創建的Module模塊類大都繼承了mx.modules.Module或者mx.modules.ModuleBase基礎類。我們都知道,MXML標簽<mx:Module>實際上是mx.modules.Module類的另一種表現方式,這也就不難理解無論在MXML標簽中還是在ActionScript中創建Module模塊其實都是相通的。自定義Module類繼承mx.modules.Module和繼承mx.modules.ModuleBase兩個基類的不同是,繼承自前者的或者基於<mx:Module>標簽的自定義Module組件將被加入到框架可視化顯示列表,后者則沒有。參考示例 :
package {
import mx.modules.ModuleBase;
public class MyModule extends ModuleBase {
public function MyModule() {
trace("MyModule was created!");
}
}
}
編譯Module模塊
前面已經提及我們有兩種方式來編譯Module模塊文件。像編譯主應用文件一樣,一種是在命令行手動編譯,最簡單的情形 可以利用這個命令
Mxmlc MyModule.mxml
另一種是在Flex Builder3中提供的自動編譯工具編譯。編譯的結果是一個可以被裝載的SWF文件,與編譯后的SWF主應用文件最大的不同就是,模塊SWF文件不能獨立運行,只能在被裝載后才能夠和其宿主共同運行。
怎樣裝載和卸載Module模塊
總體來說,有幾種方式可以實現Module模塊的裝載和卸載。
利用ModuleLoader:ModuleLoader類提供了一系列高層處理Module的編程接口。
利用ModuleManager: ModuleManager類提供了低層次的處理Module的裝載卸載以及事件響應等的變成接口。
下面將詳細地講解這兩種處理方式。
我們可以利用ModuleLoader類在主應用文件或者別的Module模塊中加載任意預編譯的Module對象。最簡單的方式是采用ModuleLoader類的標簽形式<mx:ModuleLoader>在主應用的MXML文件中顯式地加載Module,然后只需要設置這個標簽的url屬性為預編譯Module的SWF文件位置,可以是相對路徑,也可以是絕對路徑。參考實例:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute">
<mx:ModuleLoader url="MXMLDemoModule.swf"/>
</mx:Application>
編程時我們可以根據需要替換此url所指向的模塊位置,實現不同模塊的更迭顯示。當ModuleLoader類初始化時或者每次更改這個url屬性值時,這個ModuleLoader類的loadModule()方法被觸發。如果設定url屬性為空字符串,ModuleLoader卸載當前加載的Module模塊。
ModuleLoader其實是一種特殊的導航式容器。和一般導航式容器如ViewStack不同的是,ModuleLoader不必在初始化時攜帶加載所有的孩子組件。了解這一點,我們可以猜想到,在一個Flex主應用中可以包含甚至嵌套包含多個ModuleLoader實例,以下示例展示在一個ViewStack容器中包含多個ModuleLoader的示例。
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel
title="Multiple Modules Demo "
height="100%"
width="100%"
paddingTop="0"
paddingLeft="0"
paddingRight="0"
paddingBottom="0"
>
<mx:VBox id="v1" label="Module1">
<mx:Label id="l1" text="Module1.swf"/>
<mx:ModuleLoader url="Module1.swf"/>
</mx:VBox>
<mx:VBox id="v2" label="Module2">
<mx:Label id="l2" text="Module2.swf"/>
<mx:ModuleLoader url="Module2.swf"/>
</mx:VBox>
<mx:VBox id="v3" label="Module3">
<mx:Label id="l3" text="Module3.swf"/>
<mx:ModuleLoader url="Module3.swf"/>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx:Application>
同時我們可以利用ModuleLoader類提供的loadModule()和unloadModule()方法動態地指定被加載的Module模塊。這兩個方法沒有輸入形參,調用時分別加載和卸載當前ModuleLoader實例url屬性所指向的Module模塊。下面示例展示了在TabNavigator容器中,按鈕點擊后分別加載卸載不同的Module模塊:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.modules.*;
public function moduleLoader(m:ModuleLoader, s:String):void {
if (!m.url) {
m.url = s;
return;
}
m.loadModule();
}
public function moduleUnloader(m:ModuleLoader):void {
m.unloadModule();
}
]]>
</mx:Script>
<mx:Panel title="Dynamically load/unload multiple Modules"
height="100%"
width="100%"
paddingTop="0"
paddingLeft="0"
paddingRight="0"
paddingBottom="0"
>
<mx:TabNavigator id="navigator"
width="100%"
height="100%"
creationPolicy="all"
>
<mx:VBox id="vb1" label="Module1">
<mx:Button label="Load" click="moduleLoader(moduleLoader, l1.text)" />
<mx:Button label="Unload" click="moduleUnloader(moduleLoader)" />
<mx:Label id="l1" text="Module1.swf"/>
<mx:ModuleLoader id="moduleLoader"/>
</mx:VBox>
<mx:VBox id="vb2" label="Module2">
<mx:Button label="Load" click="moduleLoader(moduleLoader2, l2.text)"/>
<mx:Button label="Unload" click="removeModule(moduleLoader2)" />
<mx:Label id="l2" text="Module2.swf"/>
<mx:ModuleLoader id="moduleLoader2"/>
</mx:VBox>
<mx:VBox id="vb3" label="Module3">
<mx:Button label="Load" click="moduleLoader(moduleLoader3, l3.text)"/>
<mx:Button label="Unload" click="removeModule(moduleLoader3)" />
<mx:Label id="l3" text="Module3.swf"/>
<mx:ModuleLoader id="moduleLoader3"/>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx:Application>
我們當然也可以利用ModuleManager類來加載Module模塊。這種方式比起純粹的ModuleLoader方式稍微復雜一點,但是ModuleManager提供了比ModuleLoader更加強大的能力來管理Module模塊的加載過程。具體操作可以分成以下幾步
通過ModuleManager實例的getModule()方法拿到Module模塊的一個索引,索引類型為IModuleInfo。
調用這個索引的load()方法。
利用這個接口的factory屬性拿到它相關連的Module工廠,調用此工廠的create()方法,並將返回值強制轉換成當前的Module類型。
參考示例 :
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import mx.modules.ModuleManager;
import mx.modules.IModuleInfo;
public var info:IModuleInfo;
private function init():void {
info = ModuleManager.getModule("Module1.swf");
info.addEventListener(ModuleEvent.READY, moduleEventHandler);
info.load();
}
private function moduleEventHandler(e:ModuleEvent):void {
vb.addChild(info.factory.create() as Module1);
}
]]>
</mx:Script>
<mx:VBox id="vb"/>
</mx:Application>
理論上說,Flex應用第一次啟動時初始化下載的大小,集成了Module模塊的應用相對比沒有集成Module模塊的相似應用要小一些。這減少了初始化頁面的等待時間。甚至在Module模塊的SWF文件還沒有下載完畢的時候,主應用文件也可以順利顯示。Module模塊第一次加載時將被緩存在客戶端IE, 已緩存的Module模塊被再次加載時,FlashPlayer將直接在緩存中加載Module實例,減少了加載時間,提高了用戶體驗。當然這有一個前提就是,客戶端IE沒有被清空。所以在實際操作中,你可以在SWF模塊真正的被使用之前將其預加載到客戶端緩存。方法是調用ModuleManager.getModule("Module1.swf").load()語句。在調用create()方法之前並沒有創建這個模塊實例,而只是純粹地將模塊SWF文件加載到客戶端內存。下面實例展示了在系統初始化時預先加載一個名字叫做Module1.swf的模塊,當此模塊需要顯示時才創建這個Module模塊實例。
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="modulePreloader()">
<mx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import mx.modules.ModuleManager;
import mx.modules.IModuleInfo;
private function modulePreloader():void {
var info:IModuleInfo = ModuleManager.getModule("Module1.swf");
info.addEventListener(ModuleEvent.READY, moduleEventHandler);
info.load();
}
private function moduleEventHandler(e:ModuleEvent):void {
trace('' + e.type);
}
]]>
</mx:Script>
<mx:Panel title="Module Preloader" height="100%" width="100%" >
<mx:TabNavigator id="tn" width="100%" height="100%" creationPolicy="all" >
<mx:VBox id="vb1" label="Module1">
<mx:ModuleLoader url="Module1.swf"/>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx:Application>
怎樣處理ModuleLoader事件
我們不僅可以以上述的方式加載卸載預編譯Module模塊,而且更進一步,也可以捕捉和處理模塊在加載卸載過程中可能會觸發的各種事件。這些事件由ModuleLoader觸發,分別有setup,ready,loading,unload,progress,error和urlChanged等。很好的利用這些內置的事件可以使我們更加精細地跟蹤模塊加載卸載過程,實現更加強大的功能。下述示例展示了一個自定義的ModuleLoader組件CustomModuleLoader,這個組件會跟蹤當模塊實例被主應用加載時所有觸發的事件。
<?xml version="1.0" encoding="iso-8859-1"?>
<mx:ModuleLoader xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
creationComplete="init()">
<mx:Script>
<![CDATA[
public function init():void {
addEventListener("urlChanged", onUrlChanged);
addEventListener("loading", onLoading);
addEventListener("progress", onProgress);
addEventListener("setup", onSetup);
addEventListener("ready", onReady);
addEventListener("error", onError);
addEventListener("unload", onUnload);
standin = panel;
removeChild(standin);
}
public function onUrlChanged(event:Event):void {
if (url == null) {
if (contains(standin))
removeChild(standin);
} else {
if (!contains(standin))
addChild(standin);
}
progress.indeterminate=true;
unload.enabled=false;
reload.enabled=false;
}
public function onLoading(event:Event):void {
progress.label="Loading module " + url;
if (!contains(standin))
addChild(standin);
progress.indeterminate=true;
unload.enabled=false;
reload.enabled=false;
}
public function onProgress(event:Event):void {
progress.label="Loaded %1 of %2 bytes...";
progress.indeterminate=false;
unload.enabled=true;
reload.enabled=false;
}
public function onSetup(event:Event):void {
progress.label="Module " + url + " initialized!";
progress.indeterminate=false;
unload.enabled=true;
reload.enabled=true;
}
public function onReady(event:Event):void {
progress.label="Module " + url + " successfully loaded!";
unload.enabled=true;
reload.enabled=true;
if (contains(standin))
removeChild(standin);
}
public function onError(event:Event):void {
progress.label="Error loading module " + url;
unload.enabled=false;
reload.enabled=true;
}
public function onUnload(event:Event):void {
if (url == null) {
if (contains(standin))
removeChild(standin);
} else {
if (!contains(standin))
addChild(standin);
}
progress.indeterminate=true;
progress.label="Module " + url + " was unloaded!";
unload.enabled=false;
reload.enabled=true;
}
public var standin:DisplayObject;
]]>
</mx:Script>
<mx:Panel id="panel" width="100%">
<mx:ProgressBar width="100%" id="progress" source="{this}"/>
<mx:HBox width="100%">
<mx:Button id="unload"
label="Unload Module"
click="unloadModule()"
/>
<mx:Button id="reload"
label="Reload Module"
click="unloadModule();loadModule()"
/>
</mx:HBox>
</mx:Panel>
</mx:ModuleLoader>
<?xml version="1.0"?>
<mx:Application xmlns="*" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
public var selectedItem:Object;
]]>
</mx:Script>
<mx:ComboBox
width="215"
labelField="label"
close="selectedItem=ComboBox(event.target).selectedItem"
>
<mx:dataProvider>
<mx:Object label="Select Coverage"/>
<mx:Object
label="Life Insurance"
module="insurancemodules/LifeInsurance.swf"
/>
<mx:Object
label="Auto Insurance"
module="insurancemodules/AutoInsurance.swf"
/>
<mx:Object
label="Home Insurance"
module="insurancemodules/HomeInsurance.swf"
/>
</mx:dataProvider>
</mx:ComboBox>
<mx:Panel width="100%" height="100%">
<CustomModuleLoader id="mod"
width="100%"
url="{selectedItem.module}"
/>
</mx:Panel>
<mx:HBox>
<mx:Button label="Unload" click="mod.unloadModule()"/>
<mx:Button label="Nullify" click="mod.url = null"/>
</mx:HBox>
</mx:Application>
應用error事件
使用ModuleLoader的error事件可以允許開發者在當不知什么原因Module模塊沒有被加載或卸載成功的時候,做一些必要的動作。參考以下示例
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import mx.modules.*;
import mx.controls.Alert;
private function errorHandler(e:ModuleEvent):void {
Alert.show("There was an error loading the module." +
" Please contact the Help Desk.");
trace(e.errorText);
}
public function createModule():void {
if (chartModuleLoader.url == ti1.text) {
// If they are the same, call loadModule.
chartModuleLoader.loadModule();
} else {
// If they are not the same, then change the url,
// which triggers a call to the loadModule() method.
chartModuleLoader.url = ti1.text;
}
}
public function removeModule():void {
chartModuleLoader.unloadModule();
}
]]>
</mx:Script>
<mx:Panel title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:HBox>
<mx:Label text="URL:"/>
<mx:TextInput width="200" id="ti1" text="ColumnChartModule.swf"/
>
<mx:Button label="Load" click="createModule()"/>
<mx:Button label="Unload" click="removeModule()"/>
</mx:HBox>
<mx:ModuleLoader id="chartModuleLoader" error="errorHandler(event)"/
>
</mx:Panel>
</mx:Application>
應用progress事件
使用ModuleLoader的progress事件可以讓你跟蹤模塊實例加載的進度信息。參考示例
<?xml version="1.0"?>
<!-- modules/SimpleProgressEventHandler.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import flash.events.ProgressEvent;
import mx.modules.*;
[Bindable]
public var progBar:String = "";
[Bindable]
public var progMessage:String = "";
private function progressEventHandler(e:ProgressEvent):void {
progBar += ".";
progMessage =
"Module " +
Math.round((e.bytesLoaded/e.bytesTotal) * 100) +
"% loaded";
}
public function createModule():void {
chartModuleLoader.loadModule();
}
public function removeModule():void {
chartModuleLoader.unloadModule();
progBar = "";
progMessage = "";
}
]]>
</mx:Script>
<mx:Panel title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:HBox>
<mx:Label id="l2" text="{progMessage}"/>
<mx:Label id="l1" text="{progBar}"/>
</mx:HBox>
<mx:Button label="Load" click="createModule()"/>
<mx:Button label="Unload" click="removeModule()"/>
<mx:ModuleLoader
id="chartModuleLoader"
url="ColumnChartModule.swf"
progress="progressEventHandler(event)"
/>
</mx:Panel>
</mx:Application>
怎樣共享和傳輸Module模塊間數據
Module模塊是一個容器,每個獨立的模塊對象都相當於一個自定義組件。有以下幾種方式可以實現模塊-模塊、模塊-主應用、主應用-模塊、模塊-一般自定義組件間的數據傳輸和通信。
利用ModuleLoader的child、ModuleManager的factory、以及Application的parentApplication屬性存取Module模塊和主應用文件對象索引。
因為Module模塊在ModuleLoader中通常用url屬性來指定,所以我們可以通過在url上面拼GET參數,然后在Module模塊中解析這些拼上去的參數的方式來傳輸數據。
通過ActionScript接口方式。你可以定義一個ActionScript接口,這個接口定義了一系列方法和屬性。Module模塊和主應用Application都可以訪問這些屬性。從而實現數據共享。
在主應用Application中訪問Module模塊對象
我們可以在主應用Application中訪問子Module模塊對象中定義的方法和屬性。下面示例展示了通過ModuleLoader的child屬性拿到子Module模塊對象索引,然后調用其定義的公共方法getTitle().
<?xml version="1.0"?>
<!-- modules/ParentApplication.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
[Bindable]
private var s:String;
private function getTitle():void {
s = (m1.child as ChildModule1).getModTitle();
}
]]></mx:Script>
<mx:Label id="l1" text="{s}"/>
<mx:ModuleLoader url="ChildModule1.swf" id="m1" ready="getTitle()"/>
</mx:Application>
<?xml version="1.0"?>
<!-- modules/ChildModule1.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%"
height="100%">
<mx:Script><![CDATA[
// Defines the method that the application calls.
public function getModTitle():String {
return "Child Module 1";
}
]]></mx:Script>
</mx:Module>
通過這種方式得到子Module模塊的索引導致了主應用Application和Module模塊之間的緊耦合,不利於模塊邏輯的重用。同樣地,利用ModuleManager的factory屬性也可以拿到當前ModuleLoader的子模塊對象的索引。參考以下示例,模塊文件為,
<?xml version="1.0"?>
<!-- modules/mxmlmodules/SimpleModule.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public function computeAnswer(a:Number, b:Number):Number {
return a + b;
}
]]>
</mx:Script>
</mx:Module>
主應用Application為
<?xml version="1.0"?>
<!-- modules/mxmlmodules/SimpleMXMLApp.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="initApp()">
<mx:Script>
<![CDATA[
import mx.modules.IModuleInfo;
import mx.modules.ModuleManager;
public var assetModule:IModuleInfo;
public var sm:Object;
[Bindable]
public var answer:Number = 0;
public function initApp():void {
// Get the IModuleInfo interface for the specified URL.
assetModule = ModuleManager.getModule("SimpleModule.swf");
assetModule.addEventListener("ready", getModuleInstance);
assetModule.load();
}
public function getModuleInstance(e:Event):void {
// Get an instance of the module.
sm = assetModule.factory.create() as SimpleModule;
}
public function addNumbers():void {
var a:Number = Number(ti1.text);
var b:Number = Number(ti2.text);
// Call a method on the module.
answer = sm.computeAnswer(a, b).toString();
}
]]>
</mx:Script>
<mx:Form>
<mx:FormHeading label="Enter values to sum."/>
<mx:FormItem label="First Number">
<mx:TextInput id="ti1" width="50"/>
</mx:FormItem>
<mx:FormItem label="Second Number">
<mx:TextInput id="ti2" width="50"/>
</mx:FormItem>
<mx:FormItem label="Result">
<mx:Label id="ti3" width="100" text="{answer}"/>
</mx:FormItem>
<mx:Button id="b1" label="Compute" click="addNumbers()"/>
</mx:Form>
</mx:Application>
在Module模塊對象中存取主應用Application
Module模塊可以通過其parentApplication屬性拿到主應用Application對象的索引,通過這個索引可以訪問主應用對象的公共方法和屬性。參考以下示例,Module模塊文件為
<?xml version="1.0"?>
<!-- modules/ChartChildModule.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%"
height="100%" creationComplete="getDataFromParent()">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var expenses:ArrayCollection;
// Access properties of the parent application.
private function getDataFromParent():void {
expenses = parentApplication.expenses;
}
]]></mx:Script>
<mx:ColumnChart id="myChart" dataProvider="{expenses}">
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{expenses}"
categoryField="Month"
/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries
xField="Month"
yField="Profit"
displayName="Profit"
/>
<mx:ColumnSeries
xField="Month"
yField="Expenses"
displayName="Expenses"
/>
</mx:series>
</mx:ColumnChart>
<mx:Legend dataProvider="{myChart}"/>
<mx:Button id="b1" click="expenses = parentApplication.getNewData();"
label="Get New Data"/>
</mx:Module>
主應用文件為
<!-- modules/ChartChildModuleLoader.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var expenses:ArrayCollection = new ArrayCollection([
{Month:"Jan", Profit:2000, Expenses:1500},
{Month:"Feb", Profit:1000, Expenses:200},
{Month:"Mar", Profit:1500, Expenses:500}
]);
public function getNewData():ArrayCollection {
return new ArrayCollection([
{Month:"Apr", Profit:1000, Expenses:1100},
{Month:"May", Profit:1300, Expenses:500},
{Month:"Jun", Profit:1200, Expenses:600}
]);
}
]]></mx:Script>
<mx:ModuleLoader url="ChartChildModule.swf" id="m1"/>
</mx:Application>
不過這種方式導致的一個缺點是,自定義的Module模塊文件的可移植特性將大打折扣。
在一個Module模塊對象中存取另一個Module模塊對象
同樣地,我們也可以利用ModuleLoader的child屬性,在一個Module模塊對象中拿到另一個Module模塊對象的索引,通過這個索引訪問當前Module模塊對象的公共方法和屬性。參考以下示例,主應用文件
<?xml version="1.0"?>
<!-- modules/TitleModuleLoader.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script><![CDATA[
]]></mx:Script>
<mx:ModuleLoader url="InterModule1.swf" id="m1"/>
<mx:ModuleLoader url="InterModule2.swf" id="m2"/>
</mx:Application>
Module1文件
<?xml version="1.0"?>
<!-- modules/InterModule1.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%"
height="100%">
<mx:Script><![CDATA[
// Defines the method that the other module calls.
public function getNewTitle():String {
return "New Module Title";
}
]]></mx:Script>
</mx:Module>
Module2文件
<?xml version="1.0"?>
<!-- modules/InterModule2.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%"
height="100%">
<mx:Script><![CDATA[
[Bindable]
private var title:String;
// Call method of another module.
private function changeTitle():void {
title = parentApplication.m1.child.getNewTitle();
}
]]></mx:Script>
<mx:HBox>
<mx:Label id="l1" text="Title: "/>
<mx:Label id="myTitle" text="{title}"/>
</mx:HBox>
<mx:Button id="b1" label="Change Title" click="changeTitle()"/>
</mx:Module>
通過拼ModuleLoader的url參數方式實現數據傳輸
在url上面拼GET參數基本上是這種格式,url=module1.swf?param1=value1¶m2=value2
比如在主應用文件Application中拼一系列GET參數到ModuleLoader的url屬性上,在Module模塊文件中解析並處理這些參數,參考以下示例,主應用文件
<?xml version="1.0"?>
<!-- modules/QueryStringApp.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" height="500"
width="400">
<mx:Script><![CDATA[
public function initModule():void {
// Build query string so that it looks something like this:
// "QueryStringModule.swf?firstName=Nick&lastName=Danger"
var s:String = "QueryStringModule.swf?" + "firstName=" +
ti1.text + "&lastName=" + ti2.text;
// Changing the url property of the ModuleLoader causes
// the ModuleLoader to load a new module.
m1.url = s;
}
]]></mx:Script>
<mx:Form>
<mx:FormItem id="fi1" label="First Name:">
<mx:TextInput id="ti1"/>
</mx:FormItem>
<mx:FormItem id="fi2" label="Last Name:">
<mx:TextInput id="ti2"/>
</mx:FormItem>
</mx:Form>
<mx:ModuleLoader id="m1"/>
<mx:Button id="b1" label="Submit" click="initModule()"/>
</mx:Application>
模塊文件
<?xml version="1.0"?>
<!-- modules/QueryStringModule.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="parseString()">
<mx:Script>
<![CDATA[
import mx.utils.*;
[Bindable]
private var salutation:String;
public var o:Object = {};
public function parseString():void {
try {
// Remove everything before the question mark, including
// the question mark.
var myPattern:RegExp = /.*\?/;
var s:String = this.loaderInfo.url.toString();
s = s.replace(myPattern, "");
// Create an Array of name=value Strings.
var params:Array = s.split("&");
// Print the params that are in the Array.
var keyStr:String;
var valueStr:String;
var paramObj:Object = params;
for (keyStr in paramObj) {
valueStr = String(paramObj[keyStr]);
ta1.text += keyStr + ":" + valueStr + "\n";
}
// Set the values of the salutation.
for (var i:int = 0; i < params.length; i++) {
var tempA:Array = params[i].split("=");
if (tempA[0] == "firstName") {
o.firstName = tempA[1];
}
if (tempA[0] == "lastName") {
o.lastName = tempA[1];
}
}
if (StringUtil.trim(o.firstName) != "" &&
StringUtil.trim(o.lastName) != "") {
salutation = "Welcome " +
o.firstName + " " + o.lastName + "!";
} else {
salutation = "Full name not entered."
}
} catch (e:Error) {
trace(e);
}
// Show some of the information available through loaderInfo:
trace("AS version: " + this.loaderInfo.actionScriptVersion);
trace("App height: " + this.loaderInfo.height);
trace("App width: " + this.loaderInfo.width);
trace("App bytes: " + this.loaderInfo.bytesTotal);
}
]]>
</mx:Script>
<mx:Label text="{salutation}"/>
<mx:TextArea height="100" width="300" id="ta1"/>
</mx:Module>
利用ActionScript接口實現Module模塊間的數據通信
在面向對象的編程中,我們講要面向接口編程。面向接口的編程方式從一定程度上解決了相互關聯的模塊間的緊密耦合問題。以上提到的所有數據傳輸和共享方式都在不同程度上導致了模塊間的緊耦合。不過,Flex提供了一種利用標准的ActionScript接口實現Module模塊間數據通信的方式。具體地說,對於Module模塊對象和主應用Application對象間的通信,我們可以定義一個ActionScript接口,Module模塊對象實現了這個接口中定義的方法和屬性,那么主應用Application就可以訪問這個接口中定義的屬性和方法。接口中定義了Module模塊對象和主應用Application需要共享的數據和方法,是兩者間共同的一個契約,同時也實現了接口和實現的分離,達到了松耦合的目的。參考以下示例,主應用Application
<?xml version="1.0"?>
<!-- modules/interfaceexample/Main.mxml -->
<mx:Application xmlns="*" xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import mx.modules.ModuleManager;
[Bindable]
public var selectedItem:Object;
[Bindable]
public var currentModuleName:String;
private function applyModuleSettings(e:Event):void {
// Cast the ModuleLoader's child to the interface.
// This child is an instance of the module.
// You can now call methods on that instance.
var ichild:* = mod.child as IModuleInterface;
if (mod.child != null) {
// Call setters in the module to adjust its
// appearance when it loads.
ichild.setAdjusterID(myId.text);
ichild.setBackgroundColor(myColor.selectedColor);
} else {
trace("Uh oh. The mod.child property is null");
}
// Set the value of a local variable by calling a method
// on the interface.
currentModuleName = ichild.getModuleName();
}
private function reloadModule():void {
mod.unloadModule();
mod.loadModule();
}
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="Current Module:">
<mx:Label id="l1" text="{currentModuleName}"/>
</mx:FormItem>
<mx:FormItem label="Adjuster ID:">
<mx:TextInput id="myId" text="Enter your ID"/>
</mx:FormItem>
<mx:FormItem label="Background Color:">
<mx:ColorPicker id="myColor"
selectedColor="0xFFFFFF"
change="reloadModule()"
/>
</mx:FormItem>
</mx:Form>
<mx:Label text="Long Shot Insurance" fontSize="24"/>
<mx:ComboBox
labelField="label"
close="selectedItem=ComboBox(event.target).selectedItem"
>
<mx:dataProvider>
<mx:Object label="Select Module"/>
<mx:Object label="Auto Insurance" module="AutoInsurance.swf"/>
</mx:dataProvider>
</mx:ComboBox>
<mx:Panel width="100%" height="100%">
<mx:ModuleLoader id="mod"
width="100%"
url="{selectedItem.module}"
ready="applyModuleSettings(event)"
/>
</mx:Panel>
<mx:Button id="b1" label="Reload Module" click="reloadModule()"/>
</mx:Application>
接口文件,
// modules/interfaceexample/IModuleInterface
package
{
import flash.events.IEventDispatcher;
public interface IModuleInterface extends IEventDispatcher {
function getModuleName():String;
function setAdjusterID(s:String):void;
function setBackgroundColor(n:Number):void;
}
}
Module模塊文件
<?xml version="1.0"?>
<!-- modules/interfaceexample/AutoInsurance.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%"
height="100%" implements="IModuleInterface">
<mx:Panel id="p1"
title="Auto Insurance"
width="100%"
height="100%"
backgroundColor="{bgcolor}"
>
<mx:Label id="myLabel" text="ID: {adjuster}"/>
</mx:Panel>
<mx:Script>
<![CDATA[
[Bindable]
private var adjuster:String;
[Bindable]
private var bgcolor:Number;
public function setAdjusterID(s:String):void {
adjuster = s;
}
public function setBackgroundColor(n:Number):void {
// Use a bindable property to set values of controls
// in the module. This ensures that the property will be set
// even if Flex applies the property after the module is
// loaded but before it is rendered by the player.
bgcolor = n;
// Don't do this. The backgroundColor style might not be set
// by the time the ModuleLoader triggers the READY
// event:
// p1.setStyle("backgroundColor", n);
}
public function getModuleName():String {
return "Auto Insurance";
}
]]>
</mx:Script>
</mx:Module>
