<mx:Component>の使い道

by matacat
♥1 | Line 202 | Modified 2010-03-09 07:21:06 | MIT License
play

ActionScript3 source code

/**
 * Copyright matacat ( http://wonderfl.net/user/matacat )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/e3tRH
 */

<?xml version="1.0" encoding="utf-8"?>

<!--
    注意: ネットに散在する産地不明な情報の寄せ集めに未熟者に
    よる邪推といかにもそれらしい理屈を存分に練り込みこねくりまわして
    ペースト状になった以下の注釈を踊り食いしないでください。
-->

<!--
     <mx:Component> タグを使ってメインの MXML にカスタム
    コンポーネントなどを記述しようというおはなし。
    
     これから述べる Component タグの使い方は Flex 開発者の
    ブログや BBS 、 wonderfl のソースにもちらほらと見受けられる
    のですが、 Adobe の各種ドキュメントを探してもこのタグの基本的な
    使用法ばかりで、応用についてまとまった情報は得られず。
     そこで、今回 Component タグについて得た知識をレポートとして
    まとめ、ここに残そうと決意した次第です。業務用アプリなどには
    間違っても応用できませんが、 wonderfl では十分実用的でしょう。
    
     普段はインラインアイテムレンダラー&エディタの子要素として登場
    する Component タグ。これは独立した Flex コンポーネントと
    して定義されているのではなく、コンパイラに「こういう実装のカスタム
    コンポーネントを用意してくれ」と命令するタグなのです。 AS3
    コンポーネントリファレンスガイドでも「MXML のみ」という、こじんまり
    した項目に隔離されています。
    
     記述の際に className 属性を指定することで、その名前を持つ
    コンポーネント(クラスとも呼べる)をその場で作ることができます。
    ちょうど ActionScript のソースで package ディレクティブの
    外側にプライベートなクラスを宣言するのと同じ感覚です。 new
    演算子でインスタンスも作れます。
     ローカルな名前空間を開いておけば MXML タグとしても記述でき
    ます。ただし、ビジュアルコンポーネントの子タグとして配置するには
    UIComponent を継承するか IUIComponent を実装しないと
    いけないようです。
-->

<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:lo="*"
    layout="absolute"
    applicationComplete="PopUpManager.addPopUp(pn, pn.parent)">
    
    <mx:Script><![CDATA[
        import mx.managers.PopUpManager;
        import mx.core.UIComponent;
    ]]></mx:Script>
    
    
<!--
     最初にもっともそれらしい使い方。
     外部 MXML ファイルにカスタムコンポーネントを記述するのと同じ
    要領で、 Flex コンポーネントを配置したりなどします。インライン
    アイテムレンダラー&エディタでも内実はこんな感じかと思われます。
     Component タグの中では独自のスコープが設定されます。
    ドキュメントクラス(?)のメンバにアクセスするには
    outerDocument キーワードを使わないといけないらしいのですが、
    trace してみても null でいまいちよくわからないのが現状です…
     ちなみに以下の使用例はこちらから引っ張ってきました。
    
        HLS <-> RGB <-> HSV
        http://wonderfl.net/code/5e6e4602edef120b1883480bbce2f6e329950b78
-->
    
    <mx:Component className="HSliderNumstep">
        <mx:HBox borderStyle="solid" verticalAlign="middle" horizontalAlign="right"
            paddingTop="5" paddingBottom="5" paddingLeft="5" paddingRight="5"
            creationComplete="init()">
            
            <mx:Metadata>
                [Event(name="change", type="flash.events.Event")]
            </mx:Metadata>
            
            <mx:Script><![CDATA[
                
                public var title:String = "";
                [Bindable] public var value:Number = 0;
                public var min:Number = 0;
                public var max:Number = 1;
                public var step:Number = 0.01;
                public var slideTick:Number = 0.1;
                public var slideLabels:Array = ["0", "0.5", "1"];
                
                private var e:Event = new Event("change");
                
                private function init():void
                {
                    lab.text = title;
                    sld.value = num.value = value;
                    sld.minimum = num.minimum = min;
                    sld.maximum = num.maximum = max;
                    sld.snapInterval = num.stepSize = step;
                    sld.tickInterval = slideTick;
                    sld.labels = slideLabels;
                }
                
            ]]></mx:Script>
            
            <mx:Label id="lab" />
            <mx:HSlider id="sld" value="{value}" liveDragging="true" change="value = sld.value; dispatchEvent(e)" />
            <mx:NumericStepper id="num" value="{value}" width="60" change="value = num.value; dispatchEvent(e)" />
        </mx:HBox>
    </mx:Component>
    
    <!-- 実装例 -->
    <mx:Panel id="pn" title="&lt;mx:Component&gt; test" y="110"
        paddingTop="5" paddingBottom="5" paddingLeft="5" paddingRight="5">
        <mx:TextArea id="tx" editable="false" width="100%" height="120" />
        <mx:Button label="reset" click="tx.text = ''; hsn.value = 0" />
        <lo:HSliderNumstep id="hsn" title="test:" change="tx.text += 'new value: ' + hsn.value + '\n'" />
    </mx:Panel>
    
    
