最新消息:网站改版咯

AS3事件机制浅析

ECMAScript Yovae 1532浏览

最近一个项目需要用AS3编写一个跨平台应用,又拿起放下很久的ActionScript3.0了,谈谈AS3的事件机制吧,AS3事件机制是AS3的核心功能之一,没有充分掌握事件机制的方方面面,就不能算是精通AS3语言。
1. AS3事件机制的主要成员

  • IEventDispatcher:事件派发对象接口,定义了添加、派发、移除、是否监听指定事件、是否触发指定事件接口。
  • EventDispatcher:事件派发对象接口的实现者,用户无法撇开EventDispatcher而自行实现IEventDispatcher接口,无法直接继承EventDispatcher时,必须把EventDispatcher作为实例变量。
  • Event:事件基类,所有事件类均基于此类实现

2. 观察者模式

AS3事件机制实现的是观察者模式,C#.NET(委托)或者Object C里面经常会接触到观察者模式。

IEventDispatcher充当了Subject角色,EventDispatcher相当于ConcreteSubject对象,Event相当于Observer,ConcreteSbserver相当于Event的子类。Attach相当于addEventListener,Detach相当于removeEventListener,Notify相当于dispatchEvent。Event没有Update。
3,显示列表中事件流的三个阶段

在一个事件的整个生命周期内,共分为三个阶段:

在捕获阶段与冒泡阶段均可能经过N个节点,在目标阶段仅可能有一个节点。
使用stopPropagation可阻止对事件流中当前节点的后续节点中的所有事件侦听器进行处理。使用stopImmediat

ePropagation可阻止对事件流中当前节点中和所有后续节点中的事件侦听器进行处理。

显示列表中事件流三阶段与事件机制本身没有直接关系。在AS3显示列表中,为什么要有事件流,为什么不是直接到达目标对象?
4,IEventDispatcher接口讲解

AS3事件机制的精髓基本全在这个接口中。

 addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

使用 EventDispatcher 对象注册事件侦听器对象,以使侦听器能够接收事件通知。
最常用的是前面二个参数。第三个参数标识该监听器是否会在捕获阶段被触发。第四个参数priority标识该同类事情监听器被调用的优化级。第五个参数标识该监听器是否易于被回收,默认为false,并且永远应该默认为false,如果监听器可以被回收,应该手动处理,而不是交给Flash Player。
对于监听同一类事件的监听器,priority高者优化被调用。在FP及Flex SDK中,priority最大不会超过200,因此,如果要设置top level的事件监听,此值应该设置在200以上。
在大型应用中,最好把各个层所要用到的priority分一下组,例如200-220分派给Core Level。

 
removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
从 EventDispatcher 对象中删除侦听器。
如果在addEventListener时,useCapture为true,此时在removeEventListener时,useCapture参数应与之相同,EventDispatcher内部维护了两个listener集合,一个盛装useCapture为false的listener,另一个盛装为true的。

5,Event类实例化讲解

