アクセシビリティ
廣畑 大雅 (taiga)氏

廣畑 大雅 (taiga)氏

taiga.jp

作成日:
2011年12月7日
ユーザレベル:
初級、中級
製品:
Flex
Flash Builder

Flex 4.6 SDKの新機能 後編 Callout/SplitViewNavigator

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

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

サンプルデータ:Flex46sample02.zip

 

必要なもの

Flash Builder 4.6


Flex SDK 4.6.0.23201


 

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では、これらの表示パターンを確認できます。

図1 verticalPosition/horizontalPositionプロパティの定数が示す位置

図1 verticalPosition/horizontalPositionプロパティの定数が示す位置

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>

図2 CalloutSampleView01.mxmlの出力結果。TextInputをオーナーとしてCalloutを表示したところ

図2 CalloutSampleView01.mxmlの出力結果。TextInputをオーナーとして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>

図3 CalloutSampleView02.mxmlの出力結果。Button をオーナーとしてCalloutを表示したところ

図3 CalloutSampleView02.mxmlの出力結果。Button をオーナーとしてCalloutを表示したところ

先述の処理は、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つ目とで、表示状態が異なります)。

図4 autoHideFirstViewNavigatorプロパティ値と、2つのViewNavigatorコンポーネントとの関係

図4 autoHideFirstViewNavigatorプロパティ値と、2つのViewNavigatorコンポーネントとの関係

次のコードは、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状態で表示することも可能です。

図5 SplitViewNavigator.mxmlの出力結果。portrait状態、1つ目のViewNavigatorは非表示

図5 SplitViewNavigator.mxmlの出力結果。portrait状態、1つ目のViewNavigatorは非表示

図6 SplitViewNavigator.mxmlの出力結果。portrait状態、1つ目のViewNavigatorをCallout表示

図6 SplitViewNavigator.mxmlの出力結果。portrait状態、1つ目のViewNavigatorをCallout表示

図7 SplitViewNavigator.mxmlの出力結果。landscape状態、2つ目のViewNavigatorが常に表示される

図7 SplitViewNavigator.mxmlの出力結果。landscape状態、2つ目のViewNavigatorが常に表示される

 

おわりに

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

 

関連記事

  • ADC MEETUP ROUND03 レポート Flash SESSION4:What's new in Flex 4.6 SDK
  •  

     

    著者について

    廣畑 大雅 (taiga)氏
    taiga.jp

    フリーランスの Web ディレクター/デザイナー、アーキテクト。
    Flash MX の頃から Flash アプリケーションの開発に携わり、現在も Flash コンテンツ、Flexアプリケーションの設計/開発を主な業務としている。

    Adobe Community Professional
    Adobe 公式ユーザーグループ「F-site」運営スタッフ
    Adobe 公式ユーザーグループ「FxUG」運営スタッフ
    クラスメソッド株式会社 プリンシパル