/**
* (#)The Timer class used for fixed cycle operationg
* @author WarGrey
* @version 2.0
* @since ActionScript 2.0
* @see mx.events.EventDispatcher
*/
import mx.events.EventDispatcher;
class wargrey.util.Timer extends EventDispatcher{
public static var INITIATE:String="initiate";
public static var TIMECYCLE:String="timecycle";
public static var TERMINATE:String="terminate";
//This timer ID
private var id:Number;
//The repetition times that has happened after begin executing
private var repetition:Number;
//The interval of each repetition
private var _interval:Number;
public function get interval():Number{
return _interval;
}
//This variable is used to show whether the timer has launched.
private var isLaunched:Boolean;
public function Timer(interval:Number){
//Set the _interval with the default value is 1000 ms.
_interval=(interval>0)?interval:1000;
//The repetition times is cleared to 0
repetition=0;
//The isLaunched is setted by false
isLaunched=false;
}
//Begin to timing and dispatch the "initiate" event.
public function start():Void{
var event:Object=new Object();
event.type="initiate";
event.target=this;
event.interval=interval;
dispatchEvent(event);
isLaunched=true;
id=setInterval(this,"run",interval);
}
//Stop the time and dispatch the "terminate" event.
public function stop():Void{
clearInterval(id);
isLaunched=false;
var event:Object=new Object();
event.type="terminate";
event.target=this;
event.times=repetition;
repetition=0;
dispatchEvent(event);
}
//Registe the observers only before this timer launched, or it will call the method named "addEventListenerFailed" of observer and return false;
public function addEventListener(event:String,handler:Object):Boolean{
if (!isLaunched){
super.addEventListener(event,handler);
return true;
}else{
if (handler["addEventListenerFailed"]!=undefined){
var failure:Object=new Object();
failure.type=event;
failure.target=this;
failure.times=repetition;
handler.addEventListenerFailed(failure);
}
return false;
}
}
//Execute the repetition method which dispatches the "timecyle" event.
private function run():Void{
repetition++;
var event:Object=new Object();
event.type="timecycle";
event.target=this;
event.times=repetition;
dispatchEvent(event);
}
public function toString():String{
if (id==undefined){
var msg="unlaunched";
}else{
var msg="Timer["+id.toString()+"]";
}
return msg;
}
}
该定时器类Timer继承于mx.events.EventDispatcher,这样一来便成了一个功能完善的广播源,代码书写上也比混入更清晰易懂。既然是基于观察者模式的定时器,现在又有了广播源,那当然也需要一个观察者通过timer.addEventListener和 timer.removeEventListener等方法来注册取消感兴趣的事件。对于一般的面向对象语言,对于观察者和广播源必须专门定制,或通过其他机制来实现,因此或多或少会造成了性能损失,不过ActionScript2也是一个相当不错的动态语言,处理类似的问题具有先天的优势,因此任何一个Object都可以充当任何广播源的观察者,自然对于定时器类来说,其提供的三个事件,即initiate、 timecycle和 terminate,因此它的观察者应至少提供其中的一个方法才能正确注册。当然不提供也行,那样Timer就当注册没发生。下面详细介绍观察者的可选方法:
initiate事件:对应于Timer.INITIATE静态成员,是启动定时器时广播的事件。
timercycle事件:对应于Timer.TIMERCYCLE静态成员,是每次定时重新执行时广播的事件。
terminate事件:对应于Timer.TERMINATE静态成员,是停止定时器时广播的事件。
addEventListenerFailed事件:当注册事件失败时广播此事件。为了避免混乱,该定时器规定只有当定时器未启动时才能注册事件,所以如果注册事件时定时器已经启动便会导致注册事件失败。
对于所有事件,都有一个Object类型的参数,该参数包含如下属性:
type:事件名称。
target:广播源对象实例。
interval:仅限于initiate事件,该定时器实例定时时间间隔。
times:initiate事件不适用,到目前为止定时器重复执行的次数。
下面是个测试用例,为方便测试做成了Flash SWC组件。
//The First Observer
class wargreytest.util.timer.TimerTestObject {
private var name;
public function TimerTestObject(name:String){
this.name=(name.length>0)?name:"default";
}
private function initiate(event:Object):Void{
_root.display(name+" has launched.");
_root.newLine();
}
private function timecycle(event:Object):Void{
_root.display(name+" recycled for "+event.times.toString()+" times.");
_root.newLine();
}
private function terminate(event:Object):Void{
_root.display(name+" has terminated.");
_root.newLine();
}
}
//The Second Observer
import wargreytest.util.timer.*;
class wargreytest.util.timer.TimerTestObject2 extends TimerTestObject{
public function TimerTestObject2(name:String){
super(name);
}
private function addEventListenerFailed(event:Object):Void{
_root.display(name+" registe '"+event.type+"' listener failed.");
_root.newLine();
}
}
//The Timer TestCase Component
import wargreytest.util.timer.*;
import wargrey.util.*;
import wargrey.extension.GraphicalInterface;
[InspectableList("interval","instanceno","seconds")]
class wargreytest.util.TimerTest extends MovieClip{
private var id:Number;
private var timer:Timer;
private var _interval:Number;
[Inspectable(name="interval",defaultValue="1000",verbose=0)]
public function set interval(itl:Number):Void{
_interval=(itl>0)?itl:1000;
}
public function get interval():Number{
return _interval;
}
private var _instanceno:Number;
[Inspectable(name="instanceno",defaultValue="3",verbose=0)]
public function set instanceno(isn:Number):Void{
_instanceno=(isn>0)?isn:3;
}
public function get instanceno():Number{
return _instanceno;
}
private var _seconds:Number;
[Inspectable(name="seconds",defaultValue="5",verbose=0)]
public function set seconds(s:Number):Void{
_seconds=(s>0)?s:5;
}
public function get seconds():Number{
return _seconds;
}
private function onLoad():Void{
this._visible=false;
var gi:GraphicalInterface=new GraphicalInterface();
gi.extend(_root);
timer=new Timer(interval);
for (var i=instanceno-1;i>=0;i--){
var temp:TimerTestObject=new TimerTestObject("timeListener"+i.toString());
timer.addEventListener("initiate",temp);
timer.addEventListener("timecycle",temp);
timer.addEventListener("terminate",temp);
}
timer.start();
id=setInterval(this,"stopTest",seconds*1000);
var temp:TimerTestObject=new TimerTestObject("timeListener"+instanceno.toString());
timer.addEventListener("initiate",temp);
var temp:TimerTestObject=new TimerTestObject2("timeListener"+(instanceno+1).toString());
timer.addEventListener("timercycle",temp);
}
private function stopTest():Void{
timer.stop();
clearInterval(id);
}
}
补充说明:定时器的定时时间间隔只能在构造Timer实例时指明,以后便不能更改,若构造时省略则默认为1000,单位毫秒。