事件處理範例:鬧鐘

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

「鬧鐘」範例包含可讓使用者指定響鈴時間的時鐘,以及要在該時間顯示的訊息。「鬧鐘」範例是根據 使用日期與時間 的 SimpleClock 應用程式所建置,它會說明在 ActionScript 3.0 中處理事件的數項觀點,其中包括:

  • 偵聽及回應事件

  • 通知事件的偵聽程式

  • 建立自訂事件類型

若要取得此樣本的 Flash Professional 應用程式檔案,請參閱 http://www.adobe.com/go/learn_programmingAS3samples_flash_tw 。若要取得此樣本的 Flex 應用程式檔案,請參閱 http://www.adobe.com/go/as3examples_tw 。您可以在 Samples/AlarmClock 檔案夾中找到「鬧鐘」應用程式檔案。這個應用程式包含下列檔案:

檔案

說明

AlarmClockApp.mxml

AlarmClockApp.fla

主應用程式檔案,在 Flash 中為 FLA,在 Flex 中為 MXML。

com/example/programmingas3/clock/AlarmClock.as

會擴充 SimpleClock 類別的類別,以增加鬧鐘的功能。

com/example/programmingas3/clock/AlarmEvent.as

自訂的事件類別 (flash.events.Event 的子類別),當做 AlarmClock 類別之 alarm 事件的事件物件來使用。

com/example/programmingas3/clock/AnalogClockFace.as

繪製圓形時鐘外觀,並根據時間繪製時針、分針和秒針 (已在 SimpleClock 範例中加以說明)。

com/example/programmingas3/clock/SimpleClock.as

具有簡單計時功能的時鐘介面組件 (已在 SimpleClock 範例中加以說明)。

鬧鐘概觀

本範例中鬧鐘的主要功能 (包含追蹤時間及顯示鬧鐘外觀) 會重複使用 SimpleClock 應用程式程式碼,這已在 日期與時間範例:簡易類比時鐘 中加以說明。AlarmClock 類別會從該範例中,藉由加入鬧鐘所需的功能來擴充 SimpleClock 類別,這些功能包括設定響鈴時間,以及當響鈴「響起」時提供通知。

建立事件的原因,就是為了工作中有事發生時能夠提供通知。AlarmClock class 會公開 Alarm 事件,為了執行所需的動作,其它物件都可以偵聽此事件。此外,AlarmClock 類別還會使用 Timer 類別的實體,以判斷何時要觸發它的響鈴。與 AlarmClock 類別相似,Timer 類別也可以在經過特定時間之後,提供事件來通知其它物件 (在此範例中,則為 AlarmClock 實體)。和大多數 ActionScript 應用程式一樣,事件構成了「鬧鐘」樣本應用程式的一部分重要功能。

觸發響鈴

如先前內容所提及,AlarmClock 類別實際提供的唯一功能會與設定和觸發響鈴有關。內建的 Timer 類別 (flash.utils.Timer) 可提供一種方式,讓開發人員定義在指定的時間量之後執行的程式碼。AlarmClock 類別會使用 Timer 實體,判斷何時要觸發響鈴。

    import flash.events.TimerEvent; 
    import flash.utils.Timer; 
 
    /** 
     * The Timer that will be used for the alarm. 
     */ 
    public var alarmTimer:Timer; 
    ... 
    /** 
     * Instantiates a new AlarmClock of a given size. 
     */ 
    public override function initClock(faceSize:Number = 200):void 
    { 
        super.initClock(faceSize); 
        alarmTimer = new Timer(0, 1); 
        alarmTimer.addEventListener(TimerEvent.TIMER, onAlarm); 
    }

在 AlarmClock 類別中定義的 Timer 實體名為 alarmTimer initClock() 方法 (會執行 AlarmClock 實體所需的設定作業) 搭配 alarmTimer 變數時,會執行兩件事。首先,它會搭配參數 (指示 Timer 實體等待 0 毫秒) 以實體化該變數,並且只觸發其 timer 事件一次。實體化 alarmTimer 之後,程式碼便會呼叫該變數的 addEventListener() 方法,表示它要偵聽該變數的 timer 事件。在經過指定的時間量之後,藉由傳送 Timer 實體的 timer 事件,讓此實體得以運作。AlarmClock 類別必須知道傳送 timer 事件的時間,以便觸發它自己的響鈴。藉由呼叫 addEventListener() ,AlarmClock 程式碼便可以使用 alarmTimer 將本身註冊為偵聽程式。這兩個參數表示 AlarmClock 類別要偵聽 timer 事件 (由 TimerEvent.TIMER 常數所表示),以及當事件發生時,要呼叫 AlarmClock 類別的 onAlarm() 方法來回應該事件。

