跳到主要內容

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
不果這東西這麼紅, 一定有他的道理在
寫道…
Tweenlite是要自己下載嗎@@?
milkmidi寫道…
Sorry, 我有用TweenMax類別
到Google打,第一筆就是他的網站
列夫 LINLI寫道…
請問老師

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

這個網誌中的熱門文章

webpack2 入門實戰 1

大家好,我是奶綠茶
前端戰場不再只是寫寫 js / css , 各種框架、前處理工具百花齊放
身為前端工程師,不只要把程式寫完,還要寫好
老師說:選對好工具,事情就完成一半
如果你還在一隻 JS 打完全部程式,一隻 css 寫所有的 style
每次存檔還在手動 reload 網頁, 圖片壓 K 壓到不要不要的
透過奶綠伯的系列教學,讓你了解 webpack2 帶來的優勢
學會 webpack 可能不會加薪,但至少可以準時下班(誤)
1. 安裝 nodejs
請參考 gulp 安裝編

2. 安裝 global webpack , 筆者使用的是 2.2.1 版本
npm i webpack@2.2.1 -g
3. 在專案的根目錄放一隻 webpack.config.js
entry:你的主 js 進入點
output.filename:webpack 打包後的檔名
output.path:webpack 打包後的路徑
var path = require('path'); module.exports = { entry: './src/app.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } };
4. require , module.exports
現在前端都 module 化
可以每個獨立的功能都寫成單一的 js module
除了好管理,也方便讓團隊使用
寫一隻 module_exports_util.js
每隻經過 webpack 打包的 js , 都會是獨立的檔案
所以變數都是私有的, 外部成員都無法得到
在這個 module 裡,我們想開放二個函式
add , getName
所以在最後的 module.exports 指定
筆記加入 jsdoc , 為了方便在開發時,能夠有型別的提示

var name = "milkmidi"; /** * @param {number} num1 * @param {number} num2 * @return {number} */ function ad…

超好用的無限免費網頁空間,無廣告,無流量限制

大家好,我是奶綠茶
今天來教大家如何申請一個無限免費速度又快的網頁空間
1 首先到 https://github.com/ 申請帳號(一直下一步,下一步,下一步)
2 到你的個人頁,切換上方的 tab 到 Repositories, 按下右鍵的 new

3 Repository name
一定要是這樣的格式 username.github.io
我的 github 網址是 github.com/milkmidi
那就要輸入 milkmidi.github.io
選擇 public, 這樣別人才看的到
private 有其他用途, 而且要付費才能使用
完成後按下 Create repository


5 安裝 SourceTree
github 並不支援 FTP 或是網頁上傳,一定要透過指令碼
在這我們選用有圖型介面的軟體,方便大家學習
https://www.sourcetreeapp.com/
下載並安裝
啟動後登入你的 github 帳號

6 clone 你的 github io 專案
右上角有個 Clone or download 點選後
複製 https 連結(不要選到 ssh )


7 將 https 的連結貼到 SourceTree



8 上傳 html
到本機 github.io 資料夾,放一個 index.html
切換到 SourceTree, 這時會看到 Unstaged files 的欄位
選擇 Stage All


9 git 要求每次的 Commit, 都一定要打說明文字(好習慣)
輸入完成後,按下右邊的 Commit


10 發佈(Push),這樣就完成啦
可以到你的 http://milkmidi.github.io/ 去查看檔案有沒有出來


其他
Commit 可以想像是做一個記錄,你可以很多的 Commit
最後再一次 Push 上去
github 原本是給程式設計師用的版本控管服務
免費版提供無限空間讓你放檔案,但一定要是 public
想要有私有的 Project ,就只能付費
github.io 只能放靜態檔案,php, aspx 服務並不支援。
祝大家學習愉快

轉載請註明出處

奶綠的 github.io Source Code

webpack2 入門實戰 3 scss,html,file-loader

大家好,我是奶綠茶
上一篇介紹了 webpack 的核心功能 loader
這篇再來補強各種常用的 loader
css-loader:解悉 css 檔
extract-loader:這個有點難翻,下面會介紹
file-loader:存成實體的檔案, 如圖片
html-loader:解悉 html 檔
sass-loader:解悉 scss 檔
url-loader:解悉圖片路徑
webpack.config.js
module.exports = { resolveLoader: { // 所有用到的 loader, -loader 可以不用打 moduleExtensions: [ "-loader" ], }, resolve: { // 在 require 檔案, 如果不想寫完整的路徑 // 可以加入這些目錄, 讓 webpack 自動尋找對的檔案 // 請注意我們加入了 src/img 路徑,後面會再介紹到 modules: [ path.resolve( 'src/html' ), path.resolve( 'src/img' ), path.resolve( 'src/css' ), path.resolve( 'src/js' ), path.resolve( "node_modules"), ], // 在 require 時可以不用打副檔名 extensions: [ ".js", ".scss" ] }, } 再來就是 loader 的介紹
scss
在這要做多組合的應用
先將 .scss 透過 scss-loader 轉換, 並產生 sourceMap 檔
再過 css-loader
最後再過 style-loader
{ test: /\.scss$/, // 多個 loader 組合, 可寫成一行,或…