Flex の Binding 具体例と内部事情の覗き見

昨日の Flex の Bindable 入門 の続きです。

Binding を MXML で具体的に説明してみます。

データバインディングを使わなかったら

データバインディングを使わない場合を MXML で書いてみました。

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  applicationComplete="init()">
    <mx:Script>
    private function init():void
    {
        input1.addEventListener("change", onchange);
    }

    private function onchange(e:Event):void
    {
        label1.text = "Input text: " + input1.text;
    }
    </mx:Script>

    <mx:TextInput id="input1" />
    <mx:Label id="label1" />
</mx:Application>

applicationComplete は JavaScript でいう onload みたいなものです。このなかで init 関数を呼び出して初期化を行います。

init 関数の中は、input1 に対して addEventListener でイベント登録してます。「input1 の中身に変化があったら onchange 関数に通知してね」とお願いしてるわけです。

ハンドラ(onchange 関数)の中では、input1 に入力された文字の前に "Input text: " を加えて、label1 に表示してます。

テキストボックスに入力した内容が、即座に表示される、という簡単な SWF です。

動作イメージ

データバインディングを使ってみる

これを Binding を使って書き直してみるとこうなります。

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  applicationComplete="init()">
    <mx:Script>
    private function init():void
    {
    }
    </mx:Script>

    <mx:TextInput id="input1" />
    <mx:Label id="label1" text="Input text: {input1.text}" />
</mx:Application>

Flex の Bindable 入門 では、監視したいデータに [Bindable] をつけましょう、と説明しました。ここでは、input1 の text プロパティを監視したいのですが、実は text プロパティは [Bindable] がついた状態で定義されています。text プロパティは、変更を他に通知する前提で作られている、ということです。

このことはFlex リファレンス ガイドでも確認できます。

リファレンス

次は通知を受け取る側です。受け取る側は中括弧をつけて {mySlider.value} のようにやる、と説明しました。ここでは、input1 の text プロパティを label1 の text プロパティに反映させるわけですから

    <mx:Label id="label1" text="Input text: {input1.text}" />

となります。

いかがでしょう。1箇所だけだとあまり効果ははっきりしませんが、複数個所になってくると効果は歴然です。中括弧の中では関数呼び出しや演算などもできるので、自由度はかなり高いです。

技術的に掘り下げてみる

ここからはプログラマ向け。

中括弧で囲んだ場所では、いったいどういうことになってるんでしょうか。

mxmlc のコンパイルオプションに -keep をつけると、MXML がどういう ActionScript に変換されているのかを知ることができます。

MXML のファイル名が test.mxml だとすると generated/test-generated.as に MXML の変換結果が出力されます。

データ変更のイベントを受け取る部分を抜粋してみます。

    private function _test_bindingsSetup():void
    {
        // 中略

        var binding:Binding;

        binding = new mx.binding.Binding(this,
            function():String
            {
                var result:* = "Input text: " + (input1.text);
                var stringResult:String = (result == undefined ? null : String(result));
                return stringResult;
            },
            function(_sourceFunctionReturnValue:String):void
            {
                label1.text = _sourceFunctionReturnValue;
            },
            "label1.text");
        _bindings[0] = binding;
    }

mx.binding.Binding クラスが出てきて複雑ですが、イベントを受け取ったときの処理を Binding クラスを使ってと登録しています。処理内容は第2引数の関数を実行して、その戻り値を第3引数に渡す、という流れです。

ソースをさらに追うと、第4引数は mx/binding/BindingManager.as の中で力技でパースしていたりして面白いのですが、なにせ Flex SDK のソースは分量も多くて複雑なので、なかなか全体像は把握しきれません。興味のある人は SDK の frameworks/source/mx の中にソースがあるので、ぜひ挑戦してみてください。