2010年2月アーカイブ

viewStackとカスタムイベントを使用した画面遷移テクニックを紹介します。

viewStackの画面遷移というと、selectedIndex++する方法が一般的ですが、

今回のサンプルは画面名を指定して画面遷移を行う事が可能となります。

以下は、コンボボックスで選択した画面名の画面に遷移を行うサンプルアプリケーションです。


画面構成は以下の通り。

[メインアプリケーション]
メインアプリケーションにはViewStackが配置してあり、

画面はViewStackに格納されます。
メインアプリケーションは、画面起動時(creationComplete)
画面遷移用に作成したカスタムイベントをリスニングし、
子画面が画面遷移イベントをディスパッチすると、子画面から送信された画面名を検索し
画面遷移を行います。

コードは以下の通り。

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



	


	
		
				
		
		
		
		  
          

 

[子画面]
子画面はコンボボックスで遷移先の画面を指定し、 ボタンクリックで画面遷移イベントをディスパッチします。
 この時、コンボボックスの選択された値(value)を画面遷移イベントにセットをして
メインアプリケーションに送信します。
コードは以下の通りです。

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

	
	
		
	
	
	
		
		
        

[カスタムイベント]
画面遷移専用のカスタムイベントです。
画面名格納用にアクセサを用意しています。

コードは以下の通り。

package events
{
	import flash.events.Event;

	public class FrameworkEvent extends Event
	{
		/**
		 *	イベントタイプ
		 */
		public static const CHANGE_VIEW:String = 'change_View';

		/**
		 *	アクセサ
		 */
		private var _viewName:String = '';
		
		public function set viewName(name:String):void
		{
			_viewName = name;
		}
		public function get viewName():String
		{
			return _viewName;
		}

				
		public function FrameworkEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
		
	}
}

親画面では画面遷移イベントをリスニングしているので、
 子画面でdispatchEventを行う事で子画面でセットした遷移先の画面名を 親画面に送信する事ができます。
 親画面は、子画面から送信された画面名を元に ViewStack内から画面を検索します。
 

mainVs.getChildByName(子画面から送信された画面名);

ViewStackのgetChildByName()を使用して画面オブジェクトを指定する訳ですが、
 mxmlで定義する際にnameプロパティを設定しておく必要があります。

 続いて、取得した画面オブジェクトがViewStackの何番目に位置するかインデックスを取得します。
 

var viewIndex:int = mainVs.getChildIndex(画面オブジェクト);

ViewStackのgetChildIndexを使用する事で、
インデックスを取得する事が可能となります。

ここまでの情報があれば、あとはselectedIndexを指定するだけです。

親画面でイベントをリスニングし、
子画面は画面遷移の時に毎回イベントをディスパッチするだけなので
標準化の観点でもおすすめできる方法です。



 

FlexBuilder3 Standard環境に、FlexBuilder3 Professionalのライセンスキーの反映方法です。
FlexBuilder3 Standardをアンインストールしても、Standard版のライセンスキーは残ってしまい、ちょっと嵌ったので、他にも嵌っている人がいるかもしれないと思いUPします。

【反映方法】
http://www.adobe.com/jp/support/flex/ts/documents/kb403934.htm

DataGridのItemRendererにButtonを設置したところ、VBox上に配置した場合と、

直接配置した場合のclickイベントのtarget、及びCurrentTargetが異なる結果となっています。
VBox上に配置した場合は、自分自身の「Button」がtargetとなるのですが、
直接配置した場合は、「・・・_inlineComponent2」がtargetになってしまいます。
詳細は、下記のサンプルをご参照ください。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()">
  <mx:Script>
  <![CDATA[
    import mx.controls.Alert;

    private var tmpArr:Array = [{col1:"", col2:"", col3:"aaa"}, {col1:"", col2:"", col3:"bbb"}];

    private function onCreationComplete():void{
      dg.dataProvider = tmpArr;
    }

    public function btnOnClickHandler(event:MouseEvent):void{
      Alert.show("Target:" + event.target.className.toString() + "\nCurrentTarget:" + event.currentTarget.className.toString());
    }
  ]]>
  </mx:Script>

  <mx:DataGrid x="32" y="23" id="dg" width="568">
    <mx:columns>
      <mx:DataGridColumn width="100" headerText="VBox有" dataField="col1" sortable="false" >
        <mx:itemRenderer>
        <mx:Component>
          <mx:VBox horizontalAlign="center">
            <mx:Button label="VBox有" click="outerDocument.btnOnClickHandler(event)" height="20">
            </mx:Button>
          </mx:VBox>
        </mx:Component>
      </mx:itemRenderer>
    </mx:DataGridColumn>
    <mx:DataGridColumn width="100" headerText="VBox無" dataField="col2" sortable="false" >
      <mx:itemRenderer>
        <mx:Component>
          <mx:Button label="VBox無" click="outerDocument.btnOnClickHandler(event)" height="20">
          </mx:Button>
        </mx:Component>
       </mx:itemRenderer>
      </mx:DataGridColumn>
      <mx:DataGridColumn headerText="項目名" dataField="col3" />
    </mx:columns>
  </mx:DataGrid>
