akihiro kamijo: Flash Player API Archives

August 28, 2008

Flash Player 10 の動的サウンド生成機能 (Sound クラス)

Flash Player 10 から動的に生成したサウンドを再生する機能がサポートされています。ベータ 2 で変更された点などもありますので遅ればせながらご紹介します。

まず、この機能を使うには Sound オブジェクトに Event.SAMPLE_DATA のイベントハンドラを設定します。そうすると Sound オブジェクトの動作が通常と変わり、定期的にイベントハンドラを呼び出しては再生するデータを読み込むようになります。このとき SoundChannel は読み込まれたデータを一連のオーディオデータストリームとして再生します。

var mySound:Sound = new Sound();
mySound.addEventListener(Event.SAMPLE_DATA, onSampleData);
var myChannel:SoundChannel = mySound.play();
 
function onSampleData(event:SampleDataEvent):void 
{
  // この中で SoundChannel にデータを書き込む
}
 

SAMPLE_DATA 用イベントハンドラ内では、イベントオブジェクトから渡される ByteArray オブジェクトにオーディオデータを書き込みます。イベントオブジェクトの型は Flash Player 10 から新しく追加された sampleDataEvent で、以下の属性が利用できます。

public data : ByteArray   // オーディオストリーム内のデータ
public position : Number  // オーディオストリーム内の位置
 

ByteArray オブジェクト (event.data) に書き込む値は 32 ビット big endian 浮動小数点数形式の 44.1KHz ステレオデータです。従って一つのサンプルデータの大きさは 8 バイト (4 バイトのデータが左右それぞれ) になります。

下は sampleData イベントハンドラの例です。単純なサイン波形をデータとして書き込んでいます。再生すると聴覚検査で聴くような音がすると思います 。

function onSampleData(event:SampleDataEvent):void {
  for (var c:int=0; c<8192; c++) {
    var rad:Number = Number(c+event.position)/Math.PI/2;
    var amp:Number = Math.sin(rad) / 4;  // -1 から 1 の間の値なら OK
    event.data.writeFloat(amp);  // 左チャネルの音
    event.data.writeFloat(amp);  // 右チャネルの音
  }
}
 

データの計算式を変えると音色が変わると思いますので試してみてください。その際、サンプルデータの値が -1 から 1 の範囲を超えないようにご注意を。

さて、イベントハンドラ内では 2048 以上 8192 以下のサンプルデータを書き込みます。パフォーマンス上はできるだけ多くのサンプルデータを (つまり 8192 個) 書くのが有利と考えられます。

書き込まれたデータが 2048 以下の場合には、データを全て再生した時点で SoundChannel は再生を停止し soundComplete イベントを生成します。明示的に再生を停止したいときには SoundChannel.stop() を呼ぶこともできます。

sampleData イベントにより書き込みを求められている場所と再生中の箇所との時間差は以下の式で求めることができます。ここでの単位は ms になります。

event.position/44.1 - myChannel.position
 

あとは、Sound オブジェクトからオーディオデータを取得するために extract() というメソッドが用意されています。ある音源を読み込んだ Sound オブジェクトからオーディオデータを取り出して他の Sound オブジェクトで動的に再生したりするのに使えます。メソッドの定義は以下のようになっています。

public extract(target:ByteArray, length:Number, startPosition:Number = -1):Number // 
 

extract() は target で指定した ByteArray に length 分のサンプルデータを抽出します。startPosition は読み込みを開始するポジションの指定ですが、明示的に指定しないと、一回目はデータの先頭から、それ以降は続きから順番に読み込みます。

なお、Sound オブジェクトに sampleDataEvent 用のイベントハンドラを設定すると load() 等のメソッドは使えなくなります。

sampleDataEvent.data に書き込むのは 32 ビット big endian なので ShaderJob の結果がそのまま使えそうですね。

Posted by ackie at 06:53 PM | Comments (0)

July 08, 2008

Flash Player 10 のローカルファイルアクセス機能 (FileReference クラス)

Flash Player 10 ではローカルファイルを直接 Flash アプリケーションに読み込んだり Flash アプリケーション内のデータを直接ローカルファイルに書き出す機能が追加されています。

Flash Player 9 でも FileReference を使ってローカルファイルにアクセスすることは可能です。が、その機能はファイルのアップロード/ダウンロードを行うためのもので、Flash アプリケーションからは、一旦サーバを経由しないとローカルファイルのデータを扱うことができませんでした。直接ファイル I/O の出来る日が来ることを心待ちにしていた方も多いことでしょう。

というわけで、Flash Player 10 では flash.net.FileReference クラスに以下の API が追加されています。

public function get data():ByteArray // 読み込まれたデータ (読み取り専用のプロパティ)
public function load():void          // 指定されたファイルの読み込み開始
public function save(data:*, name:String = null):void 
                                     // 保存先を選択するダイアログを表示、その後データ保存開始
 

ファイルの読み込みを開始するには load() メソッドを使用します。読み込むファイルの指定は、browse() メソッドを呼ぶとダイアログが表示されるので、その中でユーザが行います。

このときプログラムから読み込むファイルを指定することはできません。これは SWF がロードされたら勝手にローカルファイルを送信してしまうといった類のアタックからユーザを保護するためです。

load() は呼び出されるとすぐに戻るので、その後の進み具合はイベント経由で確認することになります。このあたりは従来の FileReference の使い方と同じです。

下は、ファイルを読み込む簡単なサンプルです。

var fr:FileReference=new FileReference();
fr.addEventListener(Event.SELECT,onSelect);
fr.browse(); // ファイル選択のダイアログを表示
 
function onSelect(e:Event):void
{
  // イベントリスナーを追加
  fr.addEventListener(ProgressEvent.PROGRESS,onProgress);
  fr.addEventListener(Event.COMPLETE,onComplete);
  // 下の 2 つはとりあえずコメントアウト
  // fr.addEventListener(Event.OPEN, onOpen);
  // fr.addEventListener(IOErrorEvent.IO_ERROR, onIoError);
  fr.load(); // 読み込み処理を開始
}
 
function onProgress(e:ProgressEvent):void
{
  trace("読み込んだバイト数:" + e.bytesLoaded + "、 全体のバイト数:" + e.bytesTotal);
}
function onComplete(e:Event):void
{
  trace(fr.data);
  // fr.type を参考にオブジェクト変換する等の処理を記述
  // fr.removeEventListener(...)
}
 

browse() メソッドに対してユーザがファイルを選択すると SELECT イベントが発行されます。上のサンプルではこのタイミングで必要なイベントハンドラを設定して load() を呼び出しています。

読み込み処理が成功すると COMPLETE イベントが発行されます。data プロパティに有効な値が設定されるのはこれ以降です。上の例でも COMPLETE イベントハンドラ内で値を参照しています。

さて、ローカルファイルにデータを保存する場合は save() メソッドを使用します。保存するデータは save() の第一引数に指定します。サポートされるデータの型は以下の 3 種類です。それ以外の型の場合は toString() で変換することで String として扱うようです。

  • String : UTF-8 のテキストファイルとして保存
  • XML : XML フォーマットで保存
  • ByteArray : データをそのまま保存

save() メソッドを呼ぶと、ユーザに保存先を選択するためのダイアログが表示されます。その際、ダイアログ内にデフォルトのファイル名を表示したい場合には save() の第二引数として指定することができます。

var fr:FileReference=new FileReference();
var dat:String ="これは UTF-8 の文字列として保存されます";
 
fr.addEventListener(Event.COMPLETE,onComplete);
fr.save(dat, "UTF8Text.dat"); // ダイアログを表示する
 
function onComplete(e:Event):void
{
  trace(fr.name); // ユーザが指定したファイル名を表示
}
 

save() では load() の 4 つのイベントに加えて SELECT と CANCEL イベントも利用できます。

例によって、操作するファイルの大きさに制限はありませんが (もちろんメモリが足りないとかは別として)、サポートされる (テストされている) のは 100MB までです。

Flash コンテンツがファイルにアクセスしないように設定したい場合は mm.cfg 内で LocalFileReadDisable を 1 にします。

Posted by ackie at 06:17 PM | Comments (3)

June 07, 2006

URI のエンコードとデコード

引き続きエンコード関連の関数を紹介します。今回は URI をエンコードするための関数です。

encodeURI() 関数と decodeURI() 関数

encodeURI() 関数 (livedocs@lab) は URI 文字列を UTF-8 値にエンコードします。英数字は変換の対象外ですが、他にも URI の一部として使われる記号は変換されません。

var str:String = "http://w.c/a?b+ま&";
trace(encodeURI(str)); // http://w.c/a?b+%E3%81%BE& が出力される

この例では「ま」だけが変換されていますが、/ 等の文字は変換されていませんね。

encodeURI() によって変換されない記号は以下のものがあります。上段は URI の区切り文字として使用される記号です。

