flash personalised_events_storing

Storing the EventDispatcher

In order to understand this concept we will look at the Administrator class developed in the chapter entitled “Object oriented programming”.

Here is the code of the Administrator class:

package
{
// the inheritance is translated by the keyword extends
public class Administrator extends Player
{
public function Administrator ( pName:String, pSurname:String,
pAge:int, pTown:String )
{
super ( pName, pSurname, pAge, pTown );
}
// method allowing the administrator to present himself
override public function toPresent ( ):void
{
// triggers the overloaded method
super.toPresent();
trace("I am a moderator");
}
// method allowing the deletion of a player of the game
public function kickPlayer ( pPlayer:Player ):void
{
trace ("Kick " + pPlayer );
}
// method allowing the playing of a sound
public function playSound ( ):void
{
trace("Play a sound");
}
}
}

On discovering the idea of personalized events we decide to broadcast an event when a player is deleted from the game. The kickPlayer method will therefore have to call the dispatchEvent method which is impossible for the moment.

The Administrator class cannot broadcast events by default, we therefore attempt to extend the EventDispatcher class. We are therefore confronted with a problem because the Administrator class already extends the Player class. How can we do it then?

In the article entitled “Object oriented programming” we saw an alternative to inheritance. This technique was a “possesses one” type relation rather than an “is one”. In summary, instead of extending a class to inherit its capabilities, we‘re going to create an instance of it in the class and delegate the functionality.

Instead of sub-classing the EventDispatcher we’re going to store an instance of the EventDispatcher class and delegate the event management to it:

package
{
import flash.events.EventDispatcher;
// the inheritance is translated by the keyword extends
public class Administrator extends Player
{
// stores the instance of the EventDispatcher
private var broadcaster:EventDispatcher;
public function Administrator ( pName:String, pSurname:String,
pAge:int, pTown:String )
{
super ( pName, pSurname, pAge, pTown );
// creation of an EventDispatcher instance
broadcaster = new EventDispatcher();
}
// method allowing the administrator to present himself
override public function toPresent ( ):void
{
// triggers the overloaded method
super.toPresent();
trace("I am a moderator");
}
// method allowing the deletion of a player of the game
public function kickPlayer ( pPlayer:Player ):void
{
// code managing the disconnection of the player concerned
trace ("Kick " + pPlayer );
}
// method allowing the playing of a sound
public function playSound ( ):void
{
trace("Play a sound");
}
}
}

Of course the Administrator class doesn’t have the dispatchEvent, addEventListener, and removeEventListener methods for the moment.

It’s up to us to implement them, to do this we implement the flash.events.IEventDispatcher interface:

package
{
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.Event;
// the inheritance is translated by the keyword extends
public class Administrator extends Player implements IEventDispatcher
{
// stores the instance of the EventDispatcher
private var broadcaster:EventDispatcher;
public function Administrator ( pName:String, pSurname:String,
pAge:int, pTown:String )
{
super ( pName, pSurname, pAge, pTown );
// creation of an EventDispatcher instance
broadcaster = new EventDispatcher();
}
// method allowing the administrator to present himself
override public function toPresent ( ):void
{
// triggers the overloaded method
super.toPresent();
trace("I am a moderator");
}
// method allowing the deletion of a player of the game
public function kickPlayer ( pPlayer:Player ):void
{
// code managing the disconnection of the player concerned
trace ("Kick " + pPlayer );
}
// method allowing the playing of a sound
public function playSound ( ):void
{
trace("Play a sound");
}
public function addEventListener( type:String, listener:Function,
useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false
):void
{
broadcaster.addEventListener( type, listener, useCapture, priority,
useWeakReference );
}
public function dispatchEvent( event:Event ):Boolean
{
return broadcaster.dispatchEvent( event );
}
public function hasEventListener( type:String ):Boolean
{
return broadcaster.hasEventListener( type );
}
public function removeEventListener( type:String, listener:Function,
useCapture:Boolean=false ):void
{
broadcaster.removeEventListener( type, listener, useCapture );
}
public function willTrigger( type:String ):Boolean
{
return broadcaster.willTrigger( type );
}
}
}

In order to make our Administrator class an event broadcaster we implement the IEventDispatcher interface. Each method defining the EventDispatcher should therefore be defined in the Administrator class. We define a constant of the KICK_PLAYER class storing the name of the event:

public static const KICK_PLAYER:String = "disconnectPlayer";

Then we modify the kickPlayer method in order to broadcast an Administrator.KICK_PLAYER event:

// method allowing the deletion of a player of the game
public function kickPlayer ( pPlayer:Player ):void
{
// code managing the disconnection of the player concerned
// broadcasting the Administrator.KICK_PLAYER event
dispatchEvent ( new Event ( Administrator.KICK_PLAYER ) );
}

