プログラマのためのFlash遊び方

第8回Flexで本格Webアプリケーションを作ってみよう

前回まではActionScript 3.0を利用したプログラミングを解説してきました。ActionScript 3.0はグラフィカルな表示には強いのですが、機能的なWebアプリケーションを作るのにはあまり向いていません。

Flashでアプリケーションを作る場合は、Flexというフレームワークが便利です。Flexと聞くとお金が必要なイメージがあるかもしれませんが、FlexはFlex 3 SDKに付属する無料のフレームワークです。

Flexフレームワークには便利なコンポーネントが用意されています。また、MXMLと呼ばれるXMLにもとづいたフォーマットでアプリケーションの見た目を記述することができます。今回は、Flexフレームワークの使い方を簡単にご紹介していきます。

MXMLでコンポーネントを配置

早速サンプルを見てみましょう。MXMLでアプリケーションの見た目を記述してみます。

<?xml version="1.0" encoding="UTF-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Panel title="Hello Flex!!"
        paddingTop="10" paddingLeft="10"
        paddingRight="10" paddingBottom="10">
        <mx:Label text="ラベル"/>
        <mx:TextInput text="text"/>
        <mx:Button label="OK"/>
    </mx:Panel>
</mx:Application>

まず、アプリケーションのルートタグとして<mx:Application>タグを記述しています。これは、MXMLで記述する際のお約束です。

<mx:Application>タグの中には<mx:Panel>タグ、さらにその子タグとして、<mx:Label>タグ、<mx:TextInput>タグ、<mx:Button>タグが配置されています。それぞれのタグには、⁠text="ラベル"」のように、属性の形でパラメータを指定しています。

このように、XMLの形式でコンポーネントの構造を記述します。HTMLに似ていますね。

コンパイルしてみよう

このファイルをFlexTest1.mxmlというファイル名で保存します。文字コードをUTF-8で保存するのを忘れないでください。

コンパイルするには、今までと同じくmxmlcを利用します。

mxmlc FlexTest1.mxml

コンパイルに成功すると、FlexTest1.swfというファイルが生成されます。

「Hello Flex!!」の枠が<mx:Panel>タグで記述したPanelコンポーネントです。Panelコンポーネントの中には、縦に3つ、Labelコンポーネント、TextInputコンポーネント、Buttonコンポーネントが並んでいます。

それぞれのコンポーネントで指定できる属性については、Flex3 リファレンスガイドを参照してください。例えば、Buttonコンポーネントであれば、mx.controlsパッケージのButtonクラスが該当します。パブリックプロパティとイベントに列挙されている名前をそのままMXMLの属性として指定できます。

このように直感的にコンポーネントを配置できるのがMXMLの魅力です。もし、これをActionScriptだけでやろうとすると、次のようになってしまいます。

<?xml version="1.0" encoding="UTF-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    applicationComplete="init()">
    <mx:Script>
        <![CDATA[
            import mx.containers.Panel;
            import mx.controls.*;
            
            private function init():void {
                var panel:Panel = new Panel();
                panel.title = "Hello Flex!!";
                panel.setStyle("paddingTop", 10);
                panel.setStyle("paddingLeft", 10);
                panel.setStyle("paddingBottom", 10);
                panel.setStyle("paddingRight", 10);
                addChild(panel);

                var label1:Label = new Label();
                label1.text = "ラベル";
                panel.addChild(label1);

                var text1:TextInput = new TextInput();
                text1.text = "text";
                panel.addChild(text1);

                var button1:Button = new Button();
                button1.label = "OK";
                panel.addChild(button1);
            }
        ]]>
    </mx:Script>
</mx:Application>

だいぶ冗長ですね。改めて、最初のMXMLと見比べると、MXMLで書くとシンプルになるのがよく分かります。

イベントを登録してみよう

それでは、ボタンが押されたときに、何か処理をするように改造してみましょう。

<?xml version="1.0" encoding="UTF-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            private function clickHandler():void{
                input1.text = "clicked!!";
            }
        ]]>
    </mx:Script>
    <mx:Panel title="Hello Flex!!"
        paddingTop="10" paddingLeft="10"
        paddingRight="10" paddingBottom="10">
        <mx:Label text="ラベル"/>
        <mx:TextInput id="input1" text="text"/>
        <mx:Button label="OK" click="clickHandler()"/>
    </mx:Panel>
</mx:Application>

完成したのがこのようなFlashです。最初の例と見た目は同じですが、ボタンをクリックすると、TextInputの中身が書き換わります。

イベント登録は、<mx:Button>タグのclick属性で行っています。click属性の中には、clickイベントが発生したときの処理を書いています。ここではclickHandler()メソッドを呼んでいます。

    <mx:Button label="OK" click="clickHandler()"/>

ActionScriptのコードは、MXMLの<mx:Script>タグで定義しています。CDATAで囲っているのは、スクリプトの中で<や>を利用するためです。

