跳到主要內容

PureMVC, 快樂玩Part1

PureMVC, 快樂玩Part1
最近除了熱血的工作, 熱血的研究Progression, 當然還有熱血的PureMVC
好吧, 我承認這東西有一定的難度, 但上手後, 真的還滿方便的。
PureMVC是一種程式語言的框架, 不限於 AS3 , 官網有很多的版本可以任君選擇。
主要有三個概念
Model, Proxy
負責將數值變數存起來, 當數值被更改時, 就發送通知, 然後就什麼事也不做了。

View,Mediator
一個Mediator就對映一個View(視覺元件), 當View做了某件事情後, 發送通知。

Controller,Command
當 Mediator 被通知到後, 就執行事件做作好的Command

有了簡單概念後, 來開始試寫看看。
先下載 PureMVC AS3 standard 2.0.4版本

在這兒我們寫了一個小型的相本, 由 xml 管理, 畫面上有左、右鍵,二個動態文字
和一個要載入圖片用的Sprite。

奶小茶的習慣是先從Proxy來開始寫, 因為載入資料是最先要做的事情。
ListProxy.as
package milkmidi.puremvc.model {
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import milkmidi.puremvc.model.vo.ListVO;
import org.puremvc.as3.interfaces.IProxy;
import org.puremvc.as3.patterns.proxy.Proxy;
public class ListProxy extends Proxy implements IProxy {  

public static const NAME    :String = "ListProxy"; 

//當 xml 載入完成後, 會發送notification
public static const DATA_LOAD_COMPLETE :String = 'dataLoadComplete';

//當 xml 索引值改變時
public static const MODEL_CHANGE  :String = 'modelChange';

//當 ListProxy 被設定新值時
public static const SET_CURRENT_INDEX :String = 'setCurrentIndex';  

private var _urlLdr   :URLLoader = new URLLoader();
private var _xml   :XML;
private var _currentIndex :uint = 0;
public function ListProxy(data:Object = null) {
super(NAME, data);
_urlLdr.addEventListener(Event.COMPLETE , completeHandler);
_urlLdr.load(new URLRequest('xml_data.xml'));
//載入 xml 。
}  
private function completeHandler(e:Event):void {
_xml = XML(e.currentTarget.data);
//載入完成後,將得到的 xml 設定到 data裡。
setData( _xml );
sendNotification(DATA_LOAD_COMPLETE , data);   
//記得要發通知喔。
}  
public function setCurrentIndex(pIndex:uint):void {
_currentIndex = pIndex;
//設定索引值
if (_currentIndex < 0) _currentIndex = 0;   
else if (_currentIndex >= length - 1)
_currentIndex = length - 1;
//判斷有沒有爆掉。

var _vo:ListVO = new ListVO();
_vo.img = currentImgURL;
_vo.title = currentTitle;
_vo.page = currentPage;
//將新的數值發送出去。
sendNotification(MODEL_CHANGE , _vo);   
}
public function get length():uint {    return _xml.item.length();  }
public function get currentImgURL():String { return _xml.item[currentIndex].img;  }
public function get currentTitle():String {  return _xml.item[currentIndex].title;  }
public function get currentPage():String {  return (_currentIndex + 1) +'/' + length; }    
public function get currentIndex():uint {   return _currentIndex; }


}
}
//在這兒也準備一個VO(ValueObject), 方便給大家使用。
ListVO.as
package milkmidi.puremvc.model.vo {  

public class ListVO {  
public var title:String = '';
public var img :String = '';
public var page :String = '';

public function ListVO()  {   

}  
} 
}

有了Proxy後,下一步來寫View跟Mediator
在這兒我們就只有一個主場景, 所以就準備一個ApplicationMediator.as
Mediator的工作就是觀查視覺元件的一切, 偵聽他的事件
更改他的數值, 就是他的保母啦。
package milkmidi.puremvc.view {
import flash.events.Event;
import milkmidi.puremvc.model.ListProxy;
import milkmidi.puremvc.model.vo.ListVO;
import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;
import milkmidi.puremvc.view.*;
public class ApplicationMediator extends Mediator implements IMediator { 
// Cannonical name of the Mediator
public static const NAME:String = "ApplicationMediator";  
public function ApplicationMediator(viewComponent:Object) {
super(NAME, viewComponent);
//偵聽主場景上的事件。
container.addEventListener('nextItem' , nextItemHandler);
container.addEventListener('prevItem' , prevItemHandler);
}  
private function prevItemHandler(e:Event):void {
sendNotification( ListProxy.SET_CURRENT_INDEX , -1 );   
//發送通知。
}  
private function nextItemHandler(e:Event):void {
sendNotification( ListProxy.SET_CURRENT_INDEX , 1 );
//發送通知。
}
override public function getMediatorName():String {
return ApplicationMediator.NAME;
}     
override public function listNotificationInterests():Array {
return [    
ListProxy.DATA_LOAD_COMPLETE,
ListProxy.MODEL_CHANGE
];
//我只關心這些通知, 其他沒寫的就通通不管。
}
override public function handleNotification(note:INotification):void {   
trace(facade.retrieveProxy( ListProxy.NAME));
//得到發送事件的 ListProxy 實體。
trace(note.getBody())   
//得到發送通知時, 一起帶過來的數值
switch (note.getName()) {        
case ListProxy.DATA_LOAD_COMPLETE:  
sendNotification( ListProxy.SET_CURRENT_INDEX , 0 );
break;
case ListProxy.MODEL_CHANGE:      
var _listVO:ListVO = note.getBody() as ListVO;
container.title_txt.text = _listVO.title + '';
container.numbers_txt.text = _listVO.page;
break;    
default:
break;  
}
}  
private function get container():Main {
return viewComponent as Main;
//轉型用, 給自己看的。
}
}
}
//另一個ImageContainerMediator.as ,是觀查場景上的載入圖片用的Sprite, 寫法差不多,就不多介紹了。

