Dispatching Events






Dispatching Events

There are lots of ways to design an application. Each has relative advantages and disadvantages. Yet there are general principles that can help to make designs more flexible and manageable. One such principle states that loosely coupled systems are more flexible than tightly coupled systems. A tightly coupled system is one in which the elements have specific rigid interfaces in which they interact. For example, A can interact with B only if it knows a specific method to call on Bthat is a tightly coupled system. A loosely coupled system is one in which the elements know little to nothing about one another. Instead, elements can send out notifications when states have changed, and any element that happens to be listening for that notification can respond. That way, A doesn't have to know anything about B, and B doesn't have to know anything about A. Instead, B can listen for a notification from A, and when A sends the notification, B can respond.

We call the notifications events, and when an object listens for an event, we call it a listener. If you've worked with the user interface (UI) components in Flash 8, you're already familiar with basic event dispatching. For example, a Button component dispatches a click event when the user clicks it with the mouse. The Button instance sends the event, and any listener that happens to have registered for notifications for the click event will respond.

The event-dispatching system that the UI components use is accessible to you to use within your classes as well. The event-dispatching system is built in a class called mx.events.EventDisatcher. The EventDispatcher class uses a methodology that is unique to prototype-based languages such as ActionScript. The class is known as a mix-in class because it injects functionality into a class at runtime. The concept of a mix-in class is not familiar to most people because prototype-based languages are not very common and because although ActionScript is prototype-based, the prototype nature of ActionScript is hidden in ActionScript 2.0. Therefore, if you aren't familiar with the way prototypes work, you needn't concern yourself with exactly how mix-in classes work. Simply know that mix-in classes are capable of adding methods and properties to objects at runtime. In the case of EventDispatcher, it adds the addEventListener(), removeEventListener(), and dispatchEvent() methods to a class at runtime.

You can add the event dispatching functionality to an instance of a class by calling the static Eventdispatcher.initialize() method and passing it a reference to the instance of the class. Typically, you'll want to call the initialize() method from the constructor. The following example adds event dispatching to the Example class:

   import mx.events.EventDispatcher;
   class Example {
     public function Example() {
       EventDispatcher.initialize(this);
     }
   }

Although the initialize() method adds the methods at runtime, you'll want to reference the methods from your code. Because you aren't defining the methods in the class the Flash compiler will throw an error. For example, if you construct an instance of Example, and you try to call addEventListener(), the compiler will thrown an error because as far as it can tell there is no addEventListener() method. The method is getting added at runtime, but you have to tell the compiler about that. You can effectively communicate to the compiler that the methods will exist by declaring properties with the method names as in the following.

   public var addEventListener:Function;
   public var removeEventListener:Function;
   private var dispatchEvent:Function;

The addEventListener() and removeEventListener() methods are likely already familiar to you because they are identical to the methods you use with UI components. The dispatchEvent() method is probably not familiar to you, however. It is a method that you use within the class to dispatch an event. The method requires one parameter. The parameter must be an object with a type property specifying the name of the event to dispatch. For example, if you want to dispatch an event called exampleEvent, the dispatchEvent() method call might look like the following:

   dispatchEvent({type: "exampleEvent"});

The event object is passed to any listeners. Therefore, any additional values you want to pass to the listener can be passed as properties of the event object. By convention, the event object generally also has a target property that references the object dispatching the event.

   dispatchEvent({type: "exampleEvent", target: this});

In this task, you'll build a class that animates the drawing of lines. When each line segment is drawn, it will dispatch an event. In this particular case, once the event is dispatched a listener will get notified, and it will draw the next segment from an array.

1.
Open a new ActionScript file and save it as LineDrawer.as in the same directory as MovieClipContainer.as. Then add the following class declaration:

   class LineDrawer extends MovieClipContainer {
   }

The LineDrawer class subclasses MovieClipContainer so that it automatically inherits the functionality that adds the new empty movie clip.

2.
Next add the following import statement to the top of the code:

   import mx.utils.Delegate;

You'll use Delegate to correct scope issues so that Flash will call a method of LineDrawer at the frame interval of the Flash file.

3.
Declare the following properties:

   private var _x0:Number;
   private var _y0:Number;
   private var _x1:Number;
   private var _y1:Number;
   private var _framesTotal:Number;
   private var _frameCount:Number;

The properties store settings specifying the endpoints of the line segment as well as the number of frames over which to draw the line segment.

