Flexの最近のブログ記事

<mx:DataGrid>のデータが変更された場合(dataProviderが変更された場合)に処理を行うとします。
その際、changeイベントをリスニングしていませんか?
変更と言うとchangeイベントを思い浮かべる方多いようです。

ですが、AdobeFlexBuilderのヘルプを参照すると、changeイベントは
selectedIndex またはselectedItem プロパティの値が変更された場合に送出されるイベントと記載されています

0926-1.jpg

実際に検証してみます。

サンプルでは、コンボボックス変更時のイベントハンドラで
  データグリッドのdataProviderの値を変更し、
データグリッドがchangeイベントを送出した際にアラートを表示するサンプルです。

0926-2.jpg

サンプルコードは以下の通り。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application creationcomplete="init(event)" layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml">    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.collections.ArrayCollection;
            
            protected function init(event:Event):void
            {
                // コンボボックスのデータを設定
                dataChooser.dataProvider = new ArrayCollection([    
                                                                {data:"1",label:"データ1"},
                                                                {data:"2",label:"データ2"}
                                                                ]);
            }
            
            protected function combo_changeHandler(event:Event):void
            {
                // コンボボックスの選択状態によってデータグリッドの値を変更
                switch(dataChooser.selectedIndex)
                {
                    case 0:
                    {
                        resultDg.dataProvider = new ArrayCollection([
                                                                    {name:"プラズマTV-20型",price:"10,000"},
                                                                    {name:"プラズマTV-27型",price:"50,000"},
                                                                    {name:"プラズマTV-32型",price:"70,000"},
                                                                    {name:"プラズマTV-37型",price:"75,000"}
                                                                    ]);
                        break;
                    }
                    case 1:
                    {
                        resultDg.dataProvider = new ArrayCollection([
                                                                    {name:"ミニ掃除機",price:"8,000"},
                                                                    {name:"サイクロン式掃除機",price:"56,800"},
                                                                    {name:"スーパーサイクロン式掃除機",price:"98,000"},
                                                                    {name:"ロケット式掃除機",price:"888,000"}
                                                                    ]);
                        break;
                    }
                    default:
                    {
                        resultDg.dataProvider = null;
                        break;
                    }
                }
            }
            
            protected function datagrid_changeHandler(event:Event):void
            {
                Alert.show("データグリッドchangeイベント","DataGrid");
            }
            
            
            
        ]]>
    </mx:Script>


    <mx:Panel x="0" y="0" width="100%" height="100%" layout="absolute" title="データグリッド変更">
        <mx:VBox x="0" y="0" width="100%" height="100%">
            <mx:Form width="100%" height="50%">
                <mx:FormItem label="データ選択">
                    <mx:ComboBox id="dataChooser" change="combo_changeHandler(event)" prompt="▼選択してください"/>
                </mx:FormItem>
            </mx:Form>
            <mx:DataGrid width="100%" height="100%" id="resultDg" change="datagrid_changeHandler(event)">
                <mx:columns>
                    <mx:DataGridColumn headerText="商品名" dataField="name"/>
                    <mx:DataGridColumn headerText="金額" dataField="price"/>
                </mx:columns>
            </mx:DataGrid>
        </mx:VBox>
    </mx:Panel>
</mx:Application>

サンプルを実行し、コンボボックスの値を変更した際
DataGridのchangeイベントハンドラで実装したアラートは表示されません。

データグリッドの値が変更されたタイミングで処理を実行したい場合、changeイベントではなく
valueCommitを指定する必要があります。

0926-3.jpg

<mx:DataGrid width="100%" height="100%" id="resultDg" valueCommit="datagrid_changeHandler(event)">

<実行結果>

0926-4.jpg

普通にテストを行っていれば、UTで発見できるとは思いますが、
うっかりテストで気が付けな買った場合、コード上は問題ないように見えてしまうので注意が必要です

以前に書いた記事 「boolean型の値は false=0,true=1 になっている」 で記載した内容に関して、一部誤りが発覚しましたので、こちらにその検証の続きを記載します。

解釈が誤っていた点

★ false=0 / true=1 ではなくて、 0=false / 0以外の値は true となる

 