; / ? : @ & = + $ , # 
- _ . ! ~ ' ( )

encodeURI() でエンコードされた文字列は decodeURI() 関数 (livedocs@lab) を使って元に戻すことができます。

var str:String = encodeURI("http://w.c/a?b+ま&");
trace(str); // http://w.c/a?b+%E3%81%BE& が出力される
trace(decodeURI(str)); // http://w.c/a?b+ま& が出力される

encodeURIComponent() 関数と decodeURIComponent() 関数

encodeURIComponent() 関数 (livedocs@lab) は encodeURI() とほぼ同じような関数ですが、encodeURI() が URI 全体をエンコードするためのものであるのに対し、encodeURIComponent() は URI コンポーネントと呼ばれる URI の一部分をエンコードするための関数です。

URI コンポーネントとは ":", "/", ";" 等の記号で区切られた文字列です。例えば http や www.adobe.com は URI コンポーネントです。

実際に encodeURI() と encodeURIComponent() でエンコードした結果を比べると以下のようになります。どこが違うか分かりますか?

var str:String = "http://w.c/a?b+ま&";
trace(encodeURI(str)); // http://w.c/a?b+%E3%81%BE& が出力される
trace(encodeURIComponent(str)); // http%3A%2F%2Fw.c%2Fa%3Fb%2B%E3%81%BE%26 が出力される

encodeURIComponent() では ":" や "/" もエンコードの対象になっています。これは encodeURIComponent() が URI の区切りに使われる記号を特別扱いしないからです。

英字と数字以外で encodeURIComponent() がエンコードしない文字は以下のとおりです。

- _ . ! ~ ' ( )

encodeURIComponent() でエンコードされた文字列は decodeURIComponent() 関数 (livedocs@lab) で元に戻します。

var str:String = encodeURIComponent("http://w.c/a?b+ま&");
trace(str); // http%3A%2F%2Fw.c%2Fa%3Fb%2B%E3%81%BE%26 が出力される
trace(decodeURIComponent(str)); // http://w.c/a?b+ま& が出力される

Posted by ackie at 06:14 PM | Comments (0)

June 06, 2006

文字列のエスケープ

escape() 関数と unescape() 関数

escape() 関数は (livedocs@lab) は文字列を URL エンコードのフォーマットに変換するグローバル関数です。基本的に、英数字以外の文字であれば % 付の16進数に変換されます。+ などいくつか変換されない文字もあります。

trace(escape("1+3%2=0")); // 1+3%252%3D0 が出力される
trace(escape("h:m@u/a?b&c")); // h%3Am@u/a%3Fb%26c が出力される
trace(escape("ハロー")); // %u30CF%u30ED%u30FC が出力される

escape() で変換された文字を戻すには unescape() 関数 (livedocs@lab) を使います。

var str:String = escape("1+3%2=0");
trace(str); // 1+3%252%3D0 が出力される
trace(unescape(str)); // 1+3%2=0 が出力される

escapeMultiByte() 関数と unescapeMultiByte() 関数

escapeMultiByte() 関数 (livedocs@lab) と unescapeMultiByte() 関数 (livedocs@lab) は flash.utils パッケージに含まれる関数です。

escapeMultiByte() は文字列を UTF-8 もしくはシステムのコードページ (Shift-JIS 等) に変換した文字列を返します。

どちらのエンコーディングになるかは System.useCodePage の値で決まります。true の場合は OS のコードページ、false の場合は UTF-8 です。

var str:String = "もなか";
// 比較のために URL エンコーディングの結果を確認
trace(escape(str)); // %u3082%u306A%u304B が出力される
// OS のコードページ(SJIS)にエンコード
System.useCodePage = true;
trace(escapeMultiByte(str)); // %82%E0%82%C8%82%A9 が出力される
// UTF-8 にエンコード
System.useCodePage = false;
trace(escapeMultiByte(str)); // %E3%82%82%E3%81%AA%E3%81%8B が出力される

変換した文字列を戻すには unescapeMultiByte() 関数を使用します。

var str:String = escapeMultiByte("もなか");
trace(str); // %82%E0%82%C8%82%A9 が出力される
trace(unescapeMultiByte(str)); // もなか が出力される

unescapeMultiByte() も escapeMultiByte() と同様に System.useCodePage の値により結果が変わります。unescapeMultiByte() で元の文字列に戻す際は escapeMultiByte() で変換した際と System.useCodePage の値が同じである必要があります。自動的に UTF-8 かどうかを判定してくれたりはしません。

また、 System.useCodePage=true で変換した場合の結果はシステムのコードページに依存します。そのため、エンコードとデコードが同じコードページのシステム上で行われないと元の文字列に戻せません。例えば Shift-JIS で変換した文字列はShift-JIS の環境でしか戻せなくなります。

Posted by ackie at 07:17 PM | Comments (0)

May 18, 2006

describeType メソッド

describeType() メソッド (livedocs@lab) は引数に渡されたオブジェクトの情報を XML 形式で返します。引数には ActionScript の任意のオブジェクトを(クラスや int なども)渡すことができます。

var mc:MovieClip = new MovieClip();
var typeInfo:XML = describeType(mc);

返される XML オブジェクトに含まれる情報は引数として渡されたオブジェクトの属性やメソッド等です。Java のリフレクションと同じような機能です。

XML オブジェクトだと扱いが面倒に思われるかもしれませんが、E4X のおかげでむしろ Object よりも便利です。例えば、上の例の続きで、戻り値が void のメソッドのリストを取得するには typeInfo.method.(@returnType == "void") のように記述できます。

出力例

以下、MovieClip のインスタンスを describeType() に渡した結果を例として具体的な出力を説明します。

まず、ルートノードは type というタグです。type タグの属性を見ると、パッケージ名とクラス名、親クラス名、その他のクラス宣言時の修飾子が分かります。

<type name="flash.display::MovieClip" base="flash.display::Sprite" isDynamic="true" isFinal="false" isStatic="false">  

残りのタグは、type の子ノードです。一つ目は extendsClass タグで、継承関係にある全ての上位クラスの名前とパッケージ名が type 属性から分かります。また implementsInterface タグで継承しているインターフェースも分かります。

<extendsClass type="flash.display::Sprite"/>
<extendsClass type="flash.display::DisplayObjectContainer"/>
<extendsClass type="flash.display::InteractiveObject"/>
<extendsClass type="flash.display::DisplayObject"/>
<extendsClass type="flash.events::EventDispatcher"/>
<extendsClass type="Object"/>
<implementsInterface type="flash.display::IBitmapDrawable"/>
<implementsInterface type="flash.events::IEventDispatcher"/>

属性情報は accessor タグから分かります。名前と、アクセス、型、宣言されたクラスが分かります。

<accessor name="totalFrames" access="readonly" type="int" declaredBy="flash.display::MovieClip"/>
<accessor name="currentFrame" access="readonly" type="int" declaredBy="flash.display::MovieClip"/>

メソッド情報は method タグです。メソッド名、宣言したクラス、戻り値の型が属性に記述されます。パラメータを持つメソッドは子ノードを持ち、そこに、順番、型、オプションかどうかの情報があります。

<method name="play" declaredBy="flash.display::MovieClip" returnType="void"/>
<method name="gotoAndPlay" declaredBy="flash.display::MovieClip" returnType="void">
  <parameter index="1" type="*" optional="false"/>
  <parameter index="2" type="String" optional="true"/>
</method>

一通り MovieClip の定義情報が終わると、上位クラスで宣言されている属性とメソッドが続きます。MovieClip の場合は Sprite から EventDispatcher まで長々と情報が表示されます。

<method name="startDrag" declaredBy="flash.display::Sprite" returnType="void">
<parameter index="1" type="Boolean" optional="true"/>
<parameter index="2" type="flash.geom::Rectangle" optional="true"/>
</method>
...
...
<method name="addEventListener" declaredBy="flash.events::EventDispatcher" returnType="void">
<parameter index="1" type="String" optional="false"/>
<parameter index="2" type="Function" optional="false"/>
<parameter index="3" type="Boolean" optional="true"/>
<parameter index="4" type="int" optional="true"/>
<parameter index="5" type="Boolean" optional="true"/>
</method>

静的情報の取得

上の例ではインスタンスの情報のみで、クラスメソッド等の情報は出てきませんでした。static 宣言されている情報を取得するには describeType() にクラスオブジェクトを渡します。

下の例では Font クラスオブジェクトを渡しています。

trace(describeType(Font));

この例での出力は以下のようになります。type タグの base 属性が Class になっています。その他の属性も、クラスオブジェクトに対するもので Font クラスの属性ではありません。

type タグの直接の子ノードになっている method や accessor タグが static 宣言されたものです。インスタンス変数やメソッドは factory タグの子ノードになっています。

