本記事では、Flex 4.6 SDKから採用された新しいコンポーネントについて解説します。なお、Flash Builder 4.6にバンドルされている標準のFlex SDK(バージョン4.6.0.23201)を対象にしています。

前編ではSpinnerList/DateSpinner/ToggleSwitchコンポーネントを解説しました。後編となる本記事では、Callout/SplitViewNavigatorコンポーネントを解説します。

Calloutコンポーネント

Calloutは、吹き出しのようなポップアップを表示するコンポーネントです。ComboBoxコンポーネントがドロップダウンリストを表示するときに内部的に使用しているPopUpAnchorコンポーネントと同じように機能します。さらに、そのオーナー(呼び出し元)に対して、吹き出しを相対的に配置することが可能で、オーナーへの方向を表示する矢印(吹き出しのしっぽ)のスキンパーツ追加オプションも兼ね備えているSkinnablePopUpContainerのサブクラスです。

MXMLでCalloutを使用するときには、基本的に<fx:Declarations>タグ内に記述して、必要とするまで表示リストから外しておきます。表示するときには次のように open()メソッドを実行します。

callout.open(owner, modal);  //owner - 呼び出し元コンポーネントのインスタンス  //modal - モーダルの必要/不要を指定するブール値

Calloutの表示位置は、verticalPositionプロパティと、horizontalPositionプロパティを使用します。指定できる値は、ArrowDirectionクラスの定数("before"、"start"、"middle"、"end"、"after")のいずれかで、組み合わせによって25通りの表示パターンを実現できます。サンプルCalloutSample.fxpでは、これらの表示パターンを確認できます。

Calloutの表示位置と矢印( 吹き出しのしっぽ )の表示位置パターン
verticalPosition / horizontalPosition before start middle end after
before 無し
start
middle 無し
end
after 無し 無し

Callout を閉じるときには次のように close () メソッドを実行します。

callout.close(commit, data);  //commit - 戻りデータがアプリケーションによってコミットされる必要があるかどうかを指定するブール値(PopUpEventオブジェクトのcommitプロパティに書き込まれる)  //data - アプリケーションに返すデータ(PopUpEventオブジェクトのdataプロパティに書き込まれる)

次のコードは、Calloutコンポーネントを使ったシンプルなサンプルです(サンプル:CalloutSample.fxp)。

ソースコード:CalloutSample/src/views/CalloutSampleView01.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Callout Sample 01 --> <s:View xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" xmlns:c = "components.*" title = "Callout Sample 01" > <s:actionContent> <s:Button label="next" click="navigator.pushView(CalloutSampleView02)" /> </s:actionContent> <fx:Declarations> <c:CustomCallout id = "callout" verticalPosition = "after" horizontalPosition = "middle" /> </fx:Declarations> <fx:Script> <![CDATA[ import spark.events.PopUpEvent; protected function textInputRemovedFromStageHandler(event:Event):void { textInput.removeEventListener(Event.REMOVED_FROM_STAGE, textInputRemovedFromStageHandler); callout.setVisible(false, true); } protected function textInputFocusInHandler(event:FocusEvent):void { textInput.addEventListener(Event.REMOVED_FROM_STAGE, textInputRemovedFromStageHandler); callout.addEventListener(PopUpEvent.CLOSE, calloutCloseHandler); callout.open(textInput, true); } protected function calloutCloseHandler(event:PopUpEvent):void { callout.removeEventListener(PopUpEvent.CLOSE, calloutCloseHandler); textInput.removeEventListener(Event.REMOVED_FROM_STAGE, textInputRemovedFromStageHandler); textInput.text = event.data; } ]]> </fx:Script> <s:TextInput id = "textInput" verticalCenter = "0" horizontalCenter = "0" focusIn = "textInputFocusInHandler(event)" prompt = "Callout owner" /> </s:View> CustomCallout.mxml <?xml version="1.0" encoding="utf-8"?> <!--- Custom Callout --> <s:Callout xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" > <s:List id="list" change="close(true, list.selectedItem)"> <s:dataProvider> <s:ArrayList> <fx:String>AAAAA</fx:String> <fx:String>BBBBB</fx:String> <fx:String>CCCCC</fx:String> <fx:String>DDDDD</fx:String> </s:ArrayList> </s:dataProvider> </s:List> </s:Callout>

オーナーがボタン系コンポーネントの場合、表示するときにDropDownControllerクラスを利用すると簡単に実装できて便利です。次のコードは、ButtonをオーナーとしてCalloutを表示するサンプルです。

