August 28, 2006

【黑羽】AS3.0教程(10):强大的事件机制(6)

ActionScript3天地会公测中...

(如蒙转载,请留下我的Blog链接:www.kingda.org, thx)
(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系列教程
//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);
}
}

P.S:本教程受Creative Commons License.协议保护,未经作者同意,不得用于商业用途。

广告:哈哈,现在刚好是阿里妈妈推广期哦,你是站长吗?推荐注册阿里妈妈赚广告费

TrackBack

TrackBack URL for this entry:
http://www.kingda.org/mt/mt-tb.cgi/98

Comments (20)

N神:

讲的很明白,相当好!
就等看你event flow怎么讲了

讲的真好,挑个语病吧.

"由于IEventDispatcher需要实现5个接口,addEventListener, hasListener, willTrigger,removeEventListener,hasEventListener,那么我们的装饰类也必须实现这五个接口。"

这里应该是实现5个接口函数吧。

sl:

问个比较弱的问题,如果想为一组按钮或影片编写相类似的事件,有没有简单的方法,不想一个一个写事件.

不用定义很多事件吧,只用定义事件的名称,如:public static const ACTION:String = "yeahyeah";
其实这是可以省略的,但为了避免错误,最好还是写一下。再在相应的地方写上对应的语句就行了吧?

上面的那位仁兄的问题,可以用自定义一个事件,从EVENT中继承来,把你的事件都定义到里面去
public static const EVENT1:String = "event1";
public static const EVENT2:String = "event2";

public static const EVENT2:String = "event2";
我个人觉得不推荐写到事件发送者类里面。我比较喜欢自己定义事件,我想黑羽在后面讲EVENT的时候,会提到的

vincent:

黑羽您好,看了您的关于事件教程,受益匪浅,但我现在有个疑问,我在闪客帝国上看贴,可是没人理睬,在网搜到了您的BLOG,请您不惜赐教,我在看ADOBE官方文档时,上面有个自定义事件类型,但是我运行时出错,请您看看,代码如下:
import flash.events.Event;
/**
* This custom Event class adds a message property to a basic Event.
*/
public class AlarmEvent extends Event
{
/**
* The name of the new AlarmEvent type.
*/
public static const ALARM:String = "alarm";
/**
* A text message that can be passed to an event handler
* with this event object.
*/
public var message:String;
/**
* Constructor.
* @param message The text to display when the alarm goes off.
*/
public function AlarmEvent(message:String = "ALARM!")
{
super(ALARM);
this.message = message;
}

}

运行的时候总是会出错,错误提示如下:

package.
public class AlarmEvent extends Event
ReferenceError: Error #1065: 变量 Timeline0_d3852b1b9b60c341967650dc57bfb0e8 未定义。

to vincent:
我按这个代码写了一个例子给你。经运行,已经成功通过了。

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	public class AlertEvent extends Sprite
	{
		
		public function AlertEvent() {
			var kingdaEvt:AlarmEvent = new AlarmEvent("Hohaha,hahaHo");
			this.addEventListener(AlarmEvent.ALARM, lisFunc); 
			this.dispatchEvent(kingdaEvt);
		}


private function lisFunc(evtObj:AlarmEvent):void {
trace ("listened:"+evtObj.type + " Message:"+evtObj.message);

}
}

}
import flash.events.Event;
/**
* This custom Event class adds a message property to a basic Event.
*/
class AlarmEvent extends Event
{
/**
* The name of the new AlarmEvent type.
*/
public static const ALARM:String = "alarm";
/**
* A text message that can be passed to an event handler
* with this event object.
*/
public var message:String;
/**
* Constructor.
* @param message The text to display when the alarm goes off.
*/
public function AlarmEvent(message:String = "ALARM!")
{
super(ALARM);
this.message = message;
}
}

HUPEIKANG1:

请问这个例子:

LearnCompositeEvents

public function doSomething():void {
//你的代码.....

//发送事件
dispatchEvent(new Event(KingdaSampleDispatcher.ACTION));
}

感觉好像功能一样啊。。。

都可以 执行 "你的代码"

请问 您能讲一下吗?有点不理解。。。谢谢了:)

to HUPEIKANG1:
没有看明白你的问题? :P
你是什么地方不理解?

HUPEIKANG1:

我的意思是 这两个 功能好像 完全一样?请问有什么区别吗?

这两个都可以自己编自己的代码阿

