Detectores de eventos

Flash Player 9 y posterior, Adobe AIR 1.0 y posterior

Los detectores de eventos, también llamados controladores de eventos, son funciones que ejecutan Flash Player y AIR como respuesta a eventos específicos. El proceso de añadir un detector de eventos consta de dos pasos. En primer lugar se debe crear una función o método de clase para que Flash Player o AIR lo ejecuten como respuesta al evento. Esto a veces recibe el nombre de función de detector o función de controlador de eventos. En segundo lugar, es necesario usar el método addEventListener() para registrar la función de detector en el destino del evento o en cualquier objeto de la lista de visualización que se encuentre en el trayecto del flujo del evento adecuado.

Creación de funciones de detector

La creación de funciones de detector es un área en la que el modelo de eventos de ActionScript 3.0 difiere del modelo de eventos DOM. En el modelo de eventos DOM existe una distinción clara entre un detector de eventos y una función de detector: un detector de eventos es una instancia de una clase que implementa la interfaz EventListener, mientras que una función de detector es un método de esa clase denominada handleEvent() . En el modelo de eventos DOM, se debe registrar la instancia de la clase que contiene la función de detector en lugar de la función de detector en sí.

En el modelo de eventos de ActionScript 3.0 no hay distinción entre un detector de eventos y una función de detector. ActionScript 3.0 carece de una interfaz EventListener y es posible definir las funciones de detector fuera de una clase o como parte de ella. Además, no es necesario que las funciones de detector se denominen handleEvent() , sino que pueden usar cualquier identificador válido. En ActionScript 3.0 se registra el nombre de la función de detector real.

Función de detector definida fuera de una clase

El siguiente código crea un archivo SWF sencillo que muestra una forma cuadrada roja. Una función de detector denominada clickHandler() , que no forma parte de ninguna clase, detecta los eventos de clic del ratón sobre el cuadrado rojo.

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

Cuando el usuario interactúa con el archivo SWF resultante haciendo clic en el cuadrado, Flash Player o AIR generan la siguiente salida de traza:

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

Cabe destacar que el objeto de evento se pasa como argumento a clickHandler() . Esto permite a la función de detector examinar el objeto de evento. En este ejemplo se usa la propiedad type del objeto de evento para determinar si se trata de un evento de clic.

En el ejemplo también se comprueba el valor de la palabra clave this . En este caso, this representa el objeto global, lo cual es totalmente lógico, ya que la función se ha definido fuera de todo objeto o clase personalizada.

Función de detector definida como método de clase

El siguiente ejemplo es idéntico al anterior en el que se define la clase ClickExample, excepto en que la función clickHandler() se define como un método de la clase 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); 
    } 
}

Cuando el usuario interactúa con el archivo SWF resultante haciendo clic en el cuadrado rojo, Flash Player o AIR generan la siguiente salida de traza:

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

La palabra clave this hace referencia a la instancia de ChildSprite denominada child . Se trata de un cambio de comportamiento respecto a ActionScript 2.0. Los usuarios que usaban componentes en ActionScript 2.0 sin duda recordarán que, al pasar un método de clase a UIEventDispatcher.addEventListener() , el ámbito del método estaba vinculado al componente que difundía el evento en lugar de a la clase en la que estaba definido el método de detector. Dicho de otro modo, si se usara esta técnica en ActionScript 2.0, la palabra clave this haría referencia al componente que realiza la difusión en lugar de a la instancia de ChildSprite.

Esto constituía un problema significativo para algunos programadores, ya que implicaba que no podían acceder a otros métodos y propiedades de la clase que contenía el método detector. Como solución, los programadores de ActionScript 2.0 podían usar la clase mx.util.Delegate para cambiar el ámbito del método detector. Esto ya no es necesario, ya que ActionScript 3.0 crea un método vinculado cuando se llama a addEventListener() . A consecuencia de ello, la palabra clave this hace referencia a la instancia de ChildSprite denominada child y el programador puede acceder a los demás métodos y propiedades de la clase ChildSprite.

