Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30480201
  • 博文数量: 708
  • 博客积分: 12163
  • 博客等级: 上将
  • 技术积分: 8240
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-04 20:59
文章分类

全部博文(708)

分类: 系统运维

2008-08-27 18:45:10

有兄弟反映,似乎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系列教程
//
//以下为一个名叫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));

}

}

 

2007-10-30update: 呵呵,更正了一个手误。最后一个代码例子中的最尾部的花括号应该放在第一个class的结尾后。这样下面的kingdaClass才是包外类。否则编译不会通过。sorry^^ 谢谢daozi的细心。)
事件机制写的太多了,我自己都有点烦了。
但没办法,sick.gif,太重要了。而且AS3做了这么多好的改进,值得我们去一一探寻,给我们日后的编程带来极大的便利。ActionScript 初学者,本节可以跳过不看。
ActionScript 2熟练工应当看看,有些价值。

今儿讲掉
4. 合成EventDispatcher进行事件发送。
5. 实现IEventDispatcher接口来进行事件发送。 与设计模式中的装饰器模式相似。
这样事件的发送和接受,就可以讲完了。

那么,事件部分就这样完了?没有!你晕,我也同晕。因为还有一个很重要的特性,Event flow机制还没讲。这就是我所说的事件冒泡机制。给我们编程带来了莫大的方便。

好,下面先讲:

4. 合成EventDispatcher进行事件发送。
什么情况下用合成EventDispatcher来发送Event呢?
一般发生在某个较复杂的类里面。
这个类可能是因为本身已经继承了其它类,无法再继承EventDispatcher。
如果仅仅是因为这个原因,那么我更加建议使用 实现IEventDispatcher接口来进行事件发送。
但如果原因不止如此,比如,我们不愿意这个类不是一个单纯事件发送类,而是在执行某个方法(method),比如doSomething()时,附带的发送一些事件。
这些事件发送者往往是这个类的组成部分,一些更小的类,通常是Sprite等。
那么用这种做法就比较合理。

看代码例子



//【黑羽】ActionScript 3.0系列教程

//

//以下为一个名叫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);

}

}

阅读(2584) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~