2011-03-30

AS3-Signals 筆記

AS3-Signals 是 ActionScript 中替代 Event 系統的方案。使用 Event 就好了,為什麼還要 signal 呢?在某些專案需要自訂 Event,通常自訂 Event 必須定義一個繼承 Event 的新類別。為了多傳遞一個資料項而定義一個類別,感覺就很累贅。此時使用 signal 就顯得清新許多。
AS3-Signals 好用,但也有些缺點:

  1. 不能設定 useCapture。其實 Event 的這個功能,在專案上個人幾乎是沒用過,所以就顯得不重要了。若要用 useCapture 就使用 Event 吧。
  2. 不能設定 useWeakReference。據作者的說明,weak reference 有 bugs ,目前沒有將這個功能加入。視情況去 remove signal listener。
值得參考的資料:
An introduction to AS3 Signals
as3signals – An Awesome Solution to Events/Signals in AS3

Signals 並不能完全取代 Event,主要原因是無法取代原生的 Event 如 MouseEvent。此時,使用NativeSignal 去做轉接的動作。
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import org.osflash.signals.natives.NativeSignal;

[SWF(width="600", height="400")]
public class Test1NativeSignal extends Sprite{
    public function Test1NativeSignal() {
        var btn:Sprite = new MyButton();
        this.addChild(btn);
        // 建立和滑鼠事件相關的 signal物件
        var clickSignal:NativeSignal = new NativeSignal(btn, MouseEvent.CLICK, MouseEvent);
        // 在 signal物件 上註冊處理器
        clickSignal.add(clickHandler);
    }

    private function clickHandler(event:MouseEvent):void {
        var txt:TextField = new TextField();
        this.addChild(txt);
        txt.autoSize = TextFieldAutoSize.LEFT;
        txt.y = this.height;
        txt.text = event.type + ':' + event.target.name;
    }
}
}

import flash.display.Graphics;
import flash.display.Sprite;

class MyButton extends Sprite {
    private static var count:int;
    public function MyButton () {
        var g:Graphics = this.graphics;
        g.beginFill(0x666699);
        g.drawRoundRect(10, 10, 100, 60, 20, 20);
        g.endFill();
        this.name = 'MyButton' + ++count;
    }
}
習慣上會將 signal 定義為某個會發送訊息物件的屬性。
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;

[SWF(width="600", height="400")]
public class Test2NativeSignal extends Sprite {
    public function Test2NativeSignal() {
        var btn:MyButton;
        for(var i:int=0; i<5; i++) {
            btn = new MyButton();
            this.addChild(btn);
            btn.x = (btn.width+10)*i;
            btn.click.add(clickHandler);
        }
    }
    private function clickHandler(event:MouseEvent):void {
        var txt:TextField = new TextField();
        this.addChild(txt);
        txt.autoSize = TextFieldAutoSize.LEFT;
        txt.y = this.height;
        txt.text = event.type + ':' + event.target.name;
    }
}
}

import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.MouseEvent;
import org.osflash.signals.natives.NativeSignal;

class MyButton extends Sprite {
    private static var count:int;
    public var click:NativeSignal;
    public function MyButton () {
        var g:Graphics = this.graphics;
        g.beginFill(0x666699);
        g.drawRoundRect(10, 10, 100, 60, 20, 20);
        g.endFill();
        this.name = 'MyButton' + ++count;
        click = new NativeSignal(this, MouseEvent.CLICK, MouseEvent);
    }
}
使用 NativeMappedSignal 轉換發出的物件格式。
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;

[SWF(width="600", height="400")]
public class Test3NativeMappedSignal extends Sprite{
    public function Test3NativeMappedSignal() {
        var btn:MyButton;
        for(var i:int=0; i<5; i++) {
            btn = new MyButton();
            this.addChild(btn);
            btn.x = (btn.width+10)*i;
            btn.click.add(clickHandler);
        }
    }
    public function clickHandler(data:String):void {
        var txt:TextField = new TextField();
        this.addChild(txt);
        txt.autoSize = TextFieldAutoSize.LEFT;
        txt.y = this.height;
        txt.text = data;
    }
}
}

import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.MouseEvent;
import org.osflash.signals.natives.NativeMappedSignal;

class MyButton extends Sprite {
    private static var count:int;
    public var click:NativeMappedSignal;
    private var clickCount:int;

    public function MyButton () {
        var g:Graphics = this.graphics;
        g.beginFill(0x666699);
        g.drawRoundRect(10, 10, 100, 60, 20, 20);
        g.endFill();
        this.name = 'MyButton' + ++count;
        click = new NativeMappedSignal(this, MouseEvent.CLICK, MouseEvent, String)
                .mapTo(function(event:MouseEvent):String {
                    return name+':' + ++clickCount;
                });
    }
}
使用 Signal 類別的例子
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import org.osflash.signals.Signal;

public class Test4Signal extends Sprite{
    public var count:int;
    private var signal:Signal;

    public function Test4Signal() {
        // 限定傳送資料的類型
        signal = new Signal(String, int);
        signal.add( listener );
        this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }

    private function listener(str:String, count:int):void {
        var txt:TextField = new TextField();
        this.addChild(txt);
        txt.autoSize = TextFieldAutoSize.LEFT;
        txt.y = this.height;
        txt.text = str + ':' + count ;
    }

    private function enterFrameHandler(event:Event):void {
        count++;
        if( count%30 == 0) {
            // 觸發
            signal.dispatch('count', count);
        }
    }
}
}
使用 DeluxeSignal 類別的例子
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;

import org.osflash.signals.DeluxeSignal;
import org.osflash.signals.events.GenericEvent;

public class Test5DeluxeSignal extends Sprite {
    public var count:int;
    private var signal:DeluxeSignal;

    public function Test5DeluxeSignal() {
        // 設定 target 為 this
        signal = new DeluxeSignal(this);
        signal.add( listener );
        this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }

    private function listener(event:GenericEvent):void {
        var txt:TextField = new TextField();
        this.addChild(txt);
        txt.autoSize = TextFieldAutoSize.LEFT;
        txt.y = this.height;
        txt.text = event.currentTarget + ':' + event.currentTarget.count ;
    }

    private function enterFrameHandler(event:Event):void {
        count++;
        if( count%30 == 0) {
            // 觸發
            signal.dispatch(new GenericEvent());
        }
    }
}
}

2 則留言:

oho 提到...

兩個問題想請教老師:
1.請問您後來有在專案使用這個Signal框架嗎?
2.對於效能有提升嗎?
謝謝

Shinder 提到...

1. 尚未在專案上使用 Signal
2. Signal 不是用來提高效能的, 它只是方便處理 events

FB 留言