<type name="flash.text::Font" base="Class" isDynamic="true" isFinal="true" isStatic="true">
  <extendsClass type="Class"/>
  <extendsClass type="Object"/>
  <method name="enumerate" declaredBy="flash.display::Font" returnType="Array"/>
  <method name="register" declaredBy="flash.display::Font" returnType="void">
    <parameter index="1" type="Class" optional="false"/>
  </method>
  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
  <factory type="flash.text::Font">
    <extendsClass type="Object"/>
    <accessor name="fontStyle" access="readonly" type="String" declaredBy="flash.text::Font"/>
    <accessor name="fontName" access="readonly" type="String" declaredBy="flash.text::Font"/>
    <method name="hasGlyphs" declaredBy="flash.text::Font" returnType="Boolean">
      <parameter index="1" type="String" optional="false"/>
    </method>
  </factory>
</type>

beta3 ではメタタグ情報が出力されないようで、使用変更というよりはバグっぽい感じです。

Posted by ackie at 06:50 PM | Comments (0)

May 17, 2006

Dictionary クラス(前の続き)

ようやく new Dictionary() が実行できるようになったので続きです。

weak-value ディクショナリー

以前の記事に書いたように、Dictionary クラスは weak-key ディクショナリーの機能を持っています。キーオブジェクトへの参照が弱い参照のため、使われなくなったキーとそのエントリーを自動的に開放するという使い方ができます。

一方、値として管理されているオブジェクトがしばらく参照されなかったら自動的に開放するという使い方はできません。

このようなケースは(本来であれば) weak-value ディクショナリーを使う場面です。weak-value ディクショナリーでは値オブジェクトへの参照が弱い参照のため、他から参照されていない値オブジェクトはガーベッジコレクションの対象になります。

さて、現状で weak-value ディクショナリーを実現するには Dictionary のキーと値の扱いを入れ替えてしまうのが簡単そうです。Proxy を使うと以下の用に実現できます。

public dynamic class MyDictionary extends Proxy {
  private var _dict:Object;
  public function MyDictionary() {
    _dict = new Dictionary(true);
  }
  override flash_proxy function setProperty(name:*, value:*):void {
    // 値とキーを逆にしてエントリーを追加
    _dict[value] = name;
  }
}

本来キーとして使われるオブジェクトを値として格納したので、取り出す際には一つ一つ調べることになります。

override flash_proxy function getProperty(name:*):* {
  for (var value:* in _dict) {
    if (_dict[value] == name) {
      return value;
    }
  }
}

ただこの方法ではエントリーが多くなるに連れ参照時のパフォーマンスが悪くなりそうです。

そこで、ちょっと贅沢ですがエントリーごとに Dictionary を生成するようにしてみます。

override flash_proxy function setProperty(name:*, value:*):void {
  // エントリ専用の Dictionary オブジェクト生成
  var dict4Val:Dictionary = new Dictionary(true);
  // 値とキーを逆にしてエントリーを追加
  dict4Val[value] = name;
  // Dictionary オブジェクトを値として設定
  _dict[name] = dict4Val;
}

今回は Dictionary が一つしかエントリを持たないため、取り出す際は最初に見つかったものをそのまま返すことができます。

override flash_proxy function getProperty(name:*):* {
  // まず対応する Dictionary オブジェクトを取り出す
  var dict4Val:Dictionary = _dict[name];
  // dict4Val は一つしかキーを持たないはず
  for (var value:* in dict4Val) {
    // 見つかった値を返す
    return value;
  }
}

Posted by ackie at 06:18 PM | Comments (0)

April 20, 2006

Dictionary クラス

Dictionay クラス (livedocs@lab) は beta2 から追加されたクラスで、オブジェクトをキーとして値を管理することができます。Object を使っても同じように連想配列を実現できますが、Dictionary ではいわゆる”弱い参照”が使えます。

また、Object では文字列がキーとして扱われるため toString() の値が同じオブジェクトは全て同じキーとして扱われますが、Dictionary では別々のオブジェクトであれば toString() の値に関わらず異なるキーとして扱われます。

具体的な例を見てみましょう。下の例では key1 と key2 という2つのオブジェクトが同じ文字列に変換されるように設定しています。

// まず2つのキーを生成
var key1:Object = new Object();
var key2:Object = new Object();
key1.toString = function():String { return "key"; };
key2.toString = function():String { return "key"; };
// 値のオブジェクトを生成
var myVal:Object = "my value";
 
// Object の場合
var myKey:Object = new Object();
myObj[key1] = myVal;
trace(myObj[key2]); // my value が出力される
 
// Dictionary の場合
var myDict:Dictionary = new Dictionary();
myDict[key1] = myVal;
trace(myDict[key2]); // undefined が出力される

Object の場合には key1 をキーとして代入した値を key2 で取得できますが、Dictionary の場合には key1 をキーとする値は key1 でしか取り出すことができません。

ところで、beta 2 では Flex Builder 2 のバグのため Dictionary クラス型が参照できません。そのため上記のコードはコンパイルできません...現時点では、このような場合のための(?)getClassByName() メソッドを使用してインスタンスを作成する必要があります。

// beta 2 のバグを回避
var dictionaryCls:Class = getClassByName("flash.util.Dictionary");
var myDict:* = new dictionaryCls(); 
// 本来はこちらの宣言でインスタンスを生成する
//var myDict:Dictionary = new Dictionary();
myDict[key1] = myVal;
trace(myDict[key2]); // undefined が出力される

さて、Dictionary の登録を削除するときは以下の方法が使用できます。それぞれ、値のみを削除する場合とエントリーを削除する場合です。

// 値オブジェクトへの参照を削除
myDict[key1] = null;
trace(myDict[key1]); // null が出力される
// myDict からエントリーを削除
delete myDict[key1];
trace(myDict[key1]); // undefined が出力される

弱い参照の使用

弱い参照とは、たとえ参照があってもガーベッジコレクションの対象となる種類の参照です。キャッシュなど”とりあえず置いておく”タイプのオブジェクトを管理するときなどに便利です。

Dictionary クラスは弱い参照を利用した weak-key と呼ばれるタイプのディクショナリーとして使うことができます。weak-key ディクショナリーでは、キーとして使われているオブジェクトへの参照が弱い参照になっています。そのため、キーオブジェクトへの参照が Dictionary からのみだと、ガーベッジコレクションのタイミングでキーオブジェクトが削除され、併せて Dictionary 内の該当するエントリが削除されます。

Dictionary クラスを weak-key ディクショナリーとして使うには、コンストラクタの引数に true を指定します。デフォルトは false になっています。

// beta 2 のバグを回避
var dictionaryCls:Class = getClassByName("flash.util.Dictionary");
var myDict:* = new dictionaryCls(true); 
// 本来はこちらの宣言でインスタンスを生成する
//var myDict:Dictionary = new Dictionary(true);
// 後の使い方は Object とほぼ同じ
var myKey:Object = new Object();
var myVal:Object = new Object();
myDict[myKey] = myVal;

上の例で myKey が myDict からのみの参照であれば myKey はガーベッジコレクションの対象になります。

Posted by ackie at 06:30 PM | Comments (0)

April 19, 2006

StringBuilder クラス

StringBuilder クラス (livedocs@lab) は String と同じように文字列を扱うクラスですが、特に文字列に追加や挿入などの操作を何回も行うような場合に使います。String と違い、操作の度に新しいオブジェクトを生成したりしないため、メモリをより有効に使用することができます。特に、細かな追加を何回も行う場合には String と比べて高速です。

StringBuilder のインスタンスを生成するにはコンストラクタを使用します。String のように直接文字列で初期化することはできません。

var sb:StringBuilder = new StringBuilder("hello world"); 

capacity 属性と length 属性

StringBuilder はインスタンス生成時に、文字列を保持するための領域をメモリ上に確保します。 領域の大きさは capacity 属性(読み取り専用)から取得することができます。文字列自体の長さは length 属性から取得できます。

var sb:StringBuilder = new StringBuilder("hello world"); 
trace(sb.length); // 11 が出力される
trace(sb.capacity); // 16 が出力される

上記のように StringBuffer は legth 以上の大きさになるようなあるまとまった単位でメモリを確保します。このため余っている領域で足りる程度の追加/挿入であればメモリを確保しなくてもよいのです。

余談(?)ですが、length は設定可能です。length を小さくすると文字列の長さも短くなります。一旦短くなった文字列は length のみ大きくしても元には戻りません。

var sb1:StringBuilder = new StringBuilder("hello world");
sb1.length = 3;
trace(sb1); // hel が出力される
sb1.length = 110;
trace(sb1); // hel が出力される
trace(sb1.capacity); // 142 が出力される

length に応じて capacity は大きくなりますが capacity を確保することが目的であれば専用のメソッド(後述)を使用するほうがよいでしょう。

append() メソッド、 insert() メソッド、 remove() メソッド

最初の例に続けて、少し文字列を付け足して見ます。追加するには append() メソッドを使用します。