下記のソースで検証します。
if文の条件に true/false ではなく、0, 1, 255 という数字を使用して評価します。評価対象になった場合には、VBoxコンテナの中にLabelを追加してメッセージを表示するつくりになっています。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" horizontalAlign="center" creationComplete="onCreationComplete()">

<mx:Script>
    <![CDATA[
        import mx.controls.Label;
        
        private function onCreationComplete():void
        {
            if (0) {
                 addLabel("0");
                 trace(0 as Boolean);
            }
            
            // 評価される → as演算子でキャストした結果は「true」
            if (!0) {
                addLabel("!0");
                trace(!0 as Boolean);
            }
            
            // 評価される → as演算子でキャストした結果は「null」
            if (1) {
                addLabel("1");
                trace(1 as Boolean);
            }
            
            if (!1) {
                addLabel("!1");
                trace(!1 as Boolean);
            }
            
            // 評価される → as演算子でキャストした結果は「null」
            if (255) {
                addLabel("255");
                trace(255 as Boolean);
            }
            
            if (!255) {
                addLabel("!255");
                trace(!255 as Boolean);
            }     
        }
        
        private function addLabel(msg:String):void
        {
            var lbl:Label = new Label();
            lbl.text = "「" + msg + "」 は評価されました";
            showList.addChild(lbl);
        }
        
    ]]>
</mx:Script>

    <mx:VBox id="showList" horizontalAlign="center" fontSize="15" fontWeight="bold" />

</mx:Application>

【実行結果】

booleanEvaluation.png

実行結果としてわかったことは、

  • 「0」という数字は false として評価される
  • false の否定である 「!0」 は true として評価される (当たり前ですが…)
  • 「0」以外の数字は、条件式ですべて true として評価される
  • 「0」以外の数字は as演算子でキャストすると null になっている (Booleanでキャストできていない)

ソースにはありませんが、if("あああ")  などの様に文字列を使用した場合にも、必ず true として評価されていました。

つまり、 boolean型の true/false の代替としては、0 を false の代わりに使うことはできるが、0 以外の値はすべて true と評価されてしまうので使用することができないことがわかりました。

画面上のコンポーネントに対しカーソルが当たっている状態で、Tabキーを押下した際に、 次のコンポーネントにフォーカスを移動させることができます。

VBox、HBox、Canvasコンテナにコンポーネントを配置したときに、Tabキーを押下した場合の遷移をそれぞれ検証してみました。

 

VBoxの場合

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
 <mx:VBox height="100%">
  <mx:TextArea text="1"/>
  <mx:TextArea text="2"/>
  <mx:TextArea text="3"/>
  <mx:TextArea text="4"/>
  <mx:TextArea text="5"/>
 </mx:VBox>
</mx:Application>

サンプルのFlash

 

Vboxコンテナの場合は、上から下へ順に移動することが確認できます。
また、MXML上の配置と画面上の配置が同じためMXMLで配置した順=Tabキー押下時にフォーカスが移動する順番になるようです。

 

HBoxの場合

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
 <mx:HBox width="100%">
  <mx:TextArea text="1"/>
  <mx:TextArea text="2"/>
  <mx:TextArea text="3"/>
  <mx:TextArea text="4"/>
  <mx:TextArea text="5"/>
 </mx:HBox>
</mx:Application>

サンプルのFlash

Hboxコンテナの場合は、左から右へ順に移動することが確認できます。
また、MXML上の配置と画面上の配置が同じためMXMLで配置した順=Tabキー押下時にフォーカスが移動する順番になるようです。

 

