Flex事件概述
Flash/Flex中的事件是编写应用的基础,是和用户交互的基础。应用和用户之间的交互就是以事件为桥梁。
应用对用户动作的响应相当于应用和外界的交互,而应用之间也有需要响应的事情,这相当于内部的交互。举个例子来说,用户单击了某个按钮,程序就应该做某些事情,这就是用户和外界的交互;而应用中某个数据的改变,应用其他部分也要随其做相应的改变就是内部交互。
Flex事件机制
1.什么是事件机制
事件可以看作是一种触发机制,当满足了一定的条件后,会触发这个事件。比如MouseEvent就是指的当鼠标进行操作之后触发的一系列的事件。很多控件中都有click事件,这个事件就是一个MouseEvent的实例,当点击鼠标后,系统会自动抛出一个名称为click的MouseEvent事件。如果此时在click上注册一个方法,那么触发该事件时就会执行这个方法。
事件的机制包括注册事件、触发事件、事件的传递
事件注册:指将事件的处理函数指定给对象的一个事件,实际上是对该事件创建一个侦听器。事件被注册后,当事件发生后,就会调用注册的事件响应函数。
事件触发:注册到对象上的事件发生时,就是事件的触发。事件触发后会调用注册在该事件上的事件响应函数,同时创建事件对象的一个实例,该实例会包含事件的信息。一个事件的信息包括以下几种:
1)属性bubbles:布尔类型,显示该事件是否为冒泡事件;
2)属性cancelable:布尔类型,显示是否可以阻止与事件相关联的行为;
3)对象currentTarget:与当前事件相关联的对象。
4)属性eventPhase:无符号整数,指示当前事件在整个事件流中的阶段;
5)对象Target:触发事件的对象。也就是整个事件流的源头。在实际过程中使用currentTarget来处理事件的当前目标;
6)字符串type:当前事件的类型。如事件是click,那这个值就是click。
一个事件被触发后,一般经历3个阶段,第一阶段就是捕获阶段,Flex应用是一层一层的关系,比如顶级Application里面有一个容器Canvas,而Canvas里面有一个VBox,真正发生事件的是VBox里的一个按钮。当用户单击这个按钮时,实际上该事件会经历3个对象,第一个就是Application,第二个就是Canvas,第三个是VBox,最后才是事件注册的对象按钮。Click事件自上而下的进行查找点击对象的工作,这个过程就是捕获阶段。第二个阶段是目标锁定阶段,当自上而下找到事件注册并触发的对象时,Flex将会自动创建一个Event的实例。这个实例包括整个事件的相关信息。第三个阶段就是冒泡阶段,与前面捕获阶段相反冒泡阶段就是时间会向上一级传递。
事件传递:对于处理时间来说,事件的传递是关键。一个事件被触发后,事件就开始冒泡,形成了一个事件流。
Flex事件分发
最终继承自EventDispatcher的对象都会含有dispatchEvent这个方法,他有一个参数,事件对象。之前说到的事件注册通道,他只是一个通道,实际上事件是由这个方法来分发出去的,通道只是一个管道而已。他的作用就是分发一个事件对象,他的分发是没有目的的,一种广播形式的,Flex的事件监听线程会接收到各种各样的事件(我们称之为捕获事件,这在后面会介绍到),那么哪种才是你要的事件,标识就通过事件的type属性来区分。
1)Flex事件对象
在分发事件时,将会分发一个事件对象出去。不管是那个事件类,都是继承自flash.events.Event对象的,他包含一些比较重要的属性,type和bubbles。
type是事件的类型,事件监听通过这个参数来识别是否是自己所监听的事件。
bubbles是个布尔值,决定了该对象是否会向上传递。默认是false。什么意思呢,
比如说,当button组件分发click事件对象时,设置的bubbles为false,dispatchEvent(newMouseEvent(“click”,false));
事件对象无法跨越组件本身,当然,除了之前讲到的注册通道(这样就很形象了吧)因此,如果没有注册通道,在Flex主应用中,就无法捕获到这个button组件分发出的事件。如果我们将Bubbles设为true,这个事件可以跨过组件本身,到达Flex主应用里。不止这样,在帮助手册中明确说到,如果在传递过程中间一直没有被捕获的话,这个事件会逐层上传,直到最终的stage,那时如果还没被捕获,这个事件就会被销毁掉。这样一来,即使我们没有click的事件通道,只要我们在Flex主应用中添加Flex事件监听器(addEventListener)那么我们就可以获得到这个分发出的click事件了。
一、事件注册通道
通道是只能在mxml的代码提示中可以看到的,他的作用就是给mxml组件提供 事件触发时所执行的方法的注册通道,而且能在代码提示中可见,这样给组件提供了很大的抽象的好处,我们可以很清楚的告诉组件的使用者,组件里包含哪些事件给你调用。
Button的click事件是继承自核心类InteractiveObject,遗憾我们看不到他的源码,但是说明了“事件注册通道”是可以继承的。我们会在自定义事件中讲述到如何声明“事件注册通道”。
该示意图对应的Flex主应用的mxml代码
<mx:Script> <![CDATA[ import mx.controls.Alert; private function clickHandler(e:MouseEvent){ Alert.show(e.currentTarget.toString()); } ]]> </mx:Script> <mx:Button id="testBtn" click="clickHandler(event)" label="测试"> </mx:Button>
1、设计视图下图所示,拖一个Panel、一个Textarea、两个按钮到设计界面。
2、源代码如下所示:
<?xml version="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" creationComplete="init()"> <fx:Script> <![CDATA[ import mx.utils.OnDemandEventDispatcher; import spark.components.TextArea; private function recordEvent(e:Event):void { writeLog(e.target.id + "被点击," + "当前对象" + e.currentTarget.id); e.stopPropagation(); } private function writeLog(str:String):void { ta.text += str + "\n"; } private function clear(e:Event):void { e.stopPropagation(); ta.text = ""; } ]]> </fx:Script> <fx:Declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:Declarations> <s:Panel width="204" height="231" click="recordEvent(event)" id="panel" x="15" y="15" title="事件触发与传递" fontSize="12"> <s:TextArea x="17" y="7" id="ta" width="170"/> <s:Button x="17" y="164" label="点击" id="btn" click="recordEvent(event)"/> <s:Button x="117" y="164" label="清空" click="clear(event)"/> </s:Panel> </s:Application>
3、编译运行,结果截图如下:
二、事件监听
在分发中,我们讲到,如果不是通过注册通道来调用触发事件,那么我们是需要一个监听来捕捉的。如何捕捉到分发出的事件,就是通过事件的type值。
<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml layout="absolute" xmlns:comp creationComplete='init()' > <mx:Script> <![CDATA[ private function init(){ testBtn.addEventListener(“click”, clickHandler); }
Flex的事件中都提供了一些静态常量,让我们调用,避免我们打错了。因此这句话可以这么写
testBtn.addEventListener(MouseEvent.CLICK,clickHandler);
我们看到,监听的回调方法中没有传递参数,是的,这和通道的写法有些不同,这里的回调方法(即clickHandler)只是个引用,并不是代表方法的执行,他的含义是,告诉eventLinstener,如果捕捉到click事件,那么就去找clickHandler,并执行它,event对象参数在执行时动态的传递。
三.绑定机制
在我们了解了事件机制后,那么理解绑定就不难了。绑定其实也是事件机制的运用
1. 什么是绑定
绑定的原理就是事件,在被绑定的对象上增加了改变事件的监听,一旦某个被绑定对象改变后,就会分发一个“propertyChange”事件(默认的,也可以改变成自己定义的事件),在其他组件中,会有propertyChange的事件监听,当捕捉到该事件后,则会去更新组件的属性并显示。
绑定的作用在于,将Flex中的变量、类、方法等与组件的值进行绑定。例如,一个变量如果被绑定后,那么引用该变量的组件的相关属性也会发生改变。我们用一个实例来表示
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx=http://www.adobe.com/2006/mxml layout="absolute" xmlns:comp > <mx:Script> <![CDATA[ import mx.controls.Alert; [Bindable] private var isSelected:Boolean; private function clickHandler(e:MouseEvent){ //Alert.show(e.currentTarget.toString()); isSelected=isSelected?false:true; //这句话的意思是如果isSelected为true,改变它为false,如果它为false,改变它为true; Alert.show(isSelected.toString()); } ]]> </mx:Script> <mx:Button id="testBtn" click="clickHandler(event)" label="测试" /> <mx:CheckBox x="60" selected="{isSelected}" /> </mx:Application>
上述程序的效果就是,当点击button时,button不是直接改变checkbox的选中状态,而是改变isSelected这个变量,由于isSelected是被绑定了的,那么会关联的改变CheckBox的选中状态。
这样看起来有些多此一举,完全可以直接改变checkbox的selected属性,我只是为了演示一下效果。如果说你的checkbox是动态构造的上百个,你不会去一个个的改变他吧。
如果这个代码中取消了[Bindable]的声明,会怎么样?isSelected不会改变了吗?
isSelected会改变,我们alert出来的结果也会显示结果改变了,但是checkbox的选择状态不会改变,因为当一个组件由创建到最终显示出来时是经过很多方法的,比如addChild,commitProperties,updateDisplayList等,updataDisplayList则是类似刷新显示效果一样的方法。
仅仅改变属性,而不去更新显示效果那么组件不会因为属性的改变而发生任何变化。
绑定的原理也是利用的事件分发。
四. 自定义事件的分发
1. 自定义事件 components/MyEventTest.as
package components { import mx.events.FlexEvent; public class MyEventTest extends FlexEvent { public static const ONCHANGE:String = "onChange"; public var eventInfo:String; //自定义的事件信息 public function MyEventTest(s:String){ super(s); //如果在构造时不设bubbles,默认是false,也就是不能传递的。 eventInfo="这个事件是:"+s; } } }
2. 自定义组件 components/ComponentForEvent.as
package components { import flash.events.EventDispatcher; //这个就是声明事件注册通道的方法了。name是事件对应的名称,也就是之前提到的type。Type是该事件的类 [Event(name="onChange", type="components.MyEventTest")] public class ComponentForEvent extends EventDispatcher { private var name:String; public function changeName(newName:String){ this.name=newName; dispatchEvent(new MyEventTest(MyEventTest.ONCHANGE) ); } } }
3. App.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:comp > <mx:Script> <![CDATA[ import mx.controls.Alert; private function changeName(){ cfe.changeName("新名称"); } ]]> </mx:Script> <mx:Button id="testBtn" click=" changeName ()" label="测试" /> <components:ComponentForEvent id="cfe" /> </mx:Application>