ソースコード:CalloutSample/src/views/CalloutSampleView02.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Callout Sample 02 --> <s:View xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" title = "Callout Sample 01" > <fx:Declarations> <s:Callout id = "callout" verticalPosition = "{verticalPositionButtonBar.selectedItem}" horizontalPosition = "{horizontalPositionButtonBar.selectedItem}" > <s:layout> <s:HorizontalLayout paddingLeft = "5" paddingTop = "5" paddingRight = "5" paddingBottom = "5" /> </s:layout> <s:Button width="100" label="ok" /> <s:Button width="100" label="cancel" /> </s:Callout> </fx:Declarations> <fx:Script> <![CDATA[ import spark.components.supportClasses.DropDownController; import spark.events.DropDownEvent; protected var dropDownController:DropDownController; protected override function createChildren():void { super.createChildren(); dropDownController = new DropDownController(); dropDownController.closeOnResize = false; dropDownController.addEventListener(DropDownEvent.OPEN, dropDownControllerOpenHandler); dropDownController.addEventListener(DropDownEvent.CLOSE, dropDownControllerCloseHandler); dropDownController.openButton = openButton; if (callout != null) { dropDownController.dropDown = callout; } openButton.addEventListener(MouseEvent.CLICK, openButtonClickaHandler); } protected function dropDownControllerOpenHandler(event:DropDownEvent):void { openButton.addEventListener(Event.REMOVED_FROM_STAGE, openButtonRemovedFromStageHandler); callout.open(openButton, false); } protected function dropDownControllerCloseHandler(event:DropDownEvent):void { openButton.removeEventListener(Event.REMOVED_FROM_STAGE, openButtonRemovedFromStageHandler); callout.close(); } protected function openButtonRemovedFromStageHandler(event:Event):void { callout.setVisible(false, true); dropDownController.closeDropDown(false); } protected function openButtonClickaHandler(event:MouseEvent):void { dropDownController.openDropDown(); } ]]> </fx:Script> <s:layout> <s:VerticalLayout paddingLeft = "10" paddingTop = "10" paddingRight = "10" paddingBottom = "10" /> </s:layout> <s:Label text="Vertical Position" /> <s:ButtonBar id="verticalPositionButtonBar" requireSelection="true"> <s:dataProvider> <s:ArrayList> <fx:String>before</fx:String> <fx:String>start</fx:String> <fx:String>middle</fx:String> <fx:String>end</fx:String> <fx:String>after</fx:String> </s:ArrayList> </s:dataProvider> </s:ButtonBar> <s:Rect height="10" /><!-- spacer --> <s:Label text="Horizontal Position" /> <s:ButtonBar id="horizontalPositionButtonBar" requireSelection="true"> <s:dataProvider> <s:ArrayList> <fx:String>before</fx:String> <fx:String>start</fx:String> <fx:String>middle</fx:String> <fx:String>end</fx:String> <fx:String>after</fx:String> </s:ArrayList> </s:dataProvider> </s:ButtonBar> <s:Rect height="10" /><!-- spacer --> <s:Group width="100%" height="100%"> <s:Button id = "openButton" verticalCenter = "0" horizontalCenter = "0" label = "Callout owner" /> </s:Group> </s:View>

先述の処理は、CalloutButtonコンポーネントを使用することで、コードをさらにシンプルにすることができます。次のコードは、CalloutSampleView02.mxmlを簡略化したCalloutButtonのサンプルです。

ソースコード:CalloutSample/src/views/CalloutSampleView03.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Callout Sample 03 --> <s:View xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" title = "Callout Sample 03" > <s:actionContent> <s:Button label="next" click="navigator.pushView(CalloutSampleView01)" /> </s:actionContent> <s:layout> <s:VerticalLayout paddingLeft = "10" paddingTop = "10" paddingRight = "10" paddingBottom = "10" /> </s:layout> <s:Label text="Vertical Position" /> <s:ButtonBar id="verticalPositionButtonBar" requireSelection="true"> <s:dataProvider> <s:ArrayList> <fx:String>before</fx:String> <fx:String>start</fx:String> <fx:String>middle</fx:String> <fx:String>end</fx:String> <fx:String>after</fx:String> </s:ArrayList> </s:dataProvider> </s:ButtonBar> <s:Rect height="10" /><!-- spacer --> <s:Label text="Horizontal Position" /> <s:ButtonBar id="horizontalPositionButtonBar" requireSelection="true"> <s:dataProvider> <s:ArrayList> <fx:String>before</fx:String> <fx:String>start</fx:String> <fx:String>middle</fx:String> <fx:String>end</fx:String> <fx:String>after</fx:String> </s:ArrayList> </s:dataProvider> </s:ButtonBar> <s:Rect height="10" /><!-- spacer --> <s:Group width="100%" height="100%"> <s:CalloutButton id = "callout" verticalCenter = "0" horizontalCenter = "0" verticalPosition = "{verticalPositionButtonBar.selectedItem}" horizontalPosition = "{horizontalPositionButtonBar.selectedItem}" label = "Callout owner" > <s:calloutLayout> <s:HorizontalLayout paddingLeft = "5" paddingTop = "5" paddingRight = "5" paddingBottom = "5" /> </s:calloutLayout> <s:calloutContent> <s:Button width="100" label="ok" /> <s:Button width="100" label="cancel" /> </s:calloutContent> </s:CalloutButton> </s:Group> </s:View>

