I was writing some control classes in AS 3 the other day and I realised I needed to create a custom event. I hadn’t done this in quite a while as my new duties don’t give me as much time to code as I’d like
. I had forgoten just how different it is from AS2. So, for my own sake (as the old memory isn’t what it used to be) and for those of you who don’t already know, here’s how to create and dispatch a custom event in ActionScript 3.0.The dispatchEvent functionality that you may have used in AS 2.0, for bespoke event broadcasts and extended event object handling, has been changed quite a bit for AS 3.0. I used dispatchEvent quite a lot in my work before AS 3.0 came along. Frankly it’s been invaluable. Much of the time I want to dispatch events that are not of the predefined variety. Or I need to send off specific, bespoke data with the event when it’s fired. Usually it’s both of these requirements. Some of my colleagues even wrap predefined events inside EventDispatchers as a method of extending the event.
So before I get to how it’s done in AS 3.0, let’s recap with an over view of how it’s done in AS 2.0, so we have a comparison.
AS 2.0
Generally you would import the Delegate and EventDispatcher classes:
import mx.utils.Delegate;
import mx.events.EventDispatcher;
Then you would set up a generic event dispatcher associated object:
public var objBroadcaster:Object;
Next you would instantiate and initialize the event dispatcher associated object.
this.objBroadcaster = new Object();
EventDispatcher.initialize(this.objBroadcaster);
The next step was to add event listeners. This would set up the event dispatcher associated object to listen for any number of bespoke events. In the addEventListener declaration you would specify the event type to listen for, followed by the event handler function that would handle the broadcast event and it’s data object:
objBroadcaster.addEventListener(”eventType”, Delegate.create(this, this.eventHandler));
I personally, often use a generic method for dispatching events to the event listeners. See below. But the principal is exactly the same. The dispatchEvent method broadcasts an event type occourance and it’s associated event data object (in our example below this is a variable or object called param).
private function dispatchCaller(eventName:String, param:Object){
dispatchEvent({type:eventName, param:param});
}
Finally, the event handler function would be called, receiving the event data object (with however much or little data you had included).
All nice and bespoke, nice and flexible.
AS 3.0
AS 3.0 is a different beast. It is based heavily on the event model, so it is natural that the event should take a primary position in this.
First, we create our new bespoke event class by extending the Event class itself. I have an example below that shows us creating an event class called MediaControlEvent.
package {
import flash.events.Event;public class MediaControlEvent extends flash.events.Event {
public static const CONTROL_TYPE:String = “headControl”;
private var command:String;public function MediaControlEvent( command:String ) {
super( CONTROL_TYPE);
this.command = command;
}
}
}
You’ll see we extend this class from the Event class. We define a public static constant variable called CONTROL_TYPE. This stores the string representation of the event type (no prizes for those of you who guessed that). We then define the command variable. This is our bespoke data extension to the Event class, and in our example, this too handles a string. We could of course extend it as much as we like and call the variables what ever we want.
The constructor receives a string and passes that through to the local command variable. Also in the constructor, we call the Event class constructor (super), passing it the CONTROL_TYPE variable.
The class which dispatches this event will need to import the following classes:
import flash.events.EventDispatcher;
import flash.events.Event;
import MediaControlEvent;
In the same class, just as with the AS 2.0 example, we need to add an event listener for our new MediaControlEvent.CONTROL_TYPE event and assign an event handler to respond to our MediaControlEvent.CONTROL_TYPE event broadcasts.
addEventListener(MediaControlEvent.CONTROL_TYPE, eventHandler);
Then, at the appropriate time (button press for example), we dispatch our event and our associated data.
dispatchEvent(new MediaControlEvent(”RW”));
In the example above I’m simply dispatching a single string data with the event (as defined in our extended event class), but you could obviously dispatch any amount of data of any type, provided your event class was extended to handle them.
Our event listener will hear this, fire off the registered event handler and pass on the accompanying data, and the event handler will deal with the data as necessary. In the example below it will just trace out the string we passed to the event.
public function eventHandler(evt:MediaControlEvent):void{
trace(evt.command)
}
So, I hope this helps with creating and dispatching bespoke events in AS3. I know it took me a while at first to figure out how they were done, but when I ws finished I realise how little code there actually was involved (it’s mostly in the extended Event class).
14 Comments to “Creating and Dispatching Custom Events in AS3”
Leave a Reply
You must be logged in to post a comment.
On July 4th, 2007 at 2:36 am
At least for AS3 you __must__ override clone() method of Event class in your sub-classes. Otherwise you get incorrect behavior when “forwarding” events.
VS
On July 4th, 2007 at 2:56 am
Valery,
could you elaborate on this. I haven’t seen this behavior, nor run into this problem so far, so it would be useful to everyone to have this clarified.
Thanks
Sean
On July 4th, 2007 at 3:16 am
What I think Valery is pointing out is that if you create a custom event and then try and bubble it you can get errors if you don’t override the clone() method. It can be a bit hit and miss though - no change there :p
On July 4th, 2007 at 7:40 am
The docs say cloning is required, but it is only necessary in certain situations - situations where the target changes for the same event. Case in point, consider dispatching a custom event to listeners. One listener, when recieving this event, wants to forward it on to, lets say, a composed instance. When re-dispatching this event through dispatchEvent(), the target for that event will need to change. When that happens, clone() is internally called for that event by the Flash Player and a new event is created (Note: new events are not cloned for bubbled events, the same event is used for bubbling, just with a modified currentTarget for each event). If clone is not defined for your event (overridden to return an instance of your custom event) a type error will occur because an Event instance will be returned (inherited clone()) where an event object of your custom event is expected.
Quick Flash CS3 example:
// CustomEvent.as
package {
import flash.events.Event;
public class CustomEvent extends Event {
public function CustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false){
super(type, bubbles, cancelable);
}
}
}
// Flash main timeline
addChild(new Sprite());
addEventListener(”custom”, fwd);
function fwd(event:CustomEvent){
getChildAt(0).dispatchEvent(event);
}
getChildAt(0).addEventListener(”custom”, getFwd);
function getFwd(event:CustomEvent){
// type error; event coming in
// as type Event, not CustomEvent
}
dispatchEvent(new CustomEvent(”custom”));
On July 7th, 2007 at 4:32 pm
@admin — sorry for not being explicit, but senocular explained absolutely correctly why this is necessary.
Btw, event re-dispatching (forwarding) is not so rare case in reality.
VS
On September 26th, 2007 at 2:06 am
If you are looking towards as3 —-
Hi,
Event listener model in cs3 looks nice it is not same as we were heaving earlier in as2 or till flash 8.
Here is an example of using and making your own custom event in as3.
This example allow user to load n number of XML file when XML file is loaded then dispatch an event “XMLLoaded” which can be listen by any other class any where.
There are two class
1. CustEvent
2. DEvt
Here are the definition of both class
CustEvent.as
/************************************************** *************************************
* Author - Sanjeev Rajput
* Date - 16-July-07
* class is used to load any XML file and dispatch an event when XML is loaded
************************************************** ***************************************/
package eventDispatch{
import flash.events.Event;
public class CustEvent extends Event {
public static const XMLLoaded:String = “XMLLoaded”;// Event Name
public var XMLData:XML // loaded XML data
public var XMLRef:String // XML file name
public function CustEvent(type:String, param:String,param1:XML) {
this.XMLData= param1;
this.XMLRef=param;
super(type);
}
}
}
DEvt.as
/************************************************** *************************************
* Author - Sanjeev Rajput
* Date - 16-July-07
* class is used to load any XML file and dispatch an event when XML is loaded
************************************************** ***************************************/
package eventDispatch{
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.events.Event;
import flash.net.URLRequest;
public class DEvt extends EventDispatcher {
private var xmlLdr:URLLoader;
private var urlr:URLRequest;
private var xmlpath_str:String;
public var XMLData:XML;
private var counter:int=0;
private var xmlRequestArr:Array
public function DEvt():void {
this.xmlRequestArr=new Array()
}
public function loadXML(fileRef:String,xmlRef:String):void {
this.xmlpath_str=fileRef;
this.xmlRequestArr.push(xmlRef)
this.xmlLdr = new URLLoader();
this.urlr=new URLRequest(this.xmlpath_str);
this.xmlLdr.addEventListener(Event.COMPLETE, completeHandler);
this.xmlLdr.load(this.urlr);
}
private function completeHandler(evt:Event) {
this.XMLData=new XML(evt.target.data);
this.dispatchEvent(new CustEvent(CustEvent.XMLLoaded,this.xmlRequestArr[this.counter],this.XMLData));
this.counter++
}
}
}
evtDispatchExample.fla
Inside this fla on very first frame I have following code
import eventDispatch.*;
var DEvt_obj:Evt=new DEvt();
DEvt_obj.loadXML(”xml.xml”,’xml0 File’);
DEvt_obj.loadXML(”xml1.xml”,’xml1 File’);
DEvt_obj.loadXML(”xml2.xml”,’xml2 File’);//—-and so on—
DEvt_obj.addEventListener(CustEvent.XMLLoaded,XMLL oaded);
function XMLLoaded(evt:CustEvent) {
//— here we can check which XML file is loaded—-
if(evt.XMLRef==’xml0′){
//— do necessary task when this file loaded
//—XML data can be found in evt.XMLData
trace(evt.XMLData) //– property of CustEvent class
}
if(evt.XMLRef==’xml1′){
//— do necessary task when this file loaded
//—XML data can be found in evt.XMLData
trace(evt.XMLData) //– property of CustEvent class
}
//—- and so on for n number of XML file—-
}
On November 15th, 2007 at 6:42 pm
The quotes you use in the MediaControlEvent package result in a syntax error in Flash if I copy from browser to Flash.
Rather than:
“headControl”
should be:
“headControl”
On November 16th, 2007 at 6:55 am
Hi Thomas,
that’s a copy and paste issue as your above comment shows perfectly - both your examples look exactly the same because the quotes have been written with the correct quotes
On December 9th, 2007 at 11:51 am
Hi there. Thanks for posting this simple, but clear glimpse in custom events. I just have one question:
Shouldn’t “private var command:String;” in MediaControlEvent be “public var command:String;”. Otherewise, I don’t see how you can trace out the ‘command’ var unless you use a getter, which you don’t.
Thanks - wes
On December 10th, 2007 at 7:49 am
Opps! Well spotted Wes. It should indeed be public. I was obviously typing in a hurry and trying to code 2 things at once (as usual).
On September 1st, 2008 at 4:40 am
hi,
Pls help me, i have buble event error in as 2.
import mx.transitions.Tween;
import mx.transitions.easing.*;
import mx.utils.Delegate;
import flash.display.Sprite;
class galleryMenu extends MovieClip {
var h:Number;//Menu heigth
var height_sel:Number;//sel height
var sel:Number;//selected menu
var oldSel:Number;//last selected menu
var cant:Number;//quantity of menues
var sqr:MovieClip;
var square:MovieClip;//This is for designers, they need some visual feedback of menu area
var desc:MovieClip;
var thumbcont:MovieClip;
var thumb:MovieClip;
public static var CurrentCount:Number;
public static var BaseHeight:Number;
public static var stSel:Number;
public static var stOldSel:Number;
public static var IsUp:Boolean;
public static var IsDown:Boolean;
/*
* Constructor
*/
function galleryMenu() {
sqr._visible = false;
//square._visible = false;
desc._visible = false;
setSel(0);//First selected by default
}
/*
* Set the menu based on passed array
*/
function setMenu(items:Array, descItems:Array, thumbs:Array) {
cant = items.length;
//trace(cant)
var t:MovieClip;
var d:MovieClip;
var k:MovieClip;
var md:MovieClip;
if (CurrentCount != cant) {
CurrentCount = cant;
}
//Attach items
for (var i = 0; i0) ? t._y+t._height : 0, nr:i});
t.menu_txt.text = items[i];
t.menu_txt._height = 23;
md = attachMovie(”thumbcont”, “thumbcont”+i, i+cant*2, {nr:i});
//a = attachMovie(”thumbcont”, “thumbcont”+i, i+cant+1, {nr:i});
d = attachMovie(”desc_item”, “desc_item”+i, i+cant, {nr:i});
d._visible = false;
new Color(this.square[”item”+stSel].menu_txt).setRGB(0×045E83);
for (var m = 0; m1) ? act[1]+”:”+act[2] : act[1];
getURL(go, “_new”);
break;
default :
//trace(_parent.ref.des.text);
//trace(”Display Data On this click”);
}
};
t.onRollOver = function() {
if (this.nr != stSel) {
var myColor = new Color(this.menu_txt).setRGB(0×045E83);//onRollOver color
}
};
t.onRollOut = function() {
if (this.nr != stSel) {
var myColor = new Color(this.menu_txt).setRGB(0xffffff);
}
//onRollOut color
};
}
for (var n = 1; n=cant?i-sel:i))
new myTween(CurrentMenu[”item”+i], “_y”, Regular.easeIn, CurrentMenu[”item”+i]._y, -27.5-((cant-i+sel)*h), (cant-1)*h, (i-sel)*h, 0.75+(cant-oldSel+sel-1), 0.75, true);
new Color(CurrentMenu[”item”+i].menu_txt).setRGB(0xffffff);//md
}
var SelTwn:Tween = new Tween(CurrentMenu[”item”+sel], “_y”, Regular.easeIn, CurrentMenu[”item”+sel]._y, 0, 1+(cant-oldSel+sel-1), true);
SelTwn.onMotionFinished = function() {
//new Tween(mySubLink, “_yscale”, None.easeInOut, 0, 100, 0.75, true);
CurrentMenu._parent[”desc_item”+stSel]._visible = true;
CurrentMenu._parent[”thumbcont”+stSel]._visible = true;
CurrentMenu._parent[”thumbcont”+stOldSel]._visible = true;
//trace(CurrentMenu._parent[”thumbcont”+stOldSel]);
//trace(CurrentMenu._parent[”thumbcont”+stSel]);
new Tween(CurrentMenu._parent[”desc_item”+stSel].desc_item_msk, “_yscale”, None.easeInOut, 0, 100, 0.65, true);
};
for (var i = sel+1; i
On September 16th, 2008 at 6:43 am
Hi, thanks for this. It really makes clear how to create totally custom services. I do have a problem though - if I try to dispatch an event from one timeline and pick it up on another, it either doesn’t register or I get a type coercion error. I’ve tried all the targeting ploys I can think of… Any ideas? DV
On January 16th, 2009 at 1:10 pm
Is it possible to create various types of the same custom event?
Like having MediaControlEvent.CONTROL_TYPE_1, MediaControlEvent.CONTROL_TYPE_2, etc.
I don´t see it possible using the call of super() under the constructor method, but I just wonder if there´s some other way to achieve this.
On July 22nd, 2009 at 7:18 am
Hi David,
The MediaControlEvent does not bubble. Try this piece of code:
package {
import flash.events.Event;
public class MediaControlEvent extends flash.events.Event {
public static const CONTROL_TYPE:String = “headControl”;
private var command:String;
public function MediaControlEvent( command:String ) {
// bubbles=true, cancelable=false
super( CONTROL_TYPE, true, false);
this.command = command;
}
}
}
Hopefully it works on multiple timelines.