In the following code, we create a moderator and a player. When a player is deleted from the game, the Administrator.KICK_PLAYER event is broadcast:

var myModo:Administrator = new Administrator("Michael", "Jackson", 48,
"Los Angeles");
// listens to the Administrator.KICK_PLAYER event
myModo.addEventListener (Administrator.KICK_PLAYER, disconnectionPlayer );
var firstPlayer:Player = new Player ("Bobby", "Womack", 66, "Detroit");
// a player is deleted from the game
myModo.kickPlayer ( firstPlayer );
// listener function
function disconnectionPlayer ( pEvt:Event ):void
{
trace( "A player has left the game!" );
}

Thanks to composition we have been able to make the Administrator class broadcast events. Inheritance is therefore not the only alternative to allowing a class to broadcast events.

Worth remembering

  • When we cannot extend the EventDispatcher class we store an instance of it and delegate the functionality.
  • The IeventDispatcher interface allows for the obligatory implementation of different methods necessary for broadcasting events.

Passing information

The majority of broadcasted events contain different information relative to the state of the object broadcasting the event. In the chapter entitled “Interactivity” we saw that the MouseEvent or KeyboardEvent classes possessed the properties to inform us regarding the state of the broadcasted event object.

In the previous exercise, the Administrator class broadcast an Administrator.KICK_PLAYER event but this contained no information. It would be interesting if it informed us regarding the deleted player. In the same way the XMLLoader class could broadcast an XMLoader.COMPLETE event containing the loaded XML feed in order for it to be easier to use by listeners.

In order to pass information upon the broadcasting of an event we have to extend the Event class in order to create a specific event object to the broadcast event. In reality, we fallback on the model defined by ActionScript 3. When we listen to a mouse event we instinctively orient ourselves to the flash.events.MouseEvent class. In the same way, to listen to the keyboard events we use the flash.events.KeyboardEvent class.

In a general way, we must create an event class for each class broadcasting events containing specific information.

We are therefore going to extend the Event class and to create a class named AdministratorEvent:

package
{
import flash.events.Event;
public class AdministratorEvent extends Event
{
public static const KICK_PLAYER:String = "disconnectPlayer";
public function AdministratorEvent ( type:String,
bubbles:Boolean=false, cancelable:Boolean=false )
{
// initialisation of the constructor of the Event class
super( type, bubbles, cancelable );
}
// the clone method should be overloaded
public override function clone ():Event
{
return new AdministratorEvent ( type, bubbles, cancelable )
}
// the toString method should be overloaded
public override function toString ():String
{
return '[AdministratorEvent type="'+ type +'" bubbles=' +
bubbles + ' cancelable=' + cancelable + ']';
}
}
}

Then we use an instance of this class in order to broadcast the event:

// method allowing the deletion of a player of the game
public function kickPlayer ( pPlayer:Player ):void
{
// code managing the disconnection of the player concerned
// broadcasts the AdministratorEvent.KICK_PLAYER event
dispatchEvent ( new AdministratorEvent (
AdministratorEvent.KICK_PLAYER ) );
}

It is important to note that until now we have broadcast events with the Event class only. When we define a specific event class we store it in a class constant. Instead of targeting the name of the event on the class broadcasting the event:

// listens to the Administrator.KICK_PLAYER event
myModo.addEventListener ( Administrator.KICK_PLAYER, playerLeaves );

We prefer to store the name of the event in the constant of the event class:

// listens to the AdministratorEvent.KICK_PLAYER event
myModo.addEventListener ( AdministratorEvent.KICK_PLAYER, playerLeaves );

At this point, no information is transmitted by the event object AdministratorEvent. In order to store the supplementary data we define the desired properties in the class then we allocate their value according to the parameters passed during the instantiation of the event object:

package
{
import flash.events.Event;
public class AdministratorEvent extends Event
{
public static const KICK_PLAYER:String = "disconnectPlayer";
public var player:Player;
public function AdministratorEvent ( type:String,
bubbles:Boolean=false, cancelable:Boolean=false, pPlayer:Player=null )
{
// initialisation of the constructor of the Event class
super( type, bubbles, cancelable );
// stores the deleted player
player = pPlayer;
}
// the clone method should be overloaded
public override function clone ():Event
{
return new AdministratorEvent ( type, bubbles, cancelable )
}
// the toString method should be overloaded
public override function toString ():String
{
return "[AdministratorEvent type : " + type +", bubbles : " +
bubbles + ", cancelable : " + cancelable + "]";
}
}
}

Then we modify the KickPlayer method of the Administrator class in order to pass the deleted player as a parameter:

// method allowing the deletion of a player of the game
public function kickPlayer ( pPlayer:Player ):void
{
// code managing the disconnection of the player concerned
// broadcasting the Administrator.KICK_PLAYER event
dispatchEvent ( new AdministratorEvent ( AdministratorEvent.
KICK_PLAYER, false, false, pPlayer ) );
}