啊,明白你的意思了。
从功能实现上,当然是一样的了。
最主要的是编程意图的不一样。
LearnCompositeEvents把事件发送逻辑封装在KingdaClass中,更加灵活。
是否需要这样的灵活度,看具体的项目需求。

第一段代码编译后错误显示:
"5006: An ActionScript file can not have more than one externally visible definition: main, a"

docmentclass设置为main

错误提示,按我的理解是一个as文件中不能有多个可视类的定义。

main是可视类。a不是啊。。

下面是我简化的代码

package {
import flash.display.Sprite;
public class main extends Sprite {
public function main() {
var s:a = new a();
}
}
import flash.events.EventDispatcher;
class a extends EventDispatcher {
public function a() {
trace("ok");
}
}
}

//2个class都在package内部
package {
import flash.display.Sprite;
public class main extends Sprite {
public function main() {
var s:a=new a();
}
}
}
import flash.events.EventDispatcher;
class a extends EventDispatcher {
public function a() {
trace("ok");
}
}
//其中一个class在package内部

第二种方式就不会出错..也能正常调用..

同样把本文中的第一段代码中的KingdaClass类放在package外部就能正常编译。功能也正常。。

黑羽能不能详解一下package的定义。。

按我的理解是package简化了原来的类路径。。但是它的用法有什么规则呢?

kita:

在一个类中dispatchEvent
的Event

能不能用一个addEventListener在主场景中 截获 Event

如果不能在其他地方截获 Event
再怎么强大也发挥不了什么作用!

白骷髅争气宝宝:

LearnDecoratorEvents例子中貌似是把助手类也放进package里了,而这样FB是报错的.......

本页Kingda的第一段代码例子有点格式错误.
应该是:
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);
}
}

但,packgage和如何定义多个类,不明白啊.

randy:

你的教程我大概已经看了5遍了,收获很大,非常感谢这么简洁明快的思路和文采!
关于事件机制,这对于as的编程是非常重要的,如果不能精通的话,整个项目的框架开发将会变得力不从心。
我前几次看到你讲到事件的发送这一块的内容时,一直没有反应过来,不好意思,可能是因为我是新手的缘故。我看了很多其它资料之后,我觉得事件处理机制有两点很重要,一是发送自己写的事件,二是事件流。能发送自己写的事件意味着程序中远隔千山万水也可以实现事件通讯,而事件流则意味着舞台上任何元件都可以响应某一事件,有种事件共享的味道吧。基于这两点,我们可以利用as3做大部分我们想做而以前觉得做起来很痛苦的事情。
这只是我自己个人的浅见,基于这两点,我希望你的教程能在这两方面写些文章,上面的自己发送事件显得有些艰涩,初学者看起来感觉一头雾水,能否重新设计一下教程?说得不对的地方见笑了,呵呵。

ycccc8202:

其实这里不写
"implements IEventDispatcher"
也一样,我始终觉得interface接口这种东西纯粹是个装饰品,只是方便其他人阅读而已,象个功能菜单~

mytansion:

IEventDispatcher还是很有用的,我自己做了个小实验,如果某个对象抛出了一个自定义的事件,只有这个对象本身可以接收这个事件,也就是说as3的事件广播并不是程序中所有EventDispatcher子类能够收到的,既然只有自己发的事件被自己收到,那么要想响应这个事件,最好还是使IEventDispatcher的手段来处理,当然程序中可以使用单态模式来使用接口类,这样就可以在程序中随心所欲的接收并响应事件。

报告一个错误

由于IEventDispatcher需要实现5个接口,addEventListener, hasListener, willTrigger,removeEventListener,hasEventListener,那么我们的装饰类也必须实现这五个接口。 上面的第二个接口应该是 dispatchEvent吧 黑羽教授笔误了.呵呵
一只眼:

好像包内只能定义一个类^_^

留下高见,发表评论

个人信息不会外泄,请放心填写。

姓名和Email必填。

发表评论后,请耐心等待,不要频繁刷新。提交完成后,刷新一下本页即可看到您的评论。^_^

请输入验证字符"f" :
文字广告:你是站长吗?注册阿里妈妈赚广告费

最近发表

归档

全部
Creative Commons License
此 Blog 中的日记
遵循以下授权:
Creative Commons
(创作共用) 授权
.
Google PageRank
本站被Adobe官方MXNA新闻聚合器收录
本站被蓝色理想BXNA收录