Event(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
创建一个作为参数传递给事件侦听器的 Event 对象。
Event 类的方法可以在事件侦听器函数中使用以影响事件对象的行为。某些事件有关联的默认行为。例如,doubleClick 事件有关联的默认行为,此行为突出显示事件发生时鼠标指针下的词。通过调用 preventDefault() 方法,您的事件侦听器可以取消此行为。通过调用 stopPropogation() 或 stopImmediatePropogation() 方法,还可以使当前事件侦听器成为要处理事件的最后一个事件侦听器。
cancelable标识该事件是否可阻止与取消。一般FP定义的内部事件类型均不可以取消,如CANCEL,CLOSE,OPEN,ADDED等,一般IMG事件均可以取消,如CLOSING,EXITING等,凡是可以取消的事件,均有一个关联的可以取消的行为。开发者在自定义事件也应遵守这一规则。
开发者应当保证type在应用程序中是唯一的,bubbles用于标识事件在到达目标对象后是否仍向下传递。
Event对象在事情流结束之后,如果没有其它引用,即可被GC回收。当前Event需要二次派发时,使用clone方法复制事件。
6,MouseEvent事件

鼠标事件是FP内InactiveObject对象内在支持的事件,这个事情由InactiveObject实例化、派发,并且总是bubble等于true的。对于不需要鼠标事件的对象,应当把mouseChildren与mouseEnabled设为flase,以优化程序性能。
7,Flash Player内部对事件的强力支持

FP本身是多线程的,只不过目前未对开发者开放API。在FP内部,有一个线程专门用于处理事件,事件的处理总是在桢周期的前期进行,并且不会受到其它线程的影响。
FP是异步的,Event的派发与listener的执行并不是紧密衔接的,当你派发一个事件之后,不能指望监听这个Event的Listener马上执行。
PureMVC放弃AS3内部支持的事情机制不用,自己用观察者模式实现了一套Command体系,是对FP中独立事件线程的非合理浪费。
8,事件与代的概念

在FP内部,从宏观上讲,总是派发一拔事件,处理一拔代码,然后再派发一拔事件,再处理一拔代码,如此反复,看起来事件具有代(generation)的概念。代与事件流有关,也与桢周期内的执行模式有关。
9,为什么要有事件流三阶段?

当用户在FP中单击时,宿主环境仅能告诉FP用户进行了单击行为以及单击的坐标,却并不能告诉FP到底单击了哪一个对象,哪一个MC,这是不可能的,因为是什么对象、有什么对象,只有FP自已知道。
在FP中,共有两种渲染模式,一种为保留模式,另一种为立即模式,无论是哪一种渲染模式,FP交给浏览器或操作系统的最终渲染内容总是一张张图片,FP像幻灯片放映一样向用户展示互动与动画。所以,当用户单击时,永远只是单击点,看得见的点,FP拿到这些点之后,在内部的显示列表结构上遍历,首先从上向下走,只要当前显示对象囊括了单击点,并且是透明的(下面还有显示对象),便一直往下走,直到目标对象,然后再原路一路向上走,这便是事件机制的三阶段。
由于显示对象可以是透明的,FP并不知道开发者设想的用户真正想单击的是不是目标对象,有可能是捕获阶段的对象,同样的对象也有可能想在冒泡阶段处理,为了提供更大的灵活性,FP在显示列表中实现了事件流的三步机制,数以千万开发者的实践证明它是非常有阶值的。
单击之外的其它鼠标事情与之类似。
10,优化程序性能的第一准则

及时移除不再需要的事情监听,是保证垃圾回收、优化程序性能的最浅显、最容易、最为开发者所忽视的行码准则之一。以下代码是通用的,在函数内部移除事件监听的方法:
e.currentTarget.removeEventListener(e.type, arguments.callee);
11,停止冒泡事情的派发

除了移除不必要的事情监听,停止冒泡事情的继续派发也是提高程序运行效率的常用方法之一。该方法多用于MouseEvent事情,代码为:
e.stopPropagation();
or
e.stopImmediatePropagation();
但是事情冒泡有时却是十分有用的,在某处阻止了事件冒泡,有可能因此另一处的监听无法触发,这种bug十分隐蔽。
sban 2009/5/27 北京。转载请注明作者及出处,非商用。

AS3的事件流就三个阶段,捕获 > 目标 > 冒泡
而在鼠标事件中,共有10种鼠标事件,分别如下:
点击事件: MouseEvent.CLICK ,MouseEvent.DOUBLE_CLICK
按键事件: MouseEvent.MOUSE_DOWN ,MouseEvent.MOUSE_UP
悬停事件: MouseEvent.MOUSE_OVER ,MouseEvent.MOUSE_OUT ,MouseEvent.ROLL_OVER ,MouseEvent.ROLL_OUT
移动事件: MouseEvent.MOUSE_MOVE
滚轮事件: MouseEvent.MOUSE_WHEEL
悬停事件: MOUSE_OVER ,MOUSE_OUT ,ROLL_OVER ,ROLL_OUT

它们的具体工用是相似的
MOUSE_OVER = ROLL_OVER
MOUSE_OUT = ROLL_OUT

 

总结:

  • 事件流是面向DisplayObject的一个过程机制,但凡显示对象触发的事件,必有这个流过程,自上而下,再自下而上
  • 事件流机制是在同一条路径上的父子关系的显示对象都会参与的(默认)
  • 参与事件流的对象,对内部子对象,同样会触发MOUSE_OUT事件的
  • 最重要的一点就是,子对象触发的事件,只要父对象有侦听,那么无论如何,父对象都会触发一次所侦听的事件而且顺序是子对象先触发事件,然后父对象再触发(这是由冒泡阶段的顺序触发的)
  • 将addEventListener函数中的第三个参数设为true,则只在捕获阶段侦听,对于没有子对象的元素,事件是不会触发的,只有当子对象同样侦听相同事件时,才会触发事件(因为没有目标阶段)
  • 反正要理解事件机制,三个阶段的执行顺序及执行因素理解好后,下面的原理就很好理解了
  • 如果设置addEventListener函数的第三个参数为true,会中断目标阶段的检测,但它始终会参加事件流,所以ROLL_OVER和ROLL_OUT是实现不参加事件流(捕获和冒泡均不参加)操作的方法

转载请注明:Yovae Studio » AS3事件机制浅析