Canvasの場合

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
 <mx:Canvas width="60" height="60">
  <mx:TextArea x="0" y="0" width="20" height="20" text="1"/>
  <mx:TextArea x="20" y="0" width="20" height="20" text="2"/>
  <mx:TextArea x="40" y="0" width="20" height="20" text="3"/>
  <mx:TextArea x="0" y="20" width="20" height="20" text="4"/>
  <mx:TextArea x="20" y="20" width="20" height="20" text="5"/>
  <mx:TextArea x="40" y="20" width="20" height="20" text="6"/>
  <mx:TextArea x="0" y="40" width="20" height="20" text="7"/>
  <mx:TextArea x="20" y="40" width="20" height="20" text="8"/>
  <mx:TextArea x="40" y="40" width="20" height="20" text="9"/>
 </mx:Canvas>
 <mx:Canvas width="60" height="60">
  <mx:TextArea x="40" y="40" width="20" height="20" text="1"/>
  <mx:TextArea x="20" y="40" width="20" height="20" text="2"/>
  <mx:TextArea x="0" y="40" width="20" height="20" text="3"/>
  <mx:TextArea x="40" y="20" width="20" height="20" text="4"/>
  <mx:TextArea x="20" y="20" width="20" height="20" text="5"/>
  <mx:TextArea x="0" y="20" width="20" height="20" text="6"/>
  <mx:TextArea x="40" y="0" width="20" height="20" text="7"/>
  <mx:TextArea x="20" y="0" width="20" height="20" text="8"/>
  <mx:TextArea x="0" y="0" width="20" height="20" text="9"/>
 </mx:Canvas>
</mx:Application>

サンプルのFlash

1つ目のCanvasは、左上から右下へ移動することが確認でき、2つ目のCanvasは、右下から左上へ移動することが確認できます。

2つ目のCanvasも画面上の見た目のコンポーネント配置は1つ目のCanvasとまったく同じですが、2つ目のCanvasは、MXMLのソースコード上では逆から並べているため、Tabキー移動で左下から右上に移動しています。

上記検証から、Tabキーの移動順序は、デフォルトでは MXMLソースファイル上での記述順で決定されているようです。

ボタンにフォーカスがある時、スペースキー(IMEオフのとき限定)を押下した際に、ボタンのクリックイベントが呼ばれます。

これをEnterキーでも押下できるようにしたいと考え、Buttonクラスを拡張して以下のCstmButtonクラスを作成してみました。

CstmButton.asファイル

package
{
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	
	import mx.controls.Button;

	public class CstmButton extends Button
	{
		public function CstmButton()
		{
			super();
		}
		
		override protected function keyDownHandler(event:KeyboardEvent):void
		{
	        if (event.keyCode == Keyboard.ENTER)
                event.keyCode = Keyboard.SPACE;
			super.keyDownHandler(event);
		}
	    override protected function keyUpHandler(event:KeyboardEvent):void
	    {
	        if (event.keyCode == Keyboard.ENTER)
                event.keyCode = Keyboard.SPACE;
            
            super.keyUpHandler(event);
	    }
		
	}
}

ソースコードを読んでもらえばわかるとは思いますが、この方法はかなりお手軽な方法(手抜き)です。
CstmButtonクラスの中身は、キーが押されたときに呼ばれているクラス(KeyboardEvent)内で、Enterキーが押されたときに呼ばれるメソッド(keyDownHandler)、離されたときに呼ばれるメソッド(keyUpHandler)をそれぞれオーバーライドして、パラメータで渡されたイベントクラス内の押されたキーの情報(keyCode)を、EnterキーからSpaceキーのKeyCodeに置き換えてからsuper()を呼んでます。

CstmButtonクラスの継承元であるButtonクラスのkeyDownHandlerメソッド、keyUpHandlerメソッドでは、押されたキー(keyCode)をみてSpaceキーの場合に処理が動いているので、Enterキー押下時にSpaceキーに置き換えることで、Enterキーでもボタンが押せるようになります。

 

以下、上記クラスを検証するためのサンプルソースです。

 .mxmlファイル

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*" horizontalAlign="left">
	<mx:Script>
	<[CDATA[
		import mx.controls.Alert;
		
		private function checkClicked():void{
			Alert.show("反応!");
		}
	]]>
	</mx:Script>

	<local:CstmButton label="カスタム" click="checkClicked()"/>
	<mx:Button label="普通" click="checkClicked()"/>
</mx:Application>

※ サンプルソースでは、ボタンが押されたという反応を確認するため、clickイベントを追加してます

コンテナ内の子コンポーネントを生成するタイミングを自分で制御したい場合には、
creationPolicy プロパティーを "none" にして自前で生成処理を記述する必要があります。
この際に、いくつかポイントがあったので備忘録として残します。
(ViewStack等で重い画面を生成する際に使えそうかな?!)

