flash - ActionScript (前編)

今回はFlashアプリの作り方を解説します。flashを利用するとリッチで動的なコンテンツをユーザに提供可能なので、さまざまなマルチメディアコンテンツに利用されています。華やかな企業のホームページもflashで作られていますが、今回はウエブアプリケーションという面に重点を置いて解説したいと思います。flashは一般的なPCブラウザに組み込まれたadobeFlashPlayer上で動作するアプリケーションで、Adobe Flashという製品を用いて開発することが一般的ですが、ActionScriptFlexSDKを用いてコンパイルしてflashアプリケーションを開発することも可能です。またFlexSDKを開発する際の統合開発環境としてFlexBuilderというソフトもあります。無料体験版およびアカデミック無料版があります(通常8万以上する製品が無料で入手可能なので学生さんはお得です)。現在はAdobeAIRという形でウエブブラウザへの組み込みなしで(ディスクトップアプリとして)flashアプリを動作させることも可能になりました。

flashプログラミング

flashプログラミングはActionScriptをベースにFlexライブラリをimportしてコーディングするスタイルがありますその方法を用いてHelloWorldプログラムを作った場合の例は

bash-3.2$ cat prog10/helloworld/HelloWorld1.as 
package {
    import flash.display.Sprite;
    import flash.text.TextField;
    public class HelloWorld1 extends Sprite {
        public function HelloWorld1(){
            var tf:TextField = new TextField;
            tf.text = "Hello World";
            addChild(tf);
        }
    }
}

となりコンパイラであるmxmlcコンパイルすると

bash-3.2$ /Applications/Adobe\ Flex\ Builder\ 3/sdks/3.0.0/bin/mxmlc HelloWorld1.as
設定ファイル "/Applications/Adobe Flex Builder 3/sdks/3.0.0/frameworks/flex-config.xml" をロードしています
/Applications/MAMP/htdocs/prog10/helloworld/HelloWorld1.swf (621 bytes)

とHelloWorld1.swf が作成されます(mxmlcのパスは上記例はFlexBuilderをデフォルトでインストールした場所)
もう一つの方法としてMXMLと呼ばれるファイルを基本としてActionScriptを利用してプログラムを記述します。
MXMLXML言語で記述され、アプリケーションのユーザインタフェースを配置することができます。

bash-3.2$ cat prog10/helloworld/HelloWorld2.mxml 
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init();">
    <mx:Script>
        <![CDATA[
            private function init():void{
                tf.text = "Hello World";
            }
        ]]>
    </mx:Script>
    <mx:Text id="tf" />
</mx:Application>

MXMLを記述したら同じくコンパイルすることによって swf (flashファイル)を作成します

bash-3.2$ /Applications/Adobe\ Flex\ Builder\ 3/sdks/3.0.0/bin/mxmlc HelloWorld2.mxml 
設定ファイル "/Applications/Adobe Flex Builder 3/sdks/3.0.0/frameworks/flex-config.xml" をロードしています
/Applications/MAMP/htdocs/prog10/helloworld/HelloWorld2.swf (153105 bytes)

実行は作成されたswfを実行すればSA(スタンドアロン)版のPlaryerで起動します。ブラウザで起動しても実行されます。

簡単なプログラムサンプルを含めて下記のページにチュートリアルがありますので参考にしてください。
https://www.adobe.com/jp/devnet/flex/quickstart/coding_with_mxml_and_actionscript/
また、Adobeが提供するflex各種ドキュメントは以下にあります

FlexBuilderで作るflashマルチメディアアプリケーション

今回は、flashの利点を生かし、マルチメディアを意識した動画検索アプリケーションを作成してみます。動画はyoutubeAPIを利用して、動画検索を行います。また、flashflv形式の動画を簡単に再生することが可能なので、youtubeからflvファイルをダウンロードし直接再生機能も持たせてみます。完成後の画面をまず見せます。

