事件偵聽程式

Flash Player 9 以及更新的版本,Adobe AIR 1.0 以及更新的版本

事件偵聽程式 (也稱為事件處理常式) 是 Flash Player 和 AIR 為回應特定事件而執行的函數。新增事件偵聽程式需要兩個步驟。首先,您必須建立函數或類別方法,讓 Flash Player 或 AIR 可用來回應事件。這有時又稱為偵聽程式函數或事件處理常式函數。接著,您必須使用 addEventListener() 方法註冊偵聽程式函數,並設定該事件的目標或位於適當事件流程中的顯示清單物件。

建立偵聽程式函數

ActionScript 3.0 事件模型與 DOM 事件模型不同的其中一個部分,就是在建立偵聽程式函數這方面。在 DOM 事件模型中,事件偵聽程式與偵聽程式函數之間有著清楚的區別:事件偵聽程式是會實作 EventListener 介面之類別的實體,而偵聽程式函數則是名為 handleEvent() 之類別的方法。在 DOM 事件模型中,您註冊的是包含偵聽程式函數的類別實體,而非實際的偵聽程式函數。

在 ActionScript 3.0 事件模型中,事件偵聽程式與偵聽程式函數之間並沒有清楚的區別。ActionScript 3.0 並沒有 EventListener 介面,因此偵聽程式函數可以在類別之外定義,或是定義為類別的一部分。此外,偵聽程式函數不一定要名為 handleEvent(),而是能以任何有效的識別項來命名。在 ActionScript 3.0 中,您註冊的是實際偵聽程式函數的名稱。

在類別之外定義的偵聽程式函數

下列程式碼會建立簡單的 SWF 檔,並顯示紅色正方形。名為 clickHandler() 的偵聽程式函數 (不屬於類別的一部分) 會偵聽紅色正方形上的滑鼠按一下事件。

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, clickHandler); 
    } 
} 
 
function clickHandler(event:MouseEvent):void 
{ 
    trace("clickHandler detected an event of type: " + event.type); 
    trace("the this keyword refers to: " + this); 
}

當使用者按一下該正方形,並與產生的 SWF 檔進行互動時,Flash Player 或 AIR 便會產生下列追蹤輸出:

clickHandler detected an event of type: click 
the this keyword refers to: [object global]

請注意,該事件物件會當做引數傳遞給 clickHandler()。這可讓您的偵聽程式函數檢查該事件物件。在此範例中,您會使用該事件物件的 type 屬性,查明該事件為 click 事件。

此範例也會檢查 this 關鍵字的值。在此範例中,this 代表全域物件,這是可以理解的,因為這個函數是在任何自訂類別或物件之外所定義。

定義為類別方法的偵聽程式函數

下列範例與定義 ClickExample 類別的上一個範例完全相同,但是 clickHandler() 函數卻定義為 ChildSprite 類別的方法:

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, clickHandler); 
    } 
    private function clickHandler(event:MouseEvent):void 
    { 
        trace("clickHandler detected an event of type: " + event.type); 
        trace("the this keyword refers to: " + this); 
    } 
}

當使用者按一下該紅色正方形,並與產生的 SWF 檔進行互動時,Flash Player 或 AIR 便會產生下列追蹤輸出:

clickHandler detected an event of type: click 
the this keyword refers to: [object ChildSprite]

請注意,this 會參考名為 child 的 ChildSprite 實體。這是與 ActionScript 2.0 不同的行為變更。如果您使用 ActionScript 2.0 中的組件,就可能還記得,當類別方法傳遞至 UIEventDispatcher.addEventListener() 中時,該方法的範圍就會限定為廣播該事件的組件,而非在其中定義偵聽程式方法的類別。換句話說,如果您使用這項 ActionScript 2.0 的技巧,this 關鍵字就會參考廣播該事件的組件,而非 ChildSprite 實體。

對某些程式設計人員而言,這是個重大的問題,因為這表示他們無法存取該類別 (包含偵聽程式方法) 的其它方法和屬性。為瞭解決此問題,ActionScript 2.0 的程式設計人員會使用 mx.util.Delegate 類別,變更偵聽程式方法的範圍。不過,因為 ActionScript 3.0 會在呼叫 addEventListener() 方法時建立繫結方法,所以便不再需要上述作法。結果,this 關鍵字便會參考名為 child 的 ChildSprite 實體,如此程式設計人員便能存取 ChildSprite 類別的其它方法和屬性。

