A raíz de una pregunta en un tip anterior explicaré cómo detectar eventos personalizados entre clases de Actionscript 3. Para poder, por ejemplo, que una clase pueda detectar cuando otra clase a ejecutado una acción en concreto o ha completado una carga de datos externa.
La interfaz IEventDispatcher define métodos para añadir o quitar detectores de eventos. Son muchas las clases que implementan esta interfaz y por lo tanto permiten lanzar y escuchar eventos, cómo lo es la clase DisplayObject con lo que en cualquier clip podremos añadirle un detector de "escuche" (por ejemplo) el evento que lanza automáticamente la clase MouseEvent cada vez que hacemos click sobre él:
function detectaEvento(e:MouseEvent):void { trace("Detecto el mouse click"); }
Ahora bien, conel método dispatchEvent() podemos lanzar nuestros propios eventos desde nuestras clases. ünicamente crearemos un evento con el nombre que queramos.
Código :
dispatchEvent(new Event("onClick"));
y para detectar:
Código :
addEventListener("onClick",detectaEvento);
Para hacerlo de una manera un poco más elegante podríamos crear una constante estática que guarde el nombre del evento. De esta manera una clase de ejemplo sería asi:
Código :
package { import flash.display.Sprite; import flash.events.EventDispatcher; import flash.events.Event; import flash.events.MouseEvent; // public class ClaseA extends Sprite { public static const CLICK:String = "onClick"; // public function ClaseA() { var clip:Sprite = new Sprite(); clip.graphics.beginFill(0x000000); clip.graphics.drawRect(0, 0, 50, 50); clip.graphics.endFill(); addChild(clip); addEventListener(MouseEvent.CLICK, lanzaEvento); addEventListener(ClaseA.CLICK, detectaEvento); } private function lanzaEvento(e:MouseEvent):void { trace("Detecto el mouse click"); dispatchEvent(new Event(ClaseA.CLICK)); } private function detectaEvento(e:Event):void { trace("Detecto el ClaseA click"); } } }
Esta clase crear un gráfico que detecta el click del mouse, y a su vez lanza y detecta su propio evento.
Lo interesante es poder detectar estos eventos desde fuera de la clase. Por ejemplo, crearemos otra clase que detecte este evento lanzado por la ClaseA:
Código :
package { import flash.display.Sprite; import flash.events.Event; // public class ClaseB extends Sprite { private var claseA:ClaseA; // public function ClaseB() { claseA = new ClaseA(); claseA.addEventListener(ClaseA.CLICK,detectaEvento); addChild(claseA); } private function detectaEvento(e:Event):void { trace("Detecto el ClaseA click"); } } }
Cómo vemos, para poder detectar el evento de una clase hemos de asignarle el addEventListener a su instancia. Es decir, este listener en la claseB no funcionaría:
Código :
claseA = new ClaseA(); addChild(claseA); addEventListener(ClaseA.CLICK,detectaEvento);
Entonces ¿cómo detectaremos eventos de clases que no están incluidas en las clases detectoras? Tendremos que hechar mano de una clase intermedia (dispatcher) que sea la que realmente lance el evento:
Código :
package { import flash.events.EventDispatcher; // public class ClaseDispatcher extends EventDispatcher { private static var _instancia:ClaseDispatcher; public static const CLASEA_CLICK:String = "onClick_en_claseA"; // public function ClaseDispatcher(s:Singleton) { } public static function getInstancia():ClaseDispatcher { if (_instancia == null) { ClaseDispatcher._instancia = new ClaseDispatcher(new Singleton); } return _instancia; } } } class Singleton { }
Esta clase está escrita siguiendo el patrón Singleton para que solo se pueda crear una única instancia y esta sea accesible dese cualquier clase (ya escribiré un tip haciendo una explicación más extensa sobre este patrón ) .
De manera que incluyendo esta clase dispatcher dentro de las clases que queramos mantener comunicadas:
Código :
package { import flash.display.Sprite; import flash.events.EventDispatcher; import flash.events.Event; import flash.events.MouseEvent; // public class ClaseA extends Sprite { private var dispatcher:ClaseDispatcher; // public function ClaseA() { var clip:Sprite = new Sprite(); clip.graphics.beginFill(0x000000); clip.graphics.drawRect(0, 0, 50, 50); clip.graphics.endFill(); addChild(clip); addEventListener(MouseEvent.CLICK, lanzaEvento); } private function lanzaEvento(e:MouseEvent):void { dispatcher = ClaseDispatcher.getInstancia(); dispatcher.dispatchEvent(new Event(ClaseDispatcher.CLASEA_CLICK)); } } }
Código :
package { import flash.display.Sprite; import flash.events.Event; // public class ClaseB extends Sprite { private var dispatcher:ClaseDispatcher; // public function ClaseB() { dispatcher = ClaseDispatcher.getInstancia(); dispatcher.addEventListener(ClaseDispatcher.CLASEA_CLICK,detectaEvento); } private function detectaEvento(e:Event):void { trace("Detecto el ClaseA click"); } } }
Maravilloso. De los mejores tips que he visto. Gracias Zguillez. Por:AXM
Como siempre, un maestro Z. Excelente tip. Por:The Fricky!
Mmm exelente tip, aunque creo haber visto un ejemplo de singleton distinto en los manuales de macromedia.
Muy buen tip!!!! Por:quien yo?_blog
quien yo?_blog :
Mmm exelente tip, aunque creo haber visto un ejemplo de singleton distinto en los manuales de macromedia.
Si lo has visto en manuales de macromedia debía ser un singleton de Actionscript 2. En Actionscript 3 ha variado un poco debido a no poder definir constructores de clase como privados. Por:Zguillez
Zguillez tengo una duda. Todo perfecto hasta lo del patrón Singleton. No es que no entienda. Si no que no entiendo en que casos se utilizaría. Por que lo que explicas hasta antes de el patrón Singleton, para mi esta perfecto. Poder crear eventos de esa manera para mi es suficientemente útil.
Pero no veo en que casos podría serme útil el patrón Singleton. Podrías mostrarme en que casos o en que problemas la mejor solución, seria utilizar el patrón Singleton, y que no sea tan recomendable utilizar la manera como lo explicas en la primera parte?. Por:AXM
AXM :
Pero no veo en que casos podría serme útil el patrón Singleton. Podrías mostrarme en que casos o en que problemas la mejor solución, seria utilizar el patrón Singleton, y que no sea tan recomendable utilizar la manera como lo explicas en la primera parte?.
Fíjate que la claseB tiene incluida a la claseA y escucha sus eventos añadiendole un addListener a la instancia de la clase
Código :
//dentro de claseB claseA = new ClaseA(); claseA.addEventListener(ClaseA.CLICK,detectaEvento);
esto te funcionará perfecto siempre y cuando quieras que la clase "padre" quiera escuchar a sus clases interiores.
Pero imagina que la claseB contiene la claseA y una claseC, y quieres que la claseC sea la que detecte el evento de la claseA.
En este caso no puedes crear un addEventListener dentro de claseC que escuche a claseA ya que claseC no contiene instancia de claseA. ¿ok?
Lo que podrías hacer aquí es que la claseA reciba el evento de la claseB y al recibir el evento se lo comunique a la claseC.
Código :
public function ClaseB() { claseA = new ClaseA(); claseC = new ClaseC(); claseA.addEventListener(ClaseA.CLICK,transmiteEventoAclaseC); } private function transmiteEventoAclaseC(e:Event):void { claseC.obtieneEventoDeClaseA(e); }
Pero esto puede resultar MUY engorroso cuando tienes una composición de clases mucho más compleja. Imagina que entre la claseA y la claseC hay 20 clases de diferencia... tendrias que crear un addEventListener en cada una de ellas para ir transmitiendo el evento de una a otra... bfff
De ahi lo de delegar el lanzamiento y detección de los eventos a una clase singleton, ya que como tiene acceso global puedes incluir una instancia suya en cualquier clase sin importar la relación de rutas de las clases implicadas.
Haaaaa, Ahora si me queda mas claro. Si, definitivamente cuando son muchas clases, como lo planteas es muy engorroso, y entonces si es mucho mas cómodo con el patrón Singleton. Pero me surge otra duda.
Si yo tengo una clase Main o principal, y creo una instancia de otra clase que se llame ClaseB, y quiero que esta ClaseB, tenga acceso a todas las variables y funciones publicas que esten en la Clase Main o principal, y ademas quiero que ClaseB también pueda acceder a MovieClips que están en el FLA, la manera correcta de hacerlo seria...
public class Main extends Sprite { private var claseB:ClaseB; public var prueba:String = "pruebita";
public function Main() { claseB = new ClaseB(this); } } }
Clase ClaseB.as
Código :
package { public class ClaseB { public function ClaseB(raiz:Object) { trace(raiz.prueba); } } }
En claseB = new ClaseB(this); this estoy enviado toda la clase principal a ClaseB. Y luego en el constructor de ClaseB coloco public function ClaseB(raiz:Object) donde recibe la clase que la aloja. Luego al hacer trace(raiz.prueba); me hace el trace correctamente.
Esta manera de hacerlo esta bien? (No he encontrado otra manera para hacerlo). O hay alguna forma de hacerlo con el patrón Singleton que sea mas adecuado?. Por:AXM
Osea que el polimorfismo privado no existe? ooo ando confundido... @_@
Gracias Por:quien yo?_blog
Buenas a tod@s... He estado leyendo un poco sobre el tema y la discusión. Cuando he terminado me he preguntado: ¿ Están haciendo una composición o una agregación ? Por:Cañi - byte_blog
a mi no me funciona.
me da error 1061:dice que la funcion dispatchevent no esta definida y eso que mi clase extiende EventDispatcher Por:LUIS-blog
ya funciona, fallo tonto...gracias Por:luismesejo-blog