</mx:Application>

この件について、Adobeに問い合わせたところ、下記の回答が返ってきました。

【Adobeの回答】
ItemRendererにおけるTarget、及びCurrentTargetの決定方法は、
コンポーネントのルートタグであるかどうかによって決定いたします。
コンポーネントのルートタグである場合は、今回のサンプルで確認されましたように
"_inlineComponent2"のように表示されます。
コンポーネントにid="xxx"プロパティを追加して、
「コンポーネントのルートタグで id 属性を指定することはできません。」
のコンパイルエラーが出るかでないかで確認することができます。

このような場合に、どのオブジェクトがイベントを発生させたかを確認する方法
といたしましては(統一させる方法)、
VBoxで統一する、もしくは例えばautomationName等のプロパティを使うなどがご
ざいます。

//参考例ここから

  <mx:Button label="VBox無" height="20" automationName="Button"
             click="outerDocument.btnOnClickHandler(event)">
     Alert.show("Target:" + event.target.automationName +
               "\nCurrentTarget:" + event.currentTarget.automationName);

//ここまで


Target、及びCurrentTargetを扱う際のご参考に!!

例えば、動的に生成されたコンポーネントをバインディングさせたい場面など、
mxmlでは設定できません。

そこでActionScriptクラス内でバインディングの登録をする事になりますが
以外に設定方法がやっかいです。

以下はそのサンプルになります。


[mxmlでバインディングを行う場合]

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
    <![CDATA[
        
        /**
         * テキストインプットに入力された情報は_bindTxtに格納されます。
         * ラベルは_bindTxtをバインディングしている為、
         * テキストインプットの内容が変わると、自動的にラベルのテキストにも反映されます。
         */
        
        [Bindable]
        private var _bindTxt:String = "";
        
        private function bindTxi_changeHandler():void
        {
            _bindTxt = bindTxi.text;
        }
        
    ]]>
    </mx:Script>

    <mx:Label x="27" y="28" text="テキストを入力してボタンクリック" width="160"/>
    <!-- バンディング元-->
    <mx:TextInput id="bindTxi" change="bindTxi_changeHandler()" x="27" y="54"/>
    <!-- バンディング先 -->
    <mx:Label text="{this._bindTxt}" x="195" y="56" width="172" id="bindlbl"/>
    
</mx:Application>



mxmlでバインディングの登録を行う場合、
ラベルのtextの指定で、{}を行い、変数に[Bindable]を書くだけで簡単にバインディングが登録されます。

では、動的にラベルが作成された場合はどうなるでしょう?

以下がサンプルです。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
    <![CDATA[
        import mx.controls.Label;
        
        /**
         * テキストインプットに入力された情報は_bindTxtに格納されます。
         * ラベルは_bindTxtをバインディングしている為、
         * テキストインプットの内容が変わると、自動的にラベルのテキストにも反映されます。
         */
        [Bindable]
        private var _bindTxt:String = "";
        
        private function bindTxi_changeHandler():void
        {
            _bindTxt = bindTxi.text;
        }
        
        // ボタンクリックでラベルを作成します。
        private function createBtnClick():void
        {
            var label:Label = new Label();
            label.width = 100;
            label.height = 30;
            // キャンバスにラベルを作成
            this.testCnb.rawChildren.addChildAt(label,0);
            
            // ボタン非活性
            createBtn.enabled = false;
        }
        
        
    ]]>
    </mx:Script>

    <mx:Label x="27" y="28" text="テキストを入力してボタンクリック" width="160"/>
    <!-- バンディング元-->
    <mx:TextInput id="bindTxi" change="bindTxi_changeHandler()" x="27" y="54"/>
    <mx:Button id="createBtn" 
        click="createBtnClick()"
        x="27" y="84" label="ラベルを作成" width="160"/>
    <mx:Canvas x="195" y="54" width="200" height="22" id="testCnb">
    </mx:Canvas>
    
</mx:Application>



ボタンクリックで、ラベルが動的に作成されるサンプルです。
動的に生成されるという事は、mxmlでバインディングの設定を行う事は出来ません。
こうした場合、ActionScriptコード内でバインディング設定を行う事になります。

ActionScriptコード内でバインディングの設定を行う場合、
BindingUtilsを使用します。
 

BindingUtils.bindProperty(バインディング先のオブジェクト(id),'プロパティ名',バインディング元のクラス,'プロパティ名');


