HOMERSS 2.0RSS comments
sizeSMALLMEDIUMLARGEXTRA LARGE
home
time
17 / Tue
04 22 08 14 47
title AS3筆記-事件流(event flow)
title
言
類
論
別

Please enable Javascript and Flash to view this Flash video.
MouseEvent’s bubbles = true;

OOP (object oriented programming) 程式概念, 主要是以物件為主,每一個物件就像導管一樣,由一些變數(輸入)來得到想要的答案(輸出),所以每一個物件只管需要什麼變數近來,處理後就傳給另一個物件,這個溝通是應該雙向性的,可是兩個方向的做法不同,基本上最基層的物件 somehow可以跟自己以上的物件溝通,像是在 A 中 new 了 B, A 要呼叫 B 裡面的 method,只要在 A 裡用 B.method()即可,通常在 B 裡面只做自己的事,不用管A要幹嘛,所以當B要跟A溝通的時候,就需要用Event了.

以人體來當作是OOP的比喻, 如果 B 是胃,他只管消化食物, 而不用管整體 A(人),看到什麼而想要吃什麼,所以當 A 吃了什麼,B 只管消化,B 的 input 是咀嚼過的食物, output 就是消化過的食物,基本上這樣運作就好,不用管太多, 至於 B 的消化(private function),要怎麼運作,A也不用知道太多, 甚至 A 搞不好根本不知道 B, 總之 B 是整個消化系統 C 的一部份, A只要控制 C 力面那張嘴就好了, 可是今天如果當 B 餓了,或痛了,必須要發出一個訊號,讓 A 知道,之後 A 可以去吃飯或吃藥, 這個訊號就是B 要發出的 Event, 而 A 要有一條神經去聽 B 的 Event (Listener). 這樣一來 B 物件就可以使用在類似的生物上像是豬的胃,牛的胃,還是貓的胃等等.

另外一例, button 只管自己有沒有被按, 而不用管按了要幹麻, 所以就可以做出很多 project 可以共用的按鈕, 可是如果整個程式不曉得按鈕被按了, 那這按鈕也就失去了按鈕基本的功能, 這時候 Event, 就搭起了串聯小物件與大系統之間友誼的橋樑.

以下是我個人的測試心得,如果有錯,歡迎糾正,教學相長,謝謝!

基本的 Event 結構分作發布端(dispatch),與接收端(listener),一般而言, O.addEventListener 的O,都是要加在發生事件 (dispatchEvent) 的那個物件,中間的”事件”本身則是包在 Event Object 裡面傳遞,用 “target” reference 到整個 O 裡面的 public properties. Flash內建很多基本的Event,像是滑鼠跟鍵盤的UI事件等等,這些事件只要加上了listener就可以被接收到並交給指定的 method 去反應.

不過基本的事件系統,用在介面上, 會造成很多傳遞的繁瑣工作, 像是, 按鈕被按, 傳給 menu, 然後 menu 再傳給主程式, 主程式在做內容的變化, 如果用純 OOP 的方式寫多層或複雜的 menu, 那麼市件傳遞會變成很長的一個接力賽,光 addEventListener + dispatchEvent 就會搞死人.

http://www.adobe.com/devnet/actionscript/articles/event_handling_as3_03.html

AS3 event handling
Event propagation(必看)

根據我查詢的結果,AS3 內定的 Event 並不是每一種都有個 Flow, Flow分三個部分 Capturin, At Target and Bubbling,在物件流過的地方加上Listener, 就可以接收到事件的訊息而做出反應,不過為什麼要這樣呢? 說穿了也是為了 OOP 裡面 code 的分配,這樣一來 每個 class 的 method 可以分的很清楚, 而不會因為為了某個 Scope 的變數而散落各處.

Capturing Phase

在內定的件流的處理中, 是從 At Target -> Bubbling 這段是關閉的, 在 addEventListener 可以使用第三個變數 addEventListener(“type”,function,true);, 去開啟Capturing 的那段逆向 Event flow 順序, 不過據說useCapture 會導致 computationally intensive,能不用就不要用為妙.

At Target

傳遞到物件本身的階段叫做 Target Phase, 一般的 Event (new Event(“type”); 第二個變數沒有去設成 true ) 就只有這個階段,此時 Listener 只能加在 O.dispatchEvent(new Event(“type”)); 的 O 上, 才可以接收到事件.

Bubbling Phase

因此有一種 Event 叫做 bubble phase, 在 new Event(“type”, true); 可以設定, 內定是 false. 用在 DisplayObject 裡面相當方便.觸發的事件會變成 Event flow 會像泡泡一樣,一層層的傳到泡泡表面(Stage), 這樣一來就不需要一層層的傳遞. Mouse events and keyboard events 就屬於這種.(你可以去看他們constructor裡面的 bubbles 內定值是 true)