メイン上部に検索窓、メイン中央に検索結果をタイル上にサムネイル表示します。右側に再生画面を表示します。サムネイルをダブルクリックすることで動画が再生される仕様とします。
flexBuilderを起動すると以下のような画面が表示されます。

これから作成するアプリのプロジェクトを作成します。メニューから[ファイル] - [新規] - [MXMLアプリケーション] を選択します

プロジェクト名を入力します。今回は youtube_player としました。

終了を押すと、元の画面にプロジェクトのひな形が作成されました。ここから開発をスタートします。左上のflexナビゲータには各種リソースがあります。 src/youtube_player.mxmlのソースに編集を加えていくことになります。右のメイン画面にはそのソースが開いた状態になっていると思います。
次にまずはデザインを配置してしまいましょう。今回使う主要なものは

  • キーワードを入力する TextInput
  • 検索ボタンの Button
  • 検索結果を表示する TileList
  • 動画を見せる player

基本的にこの4つがあれば、デザインはどんなものでも良いです。メインパネルの「ソース」と「デザイン」の切り替えで「デザイン」を押して作ります。(もちろんソースに直に書いてもokです). 私が行った方法を例にあげます。

Application(全体) の レイアウトを vertical にする.
ApplicationControlBar を配置し width:100% にする.
その中に TextInput と Button を配置する.
Button のラベルを youtube search にする
次にApplicationControlBarの下部にHDividedBoxを配置する
width:100% height:100%にする
その中に TileList を width:100% height:100%で配置する

この時点で youtube_player.mxmlのソースは自動的に編集され

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:ApplicationControlBar width="100%">
        <mx:TextInput/>
        <mx:Button label="youtube search"/>
    </mx:ApplicationControlBar>
    <mx:HDividedBox width="100%" height="100%">
        <mx:TileList width="100%" height="100%"></mx:TileList>
    </mx:HDividedBox>
</mx:Application>

となっているはずです。
では、ちょっとずつ勉強しながら作りましょう.まず TextInputにキーワードを入力して Buttonをクリックしたら Alertがあがるようにしましょう。そのためには、TextInput に id をつけて Button のクリック動作に関数を割り当てましょう.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            public function send_request():void {
                Alert.show(keyword.text);
            }
        ]]>
    </mx:Script>
    <mx:ApplicationControlBar width="100%">
        <mx:TextInput id="keyword" />
        <mx:Button label="youtube search" click="send_request()"/>
    </mx:ApplicationControlBar>
    <mx:HDividedBox width="100%" height="100%">
        <mx:TileList width="100%" height="100%"></mx:TileList>
    </mx:HDividedBox>
</mx:Application>

なにをやったかというと, TextInput に keywordというidを与え、Buttonの click イベントに send_request の関数を与えました。 mx:Script のタグを新たに設け、その中に send_requestの関数を処理を記入しました。今回は keywordの中身の文字列のAlert を出すのでその処理を記入しました。(Alertを出すには mx.controls.Alert を import する必要があるのでそれも加えてあります)
ここで実行を一度してみましょう。flex builder上部の緑色の丸いプレイアイコンをクリックするか 実行メニューを選ぶとswfをコンパイルしてブラウザが勝手に起動されると思います。 そこに先ほどデザインしたものが表示され、キーワード窓に適当に入力し、ボタンをクリックしたら Alertダイアログが表示されその中身はキーワード窓で入力したものが表示されていると思います。
さて、次に youtube APIを利用して動画リストを取得するのですが、 youtube APIの仕様を確認しておきましょう. APIにはさまざまなリクエスト方法がありますが、今回使用するのは単純にキーワード検索ですので

http://gdata.youtube.com/feeds/api/videos?vq=[キーワード]

です。実際にブラウザでアクセスしてみるとXMLが取得できると思います.flashで外部ドメインにアクセスする場合、外部APIのホスト直下にcrossdomain.xmlが設置され、かつ許可がされている場合に限りアクセス可能です
http://gdata.youtube.com/crossdomain.xml

