Eventos

En este post se van a intentar cubrir algunos aspectos básicos del modelo de eventos de Flex. Tras algunas definiciones, se describirá el ciclo de vida de éstos y se tratará el tema de los custom events.


Definición

Los eventos son sucesos que ocurren en algún punto o momento de la vida de una aplicación y que nos permiten notificar a otras partes de la misma de estos sucesos. El modelo de eventos de Flex está basado en la Document Object Model (DOM) Level 3 Events Specification (http://www.w3.org/TR/DOM-Level-3-Events/).

Los eventos, cuando son despachados, recorren la DisplayList (la lista de “objectos visibles” jerarquizada que utiliza Flash Player) en orden ascendente o descendiente según en la fase en la que se encuentran (más sobre las fases de los eventos en el siguiente apartado). En su recorrido buscan objetos que se hayan registrado a su mismo tipo, y al ser procesados, éstos llaman a un método previamente definido.

Tipos de eventos

Existen dos tipos de eventos definidos según su procedencia:
De sistema: Son eventos lanzados por el sistema, como “CREATION_COMPLETE” o “INITIALIZE”, entre otros.
De usuario: Estos eventos se originan mediante acciones realizadas por el usuario, como “CLICK” o “MOUSE_OVER”, entre otros.

Listeners

Cuando un evento es propagado, necesitamos definir un objeto que quedará a la escucha del evento y llamará al método correspondiente. Esto es lo que se conoce como listener.

Los listeners se pueden definir de dos maneras; mediante MXML o mediante ActionScript.

– Desde MXML

Se declara utilizando el tipo del evento e igualándolo a la función que queremos que se llame cuando se detecta este evento.

Ejemplo:
[ftf w=”550″ h=”50″][/ftf]

Cuando se presione el botón se despachará el evento “click” (MouseEvent.CLICK) y se llamará a la función clickHandler que recibirá por parámetro un MouseEvent.

– Desde ActionScript:

Se utiliza el método addEventListener que se hereda de la clase EventDispatcher.

[scope].addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

scope: Representa el objeto donde se registrará el listener. Esta clase debe de haber heredado de EventDispatcher o bien debe implementar la interfaz IEventDispatcher. Cuando en su recorrido, el evento sea procesado en este objeto (más sobre las fases de los eventos en el siguiente apartado), la función definida por el parámetro listener será llamada.
type: Este parámetro es un String que identifica el tipo del evento. Se suelen utilizar constantes estáticas para evitar errores tipográficos.
listener: Esta es la función a la que se llama cuando un evento del tipo [type] es detectado por el objeto [scope].
useCapture: Este booleano indica si este listener será llamado también en la fase de Capture o sólo en las de Targeting y Bubbling (más sobre las fases de los eventos en el siguiente apartado).
priority: Este parámetro es un entero que permite designar una prioridad al evento. Un evento de prioridad 15 se evaluará antes que un evento de prioridad 10. En caso de tener difinidas prioridades iguales se evaluarán según el orden de creación.
useWeakReference: Por defecto se utilizan strong references, lo que quiere decir que nuestro listener no será borrado por el Garbage Collector.

Ejemplo:

[ftf w=”550″ h=”40″]this.addEventListener(MouseEvent.CLICK, clickHandler);[/ftf]

Para eliminar un listener se utiliza el método removeEventListener pasándole los tres primeros parámetros que se hayan utilizado al llamar al addEventListener (type, listener y useCapture). De esta manera para borrar el listener del ejemplo anterior se usaría:

[ftf w=”550″ h=”40″]this.removeEventListener(MouseEvent.CLICK, clickHandler);[/ftf]

Ciclo de vida de un evento. Fases.

Los eventos al ser despachados, en su ciclo de vida, se componen de tres fases bien diferenciadas:

– Capturing

En esta primera fase, se recorren todos los objetos desde el situado más arriba de la jerarquía (Stage) hasta el que contiene el nodo que originó el evento, comprobando si tienen listeners asociados. Los handlers de los listeners encontrados serán llamados sólo si el flag useCapture del addEventListener tiene valor true (por defecto es false). Los listeners definidos de esta forma sólo reaccionarán a los eventos en la fase de Capturing, por lo que en la fase de Bubbling serán ignorados.

Esta fase finaliza cuando se alcanza el parent del target.

– Targeting

En esta fase se comprueba si hay listeners registrados en el objeto que originó el evento (el target) y, en tal caso, se llamará a la función o funciones correspondientes.

– Bubbling

En este punto se realiza el camino contrario de la fase de Capturing; buscando listeners desde el parent del target hasta el objeto situado más arriba en la DisplayList (normalmente el objeto Stage). Esta fase finaliza cuando se alcanza dicho objeto.

Utilización de target/currentTarget

Se puede definir un listener en cualquier punto del recorrido de un evento. Cuando este listener sea llamado (independientemenete de en que fase se encuentre) el método que hace de handler recibirá un parámetro del tipo Event o de alguna subclase. En este evento se pueden encontrar las propiedades target y currentTarget. La primera siempre hace referencia al objeto que originó el evento (el que hace el dispatch), y la segunda hace referencia al objeto en el que se está procesando este listener.

Ejemplo:

Aplicación – Panel – Botón

Se registra un listener de la forma miPanel.addEventListener(MouseEvent.CLICK, onClickHandler);. En el momento en que el usuario presione el botón, el evento empezará su camino a través de la display list. Cuando el listener definido sea procesado (en este caso se llamará en la fase de bubbling porque no hemos usado el flag useCapture), se llamará al método onClickHandler y recibirá el evento por parámetro. En este instante el valor de target en este evento es “miBoton” y el valor de currentTarget es “miPanel”. Es recomendable utilizar la propiedad currentTarget ya que se obtiene más certeza sobre a qué objeto se refiere. De otra manera si por ejemplo nuestra aplicación contiene un gráfico con un texto encima, se obtendrán resultados diferentes del target según se clique en el gráfico o en el texto.

Cancelacion de eventos

Principalmente hay dos mecanismos para detener la propagación de un evento: stopPropagation y stopImmediatePropagation. Ambos son muy parecidos y evitan que se procesen los listeners para ese evento a partir de ese momento. La diferencia reside en el hecho de que stopPropagation evita el proceso de listeners a partir de los siguientes niveles, mientras que stopImmediatePropagation además evita que se procesen los eventos definidos en ese mismo nivel.

Ejemplo:

Aplicación – Panel – Botón

[ftf w=”550″ h=”170″]Application.application.addEventListener(MouseEvent.CLICK, onClickApp);
miPanel.addEventListener(MouseEvent.CLICK, onClickMiPanel1);
miPanel.addEventListener(MouseEvent.CLICK, onClickMiPanel2);

private function onClickMiPanel1(evt:Event):void
{
stopPropagation();
}
[/ftf]

En este caso se procesarán los dos listeners que existen en miPanel aunque se esté llamando a stopPropagation en onClickPanel1. Como el listener de Application está en otro nivel, éste no será llamado.

Al cambiar el primer handler por:

[ftf w=”550″ h=”100″]private function onClickMiPanel1(evt:Event):void
{
stopImmediatePropagation();
}
[/ftf]

Además de no procesarse el listener de Application, tampoco se pocesaría el segundo de miPanel, por lo que onClickMiPanel2 no se llegaría a llamar.

dispatchEvent

En ocasiones se requiere despachar manualmente un evento, como por ejemplo cuando se quiere indicar que una propiedad concreta ha cambiado. Esto es una práctica habitual en la creaión de componentes, donde se recomienda utilizar eventos para informar al resto de la aplicación de los sucesos ocurridos dentro del componente (con esto nos acercamos al concepto ‘loosely coupled’, que permite la reutilización de componentes). Para poder despachar estos eventos la clase EventDispatcher dispone del método dispatchEvent, que acepta un objeto del tipo (o subtipo) Event como único parámetro.

Ejemplo:

[ftf w=”550″ h=”66″]var evt:Event = new Event(“customType”, true, true);
miObj.dispatchEvent(evt);
[/ftf]

Como se ha visto, la única condición en el ejemplo anterior es que el objeto ‘miObj’ derive de EventDispatcher.

Eventos personalizados

A menudo se necesita incluir alguna información adicional para que sea transmitida con el evento. En estos casos la mejor práctica suele ser crear una clase que extienda de Event y añadirle los parámetros necesarios.

Al utilizar esta técnica, es muy recomendable sobreescribir el método clone, ya que Flex lo utilizará internamente para crear copias del Evento.

Ejemplo de un Evento custom:

[ftf w=”550″ h=”350″]package
{
import flash.events.Event;

public class CustomEvent extends Event
{
public var miPropiedad:String;

public function CustomEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=true)
{
super(type, bubbles, cancelable);
}

override public function clone():Event
{
return new CustomEvent(type, bubbles, cancelable);
}
}
}
[/ftf]