為了要實際設定響鈴,會呼叫 AlarmClock 類別的 setAlarm() 方法,如下所示:

    /** 
     * Sets the time at which the alarm should go off. 
     * @param hour The hour portion of the alarm time. 
     * @param minutes The minutes portion of the alarm time. 
     * @param message The message to display when the alarm goes off. 
     * @return The time at which the alarm will go off. 
     */ 
    public function setAlarm(hour:Number = 0, minutes:Number = 0, message:String = "Alarm!"):Date 
    { 
        this.alarmMessage = message; 
        var now:Date = new Date(); 
        // Create this time on today's date. 
        alarmTime = new Date(now.fullYear, now.month, now.date, hour, minutes); 
 
        // Determine if the specified time has already passed today. 
        if (alarmTime <= now) 
        { 
            alarmTime.setTime(alarmTime.time + MILLISECONDS_PER_DAY); 
        } 
     
        // Stop the alarm timer if it's currently set. 
        alarmTimer.reset(); 
        // Calculate how many milliseconds should pass before the alarm should 
        // go off (the difference between the alarm time and now) and set that 
        // value as the delay for the alarm timer. 
        alarmTimer.delay = Math.max(1000, alarmTime.time - now.time); 
        alarmTimer.start(); 
     
        return alarmTime; 
    }

這個方法會執行數項工作,其中包含儲存響鈴訊息以及建立 Date 物件 ( alarmTime ) 代表響鈴要響起時的實際時間。與目前論述最有關聯的部分,也就是此方法的最後幾行,會設定並啟動 alarmTimer 變數的計時器。首先,呼叫它的 reset() 方法,如果計時器正在執行的話,這麼做便會將計時器停止並重新設定。接著,再從 alarmTime 變數的值中減去目前的時間 (以 now 變數表示),以判斷在響鈴響起之前還需多少毫秒。由於 Timer 類別不會在絕對時間觸發它的 timer 事件,所以這會是指定給 alarmTimer delay 屬性的這個相對時間差。最後會呼叫 start() 方法,實際啟動計時器。

一旦經過指定的時間量之後, alarmTimer 便會傳送 timer 事件。由於 AlarmClock 類別已經將本身的 onAlarm() 方法註冊為該事件的偵聽程式,所以當 timer 事件發生時,就會呼叫 onAlarm()

    /** 
     * Called when the timer event is dispatched. 
     */ 
    public function onAlarm(event:TimerEvent):void  
    { 
        trace("Alarm!"); 
        var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage); 
        this.dispatchEvent(alarm); 
    }

註冊為事件偵聽程式的方法必須以適當的記號 (亦即此方法的參數集與傳回類型) 來定義。若要成為 Timer 類別之 timer 事件的偵聽程式,方法必須定義一個參數,而且此參數的資料類型必須是 TimerEvent (flash.events.TimerEvent),它是 Event 類別的子類別。當 Timer 實體呼叫本身的事件偵聽程式時,它會傳遞 TimerEvent 實體做為事件物件。

通知其它偵聽程式有關此響鈴事件

與 Timer 類別相似,AlarmClock 類別也可以在響鈴響起時,提供可允許其它程式碼接收通知的事件。對於要使用內建於 ActionScript 中之事件處理架構的類別而言,此類別必須實作 flash.events.IEventDispatcher 介面。最常見的作法是藉由擴充 flash.events.EventDispatcher 類別的方式來完成上述實作,這種方式能提供 IEventDispatcher 的標準實作 (或是藉由擴充 EventDispatcher 的其中一項子類別來完成)。如先前所述,AlarmClock 類別延伸出 SimpleClock 類別,而後者又 (透過一連串繼承) 延伸出 EventDispatcher 類別。這些全部都表示 AlarmClock 類別已經有內建的功能來提供它自己的事件。