<?xml version="1.0"?>
<!-- http://gdata.youtube.com/crossdomain.xml -->
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*"/>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

と書かれているので任意のドメインよりflashにてアクセス可能となっています。flexにてapiにアクセスする一般的な方法は HTTPServiceを利用することです。先ほどの youtube_player.mxml

    <mx:HTTPService id="youtubeapi" 
             url="http://gdata.youtube.com/feeds/api/videos" method="GET"
             result="youtubeapi_result(event)" />

をまず加えます. 次に先ほど Alertを出していた関数にsend_request にAPIリクエストを投げる処理を加え、APIの応答が戻ってきたときに呼ばれる(HTTPService関数のresultで指定した)youtubeapi_result関数を作れば、youtubeAPIに flexからリクエストを投げて応答を受け取ったことになります. ここまでで プログラムは以下になります

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:Script>
        <![CDATA[
            import mx.rpc.events.ResultEvent;
            import mx.controls.Alert;
            public function send_request():void {
                var uv:URLVariables = new URLVariables;
                uv.vq = keyword.text;
                youtubeapi.request = uv;
                youtubeapi.send();
            }
            
            public function youtubeapi_result(event:ResultEvent):void {
                Alert.show("get youtube result");
            }
        ]]>
    </mx:Script>
    <mx:ApplicationControlBar width="100%">
        <mx:TextInput id="keyword" />
        <mx:Button label="youtube search" click="send_request()"/>
    </mx:ApplicationControlBar>
    <mx:HDividedBox width="100%" height="100%">
        <mx:TileList width="100%" height="100%"></mx:TileList>
    </mx:HDividedBox>
    
    <mx:HTTPService id="youtubeapi" 
                    url="http://gdata.youtube.com/feeds/api/videos" method="GET"
                    result="youtubeapi_result(event)" />
                    
</mx:Application>

説明していない部分は, send_request の中の

        var uv:URLVariables = new URLVariables;
        uv.vq = keyword.text;
        youtubeapi.request = uv;
        youtubeapi.send();

ですが、 URLVariables クラスを用いると一般的なAPIの引数の &key1=val1&key2=val2 値を 一つの変数で保持できます. 今回は vq=キーワードなので uv.vq = キーワード となります. 後はこの変数を HTTPServiceの request にセットして APIに投げます (send).
ここでもう一度実行してみてください。 検索ボタンをクリックしてしばらく待つと応答が帰ってくると思います.
さて、youtube からの応答がXML帰ってきました。が中身を知らなければ何もできません. ブラウザでアクセスしてみて応答をソース表示し、中身のXMLを一度確認してみましょう. 注意深く見ると以下の構造をしていることが分かります

<?xml version='1.0' encoding='UTF-8'?>
<feed .....>
しばらく、ヘッダー的なタグが続く
<entry>1件目の動画情報<entry>
<entry>2件目の動画情報<entry>
<entry>3件目の動画情報<entry>
<entry>....<entry>
<entry>n件目の動画情報<entry>
</feed>

entryの中身に、タイトル,サムネイル情報,youtubeURLなどの情報があることが分かると思います。
それでは、flexからこのXMLの各要素にアクセスしてみましょう。応答が帰ってきたときに 呼ばれるために作った関数 youtubeapi_resultの引数に event:ResultEvent があります。これの event.result要素がこのXMLになります。なので、例えば entryの5件目のtitle は

event.result.feed.entry[5].group.title

になります。それでは、ひとまず正しくXMLを取得できているか確認するために youtubeapi_result関数を

    public function youtubeapi_result(event:ResultEvent):void {
        Alert.show(event.result.feed.entry.length);
        for(var i:int=0;i<event.result.feed.entry.length;i++) {
            Alert.show(event.result.feed.entry[i].group.title);
        }
    }

