flash personalised_events_eventdispatcher

The EventDispatcher class

As we saw at the start of these articles, ActionScript 3 is a language based on the event model called Document Object Model. This relies on the EventDispatcher class from which all the native classes of Flash player’s API are inherited.

In the following code we see the relation between the EventDispatcher class and two graphic classes:

var mySprite:Sprite = new Sprite();
// displays : true
trace( mySprite is EventDispatcher );
var myClip:MovieClip = new MovieClip();
// displays : true
trace( myClip is EventDispatcher );

Remember, all of the classes coming from the flash package are sub-classes of the EventDispatcher and have this common type.

We create a Sprite then we listen to a personalised event in it:

// creation of a Sprite
var mySprite:Sprite = new Sprite();
// listening to the myEvent event
mySprite.addEventListener ( "myEvent", listener );
// listener function
function listener ( pEvt:Event ):void
{
trace( pEvt );
}

The Sprite class doesn’t broadcast myEvent events by default, but we can broadcast it by using the dispatchEvent method. Here is its signature:

public function dispatchEvent(event:Event):Boolean

In ActionScript 2 the dispatchEvent method broadcast a non-typed event object. We generally use a literal object to which we manually add different properties.

In ActionScript 3, to broadcast an event we have to create an event object, represented by an instance of the flash.events. class:

// creation of the event object
var eventObject:Event = new Event (type, bubbles, cancelable);

The constructor of the Event class takes three parameters:

  • type: the name of the event to broadcast.
  • bubbles: indicates if the event participates in the bubbling phase.
  • cancelable: indicates if the event can be cancelled.

In the majority of situations we only use the first type parameter of the Event class.

Once the event object is created, we pass it to the dispatchEvent method:

// creation of a Sprite
var mySprite:Sprite = new Sprite();
// listening to the myEvent event
mySprite.addEventListener ( "myEvent", listener );
// listener function
function listener (pEvt:Event):void
{
// displays [Event type="myEvent" bubbles=false cancelable=false
eventPhase=2]
trace( pEvt );
}
// creation of the event object
var eventObject:Event = new Event (“myEvent”, bubbles,
cancelable);
// we diffuse the myEvent event
mySprite.dispatchEvent (eventObject);

Generally we don’t create the event object separately, we instantiate it directly as a parameter of the dispatchEvent method:

// creation of a Sprite
var mySprite:Sprite = new Sprite();
// listening to the myEvent event
mySprite.addEventListener ("myEvent", listener );
// listener function
function listener ( pEvt:Event ):void
{
// displays [Event type="myEvent" bubbles=false cancelable=false
eventPhase=2]
trace( pEvt );
}
// we diffuse the myEvent event
mySprite.dispatchEvent ( new Event (“myEvent”) );

This example show us the ease with which we can broadcast an event in ActionScript 3.

Worth remembering

  • The ActionScript 3 event model relies on the EventDispatcher class.
  • All of the classes in the flash package can broadcast native or personalized events.
  • In order to broadcast an event, we use the dispatchEvent method.
  • The dispatchEvent method accepts an instance of the Event class as a parameter.

Putting it into practice

We will now develop a class allowing a symbol to move in different directions. When it arrives at its destination we want to broadcast an appropriate event.

Alongside a Flash CS3 document we save a class called Ball.as containing the following code:

package
{
import flash.display.Sprite;
// the Ball class extends the Sprite class
public class Ball extends Sprite
{
public function Ball ()
{
trace( this );
}
}
}

We link our circular shape symbol to this by using the Linking properties panel. Then we instantiate the symbol:

// creation of the symbol
// displays : [object Ball]
var myBall:Ball = new Ball();
// added to the display list
addChild ( myBall );

On each mouse click on the stage, the ball should go to the area clicked. We should therefore listen to the MouseEvent.CLICK event in a global way in the Stage object.

We saw in the previous chapter how to access the Stage object in a secure way. We integrate the same mechanism on the Ball class:

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
// the Ball class extends the Sprite class
public class Ball extends Sprite
{
public function Ball ()
{
// listening to the Event.ADDED_TO_STAGE event
addEventListener ( Event.ADDED_TO_STAGE, addDisplay );
}
private function addDisplay( pEvt:Event ):void
{
// listening to the MouseEvent.CLICK event
stage.addEventListener ( MouseEvent.CLICK, mouseClick );
}
private function mouseClick ( pEvt:MouseEvent ):void
{
// displays : [MouseEvent type="click" bubbles=true
cancelable=false eventPhase=2 localX=81 localY=127 stageX=81 stageY=127
relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
trace( pEvt );
}
}
}

On each click on the stage, the MouseEvent.CLICK event is broadcast, the mouseClick listener function is therefore triggered.

We are going to integrate the idea of movement. We will define the two properties mouseX and mouseY in the class. These will allow us to store the position of the mouse:

// stores the coordinates of the mouse
private var mouseX:Number;
private var mouseY:Number;

Then we modify the mouseClick method in order to give it its properties:

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
// the Ball class extends the Sprite class
public class Ball extends Sprite
{
// stores the coordinates of the mouse
private var mouseX:Number;
private var mouseY:Number;
public function Ball ()
{
// listening to the Event.ADDED_TO_STAGE event
addEventListener ( Event.ADDED_TO_STAGE, addDisplay );
}
private function addDisplay( pEvt:Event ):void
{
// listening to the MouseEvent.CLICK event
stage.addEventListener ( MouseEvent.CLICK, mouseClick );
}
private function mouseClick ( pEvt:MouseEvent ):void
{
// allocates the coordinates to the properties
mouseX = pEvt.stageX;
mouseY = pEvt.stageY;
}
}
}