ApplicationFacade, PureMVC 的總代理, 在這奶小茶使用的是 Standard版
所以只會有一個Facade, 如果要多個應用程式, 多個Facade的話,可以選用 MultiCore版本。
所有的 Proxies , Mediators 跟 Commands 註冊
他的程式碼很少, 都是固定的寫法。
package milkmidi.puremvc {
import flash.display.Stage;
import org.puremvc.as3.interfaces.IFacade;
import org.puremvc.as3.patterns.facade.Facade;
import milkmidi.puremvc.model.*;
import milkmidi.puremvc.view.*;
import milkmidi.puremvc.controller.*;
public class ApplicationFacade extends Facade implements IFacade {
// Notification name constants
public static const STARTUP:String = "startup";  
public static function getInstance():ApplicationFacade {
if (instance == null) instance = new ApplicationFacade();
return instance as ApplicationFacade;
}  
// Register commands with the controller
override protected function initializeController():void {
super.initializeController();   
registerCommand(STARTUP, StartupCommand);
}
public function startup( pApp:Main ) : void {   
sendNotification( STARTUP, pApp );
//這兒是用來啟動整個 PureMVC用的
}  
} 
}

啟動用的Command
StartupCommand.as
package milkmidi.puremvc.controller { 
import milkmidi.puremvc.model.ListProxy;
import milkmidi.puremvc.view.ApplicationMediator;
import milkmidi.puremvc.view.ImageContainerMediator;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.command.SimpleCommand;
public class StartupCommand extends SimpleCommand{
public function StartupCommand(){
super();
}
override public function execute(notification:INotification):void {   
var _main:Main = notification.getBody() as Main;   
facade.registerMediator( new ApplicationMediator( _main));
//注冊 Mediator
facade.registerMediator( new ImageContainerMediator( _main.container_mc));
//注冊 Mediator, 在這兒是用來載入外圖品片的容器。
facade.registerProxy( new ListProxy());   
//注冊 Proxy。
facade.registerCommand( ListProxy.SET_CURRENT_INDEX , ModelChangeCommand);   
//注冊 Command
}
}
}
//當要更改 ListProxy裡數值用的Command
package milkmidi.puremvc.controller {
import milkmidi.puremvc.model.ListProxy;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.command.SimpleCommand;
import org.puremvc.as3.patterns.observer.Notification;
public class ModelChangeCommand extends SimpleCommand {

override public function execute(note:INotification):void {  
var _listProxy :ListProxy = facade.retrieveProxy( ListProxy.NAME ) as ListProxy;
var _data  :int = int(note.getBody());  
_listProxy.setCurrentIndex(_listProxy.currentIndex + _data);   
}

}
}

再來就是整個Clinet的主程式。
package  {  
import flash.display.MovieClip;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import milkmidi.puremvc.ApplicationFacade;  
public class Main extends Sprite {   
//場景上的元件
public var right_btn    :SimpleButton;
public var left_btn     :SimpleButton;
public var numbers_txt  :TextField;
public var title_txt    :TextField;
public var container_mc :MovieClip;  
public function Main()  { 
right_btn.addEventListener(MouseEvent.CLICK , clickHandler);
left_btn.addEventListener(MouseEvent.CLICK , clickHandler);
ApplicationFacade.getInstance().startup( this );
//啟動PureMVC。
}    
private function clickHandler(e:MouseEvent):void {   
//左、右鍵只管發送事件, 就沒啦。
if (e.currentTarget == left_btn) 
dispatchEvent(new Event('prevItem'));
else 
dispatchEvent(new Event('nextItem'));   
}
} 
}

完成啦, 搞的大家都很不熟, 這就是PureMVC的精神呀
下次在專案上實作看看。
SourceCodeDownload

留言

jerry寫道…
最近我也在用mvc,但只是將寫出來的CLASS,分成MODEL VIEW CONTROLLER,請問老師,用puermvc的特色在哪呢?
milkmidi寫道…
他將物件分的更多,
相對程式碼也會比較多
如果只是小型的應用程式
不一定要用pureMVC
不果這東西這麼紅, 一定有他的道理在
Unknown寫道…
Tweenlite是要自己下載嗎@@?
milkmidi寫道…
Sorry, 我有用TweenMax類別
到Google打,第一筆就是他的網站
Unknown寫道…
請問老師

在規劃程式的時候`,你會先在紙上做UML圖的規劃嗎?
milkmidi寫道…
不會也,我跟 UML 超不熟的
通常都以功能面來規劃
天下為公寫道…
站長問一下flash AS3要怎麼讓滑鼠的游標改變成手指啊?? 我不要一直用箭頭~~
milkmidi寫道…
只要對物件使用buttonMode=true;即可
請問一下我在試做puremvc我重新啟動了startup, sendNotification 就傳送了兩次,我該怎麼讓他只傳送一次.