不應該使用的事件偵聽程式

此外,還有第三種技巧,您可以使用這項技巧建立一般物件,而且該物件的屬性會指向以動態方式指派的偵聽程式函數,但是這項技巧並不建議使用。我們在此討論它的原因是因為 ActionScript 2.0 常常會用到它,但是在 ActionScript 3.0 請勿使用它。不建議使用這個技巧的原因,是因為 this 關鍵字會參考全域物件而不是您的偵聽程式物件。

下列範例與上一個 ClickExample 類別範例完全相同,但是偵聽程式函數卻定義為名為 myListenerObj 之一般物件的一部分:

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, myListenerObj.clickHandler); 
    } 
} 
 
var myListenerObj:Object = new Object(); 
myListenerObj.clickHandler = function (event:MouseEvent):void 
{ 
        trace("clickHandler detected an event of type: " + event.type); 
        trace("the this keyword refers to: " + this); 
}

追蹤的結果,看起來如下所示:

clickHandler detected an event of type: click 
the this keyword refers to: [object global]

您期望 this 會參考 myListenerObj,並且追蹤輸出會是 [object Object],但是它卻改為參考全域物件。當您將動態屬性名稱當做引數傳遞給 addEventListener() 時,Flash Player 或 AIR 便無法建立繫結方法。這是因為您傳遞做為 listener 參數的只不過是偵聽程式函數的記憶體位址,而且 Flash Player 和 AIR 無法將這個記憶體位址與 myListenerObj 實體加以連結。

管理事件偵聽程式

您可以使用 IEventDispatcher 介面的方法,管理您的偵聽程式函數。IEventDispatcher 介面是 DOM 事件模型之 EventTarget 介面的 ActionScript 3.0 版本。雖然 IEventDispatcher 這個名稱看起來暗指它的主要目的是要發送 (或傳送) 事件物件,但是此類別的方法卻更常用於註冊、檢查及移除事件偵聽程式。IEventDispatcher 介面會定義五個方法,如下列程式碼中所示:

package flash.events 
{ 
    public interface IEventDispatcher 
    { 
        function addEventListener(eventName:String,  
                        listener:Object, 
                        useCapture:Boolean=false, 
                        priority:Integer=0, 
                        useWeakReference:Boolean=false):Boolean; 
 
        function removeEventListener(eventName:String,  
                    listener:Object, 
                    useCapture:Boolean=false):Boolean; 
 
        function dispatchEvent(eventObject:Event):Boolean; 
 
        function hasEventListener(eventName:String):Boolean; 
        function willTrigger(eventName:String):Boolean; 
    } 
}

Flash Player API 會使用 EventDispatcher 類別實作 IEventDispatcher 介面,這個類別是做為所有可以是事件目標或事件流程之一部分類別的基底類別來使用。例如,DisplayObject 類別便是繼承自 EventDispatcher 類別。這表示顯示清單中的任何物件都可以存取 IEventDispatcher 介面的方法。

新增事件偵聽程式

addEventListener() 對 IEventDispatcher 介面而言是非常有用的方法,您可以使用這個方法來註冊偵聽程式函數。使用這個方法需要搭配 type 和 listener 參數。type 參數是用來指定事件的類型,而 listener 參數則是用來指定當事件發生時,將會執行的偵聽程式函數。listener 參數可以是某個函數或類別方法的參考。

當您指定 listener 參數時,請勿使用括號。例如,在下列對 addEventListener() 方法的呼叫中,指定 clickHandler() 函數時是不使用括號的:
addEventListener(MouseEvent.CLICK, clickHandler)

addEventListener() 方法的 useCapture 參數可讓您控制偵聽程式將在其中成為使用中狀態的事件流程階段。如果將 useCapture 設定為 true,您的偵聽程式在事件流程的捕捉階段期間將會成為使用中狀態。如果將 useCapture 設定為 false,您的偵聽程式在事件流程的目標與反昇階段期間將會成為使用中狀態。若要在事件流程的所有階段都偵聽某個事件,您必須呼叫 addEventListener() 兩次,第一次先將 useCapture 設定為 true,另一次再將 useCapture 設定為 false。