Detector de eventos de uso no recomendado

Existe una tercera técnica, que no se recomienda, en la que se crea un objeto genérico con una propiedad que señala a una función de detector asignada dinámicamente. Se analiza aquí porque se solía utilizar en ActionScript 2.0, aunque no se debe emplear en ActionScript 3.0. Esta técnica no se recomienda, ya que la palabra clave this hará referencia al objeto global en lugar de al objeto detector.

El siguiente ejemplo es idéntico al anterior de la clase ClickExample, excepto en que la función de detector se define como parte de un objeto genérico denominado 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); 
}

El resultado de la traza es el siguiente:

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

Cabría esperar que this hiciera referencia a myListenerObj y que la salida de traza fuese [object Object] pero, en vez de eso, hace referencia al objeto global. Al pasar el nombre de una propiedad dinámica como un argumento a addEventListener() , Flash Player o AIR no pueden crear un método vinculado. Esto se debe a que lo que se transmite como parámetro listener no es más que la dirección de memoria de la función de detector y Flash Player y AIR son incapaces de vincular esa dirección de memoria con la instancia de myListenerObj .

Administración de detectores de eventos

Es posible administrar las funciones de detector usando los métodos de la interfaz IEventDispatcher. La interfaz IEventDispatcher es la versión de ActionScript 3.0 de la interfaz EventTarget del modelo de eventos DOM. Si bien el nombre IEventDispatcher parece implicar que su objetivo principal es enviar (o distribuir) objetos de evento, en realidad los métodos de esta clase se usan con mucha más frecuencia para registrar detectores de eventos, comprobar su existencia y eliminarlos. La interfaz IEventDispatcher define cinco métodos, según se muestra en el siguiente código:

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

La API de Flash Player implementa la interfaz IEventDispatcher con la clase EventDispatcher, que sirve como clase base de todas las clases que pueden ser destinos de eventos o parte de un flujo de eventos. Por ejemplo, la clase DisplayObject hereda de la clase EventDispatcher. Esto quiere decir que cualquier objeto de la lista de visualización tiene acceso a los métodos de la interfaz IEventDispatcher.

Añadir detectores de eventos

El método addEventListener() es el elemento más ampliamente utilizado de la interfaz IEventDispatcher. Se utiliza para registrar las funciones de detector. Los dos parámetros necesarios son type y listener . Se puede usar el parámetro type para especificar el tipo de evento. El parámetro listener , por su parte, se emplea para especificar la función de detector que se ejecutará cuando se produzca el evento. El parámetro listener puede ser una referencia a una función o a un método de clase.

No utilice paréntesis al especificar el parámetro listener . Por ejemplo, la función clickHandler() se especifica sin paréntesis en la siguiente llamada al método addEventListener() :
addEventListener(MouseEvent.CLICK, clickHandler)

El parámetro useCapture del método addEventListener() permite controlar la fase del flujo del evento en la que estará activa el detector. Si useCapture se establece en true , el detector estará activo durante la fase de captura del flujo del evento. Si useCapture se establece en false , el detector estará activo durante las fases de destino y de propagación del flujo del evento. Para detectar un evento durante todas las fases del flujo del evento se debe llamar a addEventListener() dos veces, una con useCapture establecido en true y luego otra con useCapture establecido en false .

El parámetro priority del método addEventListener() no es parte oficial del modelo de eventos DOM de nivel 3. Se incluye en ActionScript 3.0 para ofrecer una mayor flexibilidad a la hora de organizar los detectores de eventos. Cuando se llama a addEventListener() , es posible establecer la prioridad de ese detector de eventos pasando un valor entero como parámetro priority . El valor predeterminado es 0, pero se le pueden asignar valores enteros negativos o positivos. Cuanto mayor sea el número, antes se ejecutará el detector de eventos. Los detectores de eventos con la misma prioridad se ejecutan en el orden en que se añadieron, de modo que cuanto antes se añada, antes se ejecutará.