When the event is broadcast, the listener function accesses the player property in order to know which player has left the game:

var myModo:Administrator = new Administrator("Michael", "Jackson", 48,
"Los Angeles");
// listens to the Administrator.KICK_PLAYER event
myModo.addEventListener ( AdministratorEvent.KICK_PLAYER, playerLeaves );
var firstPlayer:Player = new Player ("Bobby", "Womack", 66, "Detroit");
// a player is deleted from the game
myModo.kickPlayer ( firstPlayer );
// listener function
function playerLeaves ( pEvt:AdministratorEvent ):void
{
// displays : [AdministratorEvent type="disconnectPlayer" bubbles=false
cancelable=false]
trace( pEvt );
// displays that Bobby has left the game!
trace( pEvt.player.name + " has left the game!");
}

By testing the previous code, the listener function PlayerLeaves is notified of the AdministratorEvent.KICK_PLAYER event and receives an AdministratorEvent type event object.

The player property returns the deleted player, we just now need to access the desired properties.

Worth remembering

  • In order to pass parameters upon the broadcast of an event we have to extend the Event class.
  • The classes and sub-classes of the Event represent the broadcasted event objects.

Menu and personalized events

At the end of the previous chapter we developed an entirely dynamic menu. We hadn’t totally finished it though, we hadn’t added personalized events.

We need to broadcast an event which contains the name of the SWF linked to the clicked button.

In the events directory placed in the org directory we create a ButtonEvent class:

package org.bytearray.events
{
import flash.events.Event;
public class ButtonEvent extends Event
{
public static const CLICK:String = "buttonClick";
public var link:String;
public function ButtonEvent ( type:String, bubbles:Boolean=false,
cancelable:Boolean=false, pLink:String=null )
{
// initialisation of the constructor of the Event class
super( type, bubbles, cancelable );
// stores the link to the clicked button
link = pLink;
}
// the clone method should be overloaded
public override function clone ():Event
{
return new ButtonEvent ( type, bubbles, cancelable, link )
}
// the toString method should be overloaded
public override function toString ():String
{
return '[ButtonEvent type="'+ type +'" bubbles=' + bubbles + '
eventPhase='+ eventPhase + ' cancelable=' + cancelable + ']';
}
}
}

In the constructor we add the following line in order to listen to the MouseEvent.CLICK event:

// listener of the MouseEvent.CLICK event
addEventListener ( MouseEvent.CLICK, mouseClick );

We define the MouseClick listener method which broadcasts the ButtonEvent.CLICK event by passing the corresponding SWF:

private function mouseClick ( pEvt:MouseEvent ):void
{
// broadcast of the ButtonEvent.CLICK event
dispatchEvent ( new ButtonEvent ( ButtonEvent.CLICK, true, false, swf
) );
}

Then we listen to the ButtonEvent.CLICK event alongside each button, we use the capture phase in order to optimise the code:

// import the Button class
import org.bytearray.ui.Button;
import org.bytearray.events.ButtonEvent;
// associative array containing data
var data:Array = new Array();
data.push ( { caption : "Homepage", speed : 1, swf : "homepage.swf",
colour : 0x999900 } );
data.push ( { caption : "Photos", speed : 1, swf : "photos.swf", colour
: 0x881122 } );
data.push ( { caption : "Blog", speed : 1, swf : "blog.swf", colour :
0x995471 } );
data.push ( { caption : "Links", speed : 1, swf : "links.swf", colour :
0xCC21FF } );
data.push ( { caption : "Forum", speed : 1, swf : "forum.swf", colour :
0x977821 } );
// number of sections
var lng:int = data.length;
// menu container
var menuContainer:Sprite = new Sprite();
addChild ( menuContainer );
for (var i:int = 0; i< lng; i++ )
{
// recuperation of information
var caption:String = data[i].caption;
var colour:Number = data[i].colour;
var speed:Number = data[i].speed;
var swf:String = data[i].swf;
// creation of buttons
var myButton:Button = new Button( 60, 120, swf, colour, speed, caption
);
// positioning
myButton.y = 50 * i;
// addition to the display list
menuContainer.addChild ( myButton );
}
// listening to the ButtonEvent.CLICK event for the capture phase
menuContainer.addEventListener ( ButtonEvent.CLICK, clickButton, true );
function clickButton ( pEvt:ButtonEvent ):void
{
// displays :
/*homepage.swf
photos.swf
blog.swf
links.swf
*/
trace( pEvt.link );
}

When the ButtonEvent.CLICK event is broadcast, the clickButton function is triggered. The Link property of the ButtonEvent type event object allows us to load the corresponding SWF. This could contain a URL to another application to access when the user clicks on a button.