以下のソースは、親画面に ViewStackコンテナ & クリックすると子コンポーネントを生成するボタンを配置してあります。
ViewStackコンテナ内の子コンポーネントは、別ファイルにして file001.mxml, file002.mxml, file003.mxml を用意しました。
(子画面は画面背景色を各々指定し、画面名を表示しただけのファイル)

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:fls = "files.*"
    layout="vertical"
    creationComplete="onCreationCompleteApp()">
    <mx:Script>
        <![CDATA[
        
            private function onCreationCompleteApp():void
            {
                this.appLbl.text = "ApplicationのCreationCompleteが動きました";
            }
            
            private function onCreationCompleteVS():void
            {
                this.vsLbl.text = "ViewStackのCreationCompleteが動きました";
            }
            
            private function onClickHandler(event:MouseEvent):void
            {
                var targetBtn:String = event.target.id;
                
                switch(targetBtn)
                {
                    case "btn1":
                        mainView.createComponentFromDescriptor(mainView.childDescriptors[0],false);
                        mainView.selectedIndex = mainView.getChildIndex(fls001);
                        break;
                    case "btn2":
                        mainView.createComponentFromDescriptor(mainView.childDescriptors[1],false);
                        mainView.selectedIndex = mainView.getChildIndex(fls002);
                        break;            
                    case "btn3":
                        mainView.createComponentFromDescriptor(mainView.childDescriptors[2],false);
                        mainView.selectedIndex = mainView.getChildIndex(fls003);
                        break;
                }
                
                mainView.validateNow();
            }
        ]]>
    </mx:Script>
    
    <mx:Label id="appLbl" fontSize="15" color="#ffffff" />
    <mx:Label id="vsLbl" fontSize="15" color="#ffffff" />
    <mx:HBox>
        <mx:Button label="画面1" id="btn1" click="onClickHandler(event)" />
        <mx:Button label="画面2" id="btn2" click="onClickHandler(event)" />
        <mx:Button label="画面3" id="btn3" click="onClickHandler(event)" />
    </mx:HBox>
    <mx:ViewStack id="mainView" width="400" height="300" creationPolicy="none" creationComplete="onCreationCompleteVS()" >
        <fls:file001 id="fls001" />
        <fls:file002 id="fls002" />
        <fls:file003 id="fls003" />
    </mx:ViewStack>
</mx:Application>
★ポイント1

createComponentFromDescriptor もしくは createComponentFromDescriptors メソッドを使用して子コンポーネントを生成する。

ざっくりとした使い分けとしては以下の通り。(詳細はAPIを参照してください)

createComponentFromDescriptor 引数で指定した1つのオブジェクトを生成する (上記ソースではこちらを採用)
createComponentFromDescriptors コンテナのすべての子を作成する

 

★ポイント2

createComponentFromDescriptor メソッドを使用した場合には、生成後の子オブジェクトを表示リストに追加しない為、
そのままでは画面に表示されません。 なので、生成後に コンテナに対して validateNow() を実行する必要がある。

 

★ポイント3

コンテナのcreationComplete イベントが走るのは、コンテナに対して validateNow() が呼び出されたタイミングになる。
親になるコンテナのcreationcomplete イベントで何か処理をしたい場合には要注意ですね。

 起動時には、ViewStack の creationComplete は動きません。
defaultDisplay.PNG ViewStack 内の子コンポーネントを生成し、validateNow が走ると ViewStack の creationComplete が動きます。
afterCreateDisplay.PNG
少し古いですが、上記の内容がわかりやすく書いてあるFlexテクニカルノートがありました。
http://www.adobe.ca/jp/support/flex/ts/documents/45fc6cf2.htm

<<前のページ 12345

このブログについて

このブログは吉祥寺にあるブレインチャイルド株式会社の社員で投稿しています。
業務ではまってしまったことや発見したこと。
自分達で新たに学習してみようと思って勉強し始めたことなどを綴っています。
こんな社員が働いているブレインチャイルドに興味がわいててきたなら、是非お問い合わせください。
会社案内
求人案内
先輩のコメント