转载自:www.kingda.org
Event机制作为重头戏,在ActionScript 3.0中加强了很多。更加统一、易用、标准、灵活。
ActionScript 2.0中有众多的事件实现机制:
回顾和比较
AS1.0玩家最爱用onClipEvent(), on(),又方便又直接。缺点在于逻辑分散到了各个舞台元件中,难以管理和维护。更加别说代码重用了。别跟俺说可以Copy, Paste,这不叫重用,这叫低级。
AS2.0中,增加了一些事件处理机制:
1.回调函数: onLoad, onComplete等。相信兄弟们最熟悉的应该就是XML.onload回调函数了。
2.事件侦听器型:
这个就是addListener(), addEventListener()这种类型的。发送事件有的是内置,有的通过dispatchEvent().
嗯,这种事件机制基本上就和AS3.0很像了。自从使用了ActionScript 2.0来开发项目,黑羽就尽可能的多用这种事件处理机制。这个习惯很好,基本上让黑羽对3.0的机制很快适应过来。
说道这里,黑羽要多扯几句2.0,抱怨一下它的不足,再让大家看看AS3.0的光明大道。
(1).2.0中除了UIComponent能自己发送事件,绝大部分类不能自己发送事件的,比如MovieClip, 或者一些自定义的类。黑羽还制作了一个EventSender的事件发送类来解决。
当然你可以通过扩展来解决MovieClip这些类来解决,但是在一些轻量级或者特殊运用中,还是用黑羽这个EventSender类更加方便。
比如说,你突然需要舞台上某个A_mc的运行到第20帧时发送一个"finished"事件出来,并且希望另外某个B_mc能够捕捉到这个事件,那么用一般的扩展方法不知道有多么麻烦!
(1.要重做一个带有事件发送功能的类和A_mc通过某种方式绑定。2.同时确保在B_mc中要能访问到发送事件的对象并addEventListenr)。
而用俺的EventSender类非常简单,A_mc中写 EventSender.send("finished", this), B_mc中写EventSender.addListener("finished", listenerFunc),并可以通过event.target属性直接定位A_mc,真是简单的不能再简单了。(请尽量以正规方法为主,不推荐频繁使用,不是好的编程习惯)![]()
(2)2.0中,侦听器的记忆是"有毛病"的。如果是新手,会经常觉得侦听器函数的this关键字指向飘忽不定,常常弄错。而且看看高手的代码,一会儿是Object做侦听器,一会儿是function做侦听器,真是让人头痛。其实MacroMedia也很头痛,所以就出了Delegate这个官方类(补丁?)来解决这些问题。
(3)侦听器注册方式也有两种,一种是addListener(),如Key,一种是addEventListener(),如UIComponent类。为什么要这样搞两种?MacroMedia无辜的望着我,喃喃道偶也不完全清楚。
超人来了,那就是ActionScript 3.0事件处理机制:
(1)统一。全部一色用addEventListener().
(2)所有的可视对象都可以接受和发送事件。
AS3.0的类继承设计是深思熟虑的,所有的可视对象所属类都是DisplayObject的子类, DisplayObject又是EventDispatcher的子类。因此它们就都可以玩Event了,所以说,有个好的老子就是好啊。
而且有了崭新的事件冒泡机制,可以使事件层层上递到最上层的Stage,绝好的功能!
有了以上两点:黑羽的EventSender类也可以歇菜了。
(3)侦听器统一使用Function,不再使用Object了。 同时this关键字的记忆力“大大增强”,Delegate类也可以下岗了。
Event涉及到的内容极多,面很广。下面黑羽将从以下几个方面讲起:
一、如何接收事件?如何做到AS3.0的标准事件编程。
二、如何发送自定义事件?如何在OOP中正确使用AS3.0强大灵活的事件架构。
三种方式及其优劣,以及在何种情况下使用。
(1)用继承EventDispatcher实现
(2)用复合EventDispatcher实例来实现。
(3)用接口IEventDispatcher实现
三、如何使用冒泡机制(即官方所称的Event Flow机制)?以及冒泡机制的原理。
四、Event的其他高级应用。
如何接收事件?如何做到AS3.0的标准事件编程?
Event改变的部分很多,这两天有空时,黑羽就在想怎样用一个有条理的方式来讲解Event和它相关的诸多内容,让我们感到比较容易理解,记忆和接受。
我准备这样来讲解:
先给个接受事件的代码例子。在例子中,指出:
(1) Event对象发生了什么变化
(2) addEventListener语法的不同,原因,和const型必要性和用法
(3) Listener和As2.0有何不同,和this关键字的“改进了的记忆力”
黑羽一贯的风格,先来一个例子。我很想给个短一点的代码例子,但是要达到清楚,全面和标准的示范,我还是决定采用这个Document Class的示例。我会在每个代码段注一些注释,大家不明白的地方回贴说一下,我会尽量解答。
Document Class的含义和相关用法并不难,忘了的兄弟看我第3篇教程:
AS3.0教程(3):Document Class特色为我们带来了什么?
好,come on baby.
新建一个as文件,拷贝以下代码,命名为AddListener.as。
新建一个fla,命名为 "黑羽黑羽我爱你.fla"(本教程推荐使用,倘若不遵从可能导致喝凉水塞牙泡MM被踢炒股被套等严重后果,霍哈哈。Just kidding。)。设置它的文档类(Document Class)为AddListener.
//【黑羽】ActionScript 3.0系列教程(6)
//http://www.kingda.org
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
//哈哈,看到了没,Document Class不仅可以扩展MovieClip,也可以扩展Sprite
//package里面的类名要和文件名相同
public class AddListener extends Sprite {
public function AddListener() {
//用package外面定义的类KingdaSprite创建一个实例,由于同文件中,所以不用import啦
var outsideChild:KingdaSprite = new KingdaSprite(0x00FF00, "outside_sprite");
addChild(outsideChild);//没有了这一句,你啥都看不到。
outsideChild.addEventListener(MouseEvent.CLICK, inclassHandler);//注册类里面的侦听器
outsideChild.addEventListener(MouseEvent.CLICK, outsideHandler);//注册类外面的侦听器
}private function inclassHandler(event:MouseEvent):void {
trace("类里面的侦听器侦听到MouseEvent事件: " + event);
trace("this关键字指向:"+this);
}
}
}function outsideHandler(event:MouseEvent):void {
trace("类外面的侦听器侦听到MouseEvent事件: " + event);
trace("this关键字指向:"+this);
}import flash.display.Sprite;
import flash.events.MouseEvent;//这个类就是画一个矩形,然后你点击这个矩形会发出标准鼠标click的事件
class KingdaSprite extends Sprite {
public var nickname:String;
public var ColorNum:uint;
//colorNumber就是#ffcc00这种类型的数,在AS3中推荐用新的uint型来标记它
public function KingdaSprite(colorNumber:uint, nameString:String) {
ColorNum = colorNumber;
nickname = nameString;
graphics.beginFill(ColorNum);
graphics.drawRect(0,0,100,100);
graphics.endFill();
}
}
鼠标一点击创建出来的绿色矩形,会看到输出
类里面的侦听器侦听到MouseEvent事件: [MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=64 localY=80 stageX=64 stageY=80 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
this关键字指向:[object AddListener]
类外面的侦听器侦听到MouseEvent事件: [MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=64 localY=80 stageX=64 stageY=80 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
this关键字指向:[object global]
演示完毕,我们来讲第一个话题
(1) Event对象发生了什么变化
AS2.0中创建event 对象是很随意的,只要这个对象有一个String属性叫做type的就可以了,甚至连target都可以省掉。不要以为这不规范不应该做,看看Flash类源码,Macromedia的程序员可不只一次的这样使用过。所谓上梁不正下梁歪,就是这样。(其实严格说也没什么不对,有时候是不需要target。但没有标准就是不好)
然后你在Event Object中爱添加什么就添加什么。极其自由,也就等于极其混乱。
3.0不同了,所有的事件必须都要继承自Event这个类(全饰名称flash.events.Event)。不然,哼哼,事件发送是不能成功地。
说说Event这个类,那叫一个NB。AS3.0的基石类是Object,Event是直接继承自Object的,开国大佬级别的。它里面定义了一些基本的事件名称,比如ACTIVATE(FlashPlayer得到系统焦点时事件),ADDED(对象被添加到显示时发送的事件)。AS3.0中有个好功能是cancel事件,但Event中这些基本事件统统是不能被cancel的,听起来似乎很NB。这不细说了,Event太多内容了。以后写高级内容时在提到如何运用吧。那叫一个爽字了的!要想Flash玩的转,Event类必须很精通。正所谓江湖人称:"平生不识Event,就称闪客也枉然。"
这样有什么好处?
好处太多了,最大的好处是一,规范了事件的定义,二,大量由此衍生的类省去了我们大量的时间和重复开发的成本。
规范事件的定义放到下一部分讲。至于内置的Event子类好处,大家请看上面fla的运行输出:
MouseEvent就是内建的一个Event子类,它的好处一看它的内容就明白:
从AS3.0起,任何一次点击事件我们都可以得到:
1.事件发生时鼠标的MC相对坐标(localX=64 localY=80)和绝对坐标(stageX=64 stageY=80)。 爽不爽?![]()
2.ctrl键,shift键,alt键有没有按下。你试试按着ctrl键再点击一下方块,那么ctrlKey就为true了。爽不爽?![]()
(其余参数不解释,涉及到高级运用。部分会在Event最后一节提到该怎么运用)
光这一个MouseEvent类,黑羽怎么着都觉得值人民币20块。
(2) addEventListener语法的不同,和const型必要性
这一节很重要,不过现在12点半了。精力不太好了,但又不想草草了事。明儿接着写,质量第一。数数也有小两千字了,先发出来顶顶先。
(2) addEventListener语法的不同,原因,和const型必要性和用法
本例如果是AS2.0,那么代码是这样写的:
outsideChild.addEventListener("click", inclassHandler)
本例是3.0,如果你trace一下其中的MouseEvent.CLICK,输出的也是字符串"click"。
看起来AddEventListener的方法和以前也没什么大的出入。和这世界上大多数相同的事物一下,表象的类似却掩盖着本质的巨大差异。
我们先从最小的差异讲起:
1.使用类静态属性,用const定义事件字符串名称变量
AS3.0中用了一个新的关键字定义了事件名称字符串变量,代码是
public static const CLICK:String = "click"
const,是英文constant的缩写,意思使不变的,常量。那就意思很明白了,一旦这种类型的常量被定义就不可再更改。好处通俗的说就是规范好项目,规范你自己,也规范任何其他项目人员,动不了这个变量。
你可能还是会不屑一顾,这么小的一个改动,对我没什么用!
可我的AS2.0开发血泪经验是,我曾经花了一个下午来找bug,最后却发现是某个类的addEventlistener()里面的事件名称"click"手误打成了"cilck"。编译器可不管这个。
如果是3.0,你打成了MouseEvent.CILCK,那么编译时立刻会报错提醒你,多好啊。一个项目一个人做十几个类还好办,自己认真点还能顶的住;如果是一个项目几个人几十个类,那么没有const和static的帮忙,管理事件类型还真是有点麻烦。即使实现了也没有AS3.0这么简单直观。
回到代码,我们要记住,日后我们开发自己的Event类时,也要像这样,用public static const来定义我们自己的事件名称。
如何自定义自己的事件,我会在EventDispatch那一节讲述。
看看代码,我们还发现AS3.0中侦听器也发生变化了,只能用function来做侦听器,不再用Object。
2.addEventListener高级运用
AS3.0中对侦听器的改进远远不止以上这些,看一看addEventListener的实现接口:
function addEventListener(eventName:String,
listener:Object,
useCapture:Boolean=false,
priority:Integer=0,
useWeakReference:Boolean=false):Boolean;
哇塞,有三个莫名奇妙的参数。可是当你知道这三个参数背后隐藏的巨大改进之后,相信你要大叫三个哇塞。
第一个神秘参数,目前暂不解释,埋个伏笔,留到EventDispatcher那一节,讲ActionScript3崭新的Event Flow事件流机制.
第二个参数:优先级。 有趣吧,在ActionScript 3.0中我们可以控制事件的优先级,从而达到控制function侦听器的执行顺序。如果你不填这个参数,那么事件默认为同一个级别0,事件的执行按先来后到的天经地义的顺序。
如果设为1,那么事件级别降一个档次,稍后执行。数字越高优先级越高。级别可以为负数。
(注意: Flex Builder 2 Beta3中事件级别是越低越高)
好好利用这个功能吧,在AS2.0中要想达到同样效果可不知道要多费多少力气!
第三个参数,讲的是是否设为弱引用。
兄弟们初学As2.0时一定经常忘了在删除Listener对象时,却忘了removeEventListener吧。这会导致很多莫名奇妙的情况发生。也是最常见的诡异bug种类之一。即使老鸟偶尔也会被阴一把。
最郁闷的是这种情况往往不被发现,在后台默默的消耗大量资源。
现在可以用弱引用可以在某种程度上解决这种bug。
设为true,就是告诉垃圾回收器,这个侦听器function的引用是弱引用。一旦这个侦听器在运行时只剩下了这一个弱引用,那么垃圾回收器可以不理它,直接把它回收咯屁了,节省资源。
从AS3.0引入这个弱引用这个概念就可以看出,AS3.0是如何的重视资源管理和有效率的运用。一个标准的重量级的程序语言必须具有这样的特征。
现在AS3.0有了!
下一节:
(3) Listener和As2.0有何不同,和this关键字的“改进了的记忆力”
本节内容:
1.弱引用的使用原则
2.listener的不同和this关键字的指向。
继续上次的话题。在讲listener 和 this关键字之前,我们先来讲讲一个高级话题:
弱引用的使用原则。
继续上次的话题。在讲listener 和 this关键字之前,我们先来讲讲一个高级话题:
弱引用的使用原则。
新手可以不看,因为暂时用不到。但这个话题很有必要。当设计大型RIA应用程序时,弱引用必须要了解。
弱引用从原则上来讲,其引入是为了防止无意识的对象保留(unintentional object retention)引起的内存泄漏。
什么是无意识的对象保留?一般情况下,对象的逻辑生命周期和实际生命周期应当相同。但是在某些情况下,比如事件侦听器机制,我们可能会创建一个引用,它在内存中生存的时间比我们预期的要长很多。比如说,下面的例子中,即使删掉了侦听器lis,但事件还是能被继续捕捉。这是由于并没有removeEventListenr,那么系统中还会保留着对侦听器的引用。
如果没有把addEventListener中对侦听器的持有改成弱引用,那么这个侦听器会一直存在、耗掉内存不被人发觉。但如果设成了弱引用,那么垃圾回收器过段时间后会将侦听器占用的内存回收,此时侦听器从物理意义上才真正的被销毁了。
在销毁之前,这个侦听器还会继续存在,继续作用。这就是为什么下例中虽然已经把useWeakReference设为了true,但是还是会有作用。
那么什么时侯垃圾回收器(Garbage Collector)才会来回收呢?
唔,这要看它高兴了,快的话10分钟,慢的话一个月。哈哈,开玩笑,垃圾回收器的工作时间咋一看确实是不可测的,但也有规律可循。但这个问题探讨在本系列教程中显得过于艰深,黑羽会单独撰文来说明垃圾回收器的部分工作机制。
那么推论来了,我们凭什么要把useWeakReference设为false呢?除非我们有意要让某个侦听器在失去了其他所有引用的时候还要工作,才有可能这样做。但谁会有这样疯狂的想法呢?道哥还是包世宏?
所以,我建议,大家不妨养成习惯,把useWeakReference设为true。
当然最好的做法是始终记得不用侦听器了,一定要removeEventListener。在这一点上,我们要坚持“始乱终弃”!!
//这个是Document Class,建个fla,绑定这个class。忘了,就看我第三篇教程。
package
{
import flash.display.MovieClip;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
public class LearnEvents extends MovieClip
{
function LearnEvents() {
var lis:Function = function () {
trace ("听到了鼠标Click事件!");
}
var kingda_s:KingdaSprite = new KingdaSprite(0xFFCC00, "kingdaSquare");
addChild(kingda_s);
kingda_s.addEventListener(MouseEvent.CLICK, lis, false, 0, true); //瞧,最后一个参数,已经把弱引用设为true了
lis = null; //这一句,我已经从逻辑上删掉lis了,大家作证啊
trace ("侦听器还在吗?:"+ lis); //lis也确实指向null了。但你只要继续点击方块,你会发现Click事件仍然被捕捉到。
}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
//这个类就是画一个矩形,
class KingdaSprite extends Sprite {
public var nickname:String;
public var ColorNum:uint;
//colorNumber就是#ffcc00这种类型的数,在AS3中推荐用新的uint型来标记它
public function KingdaSprite(colorNumber:uint, nameString:String) {
ColorNum = colorNumber;
nickname = nameString;
graphics.beginFill(ColorNum);
graphics.drawRect(0,0,100,100);
graphics.endFill();
}
}
Listener和As2.0有何不同,和this关键字的“改进了的记忆力”
DOM Level 3中规定的是用Object来做listener。该Object有用来处理事件的方法(method)。
AS3虽然是按照DOM Level 3事件机制设计的,但也不完全听话,有自己独立的想法。在AS3中,侦听器就是函数。也只能用函数来侦听。
其实在ActionScript3中,Function实质也是一种特殊的Object,和AS2.0有相似之处。
见我的文章:
ActionScript高级技巧:深入了解Function
因此AS3.0为什么限定用Function,这和它的架构有关,不细说了。
但有趣的是,在AS3.0中,对IEventDispatcher的定义中,仍然按照DOM3标准用Object来做Listener。见黑羽上一篇教程和随后的评论。
那么问题来了,function中this关键字的指向会怎样?
AS2.0开发者对AS2.0 事件机制中 this关键字的水性杨花应该深有认识。如果用function做侦听器,那么谁发出事件,this就指向谁。这就等于对象之间乱搞关系,啊呀呀。所以MM派了一个Delegate代理警察类来管理。唉,糜乱的岁月不堪回首啊。
//ActionScript 2.0例子:拷贝以下代码到第一帧,拖一个button到舞台,命名为kingda_btn; function lisFunc() { trace ("亲爱的,你会指向谁:"+ this.name); //我们本意应当是指向_root; }kingda_btn.addEventListener("click", lisFunc); //点一下button,看看指向谁。
回到ActionScript 3.0,来看看坚定的夫妻关系,黑羽总结为:嫁鸡随鸡,嫁狗随狗,什么都不嫁,那就属于global。我靠,读一下,还挺押韵。大家也好记。
简单的解释一下,函数在哪个对象里(应该叫method),那么this就指向谁。不在对象里,那么就指向global。
//【黑羽】ActionScript 3.0系列教程 //http://www.kingda.org package { import flash.display.Sprite; import flash.events.MouseEvent;public class AddListener extends Sprite {
public function AddListener() {//用package外面定义的类KingdaSprite创建一个实例,由于同文件中,所以不用import啦
var outsideChild:KingdaSprite = new KingdaSprite(0x00FF00, "outside_sprite");
addChild(outsideChild);//没有了这一句,你啥都看不到。
outsideChild.addEventListener(MouseEvent.CLICK, inclassHandler);//注册类里面的侦听器
outsideChild.addEventListener(MouseEvent.CLICK, outsideHandler);//注册类外面的侦听器
}private function inclassHandler(event:MouseEvent):void {
trace("类里面的侦听器侦听到MouseEvent事件: " + event);
trace("this关键字指向:"+this);
}
}
}function outsideHandler(event:MouseEvent):void {
trace("类外面的侦听器侦听到MouseEvent事件: " + event);
trace("this关键字指向:"+this);
}import flash.display.Sprite;
import flash.events.MouseEvent;//这个类就是画一个矩形,然后你点击这个矩形会发出标准鼠标click的事件
class KingdaSprite extends Sprite {
public var nickname:String;
public var ColorNum:uint;
//colorNumber就是#ffcc00这种类型的数,在AS3中推荐用新的uint型来标记它
public function KingdaSprite(colorNumber:uint, nameString:String) {
ColorNum = colorNumber;
nickname = nameString;
graphics.beginFill(ColorNum);
graphics.drawRect(0,0,100,100);
graphics.endFill();
}
}有兄弟反映,似乎AS3的事件机制有些复杂。
在我看来,编程上 “复杂”这个词一般有两种定义:实现麻烦 ,或者内容众多。
AS3中的事件机制其实现并不麻烦,逻辑更加清楚简单,因此不是“实现麻烦”这一类。
那应该指的是“内容众多”这个意思。黑羽倒觉得"内容众多"往往是褒义词,意味着API丰富,控制范围和深度大。那么一旦得其要领,即思路通畅,记忆深刻,也就不会觉得“复杂”了。我也会尽量写的简单通俗,照顾新手。但我又要同时考虑到AS2老手们的需求,不让他们打瞌睡。所以AS初学者 看到不懂的地方可以跳过,多用用Event后,有需要再回过头看看我教程的其它部分,一定会有收获。
拿今天要讲的事件发送来说,我预计写以下内容:
1. EventDispatcher和Event的简介
2. 回顾AS2.0事件发送
3. 继承EventDispatcher进行事件发送。
4. 合成EventDispatcher进行事件发送。
5. 实现IEventDispatcher接口来进行事件发送。 与设计模式中的装饰器模式相似。那么新手看第一,第二,第三部分已经足够应付一般应用。AS2老手们要看看第四部分。 开发大型项目的AS开发者,则第五部分必看。
1.EventDispatcher和Event的简介。
AS3中Object是万物之宗母,且生养众多,共有220多个子类。Event(事件类)和EventDispatcher(事件发送者类)就是在这一代之中。 Event和EventDispatcher是事件机制的两大主角,二者缺一不可。
Event类及其子类的功能就是提供各种具体的事件供EventDispatcher使用,不能干别的。
EventDispatcher则是要让它所有的子类都能发送事件。
那么,AS3就让所有要发送事件的类都统统继承于EventDispatcher。而需要发送事件的类毫无疑问,都是些很重要的类,比如URLLoader,DisplayObject。
其中DisplayObject更是一代霸父,所有可视对象,比如MovieClip,Spirit等等等等统统都是它的子孙。
因此,父以子贵,EventDispatcher在Object的第一代子女中,位高权重,影响极大。2. 回顾 AS2.0中发送事件
AS2.0中发送事件是怎么干的?
在类中留几个函数对象,如 dispatchEvent:Function,再搞一个EventDispatcher.initialize()来初始化一下要发送事件的类,然后,就搞定了。.....好简单。
但AS3中更简单,更加标准。3.继承EventDispatcher进行事件发送。
继承是最方便最快的一种。
刚刚说过了,所有的可视对象都继承DisplayObject,是EventDispatcher的孙子辈,因此都可以直接使用dispatchEvent()来发送事件了。
看代码,我们让一个普通的MovieClip, KingdaMC,来发送一个莫名其妙的事件,比如说"KingdaPlaySC"。
KingdaMC.dispatchEvent(new Event("KingdaPlaySC"));
现在我们要让一个我们自己的类来发送事件。
//【黑羽】ActionScript 3.0系列教程 //http://www.kingda.org //以下为一个名叫KingdaSampleClass的Document Class,请自行和一个Fla绑定。 //如果忘了怎么弄,看我第三篇教程package {
import flash.display.Sprite;
import flash.events.Event;public class KingdaSampleClass extends Sprite {
public function KingdaSampleClass() {
//生成一个KingdaSampleDispatcher的实例
var dispatcher:KingdaSampleDispatcher = new KingdaSampleDispatcher();
//标准的实现
dispatcher.addEventListener(KingdaSampleDispatcher.ACTION, actionHandler);
dispatcher.doSomething();
//可以用直接字符串标识事件类型,但推荐使用静态加const的字符串
dispatcher.addEventListener("KingdaPlaySC", anotherHandler);
//直接用new Event生成一个新的事件对象,该对象的事件类型为"KingdaPlaySC"
dispatcher.dispatchEvent(new Event("KingdaPlaySC"));
//输出:
//action的侦听器: [Event type="action" bubbles=false cancelable=false eventPhase=2]
//KingdaPlaySC的侦听器: [Event type="KingdaPlaySC" bubbles=false cancelable=false eventPhase=2]
}private function actionHandler(event:Event):void {
trace("action的侦听器: " + event);
}
private function anotherHandler(event:Event):void {
trace("KingdaPlaySC的侦听器: " + event);
}
}
}import flash.events.EventDispatcher;
import flash.events.Event;class KingdaSampleDispatcher extends EventDispatcher {
public static var ACTION:String = "action";//如果你需要在自己类中某个方法中发送事件,那么示例如下
public function doSomething():void {
//你的代码.....
//发送事件
dispatchEvent(new Event(KingdaSampleDispatcher.ACTION));
}
}
事件机制写的太多了,我自己都有点烦了。
但没办法,,太重要了。而且AS3做了这么多好的改进,值得我们去一一探寻,给我们日后的编程带来极大的便利。ActionScript 初学者,本节可以跳过不看。
ActionScript 2熟练工应当看看,有些价值。今儿讲掉
4. 合成EventDispatcher进行事件发送。
5. 实现IEventDispatcher接口来进行事件发送。 与设计模式中的装饰器模式相似。
这样事件的发送和接受,就可以讲完了。那么,事件部分就这样完了?没有!你晕,我也同晕。因为还有一个很重要的特性,Event flow机制还没讲。这就是我所说的事件冒泡机制。给我们编程带来了莫大的方便。
好,下面先讲:
4. 合成EventDispatcher进行事件发送。
什么情况下用合成EventDispatcher来发送Event呢?
一般发生在某个较复杂的类里面。
这个类可能是因为本身已经继承了其它类,无法再继承EventDispatcher。
如果仅仅是因为这个原因,那么我更加建议使用 实现IEventDispatcher接口来进行事件发送。
但如果原因不止如此,比如,我们不愿意这个类不是一个单纯事件发送类,而是在执行某个方法(method),比如doSomething()时,附带的发送一些事件。
这些事件发送者往往是这个类的组成部分,一些更小的类,通常是Sprite等。
那么用这种做法就比较合理。看代码例子
//【黑羽】ActionScript 3.0系列教程
//http://www.kingda.org
//以下为一个名叫KingdaSampleClass的Document Class,请自行和一个Fla绑定。
//如果忘了怎么弄,看我第三篇教程
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
public class LearnCompositeEvents extends Sprite {
public function LearnCompositeEvents() {
var kingdaObj:KingdaClass = new KingdaClass();
//一定要用kingdaObj.getSender()来返回事件发送对象,才能addEventListener
kingdaObj.getSender().addEventListener(KingdaClass.ACTION, lisFunc);
kingdaObj.doSomething();
//输出:
//doSomething
//listened:yeahyeah
}
//侦听器
private function lisFunc(evtObj:Event):void {
trace ("listened:"+evtObj.type);
}
}
import flash.events.EventDispatcher;
import flash.events.Event;class KingdaClass extends EventDispatcher {
public var _dispatcher:EventDispatcher;
public static const ACTION:String = "yeahyeah";
public function KingdaClass() {
initSender();
}
private function initSender():void {
_dispatcher = new EventDispatcher();
}
//调用一个专门的方法(method)来返回发送事件的EventDispatcher。
public function getSender():EventDispatcher {
return _dispatcher;
}
public function doSomething():void {
trace("doSomething");
//除了以下两行发送事件,还可以写入其它你要干的事儿。灵活。
var evtObj:Event = new Event(KingdaClass.ACTION);
_dispatcher.dispatchEvent(evtObj);
}
}
}
5.实现IEventDispatcher接口来进行事件发送。
在哪种情况下使用?
类可能是因为本身已经继承了其它类,无法再继承EventDispatcher。
而我们恰恰希望它能实现EventDispatcher类所有功能,比如说addEventListener, hasListener等等,看起来简直和继承EventDispatcher没什么分别。
那么OK,我建议使用 实现IEventDispatcher接口来进行事件发送。
其实质是一个装饰器模式(Decorator),以对客户端透明的方式扩展对象功能,是继承关系的一个替代方案。其关键在于扩展是完全透明的,使用起来和继承父类几乎没什么区别。
具体方法
由于IEventDispatcher需要实现5个接口,addEventListener, hasListener, willTrigger,removeEventListener,hasEventListener,那么我们的装饰类也必须实现这五个接口。
其余看代码优点:
1.类的用户完全感觉不到差别
2.在被包装的方法中还可以加入其它自己希望加进去的动作,比如,在addEventListenr方法中可以再插入一个计数,看看到底被add了多少次,超过某些次后,调用某个方法等等。
总而言之,给我们带来了极大的灵活性。这就是装饰器模式的好处。
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.EventDispatcher;
public class LearnDecoratorEvents extends Sprite {
public function LearnDecoratorEvents() {
var kingdaObj:KingdaClass = new KingdaClass();
kingdaObj.addEventListener(KingdaClass.ACTION, lisFunc); //用起来和EventDispatcher对象一样哦,呵呵。
var evtObj:Event = new Event(KingdaClass.ACTION);
kingdaObj.dispatchEvent(evtObj);//确实一样吧 :)
//输出:listened:yeahyeah
}
private function lisFunc(evtObj:Event):void {
trace ("listened:"+evtObj.type);
}
}
import flash.events.IEventDispatcher;
import flash.events.EventDispatcher;
import flash.events.Event;
class KingdaClass implements IEventDispatcher{
public var _dispatcher:EventDispatcher;
public static const ACTION:String = "yeahyeah";
public function KingdaClass() {
// other ....
initSender();
}
private function initSender():void {
_dispatcher = new EventDispatcher(this);
}
//哈哈,在实现接口时还可以乘机干点别的,比如我喜欢吧useWeakReference设为true
public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = true):void{
// do other things;
_dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
public function dispatchEvent(evt:Event):Boolean{
// do other things;
return _dispatcher.dispatchEvent(evt);
}
public function hasEventListener(type:String):Boolean{
// do other things;
return _dispatcher.hasEventListener(type);
}
public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void{
// do other things;
_dispatcher.removeEventListener(type, listener, useCapture);
}
public function willTrigger(type:String):Boolean {
// do other things;
return _dispatcher.willTrigger(type);
}
}
}
浙公网安备 33010602011771号