以下はその設定例です。
 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
    <![CDATA[
        import mx.binding.utils.BindingUtils;
        import mx.controls.Label;
        
        /**
         * テキストインプットに入力された情報は_bindTxtに格納されます。
         * ラベルは_bindTxtをバインディングしている為、
         * テキストインプットの内容が変わると、自動的にラベルのテキストにも反映されます。
         */
        private var _bindTxt:String = "";
        
        private function bindTxi_changeHandler():void
        {
            _bindTxt = bindTxi.text;
        }
        
        // ボタンクリックでラベルを作成します。
        private function createBtnClick():void
        {
            var label:Label = new Label();
            label.width = 100;
            label.height = 30;
            label.text = "XXXX";
            // キャンバスにラベルを作成
            this.testCnb.rawChildren.addChildAt(label,0);
⇒          //Bindingの設定
⇒          BindingUtils.bindProperty(label,'text',bindTxi,'text');
            
            // ボタン非活性
            createBtn.enabled = false;
        }
        
        
    ]]>
    </mx:Script>

    <mx:Label x="27" y="28" text="テキストを入力してボタンクリック" width="160"/>
    <!-- バンディング元-->
    <mx:TextInput id="bindTxi" change="bindTxi_changeHandler()" x="27" y="54"/>
    <!-- バンディング先 -->
    <mx:Button id="createBtn" 
        click="createBtnClick()"
        x="27" y="84" label="ラベルを作成" width="160"/>
    <mx:Canvas x="195" y="54" width="200" height="22" id="testCnb">
    </mx:Canvas>
</mx:Application>



バインディング先のプロパティ名を指定したりなど、
全て自分で指定してあげる必要があります。

これにより、バインディングの設定を行う事が可能となります。

今回のサンプルで分かったように、mxmlで設定する場合
flexが自動的にやってくれる事が多い事が分かりました。
それに対して、ActionScriptで同じ機能を実現しようとすると
自分で登録すべき事が多く(今回のサンプルに限らず)
難易度は高くなるのですが、実現できる事も多いので
是非知っておく事をお勧めします。

 

関数クロージャーについて
[参考URL:http://livedocs.adobe.com/flex/3_jp/html/help.html?content=03_Language_and_Syntax_21.html]

addEventListennerなどで、いちいちイベントハンドラを作成していませんか?
そんな時は関数クロージャーがお勧めです。

関数クロージャーを活用する事により、より汎用的ですっきりとしたコードにする事が出来ます。
以下において、その活用例を紹介いたします。

関数クロージャーを使用しない一般的なイベントリスニングのパターンは以下の通り
 

 
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
    <![CDATA[
        import mx.controls.Alert;
       
        /**
         * ボタンクリックでタイマーを処理するサンプル
         */
        private function timerBtn_clickHandler():void
        {
            // タイマーの作成
            var timer:Timer = new Timer(1,1000);
            // タイマーイベントをリスニング
            timer.addEventListener(TimerEvent.TIMER, timerHandler);
            // タイマースタート
            timer.start();
        }

        private function timerHandler(event:TimerEvent):void
        {
            Alert.show("タイマーです");
            //タイマーストップ
            Timer(event.currentTarget).stop();
        }
    ]]>
    </mx:Script>

    <mx:Button id="timerBtn" click="timerBtn_clickHandler()"
        x="309" y="64" label="ボタン"/>
</mx:Application>




上記の様に、2つのイベントハンドラにおいて処理を行います。
明確に処理の分断を行うシーンでは、上記の様に実装すべきですが、
ちょっとした処理などでイベントハンドラを乱立させては、分かりづらいコードになってしまいます。


以下は、クロージャーを使用したサンプルになります。
 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
    <![CDATA[
        import mx.controls.Alert;
        
        /**
         * ボタンクリックでタイマーを処理するサンプル
         */
        private function timerBtn_clickHandler():void
        {
            // タイマーの作成
            var timer:Timer = new Timer(1,1000);
            // 関数クロージャーの作成
            var timerHandler:Function = function(event:TimerEvent):void
                                        {
                                            Alert.show('タイマーです');
                                            timer.stop();
                                        }
            
            // タイマーイベントをリスニング
            timer.addEventListener(TimerEvent.TIMER, timerHandler);
            // タイマースタート
            timer.start();
        }    
        
    ]]>
    </mx:Script>

    <mx:Button id="timerBtn" click="timerBtn_clickHandler()"
        x="309" y="64" label="ボタン"/>
</mx:Application>




関数クロージャーを作成し、タイマーイベントのリスニング時に
作成したクロージャーを指定しました。
更に省略したパターンは以下の通り。
 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
    <![CDATA[
        import mx.controls.Alert;
        
        /**
         * ボタンクリックでタイマーを処理するサンプル
         */
        private function timerBtn_clickHandler():void
        {
            // タイマーの作成
            var timer:Timer = new Timer(1,1000);
            // タイマーイベントをリスニング
            timer.addEventListener(TimerEvent.TIMER, 
                function(event:TimerEvent):void
                {
                    Alert.show('タイマーです');
                    timer.stop();    
                });
            // タイマースタート
            timer.start();
        }    
        
    ]]>
    </mx:Script>

    <mx:Button id="timerBtn" click="timerBtn_clickHandler()"
        x="309" y="64" label="ボタン"/>
</mx:Application>




上記では、タイマーイベントリスニング時に直接関数をコーディングしました。
とてもすっきりしたコードになります。

更に、関数クロジャーの定義により再帰呼び出しなども可能となりますので
知っておくと活用シーンは多くなります。

是非、お試しください。


 


 

ブレインチャイルド技術情報公開ブログを開始しました。

当面は、Flexの技術情報を中心に公開します。

bc_small.gif