It would be totally possible to define new properties in the ButtonEvent class such as caption, colour or speed or others in order to pass the characteristics of each button.

Here is the complete code of the Button class:

package org.bytearray.ui
{
import flash.display.Shape;
import flash.display.Sprite;
import flash.text.Font;
import flash.text.TextFormat;
import org.events.ButtonEvent;
// importation of classes linking Tween to the movement
import fl.transitions.Tween;
import fl.transitions.easing.Bounce;
// importation of the MouseEvent class
import flash.events.MouseEvent;
// importation of the TextField and TextFieldAutoSize classes
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
public class Button extends Sprite
{
// stores the button background
private var buttonBackground:Shape;
// stores the Tween object for the different button states
private var etatTween:Tween;
// stores the button references
private static var buttonsArray:Array = new Array();
// stores the current colour of the button
private var colour:Number;
// stores the opening speed of each button
private var speed:Number;
// button caption
private var caption:TextField;
// caption formatting
private var formatting:TextFormat;
// associated swf
private var swf:String;
public function Button ( pWidth:Number, pHeight:Number, pSWF:String,
pColour:Number, pSpeed:Number, pCaption:String )
{
// adds each instance to the array
Button.buttonsArray.push ( this );
// creation of button background
buttonBackground = new Shape();
// addition to the display list
addChild ( buttonBackground );
// creates the textfield
caption = new TextField();
// automatic resizing of the textfield
caption.autoSize = TextFieldAutoSize.LEFT;
// addition to the display list
addChild ( caption );
// allocates the caption
caption.text = pCaption;
// activates the use of the font embedded
caption.embedFonts = true;
// creates a formatting object
formatting = new TextFormat();
// font size
formatting.size = 12;
// instancing of the embedded font
var font:MyFont = new MyFont();
// allocation of the font to the formatting
formatting.font = font.fontName;
// allocation of formatting to the textfield
caption.setTextFormat ( formatting );
// makes the textfield unselectable
caption.selectable = false;
// stores the colour passed as a parameter
colour = pColour;
// stores the name of the SWF
swf = pSWF;
// draws the button
buttonBackground.graphics.beginFill ( colour, 1 );
buttonBackground.graphics.drawRect ( 0, 0, pWidth, pHeight );
// activation of the button mode
buttonMode = true;
// deactivation of the child objects
mouseChildren = false;
// allocates the speed passed as a parameter
setSpeed ( pSpeed );
// creation of the Tween object
etatTween = new Tween ( buttonBackground, "scaleX", Bounce.easeOut, 1,
1, speed, true );
// listens to the MouseEvent.ROLL_OVER event
addEventListener ( MouseEvent.ROLL_OVER, rolloverMouse );
// listener of the MouseEvent.CLICK event
addEventListener ( MouseEvent.CLICK, mouseClick );
}
private function mouseClick ( pEvt:MouseEvent ):void
{
// broadcast of the ButtonEvent.CLICK event
dispatchEvent ( new ButtonEvent ( ButtonEvent.CLICK, true, false,
swf ) );
}
// triggered on button click
private function rolloverMouse ( pEvt:MouseEvent ):void
{
// stores the array length
var lng:int = Button.buttonsArray.length;
for (var i:int = 0; i<lng; i++ )
Button.buttonsArray[i].close();
// triggering the animation
etatTween.continueTo ( 2, speed );
}
// method allowing the closure of the button
private function close ():void
{
// closes the button
etatTween.continueTo ( 1, speed );
}
// manages the allocation of the speed
public function setSpeed ( pSpeed:Number ):void
{
// allocates the speed
if ( pSpeed >= 1 && pSpeed <= 10 ) speed = pSpeed;
else
{
trace("Error : Speed not correct, the value should be
between 1 and 10");
speed = 1;
}
}
}
}

The broadcast of personalized events is a key point in all object oriented development in ActionScript. By broadcasting our own events we make our objects compatible with ActionScript 3’s event model and make them easily reusable. When delivering a class to a developer, he will start by looking at the capabilities offered then the different events broadcast to see how to communicate with it.

Worth remembering

  • We can define as many properties as we wish in the broadcast object event.

We are now going to look at the concept of the class of a document. This new element brought by Flash CS3 will allow us to improve our ActionScript 3 developments.








Adobe Training center


Mediabox Training Centre © 2000 - 2008 All rights reserved.
Adobe Authorized Training Centre. State convention under number 25 14 02167 14.
Mediabox : SARL au capital de 62.000€ - Activity number: 25 14 02167 14 - SIRET : 493 716 468 00027
MEDIABOX, 102 Avenue des Champs Elysées, 75008 PARIS - Tel. +33(0)2.31.91.96.89 - Fax. +33(0)2.72.68.56.42