藉由呼叫 AlarmClock 從 EventDispatcher 繼承的 addEventListener() 方法,其它程式碼便可以註冊以接收 AlarmClock 類別之 alarm 事件通知。當 AlarmClock 實體已準備好通知其它程式碼該實體的 alarm 已經引發時,就會藉由呼叫 dispatchEvent() 方法來完成,這個方法也是繼承自 EventDispatcher。

        var alarm:AlarmEvent = new AlarmEvent(this.alarmMessage); 
        this.dispatchEvent(alarm);

下列程式碼是取自 AlarmClock 類別的 onAlarm() 方法 (已顯示在它之前的整個程式碼中)。呼叫 AlarmClock 實體的 dispatchEvent() 方法,這樣接著便會通知所有已觸發 AlarmClock 實體之 alarm 事件的已註冊偵聽程式。傳遞給 dispatchEvent() 的參數,就是將一併傳遞給偵聽程式方法的事件物件。在此範例中,它是 AlarmEvent 類別的實體,此類別是特別建立供這個範例使用的 Event 子類別。

提供自訂的 alarm 事件

所有事件偵聽程式都會接收事件物件參數 (包含遭觸發之特定事件的相關資訊)。在許多情況下,這個事件物件都會是 Event 類別的實體。然而,在某些情況下,提供事件偵聽程式額外的資訊會很有用。通常若要達到這個目的,您就必須定義新的類別 (Event 類別的子類別),並將此新類別的實體當做事件物件來使用。在此範例中,當傳送 AlarmClock 類別的 alarm 事件時,AlarmEvent 實體便會當做事件物件來使用。這裡所顯示的 AlarmEvent 類別會提供關於 alarm 事件的額外資訊,特別是響鈴訊息:

    import flash.events.Event; 
     
    /** 
     * This custom Event class adds a message property to a basic Event. 
     */ 
    public class AlarmEvent extends Event  
    { 
        /** 
         * The name of the new AlarmEvent type. 
         */ 
        public static const ALARM:String = "alarm"; 
         
        /** 
         * A text message that can be passed to an event handler 
         * with this event object. 
         */ 
        public var message:String; 
         
        /** 
         *Constructor. 
         *@param message The text to display when the alarm goes off. 
         */ 
        public function AlarmEvent(message:String = "ALARM!") 
        { 
            super(ALARM); 
            this.message = message; 
        } 
        ... 
    }

建立自訂事件物件類別的最佳方式,就是定義會擴充 Event 類別的類別,如先前的範例所示。為了補充所繼承的功能,AlarmEvent 類別會定義 message 屬性,此屬性包含了與該事件有關聯的響鈴訊息文字; message 值則會當做 AlarmEvent 建構函式中的參數來傳入。AlarmEvent 也會定義 ALARM 常數,此常數可以用來在呼叫 AlarmClock 類別的 addEventListener() 方法時,參考特定的事件 ( alarm )。

除了加入自訂的功能外,每一個 Event 子類別都必須覆寫所繼承的 clone() 方法,做為 ActionScript 事件處理架構的一部分。Event 子類別也可以選擇性地覆寫所繼承的 toString() 方法,以便在呼叫 toString() 方法時,將自訂事件的屬性包含在傳回的值中。

    /** 
     * Creates and returns a copy of the current instance. 
     * @return A copy of the current instance. 
     */ 
    public override function clone():Event 
    { 
        return new AlarmEvent(message); 
    } 
     
    /** 
     * Returns a String containing all the properties of the current 
     * instance. 
     * @return A string representation of the current instance. 
     */ 
    public override function toString():String 
    { 
        return formatToString("AlarmEvent", "type", "bubbles", "cancelable", "eventPhase", "message"); 
    }

遭到覆寫的 clone() 方法必須傳回自訂 Event 子類別的新實體,並設定所有的自訂屬性以符合目前的實體。在遭到覆寫的 toString() 方法中,會使用公用程式方法 formatToString() (繼承自 Event) 以提供具有自訂類型名稱的字串,以及所有其屬性的名稱與值。