處理事件流可以想成人體,例如身體在偵測有沒有被捏,上半身被捏,手被捏,前臂被捏等等,也就是你可以從stage偵測(MouseEvent.CLICK)到某個按鈕被按(我被捏),可是卻無法知道是那一個按鈕被按(身體哪裡被捏),除非你去分析Event裡面的資訊,要不就是聽MouseEvent,把他轉換成另一個 Event 再 dispatch 出去.
三個階段都可以用 currentTarget 去看 event 流到哪? 然後用 currentTarget 看事件流到哪一個物件, 用 target 看是哪裡發出來的事件.可以利用這些去決定是什麼東西被按了,然後作出反應,跟以前單純的 onPress 的方式用法相當不同,也跟 2.0 要自己包裝傳遞的 event object,更加統一完整.

範例適用 MouseEvent.CLICK which default bubbles=true.

CODE: event bubble fla

//count
var count:Number = 0;
//useCapture = true
parent1.addEventListener(MouseEvent.MOUSE_DOWN,parent1Event,true);
parent1.child.addEventListener(MouseEvent.MOUSE_DOWN,child1Event,true);
parent1.child.btn.addEventListener(MouseEvent.MOUSE_DOWN,btn1Event,true);
parent1.addEventListener(MouseEvent.MOUSE_UP,rout1,true);
parent1.child.addEventListener(MouseEvent.MOUSE_UP,rout1,true);
parent1.child.btn.addEventListener(MouseEvent.MOUSE_UP,rout1,true);
function parent1Event(e:Event) {
count++;
output.appendText(count+" parent1:"+e.target.name+"|current:"+e.currentTarget.name+"\n");
parent1.gotoAndStop(2);
}
function child1Event(e:Event) {
count++;
output.appendText(count+" child1:"+e.target.name+"|current:"+e.currentTarget.name+"\n");
parent1.child.gotoAndStop(2);
}
function btn1Event(e:Event) {
count++;
output.appendText(count+" btn1:"+e.target.name+"|current:"+e.currentTarget.name+"\n");
parent1.child.btn.gotoAndStop(2);
}
//useCapture = false
parent2.addEventListener(MouseEvent.MOUSE_DOWN,parent2Event);
parent2.child.addEventListener(MouseEvent.MOUSE_DOWN,child2Event);
parent2.child.btn.addEventListener(MouseEvent.MOUSE_DOWN,btn2Event);
parent2.addEventListener(MouseEvent.MOUSE_UP,rout2);
parent2.child.addEventListener(MouseEvent.MOUSE_UP,rout2);
parent2.child.btn.addEventListener(MouseEvent.MOUSE_UP,rout2);
function parent2Event(e:Event) {
count++;
output.appendText(count+" parent2:"+e.target.name+"|current:"+e.currentTarget.name+"\n");
parent2.gotoAndStop(2);
}
function child2Event(e:Event) {
count++;
output.appendText(count+" child2:"+e.target.name+"|current:"+e.currentTarget.name+"\n");
parent2.child.gotoAndStop(2);
}
function btn2Event(e:Event) {
count++;
output.appendText(count+" btn2:"+e.target.name+"|current:"+e.currentTarget.name+"\n");
parent2.child.btn.gotoAndStop(2);
}
//mouse up
function rout1(e:Event) {
count=0;
parent1.gotoAndStop(1);
parent1.child.gotoAndStop(1);
parent1.child.btn.gotoAndStop(1);
}
function rout2(e:Event) {
count=0;
parent2.gotoAndStop(1);
parent2.child.gotoAndStop(1);
parent2.child.btn.gotoAndStop(1);
}

一般而言, 並不會這樣用,這個範例是在測試event flow的優先順序,useCapture:Boolean 的 default 值是 false,設定成 true,跟 false,只有順序不同,發佈物件以下的 displayObject 都可以抓到 dispatch 的event. 非 displayObject經過測試eventFlow Test.zip 發現整個 Event Flow 根本沒有運作,所以 Listener只能乖乖的加在發佈端,才可以被聽到.

“Capturing and bubbling happen as the Event object moves from node to node in the display list: parent-to-child for capturing and child-to-parent for bubbling. This process has nothing to do with the inheritance hierarchy. Only DisplayObject objects (visual objects such as containers and controls) can have a capturing phase and a bubbling phase in addition to the targeting phase.” <link>

心得:

0. 只有 DiplayObject (inherit from InteractiveObject?) 會有事件流,這也難怪,因為其實最複雜的 heirarchical structure,就是在 UI 介面了,其他的像是XML的讀取事件,或是Video的串流事件,根本不太需要用到 Event Flow,大概一個層級就可以解決了.

1. 如果事件發生端以下全部的物件都屬於 displayObject的話, useCapture(Listener) 設成 true,或是 bubbles(Event) 設成 true ,在事件發生以下的物件(以一層層疊來看),都是可以聽的到的, 只是執行順序不同而已.

2. 發生事件的本身應屬於 useCapture = false 的範圍(也就是包括在 bubble phase 裡), 所以當 useCapture= true的時候會聽不到自己.

