事件侦听器

Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本

事件侦听器也称为事件处理函数,是 Flash Player 和 AIR 为响应特定事件而执行的函数。添加事件侦听器的过程分为两步。首先,为 Flash Player 或 AIR 创建一个为响应事件而执行的函数或类方法。这有时称为侦听器函数或事件处理函数。然后,使用 addEventListener() 方法,在事件的目标或位于适当事件流上的任何显示列表对象中注册侦听器函数。

创建侦听器函数

创建侦听器函数是 ActionScript 3.0 事件模型与 DOM 事件模型不同的一个方面。在 DOM 事件模型中,事件侦听器和侦听器函数之间有一个明显的不同:即事件侦听器是实现 EventListener 接口的类的实例,而侦听器是该类的名为 handleEvent() 的方法。在 DOM 事件模型中,您注册的是包含侦听器函数的类实例,而不是实际的侦听器函数。

在 ActionScript 3.0 事件模型中,事件侦听器和侦听器函数之间没有区别。ActionScript 3.0 没有 EventListener 接口,侦听器函数可以在类外部定义,也可以定义为类的一部分。此外,无需将侦听器函数命名为 handleEvent() — 可以将它们命名为任何有效的标识符。在 ActionScript 3.0 中,您注册的是实际侦听器函数的名称。

在类外部定义的侦听器函数

以下代码创建一个显示红色正方形的简单 SWF 文件。名为 clickHandler() 的侦听器函数(不是类的一部分)侦听红色正方形上的鼠标单击事件。

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, clickHandler); 
    } 
} 
 
function clickHandler(event:MouseEvent):void 
{ 
    trace("clickHandler detected an event of type: " + event.type); 
    trace("the this keyword refers to: " + this); 
}

当用户通过单击正方形与生成的 SWF 文件交互时,Flash Player 或 AIR 生成以下跟踪输出:

clickHandler detected an event of type: click 
the this keyword refers to: [object global]

请注意,事件对象作为参数传递到 clickHandler()。这就允许您的侦听器函数检查事件对象。在该示例中,使用事件对象的 type 属性来确定该事件是否为单击事件。

该示例还检查 this 关键字的值。在本例中,this 表示全局对象,这是因为函数是在任何自定义类或对象外部定义的。

定义为类方法的侦听器函数

下面的示例与前面定义 ClickExample 类的示例相同,只是将 clickHandler() 函数定义为 ChildSprite 类的方法:

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, clickHandler); 
    } 
    private function clickHandler(event:MouseEvent):void 
    { 
        trace("clickHandler detected an event of type: " + event.type); 
        trace("the this keyword refers to: " + this); 
    } 
}

当用户通过单击红色正方形与生成的 SWF 文件交互时,Flash Player 或 AIR 生成以下跟踪输出:

clickHandler detected an event of type: click 
the this keyword refers to: [object ChildSprite]

请注意,this 关键字引用名为 child 的 ChildSprite 实例。这是与 ActionScript 2.0 相比行为方面的一个变化。如果您使用过 ActionScript 2.0 中的组件,您可能会记得,将类方法传递给 UIEventDispatcher.addEventListener() 时,方法的作用域被绑定到广播事件的组件,而不是在其中定义侦听器方法的类。也就是说,如果在 ActionScript 2.0 中使用该技术,this 关键字将引用广播事件的组件,而不是 ChildSprite 实例。

这对于某些程序员来说是一个重要问题,因为这意味着他们无法访问包含侦听器方法的类的其他方法和属性。过去,ActionScript 2.0 程序员可以使用 mx.util.Delegate 类更改侦听器方法的作用域以解决该问题。不过,现在已不再需要这样做了,因为 ActionScript 3.0 在调用 addEventListener() 时会创建一个绑定方法。这样,this 关键字引用名为 child 的 ChildSprite 实例,且程序员可以访问 ChildSprite 类的其他方法和属性。

不应使用的事件侦听器

