事件机制是flex的核心机制之一。我们应该会在很多介绍flex框架、机制或者类似flex入门之类的文章中看到这样一句话:“flex是由事件驱动的”。由这句话我们可以知道,事件对于flex来讲是非常重要的,甚至可以说事件构成了flex的基础。那什么是事件呢?事件就是一种特殊的消息,在数据或程序状态发生变化时被发出。
“事件目标是 Flash® Player 和 Adobe® AIR™ 事件模型的重要组成部分。事件目标是事件如何通过显示列表层次结构这一问题的焦点。当发生鼠标单击或按键等事件时,Flash Player 或 AIR 应用程序会将事件对象调度到从显示列表根开始的事件流中。然后该事件对象在显示列表中前进,直到到达事件目标,然后从这一点开始其在显示列表中的回程。在概念上,到事件目标的此往返行程被划分为三个阶段:捕获阶段包括从根到事件目标节点之前的最后一个节点的行程,目标阶段仅包括事件目标节点,冒泡阶段包括回程上遇到的任何后续节点到显示列表的根。”
这是来自帮助文档上的一段话,从这段话我们知道,事件处理包括3个阶段:捕获阶段、目标阶段和冒泡阶段。如下图所示:
①
事件派发之后,事件对象被送到显示列表的根结点开始的事件流中。“父节点”是相对于派发事件的对象而言的。
②
当事件到达目标阶段时停止前进,并开始由底向上的冒泡。
冒泡阶段是非必要的,在派发事件时可以取消。当派发事件的对象没有在显示列表中,则不会有捕获和冒泡阶段。
说明:对于事件的3个阶段,我们都可以对事件添加侦听进行处理。后文将会有对这方面的详细说明。
在介绍事件如何使用之前,我们也看看与事件相关的一些类及方法的介绍。
flash.events.EventDispatcher是可调度事件的所有类的基类,并且是任何可显示对象类的基类。事件对象是flash.events.Event类或其某个子类的实例。
首先,flash.events.Event作为所有事件的基类,定义了事件基本的属性和方法。下面我们看看Event一些常用的方法和属性。
1)
构造函数
public functionEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
n
type:事件的类型的字符串表示,一般作为静态常量定义(非必要)。作为派发和监听事件的根据。
n
bubbles:确定Event 对象是否参与事件流的冒泡阶段。如果想要在冒泡阶段处理事件,必须设置为true。
n
cancelable:表示监听事件的默认行为是否可以取消。如需创建可被取消默认行为的事件,该参数设置为true。该参数对于定义组件的可取消的默认行为非常有用,需要与事件的preventDefaul()方法结合使用。
2)
stopImmediatePropagation()与stopPropagation()方法
这2种方法都是阻止事件流中相关侦听器的处理的。stopPropagation()防止对事件流中当前节点的后续节点中的所有事件侦听器进行处理,
stopImmediatePropagation()防止对事件流中当前节点中和所有后续节点中的事件侦听器进行处理。这2个方法的区别就在与对当前节点的侦听器的作用,stopPropagation()不会影响当前节点的侦听器执行,而stopImmediatePropagation()则也会阻止当前节点的后续侦听器的执行。比如说,一个节点添加了2个侦听listener1和listener2,listener1比listener2先执行,如果在listener1中使用了stopPropagation(),listener2不受影响,仍然会执行,而如果在listener1中使用了stopImmediatePropagation(),listener2中的代码将不再执行。
其次,flash.events.EventDispatcher是可调度事件的所有类的基类,定义了处理事件的一些方法。
1)
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false)
该方法用于给事件流中的节点添加事件的侦听。
n
type:事件的类型的字符串表示,与所要监听的Event对象的type属性对应,该属性可在实例化Event对象时通过构造函数的type参数传入。
n
listener:事件的处理方法,参数为Event对象,无返回结果。
n
useCapture:确定侦听器运行的事件流的阶段。如果为true,则运行于捕获阶段,为false,运行于目标和冒泡阶段。如果在所有阶段都要监听,则需要调用addEventListener两次。
n
priority:侦听器的优先级。在事件流的同一个节点如果注册了多个侦听器,就需要设置此参数确定侦听器的运行先后顺序。如果优先级相同,则根据侦听器的注册先后执行。
n
useWeakReference:是否使用弱引用。此参数涉及FP的内存管理。请参看帮助中的相关说明与垃圾回收的相关资料,这里就不作详细的说明了。
2)
dispatchEvent(event:Event)
将事件添加到事件流中,即派发事件。
3)
removeEventListener (type:String, listener:Function, useCapture:Boolean = false)
从 EventDispatcher 对象中删除侦听器。在某些情况下,为了防止内存泄漏,我们应该调用此方法移除侦听。
前面从原理上介绍了事件机制。下面介绍一下如何在应用中使用事件,以及使用时需要注意的问题。实例中,我们将通过单击一个按钮派发我们的自定义的事件,这样便于我们测试事件机制的一些特性。
1、事件流的3个阶段
代码一:
<?xmlversion="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
applicationComplete="init(event)"
>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected functionbtn_clickHandler(event:MouseEvent):void
{
/** bubbles参数设置为true,允许事件参与冒泡阶段 */
btn.dispatchEvent(new Event("test",true));
}
protected function init(event:FlexEvent):void
{
/* application用于捕获阶段的侦听器 */
addEventListener("test",appListener1,true);
/* application用于冒泡和目标阶段的侦听器 */
addEventListener("test",appListener);
/* group用于捕获阶段的侦听器 */ group.addEventListener("test",groupListener1,true);
/* group用于冒泡和目标阶段的侦听器 */
group.addEventListener("test",groupListener);
/* button用于捕获阶段的侦听器 */
btn.addEventListener("test",btnListener1,true);
/* button用于冒泡和目标阶段的侦听器 */
btn.addEventListener("test",btnListener);
}
private function appListener1(e:Event):void
{
trace("这是application添加的事件侦听,捕获阶段");
}
private function appListener(e:Event):void
{
trace("这是application添加的事件侦听,冒泡和目标阶段");
}
private function groupListener1(e:Event):void
{
trace("这是group添加的事件侦听,捕获阶段");
}
private function groupListener(e:Event):void
{
trace("这是group添加的事件侦听,冒泡和目标阶段");
}
private function btnListener1(e:Event):void
{
trace("这是目标button添加的事件侦听,捕获阶段");
}
private function btnListener(e:Event):void
{
trace("这是目标button添加的事件侦听,冒泡和目标阶段");
}
]]>
</fx:Script>
<s:Group id="group">
<s:Button id="btn" click="btn_clickHandler(event)"/>
</s:Group>
</s:Application>
运行结果:
这是application添加的事件侦听,捕获阶段
这是group添加的事件侦听,捕获阶段
这是目标button添加的事件侦听,冒泡和目标阶段
这是group添加的事件侦听,冒泡和目标阶段
这是application添加的事件侦听,冒泡和目标阶段
由此可以看出,事件流的3个阶段与之前所说的是一致的。派发事件的对象(这里是button)只存在于目标阶段,即目标节点。
如果将btn.dispatchEvent(new Event("test",true))改为btn.dispatchEvent(new Event("test",false)),取消事件流的冒泡阶段,运行结果如下:
这是application添加的事件侦听,捕获阶段
这是group添加的事件侦听,捕获阶段
这是目标button添加的事件侦听,冒泡和目标阶段
冒泡阶段的侦听器是没有作用的。
2、侦听器的优先级与阻止侦听器的处理
代码二:
<?xmlversion="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
applicationComplete="init(event)"
>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected functionbtn_clickHandler(event:MouseEvent):void
{
/** bubbles参数设置为true,允许事件参与冒泡阶段 */
btn.dispatchEvent(new Event("test",true));
}
protected functioninit(event:FlexEvent):void
{
/* application用于捕获阶段的侦听器 */
addEventListener("test",appListener1,true);
/* application用于冒泡和目标阶段的侦听器 */
addEventListener("test",appListener);
/* group用于捕获阶段的侦听器 */
group.addEventListener("test",groupListener1,true);
/* group用于冒泡和目标阶段的侦听器 */
group.addEventListener("test",groupListener);
/* button注册了2个test侦听器,优先级相同 */
btn.addEventListener("test",btnListener);
btn.addEventListener("test",btnListener1);
}
private function appListener1(e:Event):void
{
trace("这是application添加的事件侦听,捕获阶段");
}
private function appListener(e:Event):void
{
trace("这是application添加的事件侦听,冒泡和目标阶段");
}
private function groupListener1(e:Event):void
{
trace("这是group添加的事件侦听,捕获阶段");
}
private function groupListener(e:Event):void
{
trace("这是group添加的事件侦听,冒泡和目标阶段");
}
private function btnListener(e:Event):void
{
trace("这是目标button添加的事件侦听,冒泡和目标阶段");
}
private function btnListener1(e:Event):void
{
trace("这是目标button添加的事件侦听1,冒泡和目标阶段");
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here-->
</fx:Declarations>
<s:Group id="group">
<s:Button id="btn" click="btn_clickHandler(event)"/>
</s:Group>
</s:Application>
这里,btn注册了2个优先级相同的侦听器,按照先注册先运行的原则,btnListener会比btnListener1先执行。结果如下:
这是application添加的事件侦听,捕获阶段
这是group添加的事件侦听,捕获阶段
这是目标button添加的事件侦听,冒泡和目标阶段
这是目标button添加的事件侦听1,冒泡和目标阶段
这是group添加的事件侦听,冒泡和目标阶段
这是application添加的事件侦听,冒泡和目标阶段
将btn.addEventListener("test",btnListener1)改为btn.addEventListener("test",btnListener1,false,1),优先级1大于优先级0(默认优先级)。此时运行结果如下:
这是application添加的事件侦听,捕获阶段
这是group添加的事件侦听,捕获阶段
这是目标button添加的事件侦听1,冒泡和目标阶段
这是目标button添加的事件侦听,冒泡和目标阶段
这是group添加的事件侦听,冒泡和目标阶段
这是application添加的事件侦听,冒泡和目标阶段
代码三:stopImmediatePropagation()与stopPropagation()
<?xmlversion="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
applicationComplete="init(event)"
>
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected functionbtn_clickHandler(event:MouseEvent):void
{
/** bubbles参数设置为true,允许事件参与冒泡阶段 */
btn.dispatchEvent(new Event("test",true));
}
protected functioninit(event:FlexEvent):void
{
/* application用于捕获阶段的侦听器 */
addEventListener("test",appListener1,true);
/* application用于冒泡和目标阶段的侦听器 */
addEventListener("test",appListener);
/* group用于捕获阶段的侦听器 */
group.addEventListener("test",groupListener1,true);
/* group用于冒泡和目标阶段的侦听器 */
group.addEventListener("test",groupListener);
/* button用于目标阶段的侦听器 */
btn.addEventListener("test",btnListener);
/* button用于目标阶段的侦听器 */
btn.addEventListener("test",btnListener1,false,1);
}
private function appListener1(e:Event):void
{
trace("这是application添加的事件侦听,捕获阶段");
}
private function appListener(e:Event):void
{
trace("这是application添加的事件侦听,冒泡和目标阶段");
}
private function groupListener1(e:Event):void
{
trace("这是group添加的事件侦听,捕获阶段");
}
private function groupListener(e:Event):void
{
trace("这是group添加的事件侦听,冒泡和目标阶段");
}
private function btnListener(e:Event):void
{
trace("这是目标button添加的事件侦听,冒泡和目标阶段");
}
private function btnListener1(e:Event):void
{
e.stopPropagation();
trace("这是目标button添加的事件侦听1,冒泡和目标阶段");
}
]]>
</fx:Script>
<s:Group id="group">
<s:Button id="btn" click="btn_clickHandler(event)"/>
</s:Group>
</s:Application>
这里,btnListener1的优先级高于btnListener,在btnListener1加上一句e.stopPropagation(),运行结果如下:
这是application添加的事件侦听,捕获阶段
这是group添加的事件侦听,捕获阶段
这是目标button添加的事件侦听1,冒泡和目标阶段
这是目标button添加的事件侦听,冒泡和目标阶段
将e.stopPropagation()换成e.stopImmediatePropagation(),运行结果如下:
这是application添加的事件侦听,捕获阶段
这是group添加的事件侦听,捕获阶段
这是目标button添加的事件侦听1,冒泡和目标阶段
3、创建可被取消的事件对象
代码四:
<?xmlversion="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
applicationComplete="init(event)"
>
<fx:Script>
<![CDATA[
import mx.core.EventPriority;
import mx.events.FlexEvent;
protected functionbtn_clickHandler(event:MouseEvent):void
{
/** bubbles参数设置为true,允许事件参与冒泡阶段 ,将第三个参数设置为true,允许取消默认行为*/
btn.dispatchEvent(new Event("test",true,true));
}
protected functioninit(event:FlexEvent):void
{
/*添加默认侦听,将优先级 设置为EventPriority.DEFAULT_HANDLER*/
btn.addEventListener("test",btnListener,false,EventPriority.DEFAULT_HANDLER);
/* 侦听器的默认优先级高于 EventPriority.DEFAULT_HANDLER*/
btn.addEventListener("test",btnListener1,false);
}
private function btnListener(e:Event):void
{
if(e.isDefaultPrevented())
{
trace("默认行为被取消了!");
}
else
{
trace("默认行为没有被取消!");
}
}
private function btnListener1(e:Event):void
{
e.preventDefault();
trace("这是目标button添加的事件侦听1,冒泡和目标阶段");
}
]]>
</fx:Script>
<s:Group id="group">
<s:Button id="btn" click="btn_clickHandler(event)"/>
</s:Group>
</s:Application>
运行结果如下:
这是目标button添加的事件侦听1,冒泡和目标阶段
默认行为被取消了!
4、很多人认为事件是异步的,在相当长的时间里,我也是这么认为的。其实这是一种错误的理解。
首先,从事件流的整个内部过程来说,毫无疑问,不存在异步的情况。
其次,从程序的执行过程来看,也不存在。下面的代码五可以说明这个情况。
代码五:
<?xmlversion="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
applicationComplete="init(event)"
>
<!--<fx:Metadata>
[Event(name="test",type="flash.events.Event")]
</fx:Metadata>-->
<fx:Script>
<![CDATA[
import flash.utils.getTimer;
import flash.utils.setTimeout;
import mx.events.FlexEvent;
protected functionbtn_clickHandler(event:MouseEvent):void
{
/** bubbles参数设置为true,允许事件参与冒泡阶段 */
btn.dispatchEvent(new Event("test",true));
trace("事件派发后执行的语句");
}
protected functioninit(event:FlexEvent):void
{
/* application用于捕获阶段的侦听器 */
addEventListener("test",appListener1,true);
/* application用于冒泡和目标阶段的侦听器 */
addEventListener("test",appListener);
/* group用于捕获阶段的侦听器 */
group.addEventListener("test",groupListener1,true);
/* group用于冒泡和目标阶段的侦听器 */
group.addEventListener("test",groupListener);
/* button用于捕获阶段的侦听器 */
btn.addEventListener("test",btnListener1,true);
/* group用于冒泡和目标阶段的侦听器 */
btn.addEventListener("test",btnListener);
}
private function appListener1(e:Event):void
{
trace("这是application添加的事件侦听,捕获阶段");
}
private function appListener(e:Event):void
{
trace("这是application添加的事件侦听,冒泡和目标阶段");
}
private function groupListener1(e:Event):void
{
trace("这是group添加的事件侦听,捕获阶段");
}
private function groupListener(e:Event):void
{
trace("这是group添加的事件侦听,冒泡和目标阶段");
}
private function btnListener1(e:Event):void
{
trace("这是目标button添加的事件侦听,捕获阶段");
}
private function btnListener(e:Event):void
{
trace("这是目标button添加的事件侦听,冒泡和目标阶段");
}
]]>
</fx:Script>
<s:Group id="group">
<s:Button id="btn" click="btn_clickHandler(event)"/>
</s:Group>
</s:Application>
这里其实只是在代码一里的btn.dispatchEvent(new Event("test",true))之后增加了一句trace("事件派发后执行的语句"),我们看看运行结果如何:
这是application添加的事件侦听,捕获阶段
这是group添加的事件侦听,捕获阶段
这是目标button添加的事件侦听,冒泡和目标阶段
这是group添加的事件侦听,冒泡和目标阶段
这是application添加的事件侦听,冒泡和目标阶段
事件派发后执行的语句
从结果中可以看出,dispatchEvent后面的代码是在事件的所有监听执行完毕之后才运行。所以,这也是非异步的。
到这里,事件机制基本介绍完了。对于事件一些比较简单或大家在使用过程中比较熟悉的属性、方法在这里并没有介绍。本文侧重事件流相关的内容介绍,希望对看到本文的童鞋们有所帮助。限于本人技术及行文水平,肯定有遗漏和不足之处。请大家斧正。欢迎与我交流。