sb.append(" wide web");
trace(sb); // hello world wide web が出力される
trace(sb.length); // 20 が出力される
trace(sb.capacity); // 34 が出力される

文字列が確保されていた領域より長くなったため capacity が大きくなっています。大体倍になっていますね。

append() は複数の引数を取ることができます。2つ以上指定した場合は、引数の順に追加されます。

文字列の途中に文字列を挿入するには insert() メソッドを使用します。第一引数が挿入位置のインデックス、第二引数が挿入する文字列です。

上の例に続けて文字列を挿入してみます。

sb.insert(5, " beautiful");
trace(sb); // hello beautiful world wide web が出力される
trace(sb.length); // 30 が出力される
trace(sb.capacity); // 34 が出力される

今回は capacity は追加することなく処理ができました。

remove() メソッドを使うと文字列の一部削除ができます。第一引数が削除開始するインデックス、第二引数が削除終了位置の次のインデックスです。

sb.remove(0, 6);
trace(sb); // beautiful world wide web が出力される
trace(sb.length); // 24 が出力される
trace(sb.capacity); // 34 が出力される

ensureCapacity() メソッドと trimToSize() メソッド

StringBuilder のインスタンスの持つ capacity を明示的に確保したい場合は ensureCapacity() メソッドを使います。事前に必要となる大きさが分かっている場合は、ちょっとずつ増やすよりは効率がよさそうです。

var sb2:StringBuilder = new StringBuilder();
sb2.ensureCapacity(1000);
trace(sb2.length); // 0 が出力される
trace(sb2.capacity); // 1000 が出力される

逆に不要な領域を開放するには trimToSize() メソッドを使います。上の例の続きです。

sb2.append("what a wonderful world");
trace(sb2.length); // 22 が出力される
trace(sb2.capacity); // 1000 が出力される
sb2.trimToSize();
trace(sb2.length); // 22 が出力される
trace(sb2.capacity); // 22 が出力される

capacity が文字列の長さまで小さくなっているのが分かります。(16 よりは小さくならないようです)

Posted by ackie at 06:13 PM | Comments (2)

April 13, 2006

Proxy クラス

Proxy クラス (livedocs@lab) は既存のオブジェクトの振る舞いを変えたいときに使います。例えば、Proxy のサブクラスである ObjectProxy クラス (livedocs@lab) は属性が変更されたとき、イベントでそれを知らせることができるようになっています。(注:ObjectProxy は Flex 2 フレームワークのクラスです)

Proxy クラスにはコンストラクタがありません。従って、Proxy クラスを使用する際は、サブクラスを定義しメソッドをオーバーライドするのが基本です。オーバーライドしていないメソッドが呼ばれた場合は例外が投げられます。

属性へのアクセスの実現

さて、Proxy クラス(のサブクラス)のインスタンスの属性を参照するには getProperty メソッドをオーバーライドします。

import flash.util.*;
 
public dynamic class MyProxy extends Proxy {
  private var _obj:Object = new Object();
 
  override flash_proxy function getProperty(name:*):* {
    return _obj[name];
  }
}

Proxy クラスのメソッドの呼ばれ方は少々変わっています。例えばここで定義している getProperty() メソッドが呼ばれるのは Proxy クラスのサブクラスのインスタンスの属性が参照されたときです。

つまり、Proxy のメソッドは普通に直接呼びだすためのものではありません。これらのメソッドには通常の属性へのアクセスと区別するために別の名前空間(flash_proxy)が指定されています。

実際に MyProxy を使用するコードは以下のようになります。

var myProxy:MyProxy = new MyProxy();
trace(myProxy.foo); // MyProxy の getProperty(foo) が呼ばれる。出力は undfined になる。

コーディング上 MyProxy に定義されていない属性(この例では foo)にアクセスすることになるため、上の MyProxy のクラス定義では dynamic なクラスとして宣言しています。(コンパイラの -strict オプションをオフにしても構いません。)

属性操作には、設定/削除/検査もありますね。これらに対応するメソッドも MyProxy に実装してみます。

override flash_proxy function setProperty(name:*, value:*):void {
  _obj[name] = value;
}		
override flash_proxy function deleteProperty(name:*):Boolean {
  return delete _obj[name];
}
override flash_proxy function hasProperty(name:*):Boolean {
  return name in _obj;
}

これで、以下のような操作が可能になります。

var myProxy:MyProxy = new MyProxy();
myProxy.foo = "foo";       // setProperty が呼ばれる
trace(myProxy.foo);        // getProperty が呼ばれる:出力は foo
trace("foo" in myProxy);   // hasProperty が呼ばれる:出力は true
trace(delete myProxy.foo); // deleteProperty が呼ばれる:出力は true

関数の呼び出しの実現

Proxy のサブクラスに対して関数をコールすると callProperty メソッドが呼ばれます。callProperty メソッドの使い方は以下のような感じです。

override flash_proxy function callProperty(name:*, ...rest):* {
  var func:Function = _obj[name] as Function;
  if (func != null) {
    return func.apply(null, rest);
  }
}
 
var myProxy:MyProxy = new MyProxy();
myProxy.bar = function():String { return "bar"; };
trace(myProxy.bar());  // callProperty が呼ばれ、bar が出力される

イテレーションへの対応

for..in や for each..in のようなイテレーションで Proxy を使えるようにするには、まず nextNameIndex() メソッドをオーバーライドします。このメソッドは次の属性のインデックス値を返します。

private var _props:Array; // 属性格納用の配列
 
override flash_proxy function nextNameIndex(index:int):int {
  if (index == 0) { // イテレーションの開始、配列を初期化する
    _props = new Array();
    for (var:* x in _obj) {
      _props.push (x);
    }
  }
  if (index < _props.length) {
    return index + 1;
  } else {
    return 0;
  }
}

for..in を使用するには nextName() メソッドもオーバーライドします。

override flash_proxy function nextName(index:int):String {
  return _props[index-1].toString();; // nextNameIndex が 1 から返すため
}
 
for (var prop:* in myProxy) { 
  trace(prop + " = " + myProxy[prop]); 
}

for each ..in を使用するには nextValue() メソッドをオーバーライドします。

override flash_proxy function nextValue(index:int):* {
  return _obj[_props[index-1]];
}
 
for each (var item:* in myProxy) { 
  trace(item); 
}

Posted by ackie at 08:04 PM | Comments (0)

April 12, 2006

Timer クラス

Timer クラス (livedocs@lab) は一定の間隔毎に処理を行いたい場合に使用します。Timer のインスタンスは、予め設定された時間が経つとイベントをディスパッチします。

Timer クラスは AS2 の setInterval() / getInterval() を置き換えるものです。AS3 でも setInterval() / getInterval() は共に flash.util パッケージ下にあり使用可能ですが、これは既存のコードとの互換性を持たせるためと考えるのがよさそうです。

Timer を使用するには、まずインスタンスを作成します。コンストラクタの引数にはイベントまでの待ち時間をミリ秒単位で指定します。

var myTimer:Timer = new Timer(1000);

次に、指定時間後に呼び出されるイベントハンドラを登録してから start() メソッドを呼びます。イベント名は timer です。経過時間のカウントは start() が呼ばれるまで開始されません。

myTimer.addEventListener("timer", timerHandler);
myTimer.start();
 
private function timerHandler(event:TimerEvent):void {
  // ここに必要な処理を記述
  trace(event.toString());
}

インスタンス生成後でも delay 属性の値を代入することで待ち時間を変えることが可能です。

コンストラクタの第二引数には、イベント発生を繰り返す回数を指定することができます。デフォルト値は 0 です。0 は無限に繰り返すという意味になります。

// 1 秒間隔で 2 回イベントを発生する
var myTimer:Timer = new Timer(1000, 2);

繰り返しの回数も repeatCount 属性から設定可能です。現在までの繰り返し回数は currentCount 属性から参照できます(こっちは参照のみです)。 設定された回数分だけ繰り返したら timerComplete イベントがディスパッチされ、Timer は停止します。ちなみに Timer が実行中かどうかは running 属性で判断することができます。

var myTimer:Timer = new Timer(1000);
 
myTimer.repeatCount = 2;
myTimer.addEventListener("timerComplete", timerCompleteHandler);
myTimer.start();
 
private function timerCompleteHandler(event:TimerEvent):void {
  trace("timer complete handler called");
}

stop() メソッドと reset() メソッド

stop() メソッドを使うと Timer を一旦中断することができます。その後 start() を呼ぶと残りの回数分 Timer が実行されます。

var myTimer:Timer = new Timer(1000, 2);
myTimer.addEventListener("timer", timerHandler);
myTimer.start();
 
private function timerHandler(event:TimerEvent):void {
  myTimer.stop();
  trace(myTimer.running); // false が出力される
  myTimer.start();
}

