Ejemplo de gestión de eventos: Reloj con alarma

Flash Player 9 y posterior, Adobe AIR 1.0 y posterior

El ejemplo del reloj con alarma consiste en un reloj que permite al usuario especificar una hora a la que sonará una alarma y se mostrará un mensaje. El ejemplo del reloj con alarma se basa en la aplicación SimpleClock de Trabajo con fechas y horas El ejemplo ilustra diversos aspectos de la utilización de eventos en ActionScript 3.0 como, por ejemplo:

  • Detección y respuesta a un evento

  • Notificación a los detectores de un evento

  • Creación de un tipo de evento personalizado

Para obtener los archivos de aplicación de Flash Professional para esta muestra, consulte http://www.adobe.com/go/learn_programmingAS3samples_flash_es. Para obtener los archivos de aplicación de Flex para esta muestra, consulte http://www.adobe.com/go/as3examples_es. Los archivos de la aplicación del reloj con alarma se encuentran en la carpeta Samples/AlarmClock. La aplicación consta de los siguientes archivos:

Archivo

Descripción

AlarmClockApp.mxml

o

AlarmClockApp.fla

El archivo de aplicación principal en Flash (FLA) o Flex (MXML).

com/example/programmingas3/clock/AlarmClock.as

Una clase que amplía la clase SimpleClock y añade la función de alarma al reloj.

com/example/programmingas3/clock/AlarmEvent.as

Una clase de eventos personalizada (una subclase de flash.events.Event) que actúa como el objeto de evento del evento alarm de la clase AlarmClock.

com/example/programmingas3/clock/AnalogClockFace.as

Dibuja una esfera de reloj redonda y las manecillas de hora, minutos y segundos, en función de la hora (descrita en el ejemplo SimpleClock).

com/example/programmingas3/clock/SimpleClock.as

Un componente de la interfaz de reloj con funciones sencillas de control de tiempo (descrito en el ejemplo de SimpleClock).

Información general sobre el reloj con alarma

Para la funcionalidad principal del reloj de este ejemplo, incluido el control del tiempo y la visualización de la esfera del reloj, se vuelve a utilizar el código de la aplicación SimpleClock, que se describe en Ejemplo de fecha y hora: Un sencillo reloj analógico. La clase AlarmClock amplía la clase SimpleClock del ejemplo añadiendo la funcionalidad necesaria para un reloj con alarma, incluido el ajuste de la hora de la alarma y la notificación cuando suena la alarma.

Los eventos están diseñados para proporcionar notificaciones cuando ocurre algo. La clase AlarmClock expone el evento Alarm, sobre el que los demás objetos pueden realizar detecciones a fin de llevar a cabo las acciones deseadas. Además, la clase AlarmClock usa una instancia de la clase Timer para determinar cuándo hay que activar la alarma. Al igual que la clase AlarmClock, la clase Timer proporciona un evento para notificar a los demás objetos (una instancia de AlarmClock en este caso) cuándo ha transcurrido una determinada cantidad de tiempo. Tal y como ocurre con la mayoría de las aplicaciones de ActionScript, los eventos forman una parte importante de la funcionalidad de la aplicación del ejemplo del reloj con alarma.

Activación de la alarma

Según se ha mencionado con anterioridad, la única funcionalidad que la clase AlarmClock ofrece realmente está relacionada con la configuración y activación de la alarma. La clase integrada Timer (flash.utils.Timer) proporciona un mecanismo para que los desarrolladores definan código que se ejecutará tras un periodo de tiempo especificado. La clase AlarmClock utiliza una instancia de Timer para determinar cuándo activar la alarma.

    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); 
    }

La instancia de Timer definida en la clase AlarmClock se denomina alarmTimer. El método initClock(), que lleva a cabo las operaciones de configuración necesarias para la instancia de AlarmClock, realiza dos operaciones con la variable alarmTimer. En primer lugar, se crea una instancia de la variable con parámetros que indican a la instancia Timer que debe esperar 0 milisegundos y activar su evento de temporizador solo una vez. Tras crear la instancia de alarmTimer, el código llama al método addEventListener() de la variable para indicar que desea realizar una detección en el evento timer de esa variable. Las instancias de Timer funcionan distribuyendo su evento timer una vez transcurrida una cantidad de tiempo especificada. La clase AlarmClock necesitará saber cuándo se distribuye el evento timer para activar su propia alarma. Al llamar a addEventListener(), el código de AlarmClock se registra como detector con alarmTimer. Los dos parámetros indican que la clase AlarmClock desea detectar el evento timer (indicado por la constante TimerEvent.TIMER) y que, cuando este se produzca, debe llamarse al método onAlarm() de la clase AlarmClock como respuesta al evento.