Un inconveniente que surge al utilizar eventos personalizados (sobretodo cuando se desarrolla con Caringorm), es que se acaban creando muchos eventos muy parecidos entre sí, en los que sólo varía el número de parámetros o el tipo. Para estos casos se puede utilizar una subclase de Event, donde los parámetros puedan añadirse de forma dinámica (utilizando por ejemplo la clase dinámica Object).

Ejemplo:

[ftf w=”550″ h=”580″]package
{
import flash.events.Event;

public class ParamEvent extends Event
{
private var _params:Object;

public function ParamEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=true)
{
super(type, bubbles, cancelable);
}

override public function clone():Event
{
return new ParamEvent(type, bubbles, cancelable);
}

public function set params(obj:Object):void
{
for (var i:Object in obj)
this._params[i] = obj[i];
}

public function get params():Object
{
if (!this._params)
this._params = {};

return this._params;
}
}
}
[/ftf]

De esta manera se puede despachar un evento del tipo ParamEvent de la siguiente forma:

[ftf w=”550″ h=”150″]var evt:ParamEvent = new ParamEvent(“custom”, true);

evt.params.edad = Math.random()*80;
evt.params.fecha = new Date();
evt.params.web = “www.madeinflex.com”;

dispatchEvent(evt);
[/ftf]