reset() メソッドを使うと Timer の処理を停止し currentCount を 0 に戻します。 従って下記のサンプルは無限にイベントが繰り返されます。

var myTimer:Timer = new Timer(1000, 2);
myTimer.addEventListener("timer", timerHandler);
myTimer.start();
 
private function timerHandler(event:TimerEvent):void {
  myTimer.reset();
  trace(myTimer.currentCount); // 0 が出力される
  myTimer.start();
}

Posted by ackie at 08:15 PM | Comments (0)

March 08, 2006

ApplicationDomain

Loader オブジェクトで (URLLoader ではありません) swf ファイルをロードするとき、ロード先のアプリケーションドメインを指定することができます。

アプリケーションドメインはクラス定義の単位になるため、実行時に swf をロードすることで動的にクラス定義を追加するといった使い方ができます。また、複数のアプリケーションドメインを持つことができるため、同じクラスの異なるバージョンを同時にアプリ内で使うことも可能です。

それでは、アプリケーションドメインの使い方について詳しく見ていきましょう。

アプリケーションドメインの階層

通常、アプリケーションドメインは階層構造をなしています。一番親のアプリケーションドメインはシステムドメインと呼ばれ、Flash Player 上に一つだけ存在します。その他のアプリケーションドメインはシステムドメインの子孫ということになります。

各アプリケーションドメインにはクラス定義が含まれますが、子のドメインは親のドメインのクラス定義を引き継ぎます。従って、下の階層になるほど多くのクラスが利用可能なわけです。

アプリケーションドメインは ApplicationDomain クラス (livedocs@lab) のインスタンスとして実装されています。ApplicationDomain のインスタンスを生成する際、親ドメインをコンストラクタの引数として指定することができます。何も指定されなければシステムドメインが親ドメインになります。

// システムドメイン下に新しいアプリケーションドメインを作成
new ApplicationDomain();
// 現在のドメイン下に新しいアプリケーションドメインを作成
new ApplicationDomain(ApplicationDomain.currentDomain);

ApplicationDomain.currentDomain は現在アプリケーションが実行されているドメインを示す静的変数です。

ロード時のアプリケーションドメインの指定

swf ファイルロード時に読込先のアプリケーションドメインを指定するには URLRequest オブジェクトの applicationDomain 属性を使用します。

var myReq:URLRequest = new URLRequest();
myReq.url = "myClasses.swf";
// 現在のドメインを設定する
myReq.applicationDomain = ApplicationDomain.currentDomain;
var myLoader:Loader = new Loader();
myLoader.load(myReq);

読み込み後は Loader の loadeeInfo 属性から myLoader.loadeeInfo.applicationDomain のようにして読み込まれた swf が属するアプリケーションドメインにアクセスすることができます。

読み込まれた側から自身の属するアプリケーションドメインを取得するには、読み込まれた swf 内のルート DisplayObject の loaderInfo から参照します。roo 以外の DisplayObject からであれば this.root.loaderInfo.applicationDomain で取得できます。

getClass() メソッドによる動的なクラス定義の利用

さて、getClass() メソッドを使うとアプリケーションドメインからクラス定義を取得することができます。つまり、アプリケーションドメインに新しく swf ファイルを読み込むことで、利用できるクラス定義を動的に追加できるわけです。

ただし既存のクラス定義を、新しくロードした swf 内の定義で上書きすることはできません。新規に見つかったクラス定義のみが追加されます。

var myReq:URLRequest = new URLRequest();
myReq.url = "myClasses.swf";
// 現在のドメインに読み込むよう指定
myReq.applicationDomain = ApplicationDomain.currentDomain;
var myLoader:Loader = new Loader();
myLoader.addEventListener(Event.COMPLETE, completeHandler);
myLoader.load(myReq);
 
private function completeHandler(event:Event):void {
  // まず読込先のドメインを取得
  var myDomain:ApplicationDomain = myLoader.loadeeInfo.applicationDomain;
  // ドメイン内のクラス定義を取得
  var myClassRef:Class = myDomain.getClass("myClass1");
  // 定義を取得したクラスのインスタンス生成
  var foo:Object = new myClassRef();
}

上記の例ではアプリケーションを実行中のアプリケーションドメインに swf をロードしています。この場合、一旦追加されたクラス定義はそのまま残り続けます。

ですが、子のドメインに swf をロードすれば swf をアンロードすることで追加されたクラス定義もガーベッジコレクションの対象とすることができます。(もちろんロードした swf への参照がないことが条件ですが)

つまり、下のように書き換えた場合にはクラス定義の追加と削除がコントロールできるようになるわけです。

...
myReq.url = "myClasses.swf";
// 新規に子ドメインを作るよう変更
myReq.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
var myLoader:Loader = new Loader();
myLoader.addEventListener(Event.COMPLETE, completeHandler);
myLoader.load(myReq);
 
...

別ドメインへのアプリケーションのロード

アプリケーションを読み込むときにアプリケーションドメインを使い分けることで、クラス定義への依存状況に柔軟に対応することができるようになります。

例えば、システムドメイン直下に新しいアプリケーションドメインを作成した場合、現在のドメインと新規ドメインのクラス定義は独立です。すなわち、アプリケーション固有のクラス定義は共有されません。

これを利用すると、同じアプリケーションの異なるバージョンを、クラス定義の違いなどを気にすることなく同時に実行することが可能です。

var myReq:URLRequest = new URLRequest();
// 別バージョンのアプリケーションを指定
myReq.url = "v2app.swf";
// システムドメイン下に新しいアプリケーションドメインを作成
myReq.applicationDomain = new ApplicationDomain();
var myLoader:Loader = new Loader();
// 独立した新規ドメインにアプリケーションをロード
myLoader.load(myReq);

また、複数の子ドメインにアプリケーションを分割すると、子ドメイン固有のクラス定義はそれぞれ独立させながら、親ドメインを共通のクラス定義の場として管理することができます。

例として、複数のパネルを持つポータル系のアプリケーションで、個々のパネル内を独立したアプリケーションとして実装した場合を考えてみます。

// パネル内のアプリケーションを指定
myReq1.url = "panel1.swf";
// 新規に子ドメインを作成
myReq1.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
myLoader1.load(myReq);
 
// 別パネル内のアプリケーションを指定
myReq2.url = "panel2.swf";
// もう一つ子ドメインを作成
myReq2.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
myLoader2.load(myReq);

このような構造だと panel1.swf に固有のクラス定義は例えば panel2.swf のような他のパネル内のアプリケーションには影響しません。そのため、共通部分(親のアプリケーションドメイン内のクラス定義)は管理しながら、かなり自由に個別の機能を開発/管理することができそうです。

Posted by ackie at 05:48 PM | Comments (2)

March 07, 2006

navigateToURL 関数

今回は、flash.net パッケージの関数2つを紹介します。

navigateToURL() 関数

AS3 アプリケーションからもう一つブラウザのウインドウを開きたい、あるいは今 AS3 アプリケーションを実行中のウインドウを他の HTML コンテンツで置き換えたいというときには navigateToURL() 関数 (livedocs@lab) を使います。以下が関数の定義です。

public function navigateToURL(request:URLRequest, window:String = null):void

1つめの引数には URLRequest オブジェクトを指定します。このオブジェクトには URLLoader クラスの記事内の例と同様に、リクエスト先の URL やヘッダ情報それから送信するデータと送信方法等を指定します。

2つ目の引数にはリクエストの結果を受信するウインドウを指定します。こちらはデフォルト値が定義されているため省略が可能です。省略した場合には新しいウインドウが開かれます。つまり "_blank" を指定したのと同等の結果になります。

下のサンプルでは navigateToURL() の第二引数を "_self" にしていますので AS3 アプリケーションを実行しているウインドウ自身のコンテンツを置き換えます。

import flash.net.navigateToURL;
 
var myVars:URLVariables = new URLVariables();
myVars.currentTime = new Date().getTime();
myVars.city = "Tokyo";
 
var myReq:URLRequest = new URLRequest();
myReq.url = "http://www.sample.com/weather";
myReq.data = myVars;
 
// 現在のウインドウのコンテンツを置き換える
navigateToURL(myReq, "_self");

navigateToURL() 関数を使うにはクラスを使用するときと同じ様にインポートします。

sendToURL() 関数

AS3 アプリケーションからサーバーにデータを送りたい、けれど結果は無視したいというときには sendToURL() 関数 (livedocs@lab) を使用します。使い方は上で紹介した navigateToURL() 関数とほぼ同じです。違いは2つ目の引数がないことくらいです。

public function sendToURL(request:URLRequest):void

あまり芸がないのですが、一応サンプルも。

import flash.net.sendToURL;
 
var myVars:URLVariables = new URLVariables();
myVars.user = "foo";
myVars.data = "hello";
 