El parámetro useWeakReference permite especificar si la referencia a la función de detector es débil o normal. Si este parámetro se establece como true se pueden evitar situaciones en las que las funciones de detector permanecen en memoria incluso cuando ya no se necesitan. Flash Player y AIR usan una técnica denominada eliminación de datos innecesarios para borrar de la memoria objetos que han dejado de utilizarse. Se considera que un objeto ha dejado de usarse si no existe ninguna referencia a él. El recolector de datos innecesarios descarta las referencias débiles, de modo que una función de detector que solo señala a una referencia débil puede ser eliminada, al considerarse como datos innecesarios.

Eliminación de detectores de eventos

El método removeEventListener() sirve para eliminar un detector de eventos que ya no se necesita. Siempre conviene eliminar los detectores que no se vayan a usar más. Los parámetros obligatorios incluyen eventName y listener , los mismos que para el método addEventListener() . Conviene recordar que se pueden detectar eventos durante todas las fases de eventos llamando a addEventListener() dos veces, una vez con useCapture establecido en true y luego otra establecido en false . Para eliminar los dos detectores de eventos sería necesario llamar a removeEventListener() dos veces, una con useCapture establecido en true y luego otra establecido en false .

Distribución de eventos

Los programadores expertos pueden usar el método dispatchEvent() para distribuir un objeto de evento personalizado en el flujo del evento. Este método solo acepta un parámetro, consistente en una referencia a un objeto de evento, que debe ser una instancia de la clase Event o una subclase de esta. Una vez distribuido, la propiedad target del objeto de evento se establece en el objeto en el que se llamó a dispatchEvent() .

Comprobación de detectores de eventos existentes

Los dos últimos métodos de la interfaz IEventDispatcher ofrecen información útil sobre la existencia de detectores de eventos. El método hasEventListener() devuelve true si se encuentra un detector de eventos para un tipo de evento específico en un objeto concreto de la lista de visualización. El método willTrigger() también devuelve true si se encuentra un detector para un objeto concreto de la lista de visualización, pero willTrigger() no solo comprueba la existencia de detectores en el objeto de la lista de visualización, sino también en todos los ascendientes del objeto de la lista de visualización para todas las fases del flujo del evento.

Eventos de error sin detectores

Las excepciones, y no los eventos, son el principal mecanismo de gestión de errores en ActionScript 3.0, pero la gestión de excepciones no funciona en operaciones asíncronas, como la carga de archivos. Si se produce un error durante una de estas operaciones asíncronas, Flash Player y AIR distribuyen un objeto de evento de error. Si no se crea un detector para el evento de error, las versiones de depuración de Flash Player y AIR mostrarán un cuadro de diálogo con información acerca del error. Por ejemplo, la versión del depurador de Flash Player genera el siguiente cuadro de diálogo que describe el error cuando la aplicación intenta cargar un archivo desde una dirección URL no válida:

La mayor parte de los eventos de error se basan en la clase ErrorEvent y, por lo tanto, tienen una propiedad denominada text que se usa para almacenar el mensaje de error que muestra Flash Player o AIR. Las dos excepciones a esta regla son las clases StatusEvent y NetStatusEvent. Ambas clases tienen una propiedad level ( StatusEvent.level y NetStatusEvent.info.level ). Cuando el valor de la propiedad level es " error ", estos tipos de evento se consideran eventos de error.

Un evento de error no hace que el archivo SWF deje de ejecutarse. Solo se manifestará como un cuadro de diálogo en las versiones de depuración de los plugins de navegadores y de los reproductores autónomos, como un mensaje en el panel de salida del reproductor de edición y como una entrada en el archivo de registro de Adobe Flash Builder. No se manifiesta en las versiones oficiales de Flash Player o AIR.