Esta práctica puede ser un tanto polémica porque, aunque reduce considerablemente la extensión de nuestro código (una única clase ParamEvent contra ‘n’ CustomEvents), dificulta considerablemente la legibilidad e incluso imposibilita la utilización del code hinting de Flex Builder por el hecho de utilizar clases dinámicas.

Conclusiones

El modelo de eventos ha cambiado mucho en AS3 respecto a AS2 y es importante familiarizarse con él. Entender el flujo de información asociada se hace a veces imprescindible para poder crear aplicaciones robustas y escalables. Como muy bien apunta Edgar Parada en este post, una buena manera de entender los eventos es identificar el emisor, el receptor y el mensaje. A partir de aquí, y conociendo su ciclo de vida, disponemos de un control absoluto sobre las implicaciones de su utilización.

9 Comentarios

  1. ilogyc

    Buena clase de eventos en Flex/AS3. Los eventos son para mi la pieza clave del framework Flex, por lo que es necesario conocer su funcionamiento al detalle y tu post ayuda mucho.

    molt be ❗

  2. warrio

    K buen post Alberto sobre los eventos en AS3 me ha ayudado bastante; enhorabuena sigue adelante estamos en contacto

  3. Pingback: Post sobre eventos en MIF » articulo » TheKlue: Flex, Flash y otras tecnologías RIA

  4. Pingback: » Artículos interesantes de los últimos días « Cerebro en la Sombra

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Acerca de Made In Flex

Made In Flex es una comunidad de desarrolladores de Apache Flex creada en 2006.

Apache Flex, anteriormente conocido como Adobe Flex, es un SDK (kit de desarrollo de software) para crear aplicaciones enriquecidas - multiplataforma basadas en Adobe Flash donado por Adobe a la fundación Apache in 2011 y promocionado a proyecto de primer nivel en Diciembre de 2012.

Actualmente estamos cambiando muchos aspectos del sitio web para ofrecer un sitio útil para toda la comunidad que tenga en cuenta las necesidades actuales.

Últimas Fotos

Instalador de Apache Flex

Entrar o Registrase