2010年6月22日 星期二

Progression 4 歡樂開發Flash網站 ( 5 ) — 增加導覽列功能

我們進一步研究一下導覽列的功能。通常當我們到了某個單元後,會希望導覽列上這個單元的Button處於Highlight的狀態,且不能夠再度點選。再者,CastButton本身,有一堆滑鼠相關的事件可以用,偏偏少了Click,這當然是因為通常Click的時候只會做Scene的移動,或者跳到其他網頁,這些只要設定sceneId以及href屬性就可以了。但我們有時候會想要在Click的時候做一些其他事情,例如去計算click數等。其他有些時候,我們在場景上可能有一些Button,它的連結跟導覽列上面那些Button是一樣的,我們希望其中一組Mouse Over時,另一組也會有相對的反應。這些都可以藉由CastButton的功能,再加上一些些擴充,就可以輕易辦到。

新增類別
再沿用上一個範例。我們製作一個CastNaviButton的類別繼承CastButton,放在myproject.index底下。打開index.fla,將IndexButton等Button元件的Base Class改成myproject.index.CastNaviButton。

判斷狀態CastButton.state,以及狀態改變事件CastMouseEvent.CAST_STATE_CHANGE
CastButton有一個state屬性,值是int,用來識別現在CastButton的狀態。值所代表的意義則是參考CastButtonState這個類別:
常數名稱 說明
CHILD 1 現在Scene是Button設定Scene的Child。
CURRENT 2 現在Scene剛好是Button設定Scene。
DISABLE 0 無效的狀態。沒有指定SceneId也沒指定href。
NEUTRAL 4 一般狀態。
PARENT 3 現在Scene是Button設定Scene的Parent。
所以CastButton加上偵聽CAST_STATE_CHANGE事件,再去判斷state的值,就可以去做出相對應的反應。順便仿照CastButton,加上atStateChange與onStateChange,讓我們可以複寫或指定屬性的方式來加上處理常式。

群組CastButton.group,以及依群組尋找實體getInstancesByGroup
CastButton可以自由設定group,內容是字串。而jp.progression.casts這個package底下有個getInstancesByGroup function,可以把相同group的實體搜出來。如果導覽列跟場景上都有個要前往Product Scene的Button,那我們可以將這兩個Button的group都設定為"productBtnGroup",然後mouse over與mouse out的時候經由getInstancesByGroup 把同樣group的實體回傳回來,然後讓它們都執行mouse over與mouse out該有的動作就可以了。

加上偵聽MouseEvent.CLICK事件
既然CastButton沒有CLICK事件,我們就偵聽最原始的MouseEvent.CLICK吧。然後一樣順便仿照CastButton,加上atCastClick與onCastClick,讓我們可以複寫或指定屬性的方式來加上處理常式。

加入enableAtChildScene做更多判斷
還有一個常見的情形,P2Scene是ProductScene的Child Scene,很多時候我們希望進入P2Scene的時候,導覽列的ProductButton也跟P2Button一樣處於Highlight且不可點的狀態。但像回首頁用的IndexButton就不能這樣做,因為所有的Scene都是IndexScene的Child Scene,這樣會讓IndexButton永遠不能點。這個參數我們讓它預設是false,而IndexButton再去設為true就可以了。

