Using Events


If you're reading this book, we assume that you already know the basics of working with events because they are so fundamental to ActionScript. However, developers often know how to work with events without really understanding how they work at a basic level. In this section, we'll look at the mechanics of events in ActionScript 3.0.

Understanding Event Elements

There are at least three elements required when working with events: event objects, event dispatchers, and event listeners. We've already talked about each of these briefly, but here we'll discuss the mechanics of each.

All event objects are of type flash.events.Event or a subtype. Event subtypes are simply types that are specific to a particular sort of behavior. For example, events raised by mouse behavior are of type MouseEvent while events that occur because of a timer are of type TimerEvent. Usually subtypes define additional properties that provide information about the type of event. For example, MouseEvent defines a property called buttonDown, which indicates whether or not the mouse button is down at the time the event occurs. This is information that is important for events related to mouse behavior, but it wouldn't be important for an event that occurs when data loads. Yet all event objects always have type and target properties that tell the listener what type of event it is and what object dispatched the event.

The second element that is always required is an event dispatcher. An event dispatcher is sometimes called a subject and sometimes called a target. The event dispatcher is the object that broadcasts an event. In ActionScript 3.0, all event dispatchers are instances of EvenTDispatcher, the built-in Flash Player class. Event dispatchers must allow listeners to register for notifications for events. The Eventdispatcher API allows listeners to register. We'll discuss this in more detail in the next section.

Event listeners in ActionScript 3.0 are always functions. You must register a listener with an event dispatcher for a specific event. When that event occurs, the dispatcher notifies the listener, and the listener gets called with an event parameter.

Registering Listeners

Listener objects register with the dispatcher object by calling the dispatcher's addEventListener() method. At a minimum, the addEventListener() method requires two parameters: the name of the event and the listener.

dispatcher.addEventListener(eventName, listener);


ActionScript 3.0 implements proper method closure, which means that functions and methods always maintain their original scope. This is a significant change from previous versions of ActionScript which required workarounds to ensure that a listener would be called within the proper scope.

The following example creates a rectangular sprite, and it uses an event listener to move the rectangle to a random location each time the user clicks on it.

package {    import flash.display.Sprite;    import flash.events.MouseEvent;    import flash.display.StageScaleMode;    import flash.display.StageAlign;    public class Test extends Sprite {       private var _rectangle:Sprite;       public function Test() {          stage.scaleMode = StageScaleMode.NO_SCALE;          stage.align = StageAlign.TOP_LEFT;          _rectangle = new Sprite();          _rectangle.graphics.lineStyle(0, 0, 0);          _rectangle.graphics.beginFill(0xFFFFFF, 1);          _rectangle.graphics.drawRect(0, 0, 100, 50);          _rectangle.graphics.endFill();          addChild(_rectangle);          _rectangle.addEventListener(MouseEvent.CLICK, onClick);       }       public function onClick(event:MouseEvent):void {          event.target.x = Math.random() * 400;          event.target.y = Math.random() * 400;       }    } }


Because the event listener moves the event target to a random location, you can also use that same listener for a different target. In this next example, two event dispatchers use the same event listener. In this case, both the rectangle and the circle move to random locations when clicked.

package {    import flash.display.Sprite;    import flash.events.MouseEvent;    import flash.display.StageScaleMode;    import flash.display.StageAlign;    public class Test extends Sprite {       private var _rectangle:Sprite;       private var _circle:Sprite;       public function Test() {          stage.scaleMode = StageScaleMode.NO_SCALE;          stage.align = StageAlign.TOP_LEFT;          _rectangle = new Sprite();          _rectangle.graphics.lineStyle(0, 0, 0);          _rectangle.graphics.beginFill(0xFFFFFF, 1);          _rectangle.graphics.drawRect(0, 0, 100, 50)          _rectangle.graphics.endFill();           addChild(_rectangle);          _circle = new Sprite();          _circle.graphics.lineStyle(0, 0, 0);          _circle.graphics.beginFill(0xFFFFFF, 1);          _circle.graphics.drawCircle(0, 0, 50);          _circle.graphics.endFill();          _circle.x = 100;          _circle.y = 100;          addChild(_circle);          _rectangle.addEventListener(MouseEvent.CLICK, onClick);          _circle.addEventListener(MouseEvent.CLICK, onClick);       }       public function onClick(event:MouseEvent):void {          event.target.x = Math.random() * 400;          event.target.y = Math.random() * 400;       }    } }