4.
Declare the constructor so it expects a MovieClip parameter and passes that to the superclass constructor.

    public function LineDrawer(parent:MovieClip) {
      super(parent);
    }

The LineDrawer() constructor needs to call the superclass constructor to add the new empty movie clip.

5.
Define a drawLine() method as follows.

   public function drawLine(thickness:Number, color:Number, ¬
     x0:Number, y0:Number, ¬
     x1:Number, y1:Number, ¬
     frames:Number):Void {
     _target.lineStyle(thickness, color);
     _target.moveTo(x0, y0);
     _target.onEnterFrame = Delegate.create(this, draw);
     _x0 = x0;
     _y0 = y0;
     _x1 = x1;
     _y1 = y1;
     _frameCount = 0;
     _framesTotal = frames;
   }

When the drawLine() method is called, it tells Flash to start calling the draw() method (see Step 6) at the frame interval of the Flash file. It sets the values of the private properties so that Flash can keep track of what to draw and for how long.

6.
Define the draw() method as follows.

   private function draw():Void {
     _frameCount++;
     var x:Number = _frameCount * (_x1 - _x0) / _framesTotal + _x0;
     var y:Number = _frameCount * (_y1 - _y0) / _framesTotal + _y0;
     _target.lineTo(x, y);
     if(_frameCount == _framesTotal) {
       delete _target.onEnterFrame;
     }
   }

Each time draw() is called, it draws part of the line segment. It draws for the duration of the specified frame count. After it's drawn for the specified number of frames, it stops drawing by deleting the onEnterFrame assignment.

7.
Open a new Flash document and save it as lineDrawer1.fla in the same directory as LineDrawer.as.

You'll use the Flash document to test the LineDrawer class.

8.
Select the first keyframe, open the Actions panel, and add the following code:

   var lineDrawer:LineDrawer = new LineDrawer(this);
   var lineSegments:Array = [[100, 100], [200, 100], [200, 200], ¬
     [100, 200], [100, 100]];
   var index:Number = 0;
   drawNext();
   function drawNext():Void {
     if(lineSegments[index + 1] == undefined) {
       return;
     }
     var vertex0:Array = lineSegments[index];
     var vertex1:Array = lineSegments[index + 1];
     lineDrawer.drawLine(1, 0x000000, vertex0[0], vertex0[1], vertex1[0], ¬
       vertex1[1], 10);
     index++;
   }

The preceding code defines an array of vertices that form a square. The drawNext() function tells the LineDrawer instance to draw the next line segment defined by the array.

9.
Test the movie.

You'll see one line segment draw over the course of 10 frames. The remaining line segments aren't drawn yet because nothing is notifying Flash to draw the next line segment.

10.
Edit LineDrawer.as and add the following import statement:

   import mx.events.EventDispatcher;

Import the EventDispatcher class so you can call the initialize() method.

11.
Add the following property declarations:

   public var addEventListener:Function;
   public var removeEventListener:Function;
   private var dispatchEvent:Function;

Declare the properties so that the compiler knows the methods will exist.

12.
Call the initialize() method from the constructor.

   public function LineDrawer(parent:MovieClip) {
     super(parent);
     EventDispatcher.initialize(this);
   }

The Eventdispatcher.initialize() method call tells the EventDispatcher class to add the event dispatching functionality to the instance of the LineDrawer class.

13.
Dispatch a complete event when the line is completely drawn.

   private function draw():Void {
     _frameCount++;
     var x:Number = _frameCount * (_x1 - _x0) / _framesTotal + _x0;
     var y:Number = _frameCount * (_y1 - _y0) / _framesTotal + _y0;
     _target.lineTo(x, y);
     if(_frameCount == _framesTotal) {
       delete _target.onEnterFrame;
       dispatchEvent({type: "complete", target: this});
     }
   }

When the line segment is completely drawn, the LineDrawer class now dispatches an event to notify any listeners.

14.
Edit the code in lineDrawer1.fla so that it listens for a complete event from the LineDrawer instance. When the complete event is dispatched, have it call drawNext().

   var lineDrawer:LineDrawer = new LineDrawer(this);
   var lineSegments:Array = [[100, 100], [200, 100], [200, 200], ¬
     [100, 200], [100, 100]];
   var index:Number = 0;
   lineDrawer.addEventListener("complete", drawNext);
   drawNext();

Now each time a line segment is drawn, Flash will listen for the event and it will call drawNext().

15.
Test the movie.

This time, you'll see four line segments drawn in succession. The following figure shows the animation in progress.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows