Multiple dispatchHandler Actionscript 3 - actionscript-3

I want to dispatch an Event 2 times:
I have a MainClass, a SecondClass and a ThirdClass.
In the ThirdClass, there's a clickEvent. I dispatch it to the SecondClass:
this.addEventListener(MouseEvent.CLICK, clickHandler);
public static const CLICKED_HANDLER:String = "clickedHandler";
public function clickHandler(e:MouseEvent):void {
dispatchEvent(new Event(CLICKED_HANDLER));
}
I catch and throw it in the SecondClass:
object.addEventListener(ThirdClass.CLICKED_Handler, clickedEventListener);
public static const CLICKED_HANDLER:String = "clickedHandler";
public function clickedEventListener(e:Event):void {
dispatchEvent(new Event(CLICKED_HANDLER));
}
And this I catch in the MainClass:
object.addEventListener(SecondClass.CLICKED_HANDLER, clickedEventListener);
public function clickedEventListener(e:Event):void {
trace("click");
}
But it wouldn't work... What am I doing wrong?
And how could I get information about the object of the ThirdClass that's clicked?
(Normaly with 1 dispatchEvent, it's with:
var thirdClassObject:ThirdClass = e.currentTarget as ThirdClass;
in the clickHandler method, but how to do this with 2 dispatchEvents?)
So: I want to know in my MainClass which ThirdClass-object is clicked.
Thanks a lot!

When you dispatch an event, the target property references the dispatcher.
What you apparently need is passing a reference to third class along with the event to use in the event handler of main class.
You have several options to achieve this.
If a dispatcher instance is on the display list, use event bubbling to handle events in parent display objects. Refer to the second argument of Event constructor and Event.bubbles property. This way you can subscribe to any events of child objects in a parent, and check Event.target property in your event handler. Most of mouse events like MouseEvent.CLICK are bubbling by default. So you could just listen to them and check targets in main class.// inside MainClass
// notice: we are subscribing to MainClass instance
this.addEventListener(MouseEvent.CLICK, clickedEventListener);
public function clickedEventListener(e:MouseEvent):void {
trace(e.target); // the target is what was actually clicked
// you may also notice the difference between e.target and e.currentTarget
}
If you still want to use CLICKED_HANDLER event, you may do so as follows:// inside ThirdClass
dispatchEvent(new Event(CLICKED_HANDLER, true));
// ---------------------------------------^
Remember: this will only work for display list members.
See the Event flow article on adobe.com.
Create a custom event class and place reference there. (And you can also use event bubbling with custom event classes.) This way you will dispatch your event as// inside SecondClass
public function clickedEventListener(e:Event):void {
var customEvent:CustomEventClass = new CustomEventClass(CLICKED_HANDLER);
customEvent.customTarget = e.target;
dispatchEvent(customEvent);
}
// inside MainClass
public function clickedEventListener(e:CustomEventClass):void {
trace(e.customTarget );
}

First, define what do you mean by "it wouldn't work..."?
Does the SecondClass receive the event at all? If so, you may try this:
//SecondClass
public function clickedEventListener(e:Event):void {
dispatchEvent(e);
}
//MainClass
object.addEventListener(ThirdClass.CLICKED_HANDLER, clickedEventListener);

Related

Actionscript 3.0 Listening for a dispatched Custom Event

I'm having some problems catching a custom event with a listener.
I have a number of objects called keys. Each key, when it is clicked, dispatches a custom event like so:
public class Key extends Sprite
{
private var letter:String;
public static const CLICKED:String = "clicked";
private function keyClicked(e:MouseEvent):void {
this.removeEventListener(MouseEvent.CLICK, keyClicked, false);
this.mouseEnabled = false;
dispatchEvent(new Event(CLICKED));
}
}
All of the keys are children of a keyboard object. One of the parents of the keyboard object has an event listener like so:
addEventListener(Key.CLICKED, keyboardGuess);
which calls
public function keyboardGuess(e:Event):void {
trace("event received");
var letter:String = e.target.getLetter();
trace(letter);
} //there will be other functionality in here when I get it listening
However, while I can tell that I am successfully dispatching the event, my listener is never picking it up. I have been going crazy over this for over an hour; can you please help me figure out what is going on?
The custom event's bubbles value should be true, so the object that contains Key could receive the event. And if the bubbles value is false, only the object who dispatch the event could receive the event, like you Key.
About bubbles.
Try
dispatchEvent(new Event(CLICKED, true));

How to dispatch an event with added data - AS3

Can any one give me a simple example on how to dispatch an event in actionscript3 with an object attached to it, like
dispatchEvent( new Event(GOT_RESULT,result));
Here result is an object that I want to pass along with the event.
In case you want to pass an object through an event you should create a custom event. The code should be something like this.
public class MyEvent extends Event
{
public static const GOT_RESULT:String = "gotResult";
// this is the object you want to pass through your event.
public var result:Object;
public function MyEvent(type:String, result:Object, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.result = result;
}
// always create a clone() method for events in case you want to redispatch them.
public override function clone():Event
{
return new MyEvent(type, result, bubbles, cancelable);
}
}
Then you can use the code above like this:
dispatchEvent(new MyEvent(MyEvent.GOT_RESULT, result));
And you listen for this event where necessary.
addEventListener(MyEvent.GOT_RESULT, myEventHandler);
// more code to follow here...
protected function myEventHandler(event:MyEvent):void
{
var myResult:Object = event.result; // this is how you use the event's property.
}
This post is a little old but if it can help someone, you can use DataEvent class like so:
dispatchEvent(new DataEvent(YOUR_EVENT_ID, true, false, data));
Documentation
If designed properly you shouldn't have to pass an object to the event.
Instead you should make a public var on the dispatching class.
public var myObject:Object;
// before you dispatch the event assign the object to your class var
myObject = ....// whatever it is your want to pass
// When you dispatch an event you can do it with already created events or like Tomislav wrote and create a custom class.
// in the call back just use currentTarget
public function myCallBackFunction(event:Event):void{
// typecast the event target object
var myClass:myClassThatDispatchedtheEvent = event.currentTarget as myClassThatDispatchedtheEvent
trace( myClass.myObject )// the object or var you want from the dispatching class.

Handle MouseEvent.CLICK, MOUSE_DOWN in subclasses

I'm building a panel of buttons, that are enclosed in Canvas container. For this purpose I created a MyButton class, which is a subclass of UIComponent. MyButton class has 2 other subclasses: MyRadioButton and MyMenuButton, which have another behavior on MouseEvent.MOUSE_DOWN. MyMenuButtoncreates and shows a menu, that I build from XML and it builds ok.
I add listeners in the superclass like this:
this.addEventListener(MouseEvent.CLICK, handleClick);
this.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
this.addEventListener(MouseEvent.MOUSE_UP, handleMouse);
It's done at the creation stage.
In my subclasses I override handleMouse handler.
override protected handleMouse(event:MouseEvent) : void
{
// subclass specific handling
}
These button objects are added to canvas container as following:
in class MyButtonsContainer.as:
this.rowChildren.addChild(button);
These buttons draw perfectly and in place. The problem is the behavior:
The event doesn't come to handleClick handler of superclass. And that's the question actually - why can it be?
Thanks in advance
EDIT: It appears that MOUSE_DOWN & MOUSE_UP interfere with CLICK event. When I remove listeners to them I get to click handler.How to cause them live together?
I managed to catch CLICK event as timing difference between MOUSE_DOWN & MOUSE_UP.
EDIT: A short/long explanation: For some reason my listeners for MOUSE_DOWN/MOUSE_UP events were interfering with MouseEvent.CLICK. I was suggested by somebody to give up on listening to MouseEvent.CLICK and instead start a timer in MouseEvent.MOUSE_DOWN and check again the timer in MouseEvent.MOUSE_UP. If the difference less than 0.1 (you can put there another threshold) then it was a click actually.
Now some sample code:
public class MyButton extends UIComponent
{
...
private var _clickTime : Number = 0.;
public function void MyButton()
{
super();
addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
addEventListener(MouseEvent.MOUSE_UP, handleMouse);
}
protected function checkForClick(event:MouseEvent) : Boolean
{
var down : Boolean = event.type == MouseEvent.MOUSE_DOWN;
if (down)
_clickTime = getTimer();
else {
var diff : Number = getTimer() - _clickTime;
if (diff/1000. < 1.) {
handleClick();
return true;
}
}
return false;
}
protected function handleClick(event:MouseEvent) : void
{
// handle the click here
}
protected function handleMouse(event:MouseEvent) : void
{
checkForClick(event);
...
// take care of your button graphic state
}
DISCLAIMER: This code works for me and good for me and my application needs. If you disagree with this way or have a better way to suggest, please do it in comments. Since this answer answers my own question and this EDIT was added only because I was asked to do so, please don't downvote it if you don't agree with it.
call the superclass method like so
override protected handleMouse(event:MouseEvent) : void
{
super.handleMouse(event);
// subclass specific handling
}
If I'm understanding you correctly, you just need to make sure that in your overriden handlers you end each one with a call to super -
override protected handleMouse(event:MouseEvent):void {
// my logic here for the subclass
trace("blah blah blah");
//now redispatch up to the superclass.
super.handleMouse(event);
}
I'm not 100% sure what you're asking, but I'd override click actions like this..
Base class:
public class MyButton extends UIComponent
{
/**
* Constructor
*/
public function MyButton()
{
// listeners
addEventListener(MouseEvent.CLICK, _click);
// ...other listeners
}
/**
* Called on dispatch of MouseEvent.CLICK
*/
private function _click(e:MouseEvent):void
{
doClick();
}
/**
* Override this method and fill with actions to
* do when this is clicked
*/
protected function doClick():void
{
trace("base class was clicked");
}
}
And the extending class:
public class MyOtherButton extends MyButton
{
/**
* Override doClick
*/
override protected function doClick():void
{
super.doClick();
trace("extending class was clicked");
}
}
After closely inspecting your code:
You've added two different listeners to one function for one, shouldn't these have seperate functions as they are called by contrasting events?
this.addEventListener(MouseEvent.MOUSE_DOWN, handleMouse);
this.addEventListener(MouseEvent.MOUSE_UP, handleMouse);
Also, it seems like you're expecting handleMouse to be fired by a click event judging by the below code, which isn't the case.
override protected handleMouse(event:MouseEvent) : void
{
// subclass specific handling
}
Either that, or you're forgetting to override handleClick as well - which is the method that you're running on a click event. You possibly need to add this code:
override protected handleClick(event:MouseEvent) : void
{
// subclass specific handling for a click
}

How do I dispatch a custom event from an actionscript 3 class and listen for it in the document root?

I built a custom event dispatcher that passes variables. I dispatch the event and then I try to listen for the event in my document root, but I never receive the event. How do I bubble the event up to my document class?
addEventListener(CustomVarEvent.pinClicked, pinClickedHandler);
function pinClickedHandler(e:CustomVarEvent) {
trace("main says " + e.arg[0] + " clicked");//access arguments array
}
package zoomify.viewer
{
import com.maps.CustomVarEvent;
protected function hotspotClickHandler(event:MouseEvent):void {
var hotspotData:Hotspot = hotspotsMap[event.currentTarget] as Hotspot;
trace(hotspotData._name + " was clicked");
/*if(hotspotData) {
navigateToURL(new URLRequest(hotspotData.url), hotspotData.urlTarget);
}*/
dispatchEvent(new CustomVarEvent("pinClicked",true,false,hotspotData._name));
}
}
package com.maps
{
// Import class
import flash.events.Event;
// CustomVarEvent
public class CustomVarEvent extends Event {
public static const pinClicked:String = "pinClicked";
// Properties
public var arg:*;
// Constructor
public function CustomVarEvent(type:String, ... a:*) {
var bubbles:Boolean = true;
var cancelable:Boolean = false;
super(type, bubbles, cancelable);
arg = a;
}
// Override clone
override public function clone():Event{
return new CustomVarEvent(type, arg);
};
}
}
The pinClicked event that is being dispatched is nested two levels deep in classes. I add an instance of class ZoomifyViewer to the stage. ZoomifyViewer adds and instance of ZoomGrid to the stage and ZoomGrid dispatches the event.
When I add the same event listener and handler function directly into my ZoomGrid class (the same class that the event is dispatched from), then the listener and handler work properly. However, when the listener and handler are in a parent class or on the stage, I get no response.
Is a dispatcher necessary to bubble up to bubble up?
Also, are these two lines functionally identical based on the constant pinClicked that is defined in my CustomVarEvent?
dispatchEvent(new CustomVarEvent(CustomVarEvent.pinClicked, hotspotData._name));
dispatchEvent(new CustomVarEvent("pinClicked", hotspotData._name));
The event can only bubble up through the display list if the object that dispatched the event is a DisplayObject (or an ancestor of DisplayObject, such as a Sprite or MovieClip) so that it can be in the display list AND it is added to the display list at the time of the event dispatch.
The code you have written does not have the line that dispatches an event as part of a class at all, just a function in a package, so I'm not sure where you intend on it bubbling to.
A quick fix for you would simply to have the same object that was clicked have the event dispatched off of it, cuz since it was clicked it is obviously a display object that is on stage, which meets our two stipulations for bubbling.
package zoomify.viewer
{
import com.maps.CustomVarEvent;
protected function hotspotClickHandler(event:MouseEvent):void
{
// BY THE WAY, event.currentTarget will return the actual object, so you
// don't need whatever the hotspotsMap[] thing is, just cast event.currentTarget
/*var hotspotData:Hotspot = hotspotsMap[event.currentTarget] as Hotspot;*/
var hotspotData:Hotspot = event.currentTarget as Hotspot;
// here we dispatch the event off of the target, since it is definitely
// in the display list already, so therefore bubbling will work right
event.target.dispatchEvent(new CustomVarEvent("pinClicked",true,false,hotspotData._name));
}
}
Events bubble up the display list. I cannot tell from your code sample what object you're dispatching the event from, only that you have a method on a class to do so. Has an instance of that class been added to the display list?
Bubbling only works when the object that dispatches the event is on the displaylist. Which is any grandchild of the stage.
That is properly your problem, but I can't see from code snippet if you add you dispatching class to the displaylist.
Also, it does not look like you made a custom EventDispatcher, but a custom Event. But I could be wrong (can't see all of your code).
In order to listen your custom event you should have a valid reference to the dispatcher in your document class.
yourDispatcher.addEventListener(CustomVarEvent.pinClicked, pinClickedHandler);
Where yourDispatcher is the class dispatching the custom event.

Flash: Listen to all events of a type with one eventlistener

It's not a matter of life or death but I wonder if this could be possible:
I got a couple of events from one type of custom event (FormEvent) now I got a FormListener that listens to all those events and handles them according to the event type. Instead of adding one eventListener at the time I wish to add all events at once.
so now it looks like this:
private function addListeners():void {
addEventListener(FormEvent.SHOW_FORM, formListener);
addEventListener(FormEvent.SEND_FORM, formListener);
addEventListener(FormEvent.CANCEL_FORM, formListener);
}
private function formListener(event:formEvent):void {
switch(event.type){
case "show.form":
// handle show form stuff
break;
case "send.form":
// handle send form stuff
break;
case "cancel.form":
// handle cancel form stuff
break;
}
}
but instead of adding every event one at the time I would rather be doing something like
private function addListeners():void {
addEventListener(FormEvent.*, formListener);
}
I wonder if something like this is possible, i would love it. I work with loads of events :)
You only really need one event listener in this case anyhow. That listener will be listening for any change with the form and a parameter equal to what the change was becomes available to the event listener function. I will show you, but please remember that this is a pseudo situation and normally I wouldn't dispatch an event off of something as simple as a method call because the dispatch is implied so there is no real need to listen for it.
First the Custom Event
package com.yourDomain.events
{
import flash.events.Event;
public class FormEvent extends Event
{
//Public Properties
public static const CANCEL_FORM:int = "0";
public static const SHOW_FORM:int = "1";
public static const SEND_FORM:int = "2";
public static const STATE_CHANGED:String = "stateChanged";
//Private Properties
private var formState:int;
public function FormEvent(formState:int):void
{
super(STATE_CHANGED);
formState = formState;
}
}
}
So we have just created our custom event class and we have set it up so that we can catch the state through the listener function as I will demonstrate once done with the pseudo form class that will dispatch the for said custom event.
Remember that this is all hypothetical as I have no idea what your code looks like or how your implementing things. What is important is to notice that when I dispatch the event I need to send a parameter with it that reflects what the new state is.
package com.yourDomain.ui
{
import flash.events.Event;
import flash.events.EventDispatcher;
import com.yourDomain.events.FormEvent;
public class Form extends EventDispatcher
{
public function Form():void
{
//Anything you want form to do upon instantiation goes here.
}
public function cancelForm():void
{
dispatchEvent(new Event(FormEvent.CANCEL_FORM);
}
public function showForm():void
{
dispatchEvent(new Event(FormEvent.SHOW_FORM);
}
public function sendForm():void
{
dispatchEvent(new Event(FormEvent.SEND_FORM);
}
}
}
And finally we create the document class that will listen for it. Please know that I realize it isn't logical to create a listener that fires when you call a method of a class because you obviously know you called the method, but for this example it will due.
package com.yourDomain.ui
{
import com.yourDomain.ui.Form;
import com.yourDomain.events.FormEvent;
//Form is in the same package so we need not import it.
public class MainDocumentClass
{
private var _theForm:Form;
public function MainDocumentClass():void
{
_theForm = new Form();
_theForm.addEventListener(FormEvent.STATE_CHANGED, onFormStateChange, false, 0, true);
/*
The following three method calls each cause the
FormEvent.STATE_CHANGE event to be dispatched.
onFormStateChange is notified and checks what
the last change actually was.
*/
_theForm.cancelForm();
_theForm.showForm();
_theForm.sendForm();
}
private function onFormStateChange(e:FormEvent):void
{
switch(e.formState)
{
case CANCEL_FORM:
trace('The form was canceled');
break;
case SHOW_FORM:
trace('The form was revealed');
break;
case SEND_FORM:
trace('The form was sent');
break;
}
}
}
}
I hope that this was helpful, its late and I may have to revise some things later, but this should help get an understanding of how to make your own events and to customize how things work.
I don't know of any routines that let you do that directly, but you could write your own. The syntax here won't be perfect, but here's a first pass:
private function addMultipleEventListeners( evts:Array, callback:function ):void
{
for each( var evt:Event in evts )
{
addEventListener( evt, callback );
}
}
You could then call that routine like so:
var evts:Array = [ FormEvent.SHOW_FORM, FormEvent.SEND_FORM, FormEvent.CANCEL_FORM ];
addMultipleEventListeners( evts, formListener );