3. 有些內定 bubbles= ture 的 Event(像是MouseEvent), 在發佈端 dispatchEvent(new Event(“dispatch”,false)); 把 bubble 的值設改成 false 的話,會阻斷 bubble 的回流過程,也就是如果 useCapture=false(內定) 的話,Listener會聽不到東西.

Custom Event

have to be in DisplayObject

* = default of

MouseEvent /

KeyBoardEvent

1

addEventListener
(“type”,func, useCapture))
O.dispatchEvent
(new Event (“type“,bubbles))
true true* o
false o
false true* o
false x

2

O.addEventListener
(“type”,func, useCapture))
O.dispatchEvent
(new Event (“type“,bubbles))
true true* x
false o
false true* o
false o

附:

觸發 MouseEvent.CLICK是你手指頭放掉按鈕的時候(同時使用 MOUSE_UP , MOUSE_UP會沒用), MouseEvent.MOUSE_DOWN(UP),則可以偵測 按下去跟彈起來~

Event 造成的 Garbage Collection (GC) 的問題:

Note:Flash Player 9 採用 Mark Sweeping 去清除已經無法使用到的 memory, 可是並不會馬上移除. 要注意當要殺掉物件的時候要全部相關的 reference 都要殺,尤其是listener.


  • 恩... 1 Stars喔~ 2 Stars好! 3 Stars酷!! 4 Stars哇!!! 5 Stars (7 票, 平均: 4.29 / 5) << 請踴躍投票


    Facebook comments:

    11 Messages to “AS3筆記-事件流(event flow)”


    1. ryan
      說:

      幹, 好難


    2. wei
      說:

      wa-ka 好難


    3. Marc
      說:

      基本的抓到就不難了, useCapture 是在抓 capturing phase 的,而 bubbles 是在開啟 bubbling phase 的.


    4. Aluan
      說:

      It’s getting really clear now. Good article.


    5. Marc
      說:

      thanks for your help.


    6. paul
      說:

      簡單的說, event flow 流程分三各部份,eventPhase順序為
      capturing(1) –> targeting(2) –> bubbling(3)

      capturing打開後, bubbles效果就會自動消失

      capturing : 從上往下找Listener,有的話則調用
      targeting:調用目標本身Listener
      bubbling:從下往上找Listener,有的話則調用

      用下列語法簡單測試下, bubbles 只有change,click,doubleClick,keyDown,deyUp,mouseDown,mouseUp
      才會是true

      下列語法可以隨時在任何一各Listener 加上capturing,輸出後可以看到trace的順序了, 從而判斷當按了一各object後, 到底event flow誰先誰後

      package{
          
          import flash.display.*;
          import flash.events.*;
          
          public class Main extends Sprite{
              
              private var canvas_1:Sprite = new Sprite();
              private var canvas_2:Sprite = new Sprite();
              private var btn_1:Sprite = new Sprite();
              
              public function Main(){
                  btn_1.graphics.beginFill(0×000000);
                  btn_1.graphics.drawRect(100,100,200,200);
                  btn_1.graphics.endFill();
                  
                  btn_1.name = "btn_1"
                  canvas_1.name = "canvas_1"
                  canvas_2.name = "canvas_2"
                  
                  btn_1.addEventListener(MouseEvent.CLICK,pressBtn);
                  canvas_1.addEventListener(MouseEvent.CLICK,pressBtn);
                  canvas_2.addEventListener(MouseEvent.CLICK,pressBtn);
                  
                  canvas_2.addChild(btn_1);
                  canvas_1.addChild(canvas_2);
                  addChild(canvas_1);
              }
              
              private function pressBtn(event:MouseEvent):void{
                  trace("bubbles: " + event.bubbles);
                  trace("target: " + event.target.name + ":" + event.eventPhase);
                  trace("currentTarget: " + event.currentTarget.name);
              }
          }
      }


    7. Marc
      說:

      "capturing打開後, bubbles效果就會自動消失",我的感覺是當你打開capturing, 意思是去抓 capturing 那一段, 跟 At target, bubbling 有沒有消失無關,我是覺得沒有消失啦! 只是他抓的是capturing那段.
      在Adobe官網上有提到用 capturing 會耗掉CPU的運算,我的感覺是在執行capturing的時候,flash runtime試用類似 for loop 去找所有存在的事件陣列,所以會耗資源.

    8. Pingback by
      Jing FLOW — 我流,靖流。 » Blog Archive » ROLL OUT? I just CLICK!
      說:

      [...] 還有絲路的討論串也可以看看喔; [...]


    9. Po-Shiun Huang
      說:

      感謝分享,講得很清楚,
      但,要完全搞清楚還真的有點難…


    10. samliu
      說:

      寫的真好, 解決了我的困惑, 感恩~


    11. Marc
      說:

      常來逛逛啊~

    Leave a Message

    :) D: :( :D more »