var myReq:URLRequest = new URLRequest();
myReq.url = "http://www.sample.com/post";
myReq.data = myVars;
myReq.method = URLRequestMethod.POST;
// サーバーにデータを送信
sendToURL(myReq);

Posted by ackie at 05:32 PM | Comments (0)

March 06, 2006

URLLoader クラス

外部からの XML データ等を読み込むときは URLLoader クラス (livedocs@lab) を使うことができます。

以前紹介した Loader クラスは swf やイメージを主に表示目的でロードするためのクラスでした。Loader クラスの記事を書いていた頃は URLLoader は Loader のサブクラスでしたが、今は独立したクラスになっています。

URLRequest クラスとデータ要求の送信

URLLoader を使ってサーバーにリクエストを送るには、まず URLRequest クラス (livedocs@lab) のインスタンスを作ります。URLRequest のオブジェクトにはリクエスト先の URL やヘッダ情報等が指定できます。

次に、作成した URLRequest オブジェクトを引数として URLLoader の load() メソッドを呼び出します。

var myReq:URLRequest = new URLRequest();
myReq.url = "foo.xml";  // コンストラクタの引数としても指定可能
var myLoader:URLLoader = new URLLoader();
myLoader.load(myReq);

HTTP ヘッダに情報を追加する場合は、項目ごとに URLRequestHeader クラス (livedocs@lab) のインスタンスを作成し、 URLRequestHeader オブジェクトの requestHeaders 属性(Array 型です)に追加します。

var myReq:URLRequest = new URLRequest("foo.xml");
var myRH:URLRequestHeader = new URLRequestHeader("pragma", "no-cache");
myReq.requestHeaders.push(myRH);
var myLoader:URLLoader = new URLLoader();
myLoader.load(myReq);

受信したデータの処理

load() を実行してからデータの読み込み処理が終わるまで ActionScript から読み込んだデータにアクセスすることはできません。URLLoader は受信処理を完了すると complete イベントを発生させるので、 complete イベントに対するイベントハンドラ内でデータ操作を行うようにします。読み込まれたデータは URLLoader の data 属性からアクセスすることができます。

myLoader.addEventListener(Event.COMPLETE, completeHandler);
myLoader.load(new URLRequest("foo.xml"));
 
private function completeHandler(event:Event):void {
  trace(myLoader.data);
}

参考までに、complete を含め URLLoader がディスパッチするイベントには以下のものがあります。

complete      : ダウンロード処理が完了した
httpStatus    : 返信の HTTP ステータスが判明した
ioError       : IO エラーによりロード処理が中断した
open          : ロード処理が開始した
progress      : ロードを実行中
securityError : 許可されない通信を行おうとした

さて、読み込まれただデータは DataFormat クラス (livedocs@lab) に定義されている3種類のフォーマットのどれかです。URLLoader オブジェクトの dataFormat 属性から値を知ることができます。

  • DataFormat.TEXT : String 型 (livedocs@lab) の文字列
  • DataFormat.BINARY : ByteArray 型 (livedocs@lab) のバイナリデータ
  • DataFormat.VARIABLES : URL エンコードされたフォーム変数を持つ URLVariables オブジェクト

3つ目の DataFormat.VARIABLES フォーマットの際に使用される URLVariables クラス (livedocs@lab) からは名前と値がペアになった連想配列として値を取り出すことができます。

データの送信

サーバにデータを送信する際も上記3種類のフォーマットが使用できます。送信するデータを URLRequest オブジェクトの data 属性にセットすれば、リクエスト時にセットしたデータが送られます。

下の例では変数&値のペアを定義した URLVariables オブジェクトを POST で送信しています。この場合、データは x-www-form-urlencoded フォーマットにエンコードされて送られます。

var myVars:URLVariables = new URLVariables();
myVars.userID = "guest";
myVars.password = "foo";
myReq.data = myVars;
myReq.url = "ww.sample.com/foo.jsp";
myReq.method = URLRequestMethod.POST;
myLoader.load(myReq);

送信方法 (GET/POST)は URLRequest オブジェクトの method 属性に URLRequestMethod クラス (livedocs@lab) に定義された値を使って指定します。デフォルトは GET です。POST する場合はデータの MIME タイプを URLRequest オブジェクトの contentType 属性に設定することができます。 ByteArray オブジェクトを送信する場合は GET は使用できません。

Posted by ackie at 05:42 PM | Comments (0)

December 14, 2005

SimpleButton クラス

マウスイベントの続きということで、遅ればせながら SimpleButton クラス (livedocs@lab) の説明です。

SimpleButton クラス

SimpleButton は InteractiveObject のサブクラスで、マウスの操作に応じてボタンらしく振舞うことができます。 Flash オーサリングツールでボタンシンボルを作成すると4つのフレームができますが、SimpleButton にもそれぞれのフレームに対応したプロパティが存在します。

public upState : DisplayObject        // アップ(通常)の状態の表示オブジェクト
public overState : DisplayObject      //(マウス)オーバーの状態の表示オブジェクト
public downState : DisplayObject      //(マウス)ダウンの状態の表示オブジェクト
public hitTestState : DisplayObject   // ヒットテスト用のオブジェクト

以上の4つのプロパティに対応するマウス操作の状態それぞれに対して、意図された表示を実現する DisplayObject を設定するのが SimpleButton の基本的な使用方法です。

var myButton:SimpleButton = new SimpleButton();
// 各ステート用のオブジェクトを作成(今回は一つの Shape を共有)
var stateShape:Shape = new Shape();
stateShape.graphics.lineStyle(2, 0x202020);
stateShape.graphics.beginFill(0xFF0000);
stateShape.graphics.drawRect(10, 10, 50, 50);
// ボタンオブジェクトのプロパティを設定
myButton.upState = stateShape;
myButton.downState = stateShape;
myButton.overState = stateShape;
myButton.hitTestState = stateShape;

SimpleButton にはその他に以下のプロパティがあります。

public trackAsMenu : Boolean       // メニューのような使い方をするか
public enabled : Boolean           // ボタンを有効にするか
public useHandCursor : Boolean     // カーソルの形状を手の形にするか

trackAsMenu は他の場所でマウスが押された場合でも click イベントを受け取りたい場合に使用します。通常 click イベントは同一オブジェクト上でマウスを押す/放すという行為をしないとディスパッチされませんが、trackAsMenu を true にしておくと、他のオブジェクト上でマウスを押してそのまま移動してある SimpleButton オブジェクト上でマウスを放した場合にも、その SimpleButton オブジェクトに click イベントがディスパッチされます。メニューを作るときに使いそうな機能ですね。

enabled プロパティはボタンの有効/無効を切り替えます。enabled を false に設定すると、実際マウスを操作しても見た目上は反応しなくなります。ただし、イベントはディスパッチされるようです。

useHandCursor プロパティはデフォルトが true です。通常は特に気にする必要はなさそうです。

Posted by ackie at 08:53 PM | Comments (0)

November 30, 2005

EventDispatcher クラス

ActionScript3 では新しいイベントモデルが導入されました。AS2 では、場合によって何種類ものイベントを使い分けなければいけなかったり、さらに、使い方によってイベントのスコープが異なったりと、使いこなすにはそれなりの熟練が要求されましたが、AS3 からは DOM Level 3 イベントモデルをベースに統一されたイベントモデルを使用します。

EventDispatcher クラス

EventDispatcher は新しいイベントモデルの基本となる機能を実装したクラスです。 イベントをディスパッチしたりイベントのターゲットになったりします。また EventDispatcher は前に紹介した DisplayObject の親クラスです。flash.display パッケージの中で取り上げたクラスがイベントを処理する機能を持っていたのは EventDispatcher の機能を継承しているためです。

イベントリスナーの登録/削除にはそれぞれ addEventListener() と removeEventLister() を使います。どちらのメソッドも最初の2つの引数が必須で、指定するのは監視対象のイベントの種類とイベントを処理するリスナー関数です。

public addEventListener(type:String, listener:Function,
       useCapture:Boolean = false, priority:int = 0) : Void
public removeEventListener(type:String, listener:Function,
       useCapture:Boolean = false) : Void

少し面白いのはリスナー登録時にプライオリティを指定できることです。(4番目の引数) これで例えば、リスナーを呼び出す際の事前/事後の条件処理を行うことが可能になります。同じプライオリティでいくつも登録できるので、一般のリスナーはデフォルト(0)で登録しておいて、事前条件チェック用のメソッドをプライオリティを -1 に、事後条件チェック用はプライオリティ 1 にすればよいわけです。ちなみに同じプライオリティのリスナー同士では登録順に呼び出されます。

下の2つはリスナーの登録状況を調べるのに便利な関数です。

public hasEventListener(type:String) : Boolean
public willTrigger(type:String) : Boolean