オーナーがボタンの場合の実装方法として2通り紹介しました。最初のDropDownControllerクラスを利用した方法は、CalloutButtonコンポーネントの仕組みを自前で実装しています。通常は、CalloutButtonコンポーネントを使えば特に問題ありませんが、その内部的な仕組みも知っておいた方が、いざというときに応用が効くので覚えておきましょう。

SplitViewNavigatorコンポーネント

SplitViewNavigatorコンポーネントは、複数のViewNavigatorコンポーネントを管理するコンテナです。デバイスの画面の向きが、portrait(縦長)状態、landscape(横長)状態のときに、それぞれ異なった画面分割方法を提供します。

SplitViewNavigatorコンポーネントの基本的な使用方法は、2つのViewNavigatorコンポーネントを配置して、autoHideFirstViewNavigatorプロパティ値にtrueを指定します。autoHideFirstViewNavigatorプロパティ値と、2つのViewNavigatorコンポーネントとの関係は図4の通りです(1つ目と2つ目とで、表示状態が異なります)。

次のコードは、SplitViewNavigatorコンポーネントを使った、シンプルなサンプルです(サンプル:SplitViewNavigatorSample.fxp)

ソースコード:SplitViewNavigatorSample/src/SplitViewNavigatorSample.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- SplitViewNavigator Sample --> <s:Application xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" resize = "resizeHandler(event)" > <fx:Script> <![CDATA[ import mx.events.ResizeEvent; protected function resizeHandler(event:ResizeEvent):void { currentState = aspectRatio; } protected function clickHandler(event:MouseEvent):void { sn.showFirstViewNavigatorInPopUp(nb); } ]]> </fx:Script> <s:states> <s:State name="portrait"/> <s:State name="landscape"/> </s:states> <s:SplitViewNavigator id = "sn" width = "100%" height = "100%" autoHideFirstViewNavigator = "true" > <s:ViewNavigator width="256" height="100%" firstView="views.MenuView" /> <s:ViewNavigator width="100%" height="100%" firstView="views.DetailView"> <s:actionContent.portrait> <s:Button id="nb" label="show menu" click="clickHandler(event)" /> </s:actionContent.portrait> </s:ViewNavigator> </s:SplitViewNavigator> </s:Application>

ソースコード:SplitViewNavigatorSample/src/views/MenuView.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Menu View --> <s:View xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" title = "Menu View" > <fx:Script> <![CDATA[ import spark.components.SplitViewNavigator; import spark.components.ViewNavigator; import spark.events.IndexChangeEvent; protected function listChangeHandler(event:IndexChangeEvent):void { var sn :SplitViewNavigator = navigator.parentNavigator as SplitViewNavigator; var vn :ViewNavigator = sn.getViewNavigatorAt(1) as ViewNavigator; vn.pushView(DetailView, list.selectedItem); } ]]> </fx:Script> <fx:Declarations> … </s:ArrayList> </fx:Declarations> <s:List id="list" width="100%" height="100%" dataProvider="{listData}" change="listChangeHandler(event)" /> </s:View>

ソースコード:SplitViewNavigatorSample/src/views/DetailView.mxml

<?xml version="1.0" encoding="utf-8"?> <!--- Detail View --> <s:View xmlns:fx = "http://ns.adobe.com/mxml/2009" xmlns:s = "library://ns.adobe.com/flex/spark" title = "{new ObjectProxy(data).label}" > <fx:Script> <![CDATA[ import mx.utils.ObjectProxy; ]]> </fx:Script> <s:Rect width="100%" height="100%"> <s:fill> <s:SolidColor color="0x333333" /> </s:fill> </s:Rect> <s:Image left = "0" top = "0" right = "0" bottom = "0" source = "{new ObjectProxy(data).data}" fillMode = "clip" smooth = "true" /> </s:View>

autoHideFirstViewNavigatorプロパティ値がtrueで、かつデバイスの画面の向きがportrait状態のときに、1つ目のViewNavigatorは自動的に隠れますが、showFirstViewNavigatorInPopUp()メソッドを使ってCallout状態で表示することも可能です。

おわりに

以上が Flex 4.6 SDK から採用された新しいモバイルコンポーネントについての紹介でした。CalloutとSpritViewNavigatorコンポーネントの実装は、コードを見ていただくと分かると思いますが、少しだけ大変です。ですが、これらのチュートリアルを確認し慣れることによって、簡単にFlexモバイルアプリケーションに組み込むことができるでしょう。

関連記事