addEventListener() 方法的 priority 參數並不屬於正式的 DOM 第 3 層事件模型的一部分。它包含在 ActionScript 3.0 中,以便讓您在組織事件偵聽程式時能更具有彈性。在呼叫 addEventListener() 時,您便可以傳遞整數值做為 priority 參數,藉以設定該事件偵聽程式的優先順序。預設值為 0,但是您可以將這個值設為負整數或正整數。數字愈高代表會愈快執行該事件偵聽程式。具有相同優先順序的事件偵聽程式則會依照它們加入時的順序來執行,所以愈早加入的偵聽程式就會愈早執行。

useWeakReference 參數可讓您指定偵聽程式函數的參考為弱參考或是一般參考。將此參數設定為 true 可讓您避免偵聽程式函數一直存在於記憶體中 (即使不再需要這些函數也一樣) 的情況。Flash Player 和 AIR 會使用名為「記憶體回收」的技巧,清理記憶體中不再使用的物件。如果對某個物件的參考不存在,就表示已將該物件視為不再使用。垃圾回收器並不在乎弱參考,這表示具有指向本身之參考的偵聽程式函數也符合垃圾收集的條件。

移除事件偵聽程式

您可以使用 removeEventListener() 方法,移除不再需要的事件偵聽程式。移除不再用到的事件偵聽程式是很好的作法。與 addEventListener() 方法所需的參數一樣,使用上述方法必須搭配 eventName 和 listener 參數。請回想之前的內容,您可以呼叫 addEventListener() 兩次,第一次先將 useCapture 設定為 true,另一次再將它設定為 false,在所有事件階段都偵聽事件。若要移除這兩個事件偵聽程式,您必須呼叫 removeEventListener() 兩次,第一次先將 useCapture 設定為 true,另一次再將它設定為 false。

傳送事件

進階程式設計人員可以使用 dispatchEvent() 方法,將自訂事件物件傳送到事件流程中。此方法唯一會採用的參數,就是事件物件的參考,這個參考必須是 Event 類別的實體或是子類別。一旦傳送之後,此事件物件的 target 屬性會設定為當做 dispatchEvent() 所呼叫之目標的物件。

檢查現有的事件偵聽程式

IEventDispatcher 介面的最後兩個方法可提供關於事件偵聽程式存在狀況的有用資訊。如果在某個清單物件上有找到特定事件類型的事件偵聽程式,hasEventListener() 方法就會傳回 true。如果有找到特定顯示清單物件的偵聽程式,willTrigger() 方法也會傳回 true,但是 willTrigger() 不僅會檢查該顯示物件上的偵聽程式,也會檢查事件流程之所有階段的顯示清單物件之全部祖先上的偵聽程式。

不含偵聽程式的錯誤事件

例外 (而非事件) 是 ActionScript 3.0 中錯誤處理的主要機制,但是例外處理並不適用於非同步作業 (例如載入檔案)。如果在這種非同步作業期間發生錯誤,Flash Player 和 AIR 便會傳送錯誤事件物件。如果您沒有為此錯誤事件建立偵聽程式,Flash Player 和 AIR 的除錯程式版本便會顯示包含該項錯誤資訊的對話方塊。例如,Flash Player 的除錯程式版本會產生下列對話方塊,描述當應用程式嘗試從無效的 URL 載入檔案時,所發生的錯誤:

大多數錯誤事件都是以 ErrorEvent 類別為基礎,而且都具有名為 text 的屬性,它會用來儲存 Flash Player 或 AIR 顯示的錯誤訊息。但是,StatusEvent 和 NetStatusEvent 類別則是例外。這兩個類別都有 level 屬性 (StatusEvent.level 和 NetStatusEvent.info.level)。當 level 屬性的值為「error」時,就會將這些事件類型視為錯誤事件。

錯誤事件並不會導致 SWF 檔停止執行,它會在瀏覽器外掛程式和獨立播放程式的除錯程式版本中顯示為對話方塊、在編寫播放程式的輸出面板中顯示為訊息,而在 Adobe Flash Builder 的記錄檔則顯示為其中的一個項目。而在 Flash Player 或 AIR 的發行版本中,它則完全不會顯示。