Para establecer la alarma, se llama al método setAlarm() de la clase AlarmClock del siguiente modo:

    /** 
     * 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; 
    }

Este método realiza varias tareas, entre ellas almacenar el mensaje de alarma y crear un objeto Date (alarmTime) que representa el instante de tiempo real en el que debe sonar la alarma. En las últimas líneas del método, el temporizador de la variable alarmTimer se define y se activa, lo que resulta de especial relevancia para este análisis. En primer lugar, se llama a su método reset(), deteniendo el temporizador y reiniciándolo en caso de que ya se estuviese ejecutando. A continuación, se resta la hora actual (representada por la variable now) del valor de la variable alarmTime para determinar cuántos milisegundos tienen que transcurrir antes de que suene la alarma. La clase Timer no activa su evento timer a una hora absoluta, de manera que es esta diferencia relativa de tiempo la que se asigna a la propiedad delay de alarmTimer. Finalmente, se llama al método start() para iniciar el temporizador.

Una vez que ha transcurrido la cantidad especificada de tiempo, alarmTimer distribuye el evento timer. Dado que la clase AlarmClock ha registrado su método onAlarm() como un detector de ese evento, cuando se produzca el evento timer se llamará a 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); 
    }

Un método que se registra como detector de eventos debe definirse con la firma adecuada (es decir, el conjunto de parámetros y el tipo de devolución del método). Para que un método pueda ser detector del evento timer de la clase Timer, debe definir un parámetro cuyo tipo de datos sea TimerEvent (flash.events.TimerEvent), una subclase de la clase Event. Cuando la instancia de Timer llama a sus detectores de eventos, pasa una instancia TimerEvent como objeto de evento.

Notificación de la alarma a otros

Al igual que la clase Timer, la clase AlarmClock proporciona un evento que permite que otro código reciba notificaciones cuando suena la alarma. Para que una clase pueda usar el marco de gestión de eventos incorporado en ActionScript, debe implementar la interfaz flash.events.IEventDispatcher. Normalmente esto se lleva a cabo ampliando la clase flash.events.EventDispatcher, que proporciona una implementación estándar de IEventDispatcher (o ampliando una de las subclases de EventDispatcher). Tal y como se ha descrito anteriormente, la clase AlarmClock amplía la clase SimpleClock, que (a través de una cadena de herencias) amplía la clase EventDispatcher. Todo esto quiere decir que la clase AlarmClock ya incorpora la funcionalidad adecuada para proporcionar sus propios eventos.

Se puede registrar otro código para que reciba notificaciones del evento alarm de la clase AlarmClock llamando al método addEventListener() que AlarmClock hereda de EventDispatcher. Cuando una instancia de AlarmClock está lista para notificar a otro código que su evento alarm se ha activado, lleva a cabo esta operación llamando al método dispatchEvent(), que también se hereda de EventDispatcher.

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

Estas líneas de código están tomadas del método onAlarm() de la clase AlarmClock (que se ha mostrado al completo anteriormente). Se llama al método dispatchEvent() de la instancia de AlarmClock que, a su vez, notifica a todos los detectores registrados que el evento alarm de la instancia de AlarmClock se ha activado. El parámetro que se pasa a dispatchEvent() es el objeto de evento que se pasará a los métodos detectores. En este caso es una instancia de la clase AlarmEvent, una subclase de Event creada específicamente para este ejemplo.

Creación de un evento de alarma personalizado

Todos los detectores de eventos reciben un parámetro de objeto de evento con información acerca del evento específico que se está activando. En muchos casos, el objeto de evento es una instancia de la clase Event. No obstante, en algunas ocasiones resulta útil proporcionar más información a los detectores de eventos. Una forma habitual de lograr esto es definir una nueva clase (una subclase de la clase Event) y usar una instancia de esa clase como objeto de evento. En este ejemplo, se usa una instancia de AlarmEvent como objeto de evento cuando se distribuye el evento alarm de la clase AlarmClock. La clase AlarmEvent, que se muestra aquí, ofrece más información acerca del evento alarm, específicamente el mensaje de alarma:

    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; 
        } 
        ... 
    }

La mejor forma de crear una clase de objetos de evento personalizados es definir una clase que amplíe la clase Event, según se muestra en el ejemplo anterior. Para complementar la funcionalidad heredada, la clase AlarmEvent define una propiedad message que contiene el texto del mensaje de alarma asociado al evento; el valor de message se pasa como un parámetro en el constructor de AlarmEvent. La clase AlarmEvent también define la constante ALARM, que se puede utilizar para hacer referencia al evento específico (alarm) al llamar al método addEventListener() de la clase AlarmClock.

Además de añadir funcionalidad personalizada, cada subclase de Event debe sustituir el método clone() heredado como parte del marco de gestión de eventos de ActionScript. Las subclases Event también pueden sustituir el método heredado toString() para incluir las propiedades del evento personalizado en el valor que se devuelve al llamar al método 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"); 
    }

El método clone() sustituido necesita devolver una nueva instancia de la subclase personalizada de Event con todas las propiedades personalizadas definidas para coincidir con la instancia actual. En el método toString() sustituido, el método de utilidad formatToString() (heredado de Event) se utiliza para proporcionar una cadena con el nombre del tipo personalizado, además de los nombres y valores de todas sus propiedades.