hasEventListner() も willTrigger() も、特定のイベントに対するリスナーが登録されているかを調べるメソッドです。両者の違いは、hasEventListener() はメソッドが所属する EventDispatcher インスタンスについてのみリスナーが登録されているかを調べるのに対して、willTrigger() はイベントフロー全体の中で対象のイベントに対するリスナーが登録されているかを調べることです。

さて、イベントフローという言葉が出てきました。AS3 では一つのイベントが複数のオブジェクトを渡り歩きます。次回はここをも少し詳しく解説します。

最後にイベントをディスパッチするメソッドも紹介して起きます。

public dispatchEvent(event:Event) : Boolean

Posted by ackie at 08:01 PM | Comments (0)

November 21, 2005

flash.display パッケージのとりあえずのまとめ

これまで紹介してきたクラスは flash.display パッケージに含まれているクラスの主要メンバーです。少し乱暴な言い方をしてしまうと、flash.display パッケージは AS2 での MovieClip が持っていた役割を再編成したものと言えるでしょう。各クラスの役割も明確になり、使いやすくなったのではないかと思います。AS2 には無かったコンセプトがいくつも出てきましたが、AS3 でプログラミングする上では外せない部分ですので実際にいろいろと試してみていただければと思います。まだアルファ版のため ”本当はこう動くはずじゃないかな?” という期待と違う動きをすることもしばしばあります。どうもバグらしいと思ったら時間のあるときで構いませんのでレポートしてください。英語が面倒な方は、当面私宛に送っていただいてもOKです。

説明だけでは分かりにくかったかも、ということで最後に簡単なサンプルコードを載せておきます。(HelloWorld です。ベタで済みません。) サンプルコードでは、コンストラクタがいわゆる main() 関数の代わりをしています!ActionScript3 ではスクリプトだけで Flash アプリケーションが出来てしまうのですね。もう fla ファイルとか mxml ファイルとかの単なる補助ではありません。ここに AS3 の可能性をちょっと感じてしまったりします。

package {
import flash.display.Sprite;
import flash.display.TextField;
import flash.text.TextFormat;
import flash.events.Event;
import flash.events.EventType;
import flash.geom.Point;
import flash.geom.Rectangle;
// Sprite のサブクラスを作成 public class HelloWorld extends Sprite {
// プライベート変数を宣言 private var label:TextField; private var target:Point = new Point();
// コンストラクタ public function HelloWorld() { createLabel(); addEventListener(EventType.ENTER_FRAME, enterFrameListener); }
// TextField に Hello World を表示させる private function createLabel():Void { label = new TextField(); label.text = "Hello World";
var format:TextFormat = new TextFormat(); format.font = "Verdana"; format.color = 0xFF0000; format.size = 16; label.setTextFormat(format);
// ディスプレイリストに追加 addChild(label); }
// enterframe のイベント処理 public function enterFrameListener(event:Event):Void { var current:Point = label.getBounds(this).topLeft; if (Point.distance(target, current) < 1) { resetTarget(); } label.x += (target.x - label.x) / 8; label.y += (target.y - label.y) / 8; }
// ターゲット座標の設定 private function resetTarget():Void { target.x = Math.random() * stage.stageWidth - 100; target.y = Math.random() * stage.stageHeight - 10; } }
}

Posted by ackie at 10:40 PM | Comments (3) | TrackBack

November 07, 2005

Stage クラス

Stage クラスは Flash コンテンツの描画領域に対応するクラスです。(livedocs@lab) DisplayObject は Stage のインスタンスの領域上に表示されます。AS2 での Stage はグローバルオブジェクトでしたが、AS3 では DisplayObject.stage プロパティからアクセスします。

Stage にはいくつかの便利なイベントがあり、Flash Player と OS 間のフォーカス情報などを知ることが出来ます。

activate    : Flash Player が OS からフォーカスを貰ったとき  
deactivate  : Flash Player が OS のフォーカスを失くしたとき 
mouseLeave  : マウスポインターが Stage の領域外に移動したとき
resize      : scaleMode=noScale で Stage の大きさが変更されたとき

Stage は DisplayObjectContainer のサブクラスですが、多くのプロパティやメソッドは Stage には適用することが出来ません。(例えば Stage を回転させるとか) これらの機能を誤って使用すると例外が投げられるようになっています。 該当するプロパティは alpha, blendMode, cacheAsBitmap, contextMenu, filters などです。一方 Stage 固有のプロパティも存在します。例えば、以下のようなものがあります。

public align : String      // ステージの配置(StageAlign.TOP,StageAlign.LEFT,...)
public quality : String    // 描画クオリティ(StageQuality.LOW,
StageQuality.BEST,...)
public scaleMode : String  // 使用するスケールモード(StageScaleMode.NO_SCALE,...)

DisplayObject.root プロパティについて

DisplayObject の root プロパティは、自身が含まれる SWF の root を指します。Loader により読み込まれた SWF 内の DisplayObject からは、root は読み込まれた SWF のトップの DisplayObject です。(AS3 では、読み込まれた SWF は Loader の子オブジェクトになるのでした。従って、読み込んだ側の Loader.content と読み込まれた SWF の DisplayObject.root は同じオブジェクトを指します。)

Stage も DisplayObject のため root プロパティを持ちますが、これはメインの root を指しています。従って、任意の DisplayObject からは DisplayObject.stage.root のようにして、メインの root を参照することができます。

Posted by ackie at 09:15 PM | Comments (0)

November 04, 2005

Splite クラスと MovieClip クラス

Sprite クラス

