akihiro kamijo: April 2006 Archives

« March 2006 | Main | May 2006 »

April 22, 2006

Flash Player 9

特に何が変わったというわけではありませんが、Flash Player 8.5 と呼ばれていた次期 Player の名前が Flash Player 9 に変わりました。もうすぐ公開予定のベータ 3 からこの名前になるとのことです。

Adobe Labs の wiki にある FAQ によると、非常に大きな変更のあるバージョンアップであることをはっきりと伝えたいというのが主な理由のようです(Labs の FAQ はこちら)。

Posted by ackie at 10:45 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 17, 2006

Flash 8 Hot fix

IE6 の仕様変更に対応した新しいパブリッシュテンプレートをサポートする Flash Professional 8 および Flash Basic 8 用 Hot Fix が公開されました。こちらからダウンロードできます。( ZIP ファイル 17K

インストール方法および使用方法は ZIP ファイル内の Readme2.html に日本語で記述されています。(日本語以外のバージョンは US の Flash サポートサイトから可能です - Flash Support Center/Downloads

また、アクティブコンテンツ関連の情報も日本のサイトで公開されました。(アクティブコンテンツセンター) こちらもあわせてご覧ください。

Posted by ackie at 02:45 PM | Comments (5)

April 14, 2006

Flash OOP の会(4/14)

今日は、Flash OOP の会がありました(というかまだやってます)。Flash を日々使用している人に興味があるようなのは、やはり AS3 によって今の制作ワークフローがどう変わるかということのようですので、AS1/2 と AS3 の関係について、ちょっと書いておきたいと思います。

現在 AS3 のアプリケーションを作れるのは Flex Builder 2 のみです。また、Flash 8 では AS3 のクラスを使用することができません。従って、現時点で可能な組み合わせは、Flash 8 で作成した AS1/2 SWF を AS3 アプリケーションに組み込む場合のみということになります。

さて、AS3 アプリに AS1/2 swf を組み込むには以下の2つの方法があります。

  1. AS3 のクラス定義内にコンパイル時に AS1/2 swf を埋め込む宣言を記述する
  2. 実行時に AS3 の Loader コンポーネントを使って AS1/2 swf を読み込む

1の埋め込みの場合は以下のように記述します。この方法では、AS1/2 で記述されたスクリプトは全て削除されるようです(少なくとも現時点)。

// MyTest.as の中
package{
  import flash.display.MovieClip;
  [Embed(source="mytest.swf")]
  public class MyTest extends MovieClip {}
}

どこまでサポートされるかは未確認ですが、上記のように AS1/2 の swf を埋め込んで、MyTest のインスタンスを addChild() すると play() や stop() 程度は使えるようです。

2の Loader を使用する方法では AS1/2 のコードも実行されます。ただし、AS3 の swf と AS1/2 の swf 間ではクロススクリプティングができません。相互のやり取りはローカルコネクションを通してのみになります(これも現時点)。

Posted by ackie at 08:31 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)

April 08, 2006

IE6 の仕様変更への対応関連情報です

IE6 のアクティブコンテンツ関連の動作を変更するパッチが公開されましたが(関連情報)、それに伴い、対応方法に関連する記事がUS のアクティブコンテンツデベロッパーセンターにて公開されました。

IE の変更に対応するためのサンプルファイルはこちらからダウンロードできます。( ZIP ファイル 4K) これには以下のファイルが含まれています。

  • AC_RunActiveContent.js: Flash や Shockwave コンテンツを表示する際に使用します
  • AC_ActiveX.js: その他のアクティブコンテンツ用です
  • SampleActiveContent.html: 上2つのJavaScript ファイルの使用例です

swf を表示するにはは AC_RunActiveContent.js ファイル内に定義されている AC_FL_RunContent 関数を Object タグの代わりに使用します。関数の引数にはパラメータの名前と属性を交互に記述します。

例として以下のような HTML ファイルがあるとします。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>Before</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  </head>
  <body>
    <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
    codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0"
    width="369" height="113">
    <param name="movie" value="flash.swf">
    <param name="quality" value="high">
    <embed src="flash.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="369" height="113"/>
    </object>
  </body>
</html>

これを書き直すと以下のようになります。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>After</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <script type="text/javascript" src="after/AC_RunActiveContent.js"></script>
  </head>
  <body>
    <script type="text/javascript">
      AC_FL_RunContent(
      'codebase','http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0',
      'width','369',
      'height','113',
      'src','flash',
      'quality','high',
      'pluginspage','http://www.macromedia.com/go/getflashplayer',
      'movie','flash' );
    </script>
    <noscript>
      <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
      codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0"
      width="369"
      height="113">
      <param name="movie" value="flash.swf">
      <param name="quality" value="high">
      <embed src="flash.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="369" height="113"/>
      </object>
    </noscript>
  </body> 
</html>

Posted by ackie at 11:32 PM | Comments (8)