overview
learn how to implement events for a class
start by building a general event system
then apply the system to an example
java's delegation event model
event broadcasting system in java is called "delegation event model"
we'll apply that system to actionscript
participants
three participants in the event delegation model:
general structure
event occurs (e.g., mouse click, ending of a game, new user joins a chat...etc)
event source broadcasts event by invoking an agreed-upon method on all event listeners
event object is passed to agreed-upon method as an argument
event listeners respond as they see fit (e.g., submit a form, show a game over screen, display a welcome message to the new chat user)
the event source
the event source includes two classes:
EventListenerList
: utility to manage event listenersEventListenerList implemenatation
EventListenerList
resides in "event
" pacakge
class event.EventListenerList { }
EventListenerList
also imports the contents of the event package
import event.*;
array of listener objects stored in instance property, listeners
private var listeners:Array;
constructor creates empty listeners array:
public function EventListenerList () { listeners = new Array(); }
addObj()
method adds a listener to the listeners
array
public function addObj (l:EventListener):Boolean { // Search for the specified listener. var len:Number = listeners.length; for (var i = len; --i >= 0; ) { if (listeners[i] == l) { return false; } } // The new listener is not already in the list, so add it. listeners.push(l); return true; }
removeObj()
method removes a listener from the listeners
array
public function removeObj (l:EventListener):Boolean { // Search for the specified listener. var len:Number = listeners.length; for (var i:Number = len; --i >= 0; ) { if (listeners[i] == l) { // We found the listener, so remove it. listeners.splice(i, 1); // Quit looking. return true; } } return false; }
getListeners()
method returns a copy of the listener list, used to broadcast an event
public function getListeners ():Array { // Return a copy of the list, not the list itself. return listeners.slice(0); }
the event object
the event object includes two classes:
EventObject
, a base class for all event objectsEventObject
subclass, whose properties and methods describe a specific eventthe subclass is user-defined, per event implementation
EventObject implementation
EventObject
resides in "event
" pacakge
class event.EventObject { }
EventObject
stores a reference to the event source in a property, source
:
private var source:Object;
EventObject
is passed event source reference at construction time
public function EventObject (src:Object) { source = src; }
getSource()
method returns the event source
public function getSource ():Object { return source; }
event source reference used by event listeners to:
subclasses of EventObject
will add methods to retrieve event-specific information
the event listener
event listener includes three participants:
EventListener
interfacethe EventListener
is a "marker interface":
EventListener implementation
source code for EventListener
interface
interface event.EventListener { }
events are listed in a subinterface
for example, suppose OrderForm
broadcasts two events: onSubmit()
and onReset()
corresponding methods are defined in an interface, as follows:
import event.EventListener; interface OrderFormListener extends EventListener { public function onSubmit (e:OrderFormEvent):Void; public function onReset (e:OrderFormEvent):Void; }
then, a class that wants to register for OrderForm
events must implement OrderFormListener
:
class OrderGUI implements OrderFormListener { public function onSubmit (e:OrderFormEvent):Void { // Code to display submission status on screen goes here... } public function onReset (e:OrderFormEvent):Void { // Code to clear form contents goes here... } }
NightSky: an example
now let's use D.E.M. in an example
a night sky with animated shooting stars
shooting stars appear randomly
participants:
util.Randomizer
: event source. triggers an event when it's time to display a shooting starutil.RandomizerListener
: interface listing Randomizer
eventsutil.RandomizerEvent
: contains the time since the last shooting starNightSky
: listens for Randomizer events, implements RandomizerListener
NightSky architecture
RandomizerListener implementation
RandomizerListener
defines a single method, onRandomAction()
:
import event.*; import util.*; interface util.RandomizerListener extends EventListener { public function onRandomAction (e:RandomizerEvent):Void; }
parameter, e
, must be a RandomizerEvent
object
guarantees that the event object will be used correctly
NightSky
implements RandomizerListener
RandomizerEvent implementation
RandomizerEvent
transfers information from event source (Randomizer
) to event listeners (NightSky
)
specifically, RandomizerEvent.getTimeSinceLast()
tells NightSky when the last shooting star occurred
class util.RandomizerEvent extends EventObject { // The number of milliseconds since the last event was broadcast. private var timeSinceLast:Number; public function RandomizerEvent (src:Randomizer, timeSinceLast:Number) { // Always pass event source to superclass constructor! super(src); // Record the time since the last event, as specified // by Randomizer. this.timeSinceLast = timeSinceLast; } public function getTimeSinceLast ():Number { return timeSinceLast; } }
Randomizer implementation
Randomizer uses setInterval()
to check for random events
because event broadcasting is our focus, we'll ignore the code that is not directly related to event broadcasting
Randomizer
stores an EventListenerList
instance in a property, listenerList:
private var listenerList:EventListenerList;
the EventListenerList
instance is created in Randomizer's constructor
listenerList = new EventListenerList();
to register to receive events, a class invokes addRandomizerListener()
:
public function addRandomizerListener (l:RandomizerListener):Boolean { return listenerList.addObj(l); }
to stop receiving events, a class invokes removeRandomizerListener()
:
public function removeRandomizerListener (l:RandomizerListener):Boolean { return listenerList.removeObj(l); }
notice that only RandomizerListener
s can register for Randomizer
events!
because all event listeners must implement RandomizerListener
, all are guaranteed to define onRandomAction()
to broadcast the "random action" event, Randomizer
uses fireOnRandomAction()
:
private function fireOnRandomAction (elapsed:Number):Void { // Create an object to describe the event. var e:RandomizerEvent = new RandomizerEvent(this, elapsed); // Get a list of the current event listeners. var listeners:Array = listenerList.getListeners(); // Broadcast the event to all listeners. for (var i:Number = 0; i < listeners.length; i++) { listeners[i].onRandomAction(e); } }
check()
determines whether a random event has occurred; if so, broadcasts the event
private function check (odds:Number):Void { // Local variables. var rand:Number = Math.floor(Math.random() * odds); var now:Date = new Date(); var elapsed:Number; // If the random event occurs... if (rand == 0) { // Determine the elapsed time since the last event. elapsed = now.getTime() - lastEventTime.getTime(); lastEventTime = now; // Fire the event. fireOnRandomAction(elapsed); } }
NightSky implementation
NightSky
class listens for Randomizer
class's events
hence, NightSky
must implement RandomizerListener
interface
class nightsky.NightSky implements RandomizerListener { }
NightSky
creates sky background and shooting stars by attaching movie clips
NightSky
defines the following properties:
target
: the movie clip that will hold the sky backgroundprivate var target:MovieClip;
sky_mc
: a reference to the sky background movie clipprivate var sky_mc:MovieClip;
skyDepth
: the depth at which to attach the sky clipprivate var skyDepth:Number = 0;
startDepth
: the depth, in sky_mc, to create shooting starsprivate var starDepth:Number = 0;
NightSky
constructor receives sky's parent and sky/star depth as parameters
public function NightSky (target:MovieClip, skyDepth:Number, starDepth:Number) { this.target = target; this.skyDepth = skyDepth; this.starDepth = starDepth;
constructor also calls makeSkyBG()
:
makeSkyBG(); }
makeSkyBG()
simply attaches the sky background clip
private function makeSkyBG ():Void { sky_mc = target.attachMovie("skybg", "skybg", skyDepth); }
when an event occurs, onRandomAction()
executes
onRandomAction()
calls makeShootingStar()
, which causes a star to appear on screen:
public function onRandomAction (e:RandomizerEvent):Void { trace("New shooting star! Time since last star: " + e.getTimeSinceLast()); makeShootingStar(); }
the time since the last shooting star is retrieved via e.getTimeSinceLast()
makeShootingStar()
source code:
private function makeShootingStar ():Void { // Create the shooting star in the sky movie clip. sky_mc.attachMovie("shootingstar", "shootingstar" + starDepth, starDepth); // Randomly position the shooting star. sky_mc["shootingstar" + starDepth]._x = Math.floor(Math.random() * target.skybg._width); sky_mc["shootingstar" + starDepth]._y = Math.floor(Math.random() * target.skybg._height); // Put the next shooting star on a higher depth. starDepth++; }
putting it all together
to use NightSky
in an application, we first import the necessary packages:
import nightsky.*; import util.*;
next, we create a NightSky
instance:
var sky:NightSky = new NightSky(bg_mc, 1, 0);
then we create a Randomizer
instance:
var starRandomizer:Randomizer = new Randomizer(1000, 3);
finally, we register the NightSky
instance to receive events from the Randomizer
instance:
starRandomizer.addRandomizerListener(sky);
summary
the delegation event model provide event handling with complete datatype safety
all participants in the system must follow the type rules, so errors are minimized