綜合以上幾點,我們就直接把CastNaviButton的原始碼貼出來:
package myproject.index{
    import flash.events.MouseEvent;
    
    import jp.progression.casts.CastButton;
    import jp.progression.casts.CastButtonState;
    import jp.progression.casts.getInstancesByGroup;
    import jp.progression.events.CastMouseEvent;
    import jp.progression.commands.tweens.DoTweenFrame;
    
    /**
     * ...
     * @author Gray Liao
     */
    public class CastNaviButton extends CastButton
    {
        private var _enableAtChildScene:Boolean = false;
        public function get enableAtChildScene():Boolean { return _enableAtChildScene; }
        public function set enableAtChildScene( value:Boolean ):void { _enableAtChildScene = value; }
  
        public function CastNaviButton(initObject:Object = null){
            super(initObject);
             
            addEventListener(CastMouseEvent.CAST_STATE_CHANGE, _stateChange);
            addEventListener(MouseEvent.CLICK, _click);
            stop();
        }
        
        override protected function atCastRollOver():void
        {
            if(group != null)
            {
                gotoOverByGroup(group);
            }else{
                onGroupOverHandler();
            }
        }
        
        override protected function atCastRollOut():void
        {
            if(group != null)
            {
                gotoUpByGroup(group);
            }else{
                onGroupOutHandler();
            }
        }

        protected function gotoOverByGroup(group:String):void
        {
            var obj:CastNaviButton;
            var groupObj:* = getInstancesByGroup(group);
            for each (obj in groupObj){
                obj.onGroupOverHandler();
            }
        }
  
        protected function gotoUpByGroup(group:String):void
        {
            var obj:CastNaviButton;
            var groupObj:* = getInstancesByGroup(group);
            for each (obj in groupObj){
                obj.onGroupOutHandler();
            }
        }
  
        public function onGroupOverHandler():void
        {
            gotoAndStop("stop");
        }
  
        public function onGroupOutHandler():void
        {
            gotoAndStop("in");
        }
  
        private function _stateChange(e:CastMouseEvent):void
        {
            atStateChange();
            if (_onStateChange != null)
            {
                _onStateChange();
            }
        }
  
        protected function atStateChange():void
        {
            switch(state)
            {
                case CastButtonState.CHILD:
                    if (_enableAtChildScene)
                    {
                        buttonEnable();
                    }else {
                        buttonDisable();
                    }
                break;
                case CastButtonState.CURRENT:
                    buttonDisable();
                break;
                default:
                    buttonEnable();
                break;
            }
        }
  
        public function get onStateChange():Function { return _onStateChange; }
        public function set onStateChange( value:Function ):void { _onStateChange = value; }
        private var _onStateChange:Function;
        
        private function _click(e:MouseEvent):void
        {
            atCastClick();
            if (_onCastClick != null)
            {
                _onCastClick();
            }
        }
        
        protected function atCastClick():void
        {
            buttonDisable();
        }
  
        public function get onCastClick():Function { return _onCastClick; }
        public function set onCastClick( value:Function ):void { _onCastClick = value; }
        private var _onCastClick:Function;
        
        protected function buttonEnable():void
        {
            mouseEventEnabled = true;
            useHandCursor = true;
            gotoAndStop("in");
        }
        
        protected function buttonDisable():void
        {
            mouseEventEnabled = false;
            useHandCursor = false;
            gotoAndStop("stop");
        }
    }
}
程式碼很單純實現上面幾個問題的解法,就不多做解釋了。

修改Navi
在Navi的建構式中,在設定Button的迴圈跑完後,多一行設定:
index_mc.enableAtChildScene = true;
而在setButton()這個function裡面把設定onCastRollOver與onCastRollOut的部分拿掉,因為在CastNaviButton裡面我們已經複寫atCastRollOver與atCastRollOut了。

測試與下載
最後來看看結果:請按此
下載範例:請按此

到此為止,應該一般網頁架構的問題都解決了,剩下一些小問題,例如:
1.現在元件都是在加入舞台時去計算該在的位置,但最好加上Satge Resize的處理,這部分因為在progression裡面stage很好抓,要加偵聽不難,況且可能大部份人都有對位的類別或組件了,就不花篇幅去介紹。
2.有些選單在首頁時,"回首頁"的Button是不在的,等到進入內頁時才出現。但這其實隨著場景去控制Navi的兩種狀態就可以了,其狀況也是沒辦法都涵蓋的了,所以就先留著了。
所以這一系列的文章就告一段落了。Progression其實還有很多有趣的東西,例如Effect,各種特殊的Cast,還有Resource功能,甚至可以在背景下載其他內容。這些等有機會再來介紹啦。

上一篇文章:Progression 4 歡樂開發Flash網站 ( 4 ) — 分割各場景元件庫

9 則留言:

  1. 你寫得好詳細!! 推推推

    回覆刪除
  2. 因為每個專案花在這些工作上的時間越少越好,所以一次整理清楚,之後就可以專心在比較有趣的程式碼上面啦

    回覆刪除
  3. 從安裝、新增專案到實際範例,一系列的文章閱讀完對Progression 也有了基本的概念,非常感謝 : )

    回覆刪除
  4. 看完有種醍醐灌頂的爽快,太感謝啦!!~~(膜拜~

    回覆刪除
  5. 這篇好讚!感謝gray分享~~~~~

    回覆刪除
  6. 好文,不推不可。按個讚!

    回覆刪除
  7. 感覺progression來做網站反而複雜了。

    回覆刪除
  8. To 迷茫feel:
    我覺得是現在網站的需求比以前複雜多了
    如果網站沒有很多進退場順序需要安排,不需要串swfAddress,那麼當然就不需要拿progression來用囉

    回覆刪除