として動作確認してみてください。取得した動画の本数とタイトルが順にAlertに表示されると思います.
さて、次はこの結果をTileListにリスト表示させます List, TileList, HorizontalList などの各要素は単純に dataProviderを用いてリスト表示をするのが単純な使い方ですが (flexリファレンス) 検索結果画面とか商品一覧とかの画面ではリストの一つ一つがUIデザインをもつものであることが多いです。そのために、この List系のコンポーネントは itemRenderer というプロパティをもちます。これを使えば、個々のItemの設計を別に定義できます (flexリファレンス)
文字で書くと良くわからないので実際にやってみます。 まず、TileList のタグを

<mx:TileList width="100%" height="100%" dataProvider="{movielist}" itemRenderer="movieitem"

とします. これの意味は TileListの要素はmovielistの変数の値います. 個々のitemの表示には movieitemコンポーネント (movieitem.mxml) を使います. という意味です. movielist (ArrayCollection) も加えた形は下記になります (movielistは Bindable として登録する必要があります). youtubeapi_resultの処理をmovielistの初期化および entry リストの代入を行うように変更しました.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
    <mx:Script>
        <![CDATA[
            import mx.rpc.events.ResultEvent;
            import mx.controls.Alert;
            import mx.collections.ArrayCollection;

            [Bindable]
            public var movielist:ArrayCollection = new ArrayCollection;    

            public function send_request():void {
                var uv:URLVariables = new URLVariables;
                uv.vq = keyword.text;
                youtubeapi.request = uv;
                youtubeapi.send();
            }
            
            public function youtubeapi_result(event:ResultEvent):void {
                movielist.removeAll();
                for(var i:int=0;i<event.result.feed.entry.length;i++) {
                    movielist.addItem(event.result.feed.entry[i]);
                }
            }
        ]]>
    </mx:Script>
    <mx:ApplicationControlBar width="100%">
        <mx:TextInput id="keyword" />
        <mx:Button label="youtube search" click="send_request()"/>
    </mx:ApplicationControlBar>
    <mx:HDividedBox width="100%" height="100%">
        <mx:TileList width="100%" height="100%" dataProvider="{movielist}" itemRenderer="movieitem" />
    </mx:HDividedBox>
    
    <mx:HTTPService id="youtubeapi" 
                    url="http://gdata.youtube.com/feeds/api/videos" method="GET"
                    result="youtubeapi_result(event)" />
                    
</mx:Application>

さて、これで実行するとエラーになるはずです. movieitem が未定義なのが原因です。 movieitem を作りましょう.
FlexBuilderのメニューから[ファイル] - [新規] - [MXMLコンポーネント] を選択して

ファイル名: movieitem ベース: VBox 幅: 150 高さ:150

で終了すれば movieitem.xml が左上の flexナビゲータに追加されます. この状態で実行すると先ほどのエラーは解消されます、がまだ何も表示されません が、マウスでTileListをなぞるとitemが選択されている状態になります。
movielist.xml の記述に入ります. メインの画面を movielist.mxmlに切り替えて Image および Text を配置します。デザインから行っても構いませんが、最終的なソースは

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="150" height="150"  horizontalAlign="center">
    <mx:Image height="97" width="130" source="{data.group.thumbnail.getItemAt(0).url}"/>
    <mx:Text  text="{data.group.title}" width="100%" height="100%"/>  
</mx:VBox>

になります. Imageはイメージ画像をロードするコンポーネントです. ここで重要なのが, TileListのdataProviderの各要素がこのコンポーネントに渡されるのですが、その各要素は data の変数から 取得できるということです. なので youtube の entry を各要素として TileListのdataProviderに渡しているからこの中では entryのtitleは data.group.title で取得できます. 1つめのサムネイル画像は data.group.thumbnail.getItemAt(0).url で取得できます (data.group.thumbnail[0].urlとするとバインディングエラーがでるので注意).
これで実行してみましょう

これで、youtube検索結果がサムネイル付きで表示できるところまで完成しました。playerの部分は次回解説します。

flash - ActionScript (後編)