还有第三种技术可用于创建一个通用对象,该对象具有指向动态分配的侦听器函数的属性,但不推荐使用该技术。在此讨论这种技术是因为它在 ActionScript 2.0 中经常使用,但在 ActionScript 3.0 中不应使用。建议不要使用此项技术,因为 this 关键字将引用全局对象,而不引用您的侦听器对象。

下面的示例与前面的 ClickExample 类示例相同,只是将侦听器函数定义为名为 myListenerObj 的通用对象的一部分:

package 
{ 
    import flash.display.Sprite; 
 
    public class ClickExample extends Sprite 
    { 
        public function ClickExample() 
        { 
            var child:ChildSprite = new ChildSprite(); 
            addChild(child); 
        } 
    } 
} 
 
import flash.display.Sprite; 
import flash.events.MouseEvent; 
 
class ChildSprite extends Sprite 
{ 
    public function ChildSprite() 
    { 
        graphics.beginFill(0xFF0000); 
        graphics.drawRect(0,0,100,100); 
        graphics.endFill(); 
        addEventListener(MouseEvent.CLICK, myListenerObj.clickHandler); 
    } 
} 
 
var myListenerObj:Object = new Object(); 
myListenerObj.clickHandler = function (event:MouseEvent):void 
{ 
        trace("clickHandler detected an event of type: " + event.type); 
        trace("the this keyword refers to: " + this); 
}

跟踪的结果将类似如下:

clickHandler detected an event of type: click 
the this keyword refers to: [object global]

您可能以为 this 引用 myListenerObj,且跟踪输出应为 [object Object],但实际上它引用的是全局对象。将动态属性名称作为参数传递给 addEventListener() 时,Flash Player 或 AIR 无法创建绑定方法。这是因为作为 listener 参数传递的只不过是侦听器函数的内存地址,Flash Player 和 AIR 无法将该内存地址与 myListenerObj 实例关联起来。

管理事件侦听器

使用 IEventDispatcher 接口的方法来管理侦听器函数。IEventDispatcher 接口是 ActionScript 3.0 版本的 DOM 事件模型的 EventTarget 接口。虽然名称 IEventDispatcher 似乎暗示着其主要用途是发送(调度)事件对象,但该类的方法实际上更多用于注册、检查和删除事件侦听器。IEventDispatcher 接口定义五个方法,如以下代码中所示:

package flash.events 
{ 
    public interface IEventDispatcher 
    { 
        function addEventListener(eventName:String,  
                        listener:Object, 
                        useCapture:Boolean=false, 
                        priority:Integer=0, 
                        useWeakReference:Boolean=false):Boolean; 
 
        function removeEventListener(eventName:String,  
                    listener:Object, 
                    useCapture:Boolean=false):Boolean; 
 
        function dispatchEvent(eventObject:Event):Boolean; 
 
        function hasEventListener(eventName:String):Boolean; 
        function willTrigger(eventName:String):Boolean; 
    } 
}

Flash Player API 使用 EventDispatcher 类来实现 IEventDispatcher 接口,该类用作可以是事件目标或事件流一部分的所有类的基类。例如,DisplayObject 类继承自 EventDispatcher 类。这意味着,显示列表中的所有对象都可以访问 IEventDispatcher 接口的方法。

添加事件侦听器

addEventListener() 方法是 IEventDispatcher 接口的主要函数。使用它来注册侦听器函数。两个必需的参数是 typelistenertype 参数用于指定事件的类型。listener 参数用于指定发生事件时将执行的侦听器函数。listener 参数可以是对函数或类方法的引用。

指定 listener 参数时,不要使用括号。例如,在下面的 addEventListener() 方法调用中,指定 clickHandler() 函数时没有使用括号:
addEventListener(MouseEvent.CLICK, clickHandler)

