2012年7月6日 星期五

PureMVC實作二

終於抽出時間來寫PureMVC實做二了。

PureMVC實做一中,我們做好了基礎架構,現在要替基礎架構做整修並要開始寫入程式碼。

一般專案開始都會先有Layout,我們也不例外,我們要的測試Layout如下:






我們將會模擬從Remote Server上取回呈現Menu的資料,這裡將會是兩筆,之後第三篇會寫入deepLink與切換頁載入不同的SWF Model。

Setp1.修改MainProxy.as Class 

  • 注意我們建構式有修改,與原先PureMVC實做一不同

MainProxy.as

package main.proxy
{
import main.mediator.MainMediator;
import mx.collections.ArrayCollection;
import org.puremvc.as3.multicore.patterns.proxy.Proxy;
public class MainProxy extends Proxy
{
//代表MainProxy的名字,用於向facade註冊。
public static const NAME:String = "MAIN_PROXY"

//代表呼叫getMenu的command名字,用於向facade註冊GetMenucommand。
public static const GET_MENU:String = "MainProxy_GetMenu";

public function MainProxy(data:Object=null)
{
super(NAME , data);
}

//模擬向server取回Menu資料,通常實際環境有驗證用參數,這裡省略。
public function getMenu():void
{
var menu:ArrayCollection = new ArrayCollection();
//通常這些會寫成Class,這裡使用Object來代替。
menu.addItem({menuName:'Page1' , menuId:'000-001', location:'http://localhost/Page1.swf'});
menu.addItem({menuName:'Page2' , menuId:'000-002', location:'http://localhost/Page2.swf'});

//取回資料後發送通知,呼叫RenderingMenuCommand,並將資料帶出
sendNotification(MainMediator.RENDERING_MENU , menu);
}
}
}



Setp2.Layout 畫面結構,修改PureMVC_Demo.mxml


  • 這裡將運用DataGroup來產生Menu
PureMVC_Demo.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
  xmlns:s="library://ns.adobe.com/flex/spark"
  xmlns:mx="library://ns.adobe.com/flex/mx"
  minWidth="955" minHeight="600"
  creationComplete="creationCompleteHandler()"
  >
<fx:Style>
@namespace s "library://ns.adobe.com/flex/spark";
@namespace mx "library://ns.adobe.com/flex/mx";
@namespace view "main.view.*";

/**id 選法*/
#nav{
backgroundColor:#000000;
}
/**class*/
.menu{
}
.menu view|MenuItem
{
backgroundColors:#2B2B2B;
}
.menu s|RichEditableText
{
color:#FFFFFF;
fontSize:18px;
paddingTop:10px;
paddingBottom:10px;
paddingRight:10px;
paddingLeft:10px;
}
</fx:Style>
<fx:Script>

<![CDATA[
import main.facade.ApplicationFacade;

public static const NAME:String = 'PURE_MVC_DEMO';

private var _facade:ApplicationFacade;
//所有顯示元件建立後觸發,如同jquery的ready
protected function creationCompleteHandler():void
{
//ApplicationFacade是一個獨體模式,使用getInstance取回唯一實體
_facade = ApplicationFacade.getInstance(NAME);

//呼叫start command
_facade.startup(this);
}
]]>
</fx:Script>
<!--設為垂直排版-->
<s:layout>
<s:VerticalLayout/>
</s:layout>

<s:SkinnableContainer id='nav' backgroundColor="#000000" width="100%">
<s:layout>
<s:HorizontalLayout gap="0"/>
</s:layout>
<!--dataGroup可以用來產生重複的排版結構,結構由ItemRenderer產生(就時一個模板,依資料筆數出現的模板)-->
<s:DataGroup id="menuDataGroup" itemRenderer="main.view.MenuItem" styleName="menu">
<s:layout>
<s:HorizontalLayout gap="0"/>
</s:layout>
</s:DataGroup>
</s:SkinnableContainer>
<s:Group>

</s:Group>

</s:Application>

  • 這裡要注DataGroup上使用了Itemrenderer,因此還會再產生一個MenuItem.mxml的IterRenderer Class

Step3.Create MenuItem.mxml

MenuItem.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
xmlns:s="library://ns.adobe.com/flex/spark" 
xmlns:mx="library://ns.adobe.com/flex/mx" 
autoDrawBackground="true"
buttonMode="true"
>
<fx:Metadata>
//這是用來給CSS可以選擇的項目,這樣設計師才有屬性可選
[Style(name="backgroundColors", type="uint", theme="spark" , inherit="yes")]
</fx:Metadata>
<fx:Script>
<![CDATA[

//style有變動時會觸發
override public function styleChanged(styleProp:String):void
{
super.styleChanged(styleProp);
if(styleProp == "backgroundColors")
{
invalidateDisplayList();
}
}

//畫面更新時觸發
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
backgroundFillColor.color = getStyle("backgroundColors");
}


//itemRenderer都有這個接口,會將collection內的項目送入
override public function set data(value:Object):void
{
//通常這個data都會寫成對應的Class,有寫的話能夠編譯時就能檢查屬性型態等等,
//這裡因為測試快速使用Object所以沒有這個效果。

if(value != super.data)
{
super.data = value;
menuName.text = (data && data.menuName)? data.menuName : '' ;
}
}
]]>
</fx:Script>
<!--背景色,不使用容器來做,因為這裡只是需要背景色一項而已,所以不需要用到SkinnableContainer,這樣比較節省大小,
radiusX="5" radiusY="5"是圓角的選項-->