Then we trigger the movement by listening to the Event.ENTER_FRAME event inherited from the Sprite class:

package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
// the Ball class extends the Sprite class
public class Ball extends Sprite
{
// stores the coordinates of the mouse
private var mouseX:Number;
private var mouseY:Number;
public function Ball ()
{
// listening to the Event.ADDED_TO_STAGE event
addEventListener ( Event.ADDED_TO_STAGE, addDisplay );
}
private function addDisplay( pEvt:Event ):void
{
// listening to the MouseEvent.CLICK event
stage.addEventListener ( MouseEvent.CLICK, mouseClick );
}
private function mouseClick ( pEvt:MouseEvent ):void
{
// allocates the coordinates to the properties
mouseX = pEvt.stageX;
mouseY = pEvt.stageY;
addEventListener ( Event.ENTER_FRAME, movement );
}
private function movement ( pEvt:Event ):void
{
// evaluates the x and y destination
var destinationX:Number = ( mouseX - width/2);
var destinationY:Number = ( mouseY - height/2);
// moves the ball with an inertia effect
x -= (x - destinationX)*.1;
y -= (y - destinationY)*.1;
}
}
}

If we test our animation, the ball moves to the area which has been clicked with an inertia effect as illustrated here:

10.2.1.jpg

When a movement has happened we generally want to know when it will stop. The Tween class broadcasts by default all the events necessary for the synchronization of an animation. So how can we broadcast our event?

The Sprite class is inherited from the EventDispatcher class and can therefore broadcast any event. In the movement method we test if the difference between the current position and its destination is less than 1. If this is the case it means that we have arrived at our destination.

private function movement ( pEvt:Event ):void
{
// evaluates the x and y destination
var destinationX:Number = ( mouseX - width/2);
var destinationY:Number = ( mouseY - height/2);
// moves the ball with an inertia effect
x -= (x - destinationX)*.1;
y -= (y - destinationY)*.1;
if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y - destinationY
) < 1 )
{
removeEventListener ( Event.ENTER_FRAME, movement );
trace ("arrived at destination!");
}
}

The Math.abs method allows us to make the distance absolute, as a distance between two points is always positive.

Choosing an event name

We delete the listener to the Event.ENTER_FRAME event when the ball arrives at its destination in order to optimize resources, then we display a message indicating that the ball has arrived.

When testing our animation we see that the message indicating that the arrival has been correctly triggered, but for the moment no event has been broadcast.

When an object broadcasts an event we should make sure that our name is simple and intuitive for the people that will be using the class. In our example we will broadcast a movementFinished event.

We modify the movement method in order for it to be broadcast:

private function movement ( pEvt:Event ):void
{
// evaluates the x and y destination
var destinationX:Number = ( mouseX - width/2);
var destinationY:Number = ( mouseY - height/2);
// moves the ball with an inertia effect
x -= (x - destinationX)*.1;
y -= (y - destinationY)*.1;
if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y - destinationY
) < 1 )
{
removeEventListener ( Event.ENTER_FRAME, movement );
// broadcast of the motionComplete event
dispatchEvent ( new Event ("movementFinished") );
}
}

Our ball symbol broadcasts a movementFinished event. We just now to listen to it:

// creation of the symbol
// displays : [object Ball]
var myBall:Ball = new Ball();
// added to the display list
addChild ( myBall );
// listener to the personalised motionComplete event
myBall.addEventListener ( "movementFinished", arrived );
// listener function
function arrived ( pEvt:Event ):void
{
trace("movement finished!");
}

The arrived listener function is triggered when the ball arrives at its destination. In the event of reusing the Ball class we know that it broadcasts the event movementFinished. It’s up to us to decide what to do when the event is broadcast, the reuse of the Ball class is therefore made easier.

Our code is working well but it is not optimized. In the chapter entitled The event model we saw that we should never qualify event names directly. It makes our code rigid and not standardized.

We prefer the use of class constants. We therefore define a constant property MOVEMENT_FINISHED in the Ball class containing the name of the broadcast event:

// stores the name of the broadcast event
public static const MOVEMENT_FINISHED:String = "movementFinished";

Then we target the property in order to listen to the event:

// listnening to the personalised Ball.MOVEMENT_FINISHED event
myBall.addEventListener ( Ball.MOVEMENT_FINISHED, arrived );

We modify the movement method in order to target the Ball.MOVEMENT_FINISHED constant:

private function movement ( pEvt:Event ):void
{
// evaluates the x and y destination
var destinationX:Number = ( mouseX - width/2);
var destinationY:Number = ( mouseY - height/2);
// moves the ball with an inertia effect
x -= (x - destinationX)*.1;
y -= (y - destinationY)*.1;
if ( Math.abs ( x - destinationX ) < 1 && Math.abs ( y - destinationY
) < 1 )
{
removeEventListener ( Event.ENTER_FRAME, movement );
// broadcast of the motionComplete event
dispatchEvent ( new Event ( Ball.MOVEMENT_FINISHED ) );
}
}

We have just covered one of the most common cases of broadcasting personalized events. Let’s now see other cases.

Worth remembering

  • An event should have a simple and intuitive name.
  • In order to store the event name we always use a constant class property.







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