通过使用 addEventListener() 方法的 useCapture 参数,可以控制侦听器将处于活动状态的事件流阶段。如果 useCapture 设置为 true,侦听器将在事件流的捕获阶段成为活动状态。如果 useCapture 设置为 false,侦听器将在事件流的目标阶段和冒泡阶段处于活动状态。若要在事件流的所有阶段侦听某一事件,您必须调用 addEventListener() 两次,第一次调用时将 useCapture 设置为 true,第二次调用时将 useCapture 设置为 false

addEventListener() 方法的 priority 参数并不是 DOM Level 3 事件模型的正式部分。ActionScript 3.0 中包括它是为了在组织事件侦听器时提供更大的灵活性。调用 addEventListener() 时,可以将一个整数值作为 priority 参数传递,以设置该事件侦听器的优先级。默认值为 0,但您可以将它设置为负整数值或正整数值。将优先执行此数字较大的事件侦听器。对于具有相同优先级的事件侦听器,则按它们的添加顺序执行,因此将优先执行较早添加的侦听器。

可以使用 useWeakReference 参数来指定对侦听器函数的引用是弱引用还是正常引用。通过将此参数设置为 true,可避免侦听器函数在不再需要时仍然存在于内存中的情况。Flash Player 和 AIR 使用一项称为“垃圾回收”的技术从内存中清除不再使用的对象。如果不存在对某个对象的引用,则该对象被视为不再使用。垃圾回收器不考虑弱引用,这意味着如果侦听器函数仅具有指向它的弱引用,则符合垃圾回收条件。

删除事件侦听器

可以使用 removeEventListener() 方法删除不再需要的事件侦听器。建议删除将不再使用的所有侦听器。必需的参数包括 eventNamelistener 参数,这些参数与 addEventListener() 方法的必需参数相同。回想一下,您可以通过调用 addEventListener() 两次(第一次调用时将 useCapture 设置为 true,第二次调用时将其设置为 false),在所有事件阶段侦听事件。若要删除这两个事件侦听器,您需要调用 removeEventListener() 两次,第一次调用时将 useCapture 设置为 true,第二次调用时将其设置为 false

调度事件

高级程序员可以使用 dispatchEvent() 方法将自定义事件对象调度到事件流。该方法唯一接受的参数是对事件对象的引用,此事件对象必须是 Event 类的实例或子类。调度后,事件对象的 target 属性将设置为对其调用了 dispatchEvent() 的对象。

检查有无现有的事件侦听器

IEventDispatcher 接口的最后两个方法提供有关是否存在事件侦听器的有用信息。如果在特定显示列表对象上发现特定事件类型的事件侦听器,hasEventListener() 方法将返回 true。如果发现特定显示列表对象的侦听器,willTrigger() 方法也会返回 true。但 willTrigger() 不但检查该显示对象上的侦听器,还会检查该显示列表对象在事件流所有阶段中的所有始祖上的侦听器。

没有侦听器的错误事件

ActionScript 3.0 中处理错误的主要机制是异常而不是事件,但对于异步操作(例如加载文件),异常处理不起作用。如果在这样的异步操作中发生错误,Flash Player 和 AIR 会调度一个错误事件对象。如果不为错误事件创建侦听器,Flash Player 和 AIR 的调试器版本将打开一个对话框,其中包含有关该错误的信息。例如,调试版的 Flash Player 会在应用程序试图从无效 URL 加载文件时生成以下对话框来描述错误:

大多数错误事件基于 ErrorEvent 类,而且同样具有一个名为 text 的属性,它用于存储 Flash Player 或 AIR 显示的错误消息。两个异常是 StatusEvent 和 NetStatusEvent 类。这两个类都具有一个 level 属性(StatusEvent.levelNetStatusEvent.info.level)。当 level 属性的值为 "error" 时,这些事件类型被视为错误事件。

错误事件将不会导致 SWF 文件停止运行。它仅在浏览器插件和独立播放器的调试器版本上显示为对话框,在创作播放器的输出面板中显示为消息,在 Adobe Flash Builder 的日志文件中显示为条目。在 Flash Player 或 AIR 的发行版中根本不会显示。