Sprite は、基本的には DisplayObjectContainer をそのまま具象クラス化したものと見做せるでしょう。(livedocs@lab) 子オブジェクトの管理やユーザーインタラクションの実現など、Flash コンテンツを開発する上でコアになる機能を提供します。 (DisplayObjectContainer + InteractiveObject + DisplayObject

DisplayObjectContainer から追加されている主な機能ははドラッグ&ドロップ操作への対応くらいです。下記はそのメソッドです。

public startDrag(lockCenter:Boolean = false, bounds:Rectangle = null) : Void
public stopDrag() : Void

Sprite のサブクラスである MovieClip よりも軽いので、AS3 では開発の中心となるクラスと位置づけられています。UIComponent のベースクラスも AS3 からは Sprite に変更されました。(というわけで UIObject クラスも必要なくなりました。) また、なにより new Sprite() と書けるのが嬉しいところです...というのは、AS2 を書いたことのない人には関係ない話でしたね。

AS3 での典型的なクラスのコーディングは次のような感じです。

// 1.Sprite (または MovieClip)のインスタンスを生成
   var foo:Sprite = new Sprite();
// 2.必要な DisplayObject を Sprite のディスプレイリストに追加 var myDo:DisplayObject = new myDisplayObject(); foo.addChild(myDo);
// 3.イベントに対するコールバック関数を定義する myDo.addEventListener(MouseEventType.MOUSE_UP, onMouseUpEvent);

MovieClip クラス

MovieClip は Sprite にタイムラインとシーン機能が追加されたものです。(livedocs@lab) スクリプトのみで Flash コンテンツを制作する場合にはあまり使う機会はないかもしれません。実際、特に理由がない限りは親クラスの Splrte を使用するようにしましょう。

MovieClip もやっと ActionScript オブジェクトになり、普通に new MovieClip() が書けるようになりました。その代わりに MovieClip のインスタンスを生成する方法はコンストラクタのみになりました。createEmptyMovieClip や duplicateMovieClip といったメソッドはありません。

Posted by ackie at 07:13 PM | Comments (0)

November 02, 2005

Loader クラス

Loader クラス(livedocs@lab)は、SWF ファイルを読み込んだり、JPEG, PNG, GIF 等のイメージデータを読み込むのに使います。Loader のメソッドは以下のとおりです。

public load(request:URLRequest) : Void    // 指定された URL からロード
public unload() : Void                     // 読み込んだオブジェクトの削除
public close() : Void                      // 実行中のロードをキャンセル
public loadBytes(bytes:ByteArray) : Void  // 引数からデータをロード

読み込まれたオブジェクトは Loader の子オブジェクトになります。Loader.content プロパティからこれを参照することが出来るようになっています。Loader は DisplayObjectContainer のサブクラスですが、子オブジェクトを一つしか持つことが出来ない特殊なクラスです。このため子オブジェクトを追加/削除する類のメソッドを呼び出すと例外が投げられます。

loadByte メソッドはちょっと面白いメソッドで、メモリ上のバイナリデータから SWF や GIF, JPEG, PNG のオブジェクトを作成することができます。

Loader のイベントとしては、ロードの状況や終了を知らせるイベントがサポートされています。

open           : 処理が開始された
progress       : ロード処理を実行中
complete       : ロードが完了した
init           : ロードした SWF が初期化された

securityError や unload といった新しいイベントも追加されました。

読み込み中のオブジェクトの情報は Loader.loadeeInfo プロパティからも得ることが出来ます。このために LoaderInfo というクラスが専用で提供されています。(livedocs@lab) Loader.loadeeInfo プロパティは読み込まれる側からも共有されていて、読み込まれた SWF からは DisplayObject.root.loaderInfo プロパティ経由でアクセスできます。このとき、読み込まれた SWF は、読み込む際に URL パラーメタが指定されていると loaderInfo.arguments プロパティから取得できるようになっています。

AS1/2 では、MoviClipLoader クラスや MovieClip.loadMovie メソッドが同じ目的に使用されていましたが、AS3 では Loader に置き換えられています。

少し話しは外れますが、AS3 からは LoadVars も Loader のサブクラスの URLLoader (livedocs@lab)で置き換えられました。併せて AS2 の XML は通信機能が省略された上で XMLDocument (livedocs@lab)というクラスに変更されています。(AS3 の XML は新しいクラスです。これについては別途詳しく解説する予定です。)

Posted by ackie at 08:35 PM | Comments (3)

November 01, 2005

DisplayObjectContainer クラス

次は、InteractieObject のサブクラスで「コンテナ」機能が追加された抽象クラスです。

DisplayObjectContainer クラス

DisplayObjectContainer クラス(livedocs@lab)は、その名のとおり DisplayObject のコンテナ、つまり他の DisplayObject を子オブジェクトとして持つことの出来るクラスです。一方 DisplayObjectContainer 自身も DisplayObject ですから子オブジェクトになることができます。つまり DisplayObject のツリー構造を作ることが出来ます。(末端ノード以外は DiplayObjectContainer である必要があります) Flash の経験者であれば MovieClip が入れ子になっているものをイメージするとよいでしょう。

このように、DisplayObectContainer は画面に表示されたオブジェクトをまとめて管理するのに使われます。DisplayObjectContainer (およびそのサブクラス)が子オブジェクトを管理する仕組みをディスプレイリストと呼んでいます。

DisplayObjectContainer に子オブジェクトを追加/削除するには、それぞれ以下のメソッドを使います。

public addChild(child:DisplayObject) : DisplayObject
public removeChild(child:DisplayObject) : DisplayObject

この際、後に追加されたものほど上に表示されます。子オブジェクトはインデックスで管理され、インデックス 0 が最背面のオブジェクトになります。addChild メソッドは子オブジェクトを追加する際、そのオブジェクトのインデックスが最大値になるように設定する訳です。追加された子オブジェクトからは DisplayObject.parent で親のコンテナを参照することができます。

任意のインデックスに子オブジェクトを追加したい場合には、以下のメソッドを使います。

public addChildAt(child:DisplayObject, index:int) : DisplayObject

また、次のメソッドを使うと既存の子オブジェクトのインデックスを変更することが出来ます。

public setChildIndex(child:DisplayObject, index:int) : Void

AS1/2 とは違い、表示の重なりの制御に depth を使用しなくなりました。

Posted by ackie at 08:44 PM | Comments (0)

October 31, 2005

InteractiveObject クラス

DisplayObject クラスのサブクラスとして Bitmap と Shape を紹介しましたが、次は、もう一つのたぶん一番良く参照されるサブクラスです。

InteractiveObject クラス

DisplayObject クラスにユーザーインタラクションのためのイベント等を追加した抽象クラスが InteractionObject クラス(livedocs@lab)です。概念的には、画面に表示されて、マウスやキーボード入力に反応するオブジェクトをモデル化したものと考えることができるでしょう。(ということは、Bitmap や Shape は、ユーザーが直接操作できないわけです。) InteractiveObject の直接のサブクラスには SimpleButton や TextField などがあります。この InteractionObject クラス(のサブクラス)はフォーカスの制御の単位にもなります。

これにより、AS1/2 では何種類もあったユーザーイベントの扱いが一つのクラスに集約され、すっきりと統一されました。AS2 の Key や Mouse といったグローバルオブジェクトによるユーザーイベントのハンドリングは AS3 にはありません。

イベントモデル自体も、新しくなっています。AS3 の新しいイベントモデルについては、重要なトピックですので別の機会に改めて詳しく説明したいと思っています。今回はとりあえず主なイベントの種類だけざっと触れておきます。

InteractiveObject がサポートするイベントは、AS2 では MovieClip や Button が持っていたイベントを引き継ぐものや、

mouseDown       : マウスボタンを押した
mouseUp         : マウスボタンを離した
keyDown         : キーを押した
keyUp           : キーを離した
focusIn         : オブジェクトがフォーカスされた
focusOut        : オブジェクトがフォーカスを失くした

V2 UIComponent のイベントと同じもの、または新規追加(ダブルクリックなど)されたものがあります。

click           : マウスがクリックされた
doubleClick     : マウスがダブルクリックされた
keyFocusChange  : キー操作でフォーカスを変えようとした

また、InteractiveOjbect には、右クリックをしたときに表示されるコンテキストメニューを InteractiveObject.contextMenu プロパティに設定することが出来ます。ただ、相変わらずトップレベルのオブジェクトにしか有効ではないようです。

Posted by ackie at 06:17 PM | Comments (0)

October 28, 2005

Bitmap クラスと Shape クラス

Bitmap クラス

Bitmap クラスはビットマップ画像を表示するのに使用されます。(livedocs@lab) Bitmap クラスの役割は表示位置やフィルターの種類の指定などの表示状態を指定することで、実際のビットマップデータは Bitmap.bitmapData プロパティから参照される BitmapData クラスのインスタンスが持ちます。Bitmap はあくまで容れ物ということです。

BitmapData クラスについては Flash8 から追加されたものと基本的に同じです。(livedocs@lab) 描画オブジェクトのスナップショットを取ったり、様々な加工を施すことができます。AS1/2 では、MovieClip に attach して使用していましたが、AS3 では Bitmap が MovieClip の代わりをします。

BitmapData のインスタンスは複数の Bitmap オブジェクトから共有できるため、画面にたくさんのビットマップ画像を表示しても、メモリを効率的に使用できるようになっています。

Shape クラス

Shape クラス(livedocs@lab)はベクターグラフィックの表示が担当です。Bitmap クラスと同じように、実際のデータは Shape.graphics というプロパティに持ちます。このプロパティのデータ型は Graphics クラスです。

Graphic クラス(livedocs@lab)は以下のように描画用の API を持っています。これらの API は、AS1/2 では MovieClip のメソッドとして実装されていましたが、AS3 では専用のクラスが用意されました。

 public moveTo(x:Number, y:Number) : Void 
 public lineTo(x:Number, y:Number) : Void 
 public beginFill(color:uint, alpha:Numer = 1.0) : Void 
 public clear() : Void 

また、新しく便利な API もいろいろと追加されています。

 public drawRect(x:Number, y:Number, width:Number, height:Number) : Void 
 public drawCircle(x:Number, y:Number, radius:Number) : Void 

Posted by ackie at 07:24 PM | Comments (0)

October 27, 2005

DisplayObject クラス

ActionScript3 では、API が大幅にリパッケージングされています。これは、描画機能が集約された重たい MovieClip への依存からの脱却が目的の一つで、描画関連の機能にも新しい考え方やクラスが導入されています。AS3 を使うにはこれらのクラスを理解しなくてはなりません。そこで、AS3 の新しいモデルについて、少し説明をしたいと思います。まずは少し抽象的な話から始めます。

注:以下の内容は、パブリックアルファ版に基づくもので、製品版までには変更されることがあります。

DisplayObjct クラス

DisplayObject は ActionScript3 から新しく導入されたクラスで、画面に表示される全てのオブジェクトのベースとなる重要なクラスです。プロパティを見ると、以下のような、表示の様子を指定する基本的なプロパティを持っていることが分かります。(livedocs@lab)

x            // インスタンスのX座標
y            // インスタンスのY座標
height       // インスタンスの高さ
width        // インスタンスの幅
alpha        // インスタンスのアルファ値

ActionScript1/2 では、これらのプロパティは MovieClip 等に属していましたが、AS3 では DisplayObject のプロパティになっています。また、名前から '_' (アンダースコア)がなくなっていることにも注意してください。

DisplayObject のプロパティには、Flash8 から追加された高度な表現を指定するものもあります。

scale9Grid    // インスタンスのX座標
blendMode     // ブレンドモードを指定
filters	      // フィルターオブジェクトの配列
cacheAsBitmap // ビットマップ形式で表示データを保持するフラグ

このように、DisplayObject は表現を指定するのに必要な機能が集められたクラスです。一方インタラクションを提供する機能は含まれていません。また、抽象クラスなので直接インスタンスかすることは出来ません。

DisplayObject の主なサブクラスとして BitMap や Shape があります。それぞれ

  • Bitmap : ビットマップデータを表示する
  • Shape : ベクターグラフィックスを表示する

という目的で使用されます。これらのクラスは MovieClip より軽量であるため、描画処理の高速化に貢献することが期待できます。

Posted by ackie at 08:47 PM | Comments (0)