clickHandler()メソッドでは、TextInputオブジェクトの文字列を書き換えています。input1というのは、TextInputに設定したidです。idを設定したコンポーネントは、MXMLによって生成されるクラスのインスタンス変数として参照できるようになります。つまり、input1.textというのは、TextInputオブジェクトの中身の文字列を表すことになります。

dataProviderを使ってデータを表示する

Flexの数あるコンポーネントの中でも強力なのが、TreeコンポーネントやDataGridコンポーネントです。これらのコンポーネントは、dataProviderというプロパティにXMLやオブジェクトを渡すだけで中身を表示するという、面白い特徴を持っています。

Treeコンポーネントを使う

まずは、Treeコンポーネントを使ってみましょう。Treeコンポーネントは階層構造を表示するためのコンポーネントです。

<?xml version="1.0" encoding="UTF-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    applicationComplete="init()">
    <mx:Script>
        <![CDATA[
            private function init():void {
                var xml:XML = <node label="ルート">
                    <node label="1">
                        <node label="1-1"/>
                        <node label="1-2"/>
                    </node>
                    <node label="2"/>
                </node>;

                tree1.dataProvider = xml;
            }
        ]]>
    </mx:Script>
    <mx:Tree id="tree1" width="200" height="100"
        labelField="@label"/>
</mx:Application>

<mx:Application>タグでappCompleteイベントが発生したときにinit()メソッドが呼ばれるようにイベント登録しています。appCompleteはアプリケーションの初期化が完了したときに1度だけ発生するイベントです。

