Flex 事件详解

guoyaping000 贡献于2013-07-30

作者   创建于2012-05-16 17:27:52   修改者  修改于2013-06-05 09:28:44字数42624

文档摘要:Flex事件(一)使用事件一、关于事件事件可以在Flex程序中发生了一些事情的时候让开发者知道所发生的事件。你可以通过用户指令创建,例如鼠标和按键,或者外部的输入,例如从一个Web服务调用返回。当事件的外观或生命周期发生改变的时候,同样会触发事件。例如:当控件重新调整尺寸的时候,创建或销毁一个组件。任何用户与程序的交互都可以产生事件。即使没有任何用户直接的交互,事件也会发生。例如,当一个摄像头被激活,或者从远程服务器加载数据完毕的时候。你可以通过在代码中添加事件处理器来处理这些事件。事件处理器是编写的用以响应特定事件的方法。
关键词:

Flex事件(一) 使用事件 一、关于事件 事件可以在Flex程序中发生了一些事情的时候让开发者知道所发生的事件。你可以通过用户指令创建,例如鼠标和按键,或者外部的输入,例如从一个Web服务调用返回。当事件的外观或生命周期发生改变的时候,同样会触发事件。例如:当控件重新调整尺寸的时候,创建或销毁一个组件。 任何用户与程序的交互都可以产生事件。即使没有任何用户直接的交互,事件也会发生。例如,当一个摄像头被激活,或者从远程服务器加载数据完毕的时候。你可以通过在代码中添加事件处理器来处理这些事件。事件处理器是编写的用以响应特定事件的方法。它们有时会被关联到一个事件监听器。 组件生成、调度并销毁他们的事件。一个对象需要关于其他对象事件注册的监听器的信息。当一个事件发生时,对象通过方法调用将事件分发到所有注册的监听器。要接同一个对象的多个事件,必须为每个事件注册监听器。 组件持有可以让你在MXML程序中的ActionScript代码块里处理的内建事件。你也可以按照Flex 事件系统的调度者——监听者模式在程序外部定义你自己的事件监听器,并且定义由你的用户自定义监听器的哪个方法来监听城建的事件。你可以向目标对象注册监听器。当目标对象调度一个事件的时候,监听器将被调用。 所有的可视对象,包括Flex控件和容器,都是DisplayObject类的子类。它们位于构成你应用程序的树状结构中。树的根是一个Stage(舞台)。在Stage下面是SystemManager对象,然后是Application对象。子容器和组件都是数的叶节点。这个树可以被理解为一个显示列表。一个在位于显示列表中的对象与一个DOM节点相似。 1. 关于事件流 你可以指示任何的容器或控件来监听被其他容器或控件调度的事件。当 Adobe Flash Player调度一个事件对象的时候,事件对象将在显示列表中创建一个从根到目标节点的路线,并在此路线的每个节点上检查注册的监听器。 事件流从概念上讲分为三个部分:捕获阶段、目标匹配阶段和冒泡阶段。 关于捕获阶段 事件流的第一部分叫做捕获阶段。这个阶段包括从根节点到目标节点的父节点的所有节点。在这个阶段,Flash Player从根节点开始检查每个节点,看它是否持有一个被注册为处理此事件的监听器。如果持有这个监听器,Flash Player将为Event对象设定适当的值,并调用监听器。Flash Player将在到达目标节点的父节点并在其父节点上执行了任意注册的监听器之后停止。 关于目标匹配阶段 事件流的第二个部分是目标匹配阶段,只包括目标节点。Flash Player为Event类设定适当的值,并检查目标节点是否注册了事件监听器,如果目标节点注册了事件监听器,则调用监听器。 关于冒泡阶段 事件流的第三个部分是冒泡阶段。包括从目标节点的父节点到根节点的所有节点,并从目标节点的父节点开始。Flesh Player为Event对象设定适当的值,并调用每个节点上的事件监听器。FlasyhPlayer将在调用根节点上的任意事件监听器之后停止。 2. 关于事件类 Flash.events.Event类是一个ActionScript类,这个类持有与发生的事件相关的信息。一个Event对象是一个隐式创建的对象,相当于JSP中被应用服务器创建的的request和response对象。 每当一个事件被调度时,Flex都会创建一个Event对象。你可以在一个事件监听器中使用 Event对象来访问关于被调度的事件,或者是关于调度这个事件的组件的详细信息。将一个Event对象传递给事件监听器,并在其中使用这个Event对象,这种做法是可选的。不过,如果你希望在监听器中访问Event对象的属性,那么你就必须将Event对象传递给监听器。 当一个事件被调度的时候,Flex只创建一个Event对象。在冒泡和捕获阶段,当Event对象在显示列表中上移或下移的时候,Flex会改变Event的值,而不是为每个节点创建新的Event属性。 3.关于事件子类 有太多的类继承了flex.events.Event类。这些类中的大部分被定义在下面这两个包中: mx.events.* flash.events.* mx.events包中定义的事件类仅限于Flex控件,包括DataGridEvent,DragEvent和ColorPickerEvent。Flash.events包描述了那些并非Flex特有的而是由Flash Player代为定义的事件。所有这些事件都可以被普遍的应用于Flex程序。 除了这两个包,一些其他的包也定义了它们自己的事件对象,例如: mx.messaging.events.ChannelEvent和mx.logging.LogEvent。 Event类的子类拥有额外的属性和方法。在一些情况下,你需要使用一个更加特殊的事件类型以访问这个类型特有的属性和方法。例如,LogEvent类拥有getLevelString()方法,这个方法是Event类没有的。 4.关于EventDispatcher类 每个位于显示列表中的对象都继承了DisplayObject类。这个DisplayObject类依次继承了EventDispatcher类。EventDispatcher类是为这个列表中的每个对象提供重要的事件模型功能的基类。因为 DisplayObject类继承自EventDispatcher类,所有在显示列表中的对象都可以访问EventDispatcher类中的方法。 这很有意义,因为在显示列表上的每个成员都可以完全的参与到事件模型中。每个显示列表上的对象都可以使用它们继承自EventDispatcher类的addEventListener()方法来监听一个特殊的事件,但是只有在这个监听器对象是事件流的一部分的时候才行。 尽管从EventDispatcher的名字上看似乎这个类的主要职责是发送事件对,不过这个类更加频繁的被用于注册监听器、检查监听器和移除监听器。 EventDispatcher实现了IEventDispatcher接口。这允许开发者不必继承EventDispatcher或EventDispatcher的子类,而是实现IEventDispatcher接口来添加他们自己的方法。 addEventListener()方法是这个类最常用的方法。你用它来注册你自己的事件监听器。 高级程序员会使用dispatchEvent()方法来手动调度事件或发送一个用户自定义事件到事件流。 EventDispatcher类的其他几个方法提供了关于事件监听器是否存在的有用信息。如果在一个特定的显示列表上找到了一个特定时间类型的监听器,那么hasEventListener()方法将返回true。willTrigger()方法在一个特定的显示列表对象上检查事件监听器,但是它在事件流的各个阶段检查显示列表上所有对象的原型(ancestors)的监听器。如果找到了一个监听器,这个方法就返回true。 Flex事件(二) 一、使用事件 在Flex中使用事件是一个分两步走的过程。首先你要编写一个函数或一个类方法,作为一个响应事件的监听器或处理器。这个函数通常会访问Event对象的属性或一些其他的程序设定状态。这个函数片段通常包含一个被传入的、指定为 Event类型的参数。 下面的例子展示了一个简单的事件监听函数,当一个控件触发事件的时候,这个监听函数会就会报告: 就像在这个例子中你所看到的,你还通过使用addEventListener()方法为一个显示列表对象注册了函数或类方法。 多数Flex控件通过在MXML标签中指定监听器来简化监听器的注册。例如,在Button控件的click事件上指定一个监听函数: 这相当于在前面那个例子中的addEventListener()方法。不过,最佳实践还是使用 addEventListener()方法。这个方法给通过允许你配置优先级和捕获设而给予你更大的控制能力。除此之外,如果你使用addEventListener()方法来添加一个事件处理器,那么在不需要时你就可以使用removeEventListener()方法来移除这个事件处理器。但如果是在标签中添加的事件监听器,就不能调用removeEventListner()方法。 每当一个组件生成一个Flex就会创建一个包含了关于事件信息的Event对象,包括事件类型以及调度控件的引用。要使用这个Event对象,你需要指定它作为事件处理器函数的一个参数。如下所示: 如果希望在MXML标签中定义的监听器内使用Event对象,就必须在 MXML标签显式地中添加event关键字来将它传递给处理器。如下所示: 在处理函数中,你并非必须要使用Event对象。下面的例子创建了两个事件处理函数,并将它们注册到一个ComboBox控件的事件上。第一个事件处理器,openEvt(),不需要任何参数。第二个处理器,changeEvt(),持有Event对象参数并且使用Event对象来访问selectedIndex的值。 AK AL AR Flex事件(三) 1. 定义对象事件 你在一个监听器函数片段中指定对象为Event类型,如下所示: function myEventListener(e:Event):void { ... } 不过,如果你像访问比被调度的事件类型更加独特的属性,你就必须指定更加具体的事件。如下所示: import mx.events.ToolTip function myEventListener(e:ToolTipEvent):void { ... } 在一些情况下,你必须在你的ActionScript块中引入事件类。 大多数对象都有关联到其上的特殊事件,并且大多数对象都可以调度一个以上类型的事件。 如果你定义了一个Event类型的事件,你可以将它转换为跟家太特殊的类型来访问它的类型特有的属性。 2. 访问当前目标的属性 事件对象包含一个到调度组件(或目标)的引用,这意味着你可以访问那个目标组件的所有属性和方法。下面的例子访问了触发事件的Button控件的id属性; 你可以访问当前目标的成员。如果你没有将当前目标转换为一个具体的类型,那么编译器将认为目标的类型为Object。Object可以拥有任意的属性或方法,因为在ActionScript中Object类型是动态的。所以,当访问当前目标的方法和属性的时候,最佳实践是总是对目标进行类型转换。这样做将赋予你强壮的编译时类型检查,避免抛出危险的运行时异常。 下面的例子在调用setSelection()方法前将当前目标转换为TextInput类,但是在尝试设定tmesis属性前没有尝试进行转换。Tmesis属性在TextInput类中并不存在。这就意味着当你尝试访问并不存在的成员的时候,你将获得一个运行时错误,而不是一个编译时错误。 你还可以将当前目标转换为UIComponent或其他更加一般化的类。这样,即使你不知道到底是哪个具体的控件调度了方法,但至少你获得了一定程度的类型检查。 Flex事件(四) 1. 为一个事件定义多个监听器 当你需要在Flex控件上注册事件处理器时,有几种策略可以使用: (1) 在MXML标签内定义一个事件处理器,如下所示: (2) 使用addEventListener()方法,如下所示: (3) 创建一个事件监听器类并且将它注册到组件,使事件处理逻辑可以服用,并且可以将事件处理集中于MXML文件之外。 在MXML标签中定义事件处理器 在Flex程序中定义事件处理器的一个简单方法是在组件的MXML标签中指示一个处理器函数。通过向标签的事件属性添加任意的ActionScript语句或方法调用来做到这一点。 语法如下: 例如,要监听一个按钮控件的点击事件,你可以在标签的click属性中添加语句。如果你添加一个函数,你需要在一个ActioinScript块中定义那个函数。下面的例子定义了submitForm()函数作为Button控件的click事件处理器: 事件处理器可以包含任何有效的ActionScript代码。包含的代码可以调用全局的函数或者将组件的属性设定为返回值。下面的例子调用了trace()全局函数: 在行内事件处理器定义中,你可以传递一个特殊的参数:event。如果你将event关键字作为一个参数添加,Flex会将Event对象传递到这个处理函数。之后,你就可以访问Event对象的全部属性。 下面的例子传递了Event对象到submitForm()处理函数并且将它的类型定义为MouseEvent: 最佳实践是,为所有行内定义的事件监听器包含event关键字并且严格定义Event对象的类型。 你可以使用Event对象来访问一个目标对象的引用,事件的类型,或其他相关的属性,比如一个基于行的控件的行号和值。你还可以使用Event对象来访问目标组件或者是调度了事件的组件的方法和属性。 尽管你通常会将整个Event对象传递给事件监听器,但是你仍然可以只传递个别的属性,如下所示: 2. 与通过addEventListener()方法注册事件监听器相比,在行内注册事件监听器将丧失一些灵活性。缺点是你不能设定Event对象的useCapture或priority属性,并且一旦添加就不能再移除监听器。 Flex事件(五) 使用addEventLlistener()方法 addEventListener()方法可以让你为指定的控件注册事件监听器方法。下面的例子为一个Button控件实例添加了myClickListener()函数,当用户点击b1,Flex将调用myClickListener()方法: b1.addEventListener(MouseEvent.CLICK, myClickListener); addEventListener()方法拥有以下片段: componentInstance.addEventListener( event_type:String, event_listener:Function, use_capture:Boolean, priority:int, weakRef:Boolean ) event_type参数是组件调度的事件种类。它可以是任何一个事件类型字符串(如click或mouseOut)或者时间类型静态常量(如MouseEvent.CLICK或 MouseEvent.MOUSE_OUT)。这个参数是必须的。 这个常量提供了一个建议的途径来引用指定的事件类型。你应该使用常量来代替字符串表示。如果你拼错了一个常量的名称,编译器将捕获到错误。如果你采用字符表示来代替常量还拼写错了,那么可能会引起意料之外的行为。 你应该尽可能的使用常量。例如,当你要测试Event对象是否属于某种类型的时候,可以使用下面的代码: if (myEventObject.type == MouseEvent.CLICK) {/* your code here */} 但不要使用像下面这样的代码: if (myEventObject.type == "click") {/* your code here */} event_listener参数是处理事件的函数。这个参数是必须的。 user_capture参数可以让你在那些将被激活的监听器中控制事件流的阶段。它将设定Event对象的useCapture属性。如果useCapture被设定为ture,你的监听器将在事件流的捕获阶段被激活。如果useCapture被设定为false,你的监听器将在事件流的目标匹配阶段和冒泡阶段被激活,而不是在捕获阶段。默认值视时间类型而定,通常是false。 priority参数设置事件监听器的优先级。数值更高的事件处理器将优先于其他关联到相同事件的事件处理器被执行。如果事件监听器有相同的优先级,那么按照添加的顺序执行。这个参数将设定Event对象的priority属性。这个属性的默认值是0,但是你可以将它设定为负数或正数。如果几个事件监听器被添加的时候都没有指定该参数,则较早添加的监听器优先执行。 weakRef参数提供给你一些监听器的内存资源的控制能力。一个强引用(当weakRef为false时)将防止监听器被垃圾回收。一个弱引用(weakRef为true)则不会阻止。默认值为false。 当你添加一个监听器函数,并且那个函数被调用的时候,Flex会隐式的为你创建一个 Event对象并且将它传递给监听器函数。你必须在你的监听器函数片段中定义Event对象。 如果你使用addEventListener()方法添加一个事件监听器,你必须为listener_function定义一个Event对象参数。如下所示: b1.addEventListener(MouseEvent.CLICK, performAction); 在这个监听器函数中,你将Event对象作为一个参数定义。如下所示: public function performAction(e:MouseEvent):void { ... } 下面的例子定义了一个新的处理器函数myClickListener()。它将Button控件的点击事件监听器注册为那个函数。当用户点击按钮时,Flex将调用myClickHandler()方法。 在MXML标签中使用addEventListener() 你可以在组件的MXML标签中使用行内的addEventListener()方法来添加事件监听器。下面的按钮控件定义在initialize属性中天价了 addEventListener()方法的调用: 这样做与直接在行内定义事件处理器等同。不过,不过,通过使用addEventListener()方法定义处理器比设置click=”handler_function”的方法可以给你更多的可控制性。比如,你可以设置Event对象的useCapture和priority属性。而且,当你使用addEventListener()方法添加处理器,你可以用removeEventListener()方法来移除这个处理器。 Flex事件(六) 使用嵌套的函数作为事件监听器 除了将事件监听器函数的名字传递给addEventListener()方法,你还可以定义一个内部函数(也可以理解为闭包)。 在下面的例子中,当按钮被点击,嵌套的函数就被调用: 闭包函数除了可以在对象或类中创建,还可以在一个方法被调用时创建。闭包函数将保持它被定义时的范围。当它作为一个参数或一个返回值被传递到不同的范围时,将产生有趣的结果。 例如,下面的代码创建了两个函数。叫做foo()的函数返回一个叫做retArea()的用来计算矩形面积的嵌套函数,叫做bar()的函数调用foo()并且将它返回的闭包函数保存在一个叫做myProduce的变量中,bar()还定义了叫做x的本地变量,并赋值2。当闭包函数myProduct()被调用时,它继续持有在foo()中定义的变量x。bar()函数将返回的数值显示在TextInput控件中,但不是8。 如果你传递给addEventListener()方法的监听器是一个嵌套的内部函数,那就不要将useWeakReference参数的值设定为true。例如: addEventListener("anyEvent", function(e:Event) { /* My listener function. */ }, false, 0, true) 在这个例子中,如果将ture作为最后一个参数的值将导致意料之外的结果。对于Flex,内部函数其实就是一个对象,并且可以被垃圾回收释放。如果你将useWeakReference参数设定为true,就像上面例子里展示的那样,那么将不会有内部函数的长久的引用,在下一次回收时,可能会释放这个函数,并且当事件再次被触发时,函数将无法被调用。 如果还有内部函数的其他引用,那么垃圾回收就不会释放它。 类级的成员函数将不会被垃圾回收,这样你就可以将useWeakReference参数设定为true。 Flex事件(七) 移除事件监听器 对于任何一个处理器,如果不再使用就将它移除,是一个好主意。移除对象的引用,即可清理内存。你可以使用removeEventListener()方法来移除一个你不再需要的事件处理器。所有可以掉红addEventListener()方法的组件也可以调用 removeEventListener()方法。removeEvenetListener()方法的语法如下: componentInstance.removeEventListener(event_type:String, listener_function:Function, use_capture:Boolean) 考虑下面的代码: myButton.removeEventListener(MouseEvent.CLICK, myClickHandler); event_type和listener_function参数是必须的。它们与addEventListener()方法的必须的参数相同。 use_capture参数同样与addEventListener()方法中的use_capture参数相同。 回想一下,你可以通过调用两次addEventListener()方法,一次将use_capture设置为true,另一次将use_capture设定为false,来对事件的各个阶段进行监听。要移除这两个监听器,就必须调用removeEventListener()方法两次:一次将use_capture参数设置为true,另一次将use_capture参数设定为false。 下面的简单程序展示了哪些类型的处理器可以被移除,哪些类型的不能被移除: 创建事件处理器类 你可以创建一个外部类文件将类中的方法作为事件处理器使用。对象本身不是事件处理器,但对象的方法可以是。定义一个处理所有事件的类,相同的事件处理逻辑就可以在程序中通用。这样,可以使增强你的MXML程序的可读性和可维护性。 要创建一个类来处理事件,你通常需要引入flash.events.Event类。通常,你还要些一个空的构造函数。下面的ActionScript类文件中,每当handleAllEvents()方法处理一个事件的时候,就调用Alert控件的show()方法: // events/MyEventHandler.as package { // Empty package. import flash.events.Event; import mx.controls.Alert; public class MyEventHandler { public function MyEventHandler() { // Empty constructor. } public function handleAllEvents(event:Event):void { Alert.show("Some event happened."); } } } 在你的MXML文件中,定义一个MyEventHandler的实例,并且使用addEventHandler()方法注册为Button控件的点击事件的处理器。如下所示: 最好将事件处理方法定义为静态的,这样你就不需要实例化类。下面的createHandler()方法不需要实例化MyStaticEventHandler类,就可以注册handleAllEvents()方法: 在文件中,你只是为方法片段加上了static关键字: // events/MyStaticEventHandler.as package { // Empty package. import flash.events.Event; import mx.controls.Alert; public class MyStaticEventHandler { public function MyStaticEventHandler() { // Empty constructor. } public static function handleAllEvents(event:Event):void { Alert.show("Some event happened."); } } } 将你的事件监听器类保存在源文件路径中。你也可以将ActionScript类保存在和MXML文件相同的目录中,尽管Adobe并不推荐这样做。 Flex事件(八) 1. 为多个组件注册一个监听器 可以通过两种方式为一个事件定义多个处理器函数。当在MXML标签中定义事件时,你可以使用分号分隔多个处理器函数。下面的例子中为click事件添加了submitForm()和debugMessage()函数; 你可以使用addEventListener()方法添加任意数量的处理器。每个添加的处理器函数都将被调用。如下所示: 你可以混合使用将事件处理器添加到组件的方法:你可以在行内添加处理器,也可以使用addEvenntListener()方法。下面的例子在行内为按钮控件添加了一个点击事件处理器,然后又根据CheckBox控件的值有条件地添加了第二个点击处理器调用logAction()方法。 你可以在addEventListener()方法中使用priority参数来设置事件监听器被调用的顺序。 当使用行内的方式添加事件监听器时,你不能设置监听函数的优先级。 Flex事件(九) 1. 为多个组件注册一个监听器 你可以为相同或不同组件的任意数量的事件注册同一个监听器函数。下面的例子为两个按钮注册了同一个监听器函数:submitForm(): 当你要使用addEventListener()方法为多个组件的事件注册同一个事件监听器的时候,需要分别为每个实例调用addEventListener()方法。如下所示: 当做完这些之后,你可以为事件监听器添加处理事件的逻辑。事件的目标(或者是调度了事件的组件)被添加到Event对象。你可以通过Event对象的target或type属性来决定如何处理事件。 下面的例为按钮控件的点击事件和一个复选框控件的点击事件注册了一个监听器函数myEventHandler()。要确定是哪个对象调用了事件监听器,监听器通过在case语句中检查Event对象的target的className属性来做到这一点: Flex事件(十) 1. 向监听器函数传递额外参数 在添加监听器时,你可以向监听器函数传递额外的参数。如果你通过addEventListener()方法来添加监听器,你不能像监听器函数传递任何额外的参数,并且那个函数也只能定义一个参数,就是Event对象。例如,下面的代码将抛出一个错误,因为clickListener()方法需要两个参数: public function addListeners():void { b1.addEventListener(MouseEvent.CLICK,clickListener); } public function clickListener(e:MouseEvent, a:String):void { ... } 因为addEventListener()方法的第二个参数十一哥函数,所以你不能在addEventListener()方法中为那个函数指定参数,你必须在监听器函数中定义那些额外的参数,并使用这些参数调用最终的方法。如果你在行内定义了一个事件监听器,你就可以添加监听器函数声明的任意数量的参数。下面的例子讲一个字符串和Event对象传递给了runMove方法: Flex事件(十一) 一、 手动触发事件 你可以通过使用组件实例的dispatcheEvent()方法来手动的调度事件。所有继承自UIComponent类的组件都拥有这个方法。这个方法继承自EventDispatcher类。 dispatchEvent()方法的语法如下所示: objectInstance.dispatchEvent(event:Event):Boolean 当调度一个事件的时候,你必须创建一个新的Event对象。Event对象的构造函数如下: Event(event_type:String, bubbles:Boolean, cancelable:Boolean) event_type参数是Event对象的type属性。bubbles和cancelable参数都是可选的并且默认为false。 你可以使用dispatchEvent()方法调度你需要的任意事件,而不仅仅是用户事件。不管用户是否点击了按钮控件,你都可以调度一个按钮控件的click事件。如下所示: 你也可以在MXML标签中手动调度事件。在下面的例子中,将鼠标指针移出按钮将触发按钮的点击事件: 你的Flex程序并非必须处理每个新调度的事件。如果你触发了一个没有监听器的事件,那么Flex将忽略这个事件。 你可以在ActionScript中设置Event对象的属性,但是你不能为Event对象添加新的属性,因为Event对象不是动态的。下面的例子拦截一个点击事件,然后创建一个新的MouseEvent对象,并且将它作为一个鼠标双击事件进行调度。它将MouseEvent的shiftKey属性设置为true,来模拟单击键盘上的Shift键。 如果你想为Event对象添加用户自定义属性,那么你必须继承Event对象并且在你的用户自定义类中定义新的属性。然后就可以使用dispatchEvent()手动触发你的用户自定义事件。 如果你创建了一个用户自定义ActionScript类来调度它自己的事件,但它又没有继承UIComponent,你可选择继承flash.events.EventDispatcher类来获得addEventLIstener()、removeEventListener()和dispatchEvent()方法的访问。 Flex事件(十二) 一、 事件传播 当事件被触发,Flex检查是否有事件监听器时会经过三个阶段。这三个阶段按照下面的顺序发生: 捕获阶段 目标匹配阶段 冒泡阶段 在每个阶段,节点都有机会对事件做出反应。例如,考虑用户点击一个包含在VBox容器中的按钮。在捕获阶段,Flex会检查Appllication对象和VBox对象的事件监听器。然后在目标匹配阶段触发按钮的监听器。在冒泡阶段,VBox和Application再一次有机会处理事件,只不过这次的顺序与捕获阶段正好相反。 在ActionScript3.0中,你可以在目标节点和在事件流中的任意节点上注册事件监听器。然而,并非所有的事件都参与事件流的全部三个阶段。一些事件类型直接被目标节点调度,并且既不参与捕获阶段,也不参与冒泡阶段。所有的对象都可以被捕获,除非它被顶级节点调度。 对于其他一些目标对象不在显示列表中的事件,例如被调度到一个Socket类实例的事件。这个事件对象直接流转到目标节点,而不参与捕获阶段或冒泡阶段。你也可以取消一个在事件模型中流转的事件,即使假设那个对象还可以继续参与其他的阶段,你都可以取消它。只有Event对象的cancelable属性被设定为true时你才能这样做。 捕获和冒泡发生在Event对象在显示列表中的一个节点移动到另一个节点时,例如从父节点移动到子节点为捕获,从子节点移动到父节点为冒泡。这个过程在继承等级结构中不做任何事。只有DisplayObject对象可以在目标匹配阶段之外额外地拥有捕获阶段和冒泡阶段。 鼠标事件和键盘事件都处于它们的冒泡阶段中。所有事件都可以被捕获,但是没有DisplayObject对象会监听捕获过程,而不是像你显式指定它们去做的那样。也就是或,捕获过程默认是无效的。 当一个非显式的事件被调度,例如一个验证器,调度一个事件,那只能是目标捕获阶段。因为没有可视的显示列表来为Event对象进行捕获和冒泡。 1. 关于目标与当前目标属性 每个Event对象都有一个target和一个currentTarget属性来帮助你来了解其位于传播过程的那个位置。Target属性引用了事件的调度者;currentTarget属性引用了被找到事件监听器的节点。 当你在一些组件上编写监听器来处理例如MouseEvent.CLICK的鼠标事件的时候,event.target并不一定会引用那个组件,而通常是一个子组件,例如定义了标签的按钮控件的UITextField。 当Flash Player或Adobe AIR调度一个事件时,它会从最接近鼠标指针的对象进行调度。因为子组件在父组件之上,就是说,Flash Player或AIR可能会从一个内部的子组件调度事件,例如按钮的UITextField。 event.target属性被设定为调度这个事件的对象,而不是被监听的对象。 MouseEvent事件向父节点链冒泡,并且可以被任意的祖先处理。在冒泡的过程中,event.target属性始终是相同的(UITextField),但是event.currentTaret属性将被设定为处理这个事件的节点。最后,currentTarget将是Button,在那时按钮控件的事件监听器将处理这个事件。因此,你应该使用event.currentTarget而不是event.target。例如: 在这个例子中,在按钮的click事件监听器中,event.currentT arget总是引用Button,而event.target则也许是Button或UITextField中的一个,取决于按钮的哪一部分被鼠标点击。 2. 捕获阶段 在捕获阶段,Flex将在显示列表中审查事件的祖先(event’s ancestors)以确定有哪些注册了事件监听器。Flex从根祖先(root ancetor)开始并且在显示列表中向下遍历,直到目标的祖先(ancestor of the target)。在大多数情况下,根祖先是Stage,然后是SystemManager,然后是Application对象。 例如,如果你的程序有一个Panel容器,Pannel容器又包含有一个TitleWindow容器,TitleWindow容器又包含一个按钮控件,那么组件结构如下: Application Panel TitleWindow Button 如果你监听按钮控件的点击事件并且可以进行捕获,那么下面的步骤将在捕获阶段发生: (1) 在Application容器中检查事件监听器; (2) 在Panel容器中检查事件监听器; (3) 在TitleWindow容器中检查事件监听器。 在捕获阶段,Flex将改变Event对象的currentTarget属性的值来匹配被调用监听器的当前节点。Event对象的target属性则始终持有事件调度者的引用。 默认的,没有容器会在捕获阶段监听。addEventListener()方法的use_capture参数的默认值是false。使被添加的监听器在捕获阶段监听的唯一的方法就是将true传递给addEventListener()方法的use_capture参数。如下所示: myPanel.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler, true); 如果你在MXML中添加了一个行内的事件监听器,那么Flex会将use_capture参数设定为false,并且你不能覆盖它。 如果你将use_capture参数设定为true,换句话说,如果一个事件在捕获阶段被传播,事件仍然可以冒泡,但是捕获阶段的监听器并不理会(指冒泡)。如果你希望事件可以穿越(traverse)捕获阶段和冒泡阶段,你就必须调用addEventListener()两次:一次将use_capture()设定为true,另一次将use_capture()设定为false。 捕获阶段很少使用,相比之下,冒泡阶段却很常用。 Flex事件(十三) 1. 目标匹配阶段 在目标匹配阶段,Flex调用事件调度者的监听器,Event对象的currentTarget和target属性的值将是相同的。 2. 冒泡阶段 在冒泡阶段,Flex会在事件的祖先(event’s ancestors)中查找事件监听器。Flex将从事件调度者的直接祖先开始向上查找,知道根祖先。这与捕获阶段相反。 例如,如果你的程序有一个Panel容器,Panel容器包含一个TitleWindow容器,TitleWindow容器又包含一个按钮控件,那么程序的结构如下所示: Application Panel TitleWindow Button 如果你监听按钮控件的点击事件,如果允许冒泡的话,下面的步骤将在冒泡阶段发生: (1) 检查TitleWindow容器上是否有点击事件监听器; (2) 检查Panel容器上是否有点击事件监听器; (3) 检查Application容器上是否有点击事件监听器。 只有在bubbles属性被设定为true时,事件才会冒泡。鼠标事件和键盘事件是冒泡的。可以冒泡的事件包括 change,click,doubleClick,keyDown,keyUp,mouseDown和mouseUp。 当Flex调用一个事件监听器时,Event对象可能实际上是被一个显示列表中的深层对象调度。最先调度事件的对象是Event对象的target属性。当前正在冒泡的对象是Event对象的currentTarget对象。所以,当你要引用事件监听器的当前对象时,必须总是使用currentTarget属性而不是target属性。 只有当对象调度事件的时候,你才能为那个对象添加事件监听器。例如,你不能让一个Form容器监听click事件,除非那个容器包含有一个按钮控件。Form容器本身不能调度点击事件。Form容器可以调度mouseDown事件,所以你可以在Form容器上添加moseDown事件监听器。如果你那样做了,每当按钮被点击或者Form容器受到了一个mouseDown事件的时候,你的事件监听器会被调用。 如果你将useCapture属性设置为true,也就是说,如果一个事件在捕获阶段被传播,那么,它将不会冒泡,而不管它默认的冒泡行为。如果你希望你的事件能够同时在捕获阶段和冒泡阶段被传播,你必须调用addEventListener()方法两次:一次将useCapture参数设定为true,另一次将useCapture参数设定为false。 事件只能在显示列表中的祖先链中冒泡。处于同一容器中的两个按钮组件不会拦截对方的事件。 3. 探测事件阶段 你可以通过使用Event对象的eventPhase属性来探查你正在使用哪个阶段。这个属性包含了一个整数。内容描述如下所示: 1:捕获阶段(CAPTURE_PHASE) 2:目标匹配阶段(AT_TARGET) 3:冒泡阶段(BUBBLING_PHASE) 下面的例子展示了当前阶段和当前目标的ID: 4. 停止传播 在任何阶段,你都可以通过调用下面的方法来组织事件在显示列表中的移动(traversal): stopPropagation() stopImmediatePropagation() 你可以通过调用Event对象的stopPropagation()方法和stopImmediatePropagation()方法来阻止Event对象在事件流的路径中继续运行。两者基本相同,不同之处在于当前节点遗留的事件监听器是否允许被调用。stopPropagation()方法只是在当前节点的所有事件监听器被调用后,阻止事件继续向下个节点传递。stopImmediatePropagation()方法同样会组织Event对象向下个节点传递,但是它也不允许当前节点的任何其他事件监听器被执行。 下面的例子创建了一个Panel容器,包含有一个TitleWSindow容器。两个容器都被注册了mouseDown事件监听器。如果你在TitleWindow容器中点击鼠标,showAlert()方法将被调用两次,除非你调用stopImmediatePropagation()方法。如下所示: Flex事件(十四) 一、 事件的优先权 你可以为一个事件注册多个事件监听器。Flex按照通过addEventListener()方法注册监听器的顺序调。不过,如果你注册了一些行内的监听器,又通过addEventLisntener()方法添加了一些监听器,事件监听器的调用顺序将不可预知。 你可以通过使用addEventListener()方法的priority参数来改变事件监听器的调用顺序。 Flex按照priority参数从高到低的顺序调用监听器。优先级最高的第一个调用。下面的例子中,Flex在saveInputDate()函数前调用verifyInputData()方法,最后一个调用的是returnResult()方法。 你可以将优先级设定为任何有效的整数,可以使正数也可以是负数。如果多个监听器拥有相同的优先级,那么它们将按照注册的顺序被调用。 一旦事件监听器被定义,如果你再想改变监听器的优先级,就必须首先调用removeEventListener()方法移除监听器,然后再使用新的优先级添加监听器。 addEventListener()方法的priority参数并不是官方的DOM Level 3事件模型的一部分。 即使你为一个事件监听器设定了一比其他监听器都要高的优先级,也不能保证这个监听器一定会在其他监听器之前被调用。你必须确定在下一个监听器被调用之前,监听器不依赖于其他监听器的完整的执行。了解这一点非常重要,Flash Player没有必要在调用下一个监听器之前等待前一个监听器完成处理。 如果你的监听器依赖于其他的监听器,你可以在一个监听器中调用其它的监听器,或者在第一个事件监听器中调度一个新的事件。 二、 使用事件子类 依赖与事件类型,Event对象可以拥有一个范围很大的属性集合。这些属性都基于W3C的说明而定义,但是Flex并没有提供全部的实现。 当你在事件监听器函数中定义一个Event对象时,你可以将它定义为Event类型,或者你可以指定一个Event对象的子类。下面的例子中,你指定事件对象的类型为MouseEvent: public function performAction(e:MouseEvent):void { ... } 多数控件都会生成一个特殊的事件类型。指定一个更加特殊的事件类型,你就可以访问特殊的属性,而不需要进行类型转换。此外,一些Event对象的子类型还提供了基础的Event类型所没有的方法。 一个在运行时定义的事件对象可以是编译期类型的一个子类。即使你没有定义特殊的事件类型,只要你将Event对象转换为一个特殊的类型,你仍然可以在事件监听器中访问那些事件的特殊属性。在下面的例子中,函数将对象类型定义为Event。不过,在函数中,必须先将Event对象转换为MouseEvent理性,你才能访问localX和localY属性: 如果你将Event对象定义为一个特殊的类型,你就不需要在函数中进行类型转换。如下所示: private function customLogEvent(e:MouseEvent):void { ... } 在前面的例子中,你也可以只在访问属性时使用下面例子中的语法转换Event对象: 这样做虽然将使用更少的内存和系统资源,但是最好还是尽可能的将事件类型定义为特殊的类型。 每个Event对象的子类都提供该类型所独有的额外的属性和事件类型。MouseEvent类定义了与输入设备有关的几种事件,包括CLICK,DOUBLE_CLLICK,MOUSE_DOWN和MOUSE_UP事件类型。 Flex事件(十五) 一、 关于键盘事件 程序响应一个按键或一系列按键并执行一些动作,例如Control+q退出程序。Flex支持所有来自后台操作系统的所有组合键操作,它同样允许你覆盖或捕获任何按键或组合键爱执行一个用户自定义动作。 1. 处理键盘事件 在一些情况下,你希望捕捉全局的按键事件,不管用户在程序的哪个地方,他们的按键操作都会被程序检测到,并且动作会被执行。F 一个处理全局按键事件的通用方法是在application上创建一个KeyboardEvent.KEY_DOWN或KeyboardEvent.KEY_UP事件监听器。不管焦点在哪儿(只要焦点在程序中,而不是在浏览器控件上或浏览器之外),只要键盘被敲击,application容器上的监听器都将被触发。在处理器内,你可以使用 KeyboardEvent类的charCode和keyCode属性检查键位码或者字符码,如下所示: 要运行这个例子,你必须首先将焦点设定到程序中的某个东西上,例如文本输入控件。 因为任何集成了UIComponent的类都可以调度keyUp和keyDown事件,你也可以捕捉当焦点位于特定组件内时的键盘敲击事件。如下所示: 2. 了解键位码和字符码属性 你可以通过访问keyCode和harCode属性来确定到底是哪个键被敲击,并触发另一个动作作为结果。keyCode属性是一个数字值,它相当键盘上的建的值;charCode属性是建在当前字符集中的数字值(默认的字符集是UTF-8)。两者主要的区别在于,keyCode描述了键盘上的一个特殊的建(第一行中的1和小键盘中的1是不同的键,但第一行的1和!是相同的键),charCode描述了一个特殊的字符(R和r是不同的)。 键和键位值的映射关系依赖于设备(键盘)和操作系统。 下面的例子展示了你敲击的键的字符和键位码: 47 && num < 58) { var strNums:String = "0123456789"; return strNums.charAt(num - 48); } else if (num > 64 && num < 91) { var strCaps:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; return strCaps.charAt(num - 65); } else if (num > 96 && num < 123) { var strLow:String = "abcdefghijklmnopqrstuvwxyz"; return strLow.charAt(num - 97); } else { return num.toString(); } } ]]> 你可以通过使用条件运算符来监听特定的键或组合键: Flex事件(十六) 1. 了解键盘事件的优先权 你过你同时为一个控件和包含它的容器定义了keyUp或keyDown事件监听器,你将注意到键盘事件会被调度到每一个组件上的监听器,因为事件冒泡。唯一的不同更在于KeyBordEvent对象的currentTarget属性发生了变化。 在下面的例子中,my_vbox容器和my_textinput控件都调度keyUp事件到keyHandler()事件处理器函数: 当你检查输出,你将注意到target属性始终是相同的,因为它引用了原始的调度者(在这个例子中是my_textinput),但是currentTarget属性变了当前的节点。 事件监听器的调用顺序是由对象等级而不是addEventListener()方法的调用顺序决定的。子控件会先于容器调度事件。在这个例子中,文本输入控件第一个调度事件,接下来是VBox调度事件,最后是application调度事件。 当处理一个可以被操作系统或浏览器识别的按键或组合键时,操作系统或浏览器会通常会率先处理事件。例如,在IE浏览器中,当按下Control+w关时,窗口将会关闭。如果你在程序中捕获这个组合键,那么IE的用户将永远不知道程序的这个功能,浏览器已经在ActiveX Flash Player有机会响应事件之前关闭了浏览器。 2. 处理与键盘相关的鼠标事件 MouseEvent类和所有MouseEvent的子类都有下面的属性来使你能够确定当鼠标事件发生时是否有一个特殊的键被按下: altKey:如果当用户按下鼠标建时Alt键已经被按住,则返回true,否则返回false; ctrlKey:如果当用户按下鼠标建时Control键已经被按住,则返回true,否则返回false; shiftKey:如果当用户按下鼠标建时Shift键已经被按住,则返回true,否则返回false; 在下面的例子中,当用户在按下Shift键的同时点击鼠标,按钮控件将被删除:

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档