<s:Rect left="0" right="0" top="0" bottom="0" alpha="0.9">
<s:fill>
<!--有預設背景色-->
<s:SolidColor id="backgroundFillColor"  color="0x2B2B2B"/>
</s:fill>
</s:Rect>

<!--當然你也可以使用bind來做,如微軟的MVVC-->
<s:RichEditableText id="menuName" editable="false" multiline="false" buttonMode="false" mouseChildren="false" mouseEnabled="false"/>
</s:ItemRenderer>


Setp4.撰寫ApplicationFacade.as

ApplicationFacade.as

package main.facade
{
import main.command.StartupCommand;

import org.puremvc.as3.multicore.core.Controller;
import org.puremvc.as3.multicore.core.Model;
import org.puremvc.as3.multicore.core.View;
import org.puremvc.as3.multicore.patterns.facade.Facade;

public class ApplicationFacade extends Facade
{
//用來向facade註冊 startup command用的名子  
public static const STARTUP:String = "startup";

//建構子
public function ApplicationFacade(key:String)
{
super(key);
}

//用來取ApplicationFacade Singleton instance
public static function getInstance(key:String):ApplicationFacade
{
if (instanceMap[key] == null)
instanceMap[key] = new ApplicationFacade(key);
return instanceMap[key] as ApplicationFacade;
}

//
public static function hasCore(key:String):Boolean
{
return (instanceMap[key] != null);
}

//
public static function removeCore(key:String):void
{
if (instanceMap[key] == null)
return;
Model.removeModel(key);
View.removeView(key);
Controller.removeController(key);
delete instanceMap[key];
}
//初始化Controller
override protected function initializeController():void
{
super.initializeController();
//StartCommand是PureMVC第一個啟動的Command,
//之後Controller、Model、Mediator部分都在StartCommand裡初始化 

registerCommand(STARTUP , StartupCommand);
}

//呼叫 startCommand
public function startup(mainView:PureMVC_Demo):void
{
sendNotification(STARTUP , mainView);
}
}
}


Step5.撰寫 StartupCommand 、 GetMenuCommand.as 、 RenderingMenuCommand.as 。

  StartupCommand.as

package main.command
{
import main.mediator.MainMediator;
import main.proxy.MainProxy;

import org.puremvc.as3.multicore.interfaces.INotification;
import org.puremvc.as3.multicore.patterns.command.SimpleCommand;

public class StartupCommand extends SimpleCommand
{
public function StartupCommand()
{
super();
}

//command被呼叫時執行
override public function execute(note:INotification):void
{
registerProxy();
registerCommand();
//note是發送通知時所帶的資料,可使用getBody取回
registerMediator(note.getBody() as PureMVC_Demo);

//呼叫取回menu的api
(facade.retrieveProxy(MainProxy.NAME) as MainProxy).getMenu();
}

//註冊Proxy
protected function registerProxy():void
{
// 原本MainProxy第一個參數需要引入註冊的名稱,
// 但因為改寫過Proxy的建構式,所以內部以呼叫super(NAME , data)將名字傳入。  
facade.registerProxy(new MainProxy());
}

//註冊Command
protected function registerCommand():void
{
facade.registerCommand(MainProxy.GET_MENU , GetMenuCommand);
facade.registerCommand(MainMediator.RENDERING_MENU , RenderingMenuCommand);
}

//註冊Mediator
protected function registerMediator(mainView:PureMVC_Demo):void
{
//NAME如Proxy在建構式內已經處理
facade.registerMediator(new MainMediator(mainView));
}
}
}



GetMenuCommand.as

package main.command
{
import main.proxy.MainProxy;

import org.puremvc.as3.multicore.interfaces.INotification;
import org.puremvc.as3.multicore.patterns.command.SimpleCommand;

/**
* 用來呼叫Proxy中的getMenu method
*/

public class GetMenuCommand extends SimpleCommand
{
public function GetMenuCommand()
{
super();
}

//command被呼叫時就會執行的開口
override public function execute(note:INotification):void
{
//facade管理著controller、Modell、View,這裡給予key請求facade去取已註冊在Modell裡的MainProxy,並呼叫getMenu()。
(facade.retrieveProxy(MainProxy.NAME) as MainProxy).getMenu();
}
}
}


RenderingMenuCommand.as

package main.command
{
import main.mediator.MainMediator;
import mx.collections.ArrayCollection;
import org.puremvc.as3.multicore.interfaces.INotification;
import org.puremvc.as3.multicore.patterns.command.SimpleCommand;

/**
* 用來呼叫MainMediator來繪製Menu部分,
* 其實這裡就是資料的傳遞,應該有更能代表這個command的名子。
*/

public class RenderingMenuCommand extends SimpleCommand
{
public function RenderingMenuCommand()
{
super();
}

//command被呼叫時執行
override public function execute(note:INotification):void
{
//呼叫取回menu的api
(facade.retrieveMediator(MainMediator.NAME) as MainMediator).renderMenu(note.getBody() as ArrayCollection);
}
}
}


這是此次進度的檔案結構圖:




































PureMVC 實做二到這裡結束,下次將撰寫deep link與 modole的載入互動。

PureMVC 實做一

沒有留言:

張貼留言