init()メソッドでは、xmlという変数にXMLを格納しています。ActionScript 3.0では、このようにソースコードの中にXMLを記述できます(ECMAScriptでXMLを扱うためのE4Xという仕様で定義されている機能です⁠⁠。

次に、TreeコンポーネントのdataProviderプロパティにxmlを設定しています。結果として、XMLの構造と同じツリー構造ができあがります。

<mx:Tree>タグでは、labelFieldプロパティが@labelに設定されているため、XMLのlabel属性の値がツリーに表示されています(@labelの@はE4Xで属性のことを表します⁠⁠。

XMLをサーバからロードする

先ほどはXMLをソースコードに埋め込みましたが、サーバから動的にロードするようにしてみましょう。

データをロードするには、ActionScript 3.0のURLLoaderクラスを利用します。先ほどのソースコードの、<mx:Script>タグの中を次のように書き換えました。

    import flash.events.Event;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    private function init():void {
        var req:URLRequest = new URLRequest("test.xml");

        var loader:URLLoader = new URLLoader();
        loader.load(req);
        loader.addEventListener("complete", function(event:Event):void {
            tree1.dataProvider = XML(loader.data);
        });
    }

XMLファイルは次のような内容です。

<node label="ルート">
    <node label="1">
        <node label="1-1"/>
        <node label="1-2"/>
    </node>
    <node label="2"/>
</node>

Flash同じ場所にtest.xmlとして保存します。文字コードをUTF-8にするのを忘れないでください。

これで、サーバのXMLを動的に読み込んで表示するようになりました。試しに、XMLを書き換えてFlashを再度表示してみると、変更後のXMLに応じた表示に変わっていると思います。

CGIを使ってtest.xmlの表示を切り替えるようにすれば、すぐにでも動的なコンテンツが作れそうですね。

DataGridコンポーネントを使う

次は表形式のデータを表示するDataGridコンポーネントです。

<?xml version="1.0" encoding="UTF-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    applicationComplete="init()">
    <mx:Script>
        <![CDATA[
            private function init():void {
                var xml:XML = <data>
                    <item name="名前1" value="値1"/>
                    <item name="名前2" value="値2"/>
                    <item name="名前3" value="値3"/>
                </data>;

                grid1.dataProvider = xml.item;
            }
        ]]>
    </mx:Script>
    <mx:DataGrid id="grid1" width="200" height="100">
        <mx:columns>
            <mx:DataGridColumn dataField="@name" headerText="名前"/>
            <mx:DataGridColumn dataField="@value" headerText="値"/>
        </mx:columns>
    </mx:DataGrid>
</mx:Application>

<mx:DataGrid>タグの子タグとして、列の設定を記述しています。ここでは、⁠名前」「値」の2つの列を表示します。dataField属性はそれぞれ、@nameと@valueに設定しています。そのため、XMLのname属性とvalue属性がそれぞれの列に表示されます。

先ほどと同じように、init()メソッドでXMLをDataGridオブジェクトのdataProviderプロパティに設定しています。xml.itemというのは、XMLの子ノードのうち、itemノードの一覧を表すE4Xの表現です。

この結果、次のような表示になります。

行のヘッダ部分をクリックするとソートされますし、ヘッダ部分をドラッグして列を並び替えることもできます。とても高機能ですね。

このように簡単に見栄えよくデータを表示できるところがFlexのコンポーネントの大きな魅力です。

自作コンポーネントを作る

最後に自作コンポーネントを作る方法を簡単にご紹介しましょう。前回作った「ロゴジェネレータ」をFlexのコンポーネントにしてみましょう。

まずは完成形のFlashをご覧ください。次のようになります。

フォントサイズを動的に変えられるようになりました。前回よりもアプリケーションっぽくなりましたね。

ソースコードは次の3つのファイルから成り立っています。

ビルドするには、3つのファイルを同じフォルダにダウンロードして、ComponentTest.mxmlをコンパイルしてください。

mxmlc ComponentTest.mxml

LogoGeneratorコンポーネントの埋め込み

それでは、MXMLから見ていきましょう。今までのものより複雑になっているので、LogoGeneratorコンポーネントを利用しているところに注目します。

<?xml version="1.0" encoding="UTF-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:comp="*">                         <!-- (1) -->
    <mx:Panel width="100%" height="100%"
        title="Logo Generator (Flex version)"
             :
             :
        <mx:Canvas width="100%" height="100%"
            borderColor="0xcccccc" borderStyle="solid">
            <comp:LogoGenerator id="logo"/> <!-- (2) -->
        </mx:Canvas>
    </mx:Panel>
</mx:Application>

<mx:Application>タグに「xmlns:comp="*"」という属性が追加されています(1⁠⁠。これは、トップレベルのパッケージ空間のコンポーネントをcompという名前空間で利用することを意味します。

(2)では先ほど定義したcomp名前空間を利用して、LogoGeneratorコンポーネントを埋め込んでいます。

TextInputコンポーネントとHSliderコンポーネントでは、値が変更された際に、LogoGeneratorコンポーネントのプロパティを更新しています。

TextInput の change イベント処理
    <mx:TextInput id="input1" text="Logo Generator"
             :
        change="logo.text = input1.text"/>
HSliderのchangeイベント処理
    <mx:HSlider id="slider"
             :
        change="logo.fontSize = slider.value"/>

全てのコンポーネントはUIComponentクラスを継承する

次に、LogoGeneratorクラスをFlexのコンポーネントにしていきます。

前回作成したLogoGeneratorクラスはSpriteを継承していましたが、このままではエラーになってしまいます。Flexのコンポーネントは、全てUIComponentクラスを継承する必要があります。LogoGeneratorクラスの宣言を次のように書き換えます。

    import mx.core.UIComponent;

    // LogoGenerator クラス
    public class LogoGenerator extends UIComponent {

プロパティの定義

コンポーネントらしくするために、表示する文字列を表すtextプロパティと、フォントサイズを表すfontSizeプロパティを実装します。

textプロパティは次のように定義しています。

    // text プロパティの実体
    private var _text:String;                     // (1)

    // text プロパティの getter
    public function get text():String {           // (2)
        return _text;
    }

    // text プロパティの setter
    public function set text(value:String):void { // (3)
        _text = value;
        update();
    }

プロパティは、実体を表すインスタンス変数(1)とgetter(2⁠⁠、setter(3)から成り立っています。

getterはtextプロパティを取得するときに呼ばれるメソッドです。functionのあとに、⁠get」というキーワードをつけて宣言します。ここではgetterは_textの値をそのまま返すように定義しています。

setterはtextプロパティの値に代入されるときに呼ばれるメソッドです。functionのあとに「set」をつけて宣言します。ここでは、代入された値を_textに上書きしたあと、update()メソッドを呼び出して表示を更新しています。

update()メソッドの修正

update()メソッドはtextプロパティの値を表示するように、fontSizeプロパティの値に応じて文字の大きさを変えるように修正しています。

また、update()メソッドの最後でinvalidateSize()メソッドを呼び出しているところに注目です。invalidateSize()は、コンポーネントのサイズが変わったことをFlexフレームワークに通知するためのメソッドです。

Flexフレームワークはサイズが変わったという通知を受けると、コンポーネントのmeasure()メソッドを呼び出します。measure()はUIComponentから継承するメソッドです。デフォルトでは何もしないメソッドなので、コンポーネントのサイズを適切に設定できるようoverrideキーワードをつけて再定義します。

    protected override function measure():void {
        measuredWidth = badge.x + badge.width;
        measuredHeight = reflection.y + reflection.height;
    }

measuredWidthおよびmeasuredHeightプロパティにサイズを設定することで、正しくコンポーネントのサイズがフレームワークに通知されます。その結果として、フォントサイズを変更すると、適切にスクロールバーが表示されるようになります。

まとめ

だいぶ駆け足でしたが、Flex 3フレームワークの特徴的な機能を紹介しました。

Flexはコンポーネントも豊富で奥も深いので、詳しく学習されたい方は、Adobe-Flex:Flexドキュメンテーション「Flex 3開発ガイド」「Adobe Flex 3コンポーネントの作成と拡張」などを参考にしてみるとよいでしょう。

次回は最終回です。今、注目のAIRをご紹介します。

おすすめ記事

記事・ニュース一覧