<!--
     次に若干無理のある使い方。
     Component タグで定義されるカスタムコンポーネントは、直下の
    コンポーネントを継承したものとして定義されます。以下の例は
    UIComponent のサブクラスを宣言するのと同等です。
     preinitialize, initialize, creationComplete
    などの各種初期化イベントが拾えるので、ハンドラを記述することで
    コンストラクタの代わりとすることができます。
-->
    
    <mx:Component className="bmp">
        <mx:UIComponent width="100" height="100" initialize="init()" click="clicked()">
            <mx:Script><![CDATA[
                
                import mx.controls.TextArea;
                
                public var outPut:TextArea;
                
                private function init():void
                {
                    b.bitmapData = new BitmapData(50, 50, true, 0x7F000000);
                    b.x = this.width - b.width >> 1;
                    b.y = this.height - b.height >> 1;
                    addChild(b);
                }
                
                private function clicked():void
                {
                    if (outPut) outPut.text += "Bitmap is clicked.\n";
                }
                
            ]]></mx:Script>
            <Bitmap xmlns="flash.display.*" id="b" />
        </mx:UIComponent>
    </mx:Component>
    
    <!-- 実装例 -->
    <lo:bmp outPut="{tx}" x="0" />
    
    
<!--
     そして壊滅的に使えない使い方。
     この例では TextField のサブクラスを宣言していますが、
    MXML タグとして記述しても画面には現れません。表示させるには
    UIComponent やそのサブクラスのインスタンスに addChild
    しなければなりません。
     MXML タグとして記述する際に初期化の機会を得るには、
    IMXMLObject の initialized メソッドを実装します。実際は
    手前で初期化メソッドを用意し、外部からそれを呼び出した方が使い
    勝手がいいのですが。
     ここで注意すべき点として、そもそも <mx:Script> タグ内では
    コンストラクタを記述できません。つまり、この Component タグに
    頼る方法ではカスタムイベントなどを作ることができません。なぜなら
    Event コンストラクタの第1パラメータ "type" が必須パラメータ
    であるにもかかわらず、値を渡す手段がないためです。
-->
    
    <mx:Component className="tf">
        <TextField xmlns="flash.text.*" implements="mx.core.IMXMLObject">
            <mx:Script><![CDATA[
                
                import mx.core.UIComponent;
                import mx.controls.TextArea;
                
                public var target:UIComponent;
                public var outPut:TextArea;
                
                // IMXMLObjectの実装(ここでは形だけ)
                public function initialized(document:Object, id:String):void
                {
                    if (target && outPut) init(target, outPut);
                }
                
                public function init(target:UIComponent, outPut:TextArea):void
                {
                    type = "input";
                    border = true;
                    wordWrap = true;
                    target.addChild(this);
                    
                    addEventListener(KeyboardEvent.KEY_UP, function (e:KeyboardEvent):void
                    {
                        if (e.keyCode == 13) {
                            outPut.text += text + "\n";
                            text = "";
                        }
                    });
                }
                
            ]]></mx:Script>
        </TextField>
    </mx:Component>
    
    <!-- 実装例 -->
    <mx:UIComponent x="100" creationComplete="new tf().init(UIComponent(event.target), tx)" />
    <mx:UIComponent id="cp" x="200" creationComplete="cpInit()" />
    <lo:tf id="tfin" target="{cp}" outPut="{tx}" />
<!--
    と書きたいところですが、initialized メソッドが実行される
    タイミングでは cp も tx も null のようなので、条件文により
    init メソッドは実行されません。しかしインスタンスは存在するので
    addChild すれば救済の余地はあります。
-->
    
    <mx:Script><![CDATA[
        private function cpInit():void
        {
            tfin.type = "input";
            tfin.background = true;
            tfin.backgroundColor = 0xCFCFCF;
            tfin.multiline = true;
            cp.addChild(tfin);
            //イベントリスナ割愛
        }
    ]]></mx:Script>
    
</mx:Application>

<!--
     いかがでしたでしょうか。 MXML のそもそもの存在意義や業界の
    ベストプラクティスからは逸脱した使い方でしたが、ひとつのファイルに
    すべてを記述しなければならない wonderfl の独特な環境では
    逆に活躍する利用法でしょう。
     最後にもうひとつ。コンパイラオプションで "-keep" を指定すると、
    コンパイル中に中間生成される .as ファイルを generated
    ディレクトリに書きだしてくれるのですが、これを眺めると MXML が
    どのように内部処理されているのかがちょっとわかった気になれます。
     以上、 Component タグの応用についてのレポートでした。次の
    機会があればまた。
                                2010/03/08 matacat
-->