Removing Event Listeners

On the flip side of adding event listeners is removing event listeners. Generally it's advisable to remove event listeners whenever you no longer need to listen for a particular event. You can achieve this by using the removeEventListener() method. The removeEventListener() method accepts two parameters: the event name and the listener, and it removes the listener for the particular event for the dispatcher from which you call the method. The following code removes onClick() as an event listener for the click event.

_circle.removeEventListener(MouseEvent.CLICK, onClick);


Understanding Event Phases

There are two types of event dispatches: display objects on the display list and all other types. When an object that is not on the display list dispatches an event, it notifies only those listeners that are registered directly with the dispatcher. For example, when a URLLoader object dispatches a complete event, the only listeners that receive the notification are those registered directly with the URLLoader. However, when a display object on the display list dispatches events, the event flow is triggered. The event flow consists of three phases: capture, target, and bubble. We'll talk about each of these phases next.

The Capture Phase

The first phase of the event flow is called the capture phase. In this phase, Flash Player is looking for the object that trigged the event. In doing so, it starts at the root display object and uses a drill-down approach through the hierarchy of the display list until it finds the event's target, meaning the object that actually dispatched the event.

By default, event listeners do not receive notifications during the capture phase. Instead, if you want to register a listener for the capture phase, you must pass a value of true for the third parameter of the addEventListener() method. Otherwise, if the parameter is omitted or false, the listener only receives notifications during the target and bubble phases. Here is an example that registers a listener for the capture phase:

object.addEventListener(MouseEvent.CLICK, onClick, true);


The capture phase may seem strange at first. And, in fact, it is not often used. However, it's an important part of the event flow because it allows the application to trap events before they reach child targets. When you want to trap events and stop them from propagating to child elements, you must call the stopImmediatePropagation() method of the event object in the event handler. Here's an example that adds to the previous examples by trapping click events so that they are never handled by the listeners for child elements:

package {    import flash.display.Sprite;    import flash.events.MouseEvent;    import flash.display.StageScaleMode;    import flash.display.StageAlign;    public class Test extends Sprite {       private var _rectangle:Sprite;       private var _circle:Sprite;       private var _loader:Loader;       public function Test() {          stage.scaleMode = StageScaleMode.NO_SCALE;          stage.align = StageAlign.TOP_LEFT;          _rectangle = new Sprite();          _rectangle.graphics.lineStyle(0, 0, 0);          _rectangle.graphics.beginFill(0xFFFFFF, 1);          _rectangle.graphics.drawRect(0, 0, 100, 50);          _rectangle.graphics.endFill();           addChild(_rectangle);          _circle = new Sprite();          _circle.graphics.lineStyle(0, 0, 0);          _circle.graphics.beginFill(0xFFFFFF, 1);          _circle.graphics.drawCircle(0, 0, 50);          _circle.graphics.endFill();           _circle.x = 100;          _circle.y = 100;          addChild(_circle);          addEventListener(MouseEvent.CLICK, onStageClick, true);          _rectangle.addEventListener(MouseEvent.CLICK, onClick);          _circle.addEventListener(MouseEvent.CLICK, onClick);       }       public function onClick(event:MouseEvent):void {          event.target.x = Math.random() * 400;          event.target.y = Math.random() * 400;       }       public function onStageClick(event:MouseEvent):void {          event.stopImmediatePropagation();       }    } }


The Target Phase

After the capture phase is the target phase. The target phase occurs when the event target (the event dispatcher responsible for the event) is reached in the event flow. This is probably the most commonly used phase of the event flow because it is the phase during which listeners that are registered directly with an event dispatcher are notified. This is the phase illustrated by most of the examples throughout this chapter and this book.

The Bubble Phase

The last phase of the event flow is the bubble phase. It allows for events to bubble up the display list. The bubble phase occurs in the reverse order of the capture phase: starting from the target and moving upward to parent containers until the root is reached.

You don't need to do anything different for event bubbling to work. You can simply register a listener to a container for any event that any child can dispatch, and the event listener will receive the notification during the bubble phase. For example, the following code achieves the same effect as an earlier example, but it registers the listener with the container rather than with the children:

package {    import flash.display.Sprite;    import flash.events.MouseEvent;    import flash.display.StageScaleMode;    import flash.display.StageAlign;    public class Test extends Sprite {       private var _rectangle:Sprite;       private var _circle:Sprite;       public function Test() {          stage.scaleMode = StageScaleMode.NO_SCALE;          stage.align = StageAlign.TOP_LEFT;          _rectangle = new Sprite();          _rectangle.graphics.lineStyle(0, 0, 0);          _rectangle.graphics.beginFill(0xFFFFFF, 1);          _rectangle.graphics.drawRect(0, 0, 100, 50);          _rectangle.graphics.endFill();           addChild(_rectangle);          _circle = new Sprite();          _circle.graphics.lineStyle(0, 0, 0);          _circle.graphics.beginFill(0xFFFFFF, 1);          _circle.graphics.drawCircle(0, 0, 50);          _circle.graphics.endFill();           _circle.x = 100;          _circle.y = 100;          addChild(_circle);          addEventListener(MouseEvent.CLICK, onClick);       }       public function onClick(event:MouseEvent):void {          event.target.x = Math.random() * 400;          event.target.y = Math.random() * 400;       }    } }


Notice in this example that the event object parameter's target property still references the actual target of the event (the circle or rectangle in this example) rather than the object for which the listener is registered (the container). If you want to retrieve a reference to the object to which the listener is registered, you can use the currentTarget property.

It's also important to note that not all event types can bubble. Only those events for which the bubbles property is set to true can bubble.

Note

When you create a custom event, you can specify whether the event will bubble. The default is true.


Event Priority

Event priority is a little-used concept, but it is important to understand what it can do. When you add an event, you have the ability to set your listener's priority. The higher the priority setting, the sooner your listener will get called. You can set the priority for a listener using the fourth parameter of the addEventListener() method.

Two listeners with the same priority level are called in the order they were added. It's important to note that priorities are relevant only within a phase (capture, target, or bubble). The order of the phases of the event flow supersedes the priority level.

Weak References

The concept of weak references is particularly useful when adding listeners to objects. To understand why weak references are so good, you first need to understand a little about garbage collection. Garbage collection is how Flash Player cleans up memory that isn't needed anymore; it's important because otherwise applications would use more and more RAM until they required the system to restart. Flash Player 9 uses two systems called reference counting and mark and sweep.

Reference counting is not new to Flash Player. It is the traditional way in which Flash Player has run garbage collection. When you create an object in ActionScript, that object exists in memory. However, the object is useful only as long as there are references (such as variables) to that object. When no more references exist, it is safe to assume that the object is no longer used and can be garbage collected. Although this system is efficient, it is not accurate because it is possible, through circular references, to have references still in existence even though none of the references is actually accessible. For example, two objects can have references to one another. If the references to those objects are deleted, we would expect that the objects themselves would be garbage collected. However, because they have references to one another, they remain in memory.

The mark-and-sweep garbage collection system solves the circular reference problem by traversing all active (accessible) nodes within an application. Any objects that exist outside of the active nodes can be assumed to be no longer in use, and they can be garbage collected.

Although the two garbage collection systems work in conjunction to efficiently and effectively manage memory usage, they do not compensate for incorrect programming. It's important that you always remove event listeners when you no longer need to use them. If you register an event listener using normal references, it will not get garbage collected even if you delete the listener or the object containing the listener because the event dispatcher will still have a reference to it. Removing the event listener using removeEventListener() ensures that the event target no longer has a reference to the listener, and that action will allow it to be properly garbage collected after you've deleted the listener.

However, sometimes we can't remove the listeners because our object is deleted elsewhere. This is where weak references come in handy. They allow for normal event listening functionality, but when the listener object is removed, the listener is removed from memory properly because the reference is weak and not counted by the reference counting function of the garbage collector.

You can specify that a listener should use a weak reference by registering it using a value of true for the fifth parameter to the addEventListener() method.

target.addEventListener(MouseEvent.CLICK, onClick, false, 0, true);





Advanced ActionScript 3 with Design Patterns
Advanced ActionScript 3 with Design Patterns
ISBN: 0321426568
EAN: 2147483647
Year: 2004
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net