AdobeFlex大师之路第6章事件驱动编程6.1从代码了解事件:信号灯应用6.2Flex的“事件之旅”6.3Event对象6.4EventDispatcher类6.55个步骤创建自定义事件6.6自定义事件代码样例对于任何涉及图形用户界面的开发来说,事件控制都是基本技术。操作系统要不断监视环境中发生的各种事件,比如鼠标点击或键盘输入等,然后负责把这些事件报告给正在运行的应用程序,由应用程序决定对这些事件做出什么样的响应。Flex当然也不例外。相对于B/S时代浏览器应用的开发者来说,那些从传统的C/S架构时代走过来的程序员能够更深切地体会到Flex应用开发的真谛:基于组件的事件驱动模型。如今这个“概念为先”的时代,层出不穷的新名词令人眼花缭乱,然而拨开迷雾看本质,我们会发现那些十几年前就提出的基础概念具有强悍的生命力,许多新技术只不过是为它们披上了一些华丽的外衣。以Flex来说,MVC、松散耦合、事件驱动、组件开发这些基本的概念构成了Flex强壮的架构。得益于Flex这件华丽的外衣,这些概念又重新回到了互联网舞台的中心。Flex开发者离不开两件事:组件和事件。可以说,Flex开发就是基于组件的事件驱动编程。事件不仅能够帮助开发者响应用户操作、完成应用任务,更重要的是,充分利用事件可以使开发者设计出松散耦合的Flex应用架构。从这个角度来说,Flex并不比10年前的VisualBasic、VisualC++等C/S时代的编程语言更特别,事件驱动模型仍然是Flex开发的核心。我们将在本章深入解析Flex的事件模型,从介绍最基本的概念开始,最终引导你定制自己的事件,打下成为Flex大师的基础。6.1从代码了解事件:信号灯应用6.1.1事件代码范例:创建信号灯应用让我们直接从代码开始。我们要创建一个简单的信号灯应用,如图6-1所示。当用户点击“绿色”、“红色”或“蓝色”按钮,将会显示相应信息。比如点击红色按钮后,会显示“交通信号灯:红色”。图6-1事件代码样例:信号灯应用创建新的Flex项目TrafficLight,设置应用的layout为“absolute”。在设计模式中,拖曳Panel容器到应用中,设置属性:width=250height=220layout=absolutehorizontalAlign=centertitle=事件样例fontsize=”16”拖曳Lable(标签)到HBox容器中,属性为:x=80y=45text=关闭id=lblLightInfohorizontalCenter=0在设计模式中,拖曳HBox容器到Panel,设置属性:x=10y=104id=ctnButtons在设计模式中,拖曳三个按钮放置在HBox中,按钮的id和lable属性分别设为:id=btnGreenlabel=绿色;id=btnRedlabel=红色;id=btnBluelabel=蓝色。切换回代码模式,现在你的代码应该看起来如代码6-1。组件的位置属性无关紧要,你可以按照你喜欢的方式进行设置。代码6-1:信号灯应用的布局?xmlversion=1.0encoding=utf-8?mx:Applicationxmlns:mx==absolutemx:Panelx=372y=56width=250height=220layout=absoluteid=traficLighttitle=事件样例fontSize=16mx:Labelx=80y=45text=关闭id=lblLightInfo/mx:HBoxx=10y=104width=100%id=”ctnButtons”mx:Buttonlabel=绿色id=btnGreen/mx:Buttonlabel=红色id=btnRed/mx:Buttonlabel=蓝色id=btnBlue//mx:HBox/mx:Panel/mx:Application在Application中加入系统事件creationComplete=initApp();,这样当应用构造完毕后,会自动调用initApp方法执行我们需要的操作,如代码6-2所示。代码6-2:信号灯应用creationComplete事件?xmlversion=1.0encoding=utf-8?mx:Applicationxmlns:mx==absolutecreationComplete=initApp();initApp()方法将为应用增加一些关键代码。当用户点击按钮时,这些代码能够引导指定的“工作人员”去处理用户的操作。在这个例子中,myEventHandler方法就是这个“工作人员”。我们在mx:Application下加入如下ActionScript代码段6-3。代码6-3:信号灯应用initApp事件mx:Applicationxmlns:mx==initApp();layout=absolutemx:Script![CDATA[privatefunctioninitApp():void{ctnButtons.addEventListener(MouseEvent.CLICK,myEventHandler);}]]/mx:ScriptinitApp方法中使用了addEventListener方法,告诉FlashPlayer,一旦发生了MouseEvent.CLICK事件(也就是某个人点击了按钮),就召唤“工作人员”myEventHandler来进行处理。接下来,我们要在Script代码段中定义myEventHandler()方法,如代码6-4所示。myEventHandler要更新标签lblLightInfo的显示,说明哪个“交通信号灯”被“打开”了。代码6-4:信号灯应用myEventHandler事件privatefunctionmyEventHandler(event:Event):void{if(event.targetisButton){lblLightInfo.text=交通信号灯:+Button(event.target).label;}}好了,我们看看最终的代码全貌,如代码6-5所示。代码6-5:信号灯应用代码?xmlversion=1.0encoding=utf-8?mx:Applicationxmlns:mx==absolutecreationComplete=initApp()mx:Script![CDATA[privatefunctioninitApp():void{ctnButtons.addEventListener(MouseEvent.CLICK,myEventHandler);}privatefunctionmyEventHandler(event:Event):void{if(event.targetisButton){lblLightInfo.text=交通信号灯:+Button(event.target).label;}}]]/mx:Scriptmx:Panelx=372y=56width=250height=220layout=absoluteid=traficLighttitle=事件样例fontSize=16mx:Labelx=80y=45text=关闭id=lblLightInfohorizontalCenter=0/mx:HBoxx=10y=104width=100%id=ctnButtonsmx:Buttonlabel=绿色id=btnGreen/mx:Buttonlabel=红色id=btnRed/mx:Buttonlabel=蓝色id=btnBlue//mx:HBox/mx:Panel/mx:Application6.1.2分析“信号灯应用”中的事件我们可以通过“信号灯应用”初步了解Flex的事件模型。这个简单的例子包含了两个事件:creationComplete和MouseEvent.CLICK。讨论MouseEvent.CLICK这里没有什么高科技,只是某人点击了按钮,然后应用做出响应,更新了标签的显示信息。从程序员的视角看,这个过程涉及了三个主要元素,也正是事件的三个要素:·事件源:事件源指的是发生事件的对象。本例中即三个按钮;·侦听器:侦听器是一个具体的方法,负责处理事件响应。侦听器要被注册到某个可以接收到事件通知的对象上。本例中,侦听器是myEventHandler方法,被注册到按钮的容器ctnButtons对象上;·事件:事件本身就是一个对象。侦听器在处理事件的时候,有时候需要知道事件的来龙去脉,比如事件源是谁?发生了什么事情?事件对象封装了这些信息。本例中,侦听器myEventHandler方法的参数即事件对象。从Flex程序编写的角度看,全过程如下:首先放置了“事件源”:三个按钮btnGreen、btnRed、btnBlue。三个按钮被置于一个容器ctnButtons中。创建事件侦听器myEventHandler方法。侦听器根据事件对象内封装的信息,决定如何响应事件。这里的情况有一点复杂,侦听器myEventHandler还须要解决两个问题。5.用户点击的不是按钮怎么办?在注册事件侦听器的时候,我们只告诉FlashPlayer在发生MouseEvent.CLICK事件时调用myEventHandler。这就意味着,用户点击任何容器中的对象,都会触发该事件。举个例子来说,如果容器中除了按钮外,还包含了一个文本输入框组件,用户点击了该组件,会发生什么?FlashPlayer会即刻调用侦听器myEventHandler,但是我们的侦听器很聪明,他会检测事件源是否为“按钮”,如果不是按钮,那么什么都不会发生。秘诀在于传递给myEventHandler的Event类型参数:event。Event是在事件发生时,由FlashPlayer隐式创建的,因此你看不到该对象的声明和创建代码。Event描述了所发生的事件,这里event.target告诉myEventHandler事件源是谁。代码event.targetisButton检查事件源是否为按钮对象。6.用户按了哪个按钮?在我们的例子中,myEventHandler知道用户点击了哪个按钮,继而相应地更新lblLightInfo标签信息。这同样是通过事件对象来实现的。Event对象实例event通过event.target告诉了myEventHandler谁是事件源,继而通过强制类型转换Button(event.target),得到了发生事件源按钮对象,从而获取了它的label属性。之后,addEventListener方法把“事件侦听器”myEventHandler注册在容器ctnButtons上:MouseEvent.CLICK事件发生之后(民间说法即:有人点击了按钮),FlashPlayer立即向“全世界”广播该事件消息。当发现容器ctnButtons注册了针对MouseEvent.CLICK事件的侦听器myEventHandler时,就调用该方法,并把事件对象发送过去。讨论creationComplete与MouseEvent.CLICK事件不同,事件creationComplete并不是由用户的操作触发的。当信号灯应用初始化完毕,FlashPlayer就会自动触发该事件。有意思的是,在这里我们并没有看到像MouseEvent.CLICK事件那样,通过addEventListener注册creationComplete事件的侦听器,那么,FlashPlayer如