心魅 - cocoromi -

半角スペース時々全角

statesプロパティを使って、UIの状態遷移を管理する

アプリケーションの状態に応じてUIを変化させたいという需要はなかなか多いような気がする。

僕はすぐにTabBarとViewStackを使ってしまうんだが、今回はUIComponentのプロパティであるstatesを使ってみようと思う。

サンプル


まずはstatesを使った簡単な例を
http://tsuyabu.in/~umezo/samples/flex/state/StateTest.swf

Main.mxml

<?xml version="1.0"?>
<mx:Application
  xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:states>
    <mx:State name="FirstState">
      <mx:RemoveChild target="{btn}"></mx:RemoveChild>
      <mx:AddChild relativeTo="{this}" position="lastChild">
        <mx:target>
          <mx:Panel title="FirstState">
            <mx:Button label="State" click="this.currentState = '' " />
          </mx:Panel>
        </mx:target>
      </mx:AddChild>
    </mx:State>
  </mx:states>
  <mx:Button id="btn" label="FirstState" click="this.currentState = 'FirstState'" />
</mx:Application>

ボタンをクリックする毎に初期状態とFirstStateという状態を行ったり来たりするという物になっている。
ほとんどASを書かなくても、UIを変化させられるところが特徴。


次に状態をどのように定義しているのかを見ていく。

初期状態の定義


初期状態の定義は簡単で以外の部分が初期状態となる。

上の例で言うとこの部分になる。

  <mx:Button id="btn" label="FirstStateへ" click="this.currentState = 'FirstState'" />

つまり、このmxmlの初期状態はボタンが1つだけ配置されている状態となる。


他の状態の定義


初期状態以外の定義はstatesプロパティの中にStateタグを記述していくことで定義できる。
状態は複数定義することも出来る。
このときStateのnameプロパティを任意の名前に設定しておくことで、状態遷移を起こすことが出来る。
というわけで、必ずnameプロパティを設定しておこう。


記述例

  <mx:states>
    <mx:State name="FirstState">
         ...
    </mx:State>
    <mx:State name="SecondState">
         ...
    </mx:State>
    <mx:State name="ThirdState">
         ...
    </mx:State>
  </mx:states>


状態を遷移させる


さて定義した状態間を遷移するためにはUIComponentのcurrentStateプロパティに状態の名前を代入する。

たとえば以下のように書くと、現在の状態からFirstStateという状態に遷移する。

this.currentState = "FirstState";


初期状態を含めた4つの状態を遷移するサンプル
http://tsuyabu.in/~umezo/samples/flex/state/StateTest2.swf

Main.xml

<?xml version="1.0"?>
<mx:Application
  xmlns:mx="http://www.adobe.com/2006/mxml">
  <mx:states>
    <mx:State name="FirstState">
      <mx:AddChild relativeTo="{this}" position="lastChild">
        <mx:target>
          <mx:Panel title="FirstState">
            <mx:Button label="to Default State" click="this.currentState = '' " />
          </mx:Panel>
        </mx:target>
      </mx:AddChild>
    </mx:State>
    <mx:State name="SecondState">
      <mx:AddChild relativeTo="{this}" position="lastChild">
        <mx:target>
          <mx:TextArea text="Second State"/>
        </mx:target>
      </mx:AddChild>
    </mx:State>
    <mx:State name="ThirdState">
      <mx:AddChild relativeTo="{this}" position="lastChild">
        <mx:target>
          <mx:TitleWindow title="Third State"></mx:TitleWindow>
        </mx:target>
      </mx:AddChild>
    </mx:State>
  </mx:states>
  
  <mx:Button label="to Default State" click="this.currentState = ''" />
  <mx:Button label="to First State" click="this.currentState = 'FirstState'" />
  <mx:Button label="to Second State" click="this.currentState = 'SecondState'" />
  <mx:Button label="to Third State" click="this.currentState = 'ThirdState'" />
</mx:Application>


最後にStateタグの中身に何を書いたらいいのか見ておこう。


Stateの中身


Stateの中身には、"初期状態からどのように変化したらその状態になるのか"を書く。
#正確にはこの表現は嘘で、初期状態以外からの遷移も定義することが出来る。が、今回はふれない。

自分で言っていて意味不明である。

初っぱなの例を参考にしよう。

  <mx:RemoveChild target="{btn}"></mx:RemoveChild>
  <mx:AddChild relativeTo="{this}" position="lastChild">
    <mx:target>
      <mx:Panel title="FirstState">
        <mx:Button label="State" click="this.currentState = '' " />
      </mx:Panel>
    </mx:target>
  </mx:AddChild>

この部分がStateの中身になる。
まず、コレの意味を説明すると、

  1. 初期状態からbtnを取り除く
  2. thisの一番最後にmx:target以下のコンポーネントを追加する

という意味になっている。

と、このように、初期状態からいらない物をRemoveChildタグで定義し、新たに追加するものとAddChildタグに定義することで、状態遷移を定義する。
Stateタグの中で使える物には以下のようなものもある

注意


currentStateには一つしか状態を設定できない


まぁ当たり前と言えば当たり前なんだが、後読みでデータがそろった順にコンポーネントを表示したい時なんかはStateで管理しずらい。
#どのコンポーネントから準備が整うかわからないため、中間の状態が一意に定義出来ない。


複雑な状態遷移の場合はあらかじめ設計を考える


states要素はmxmlのルート要素にしか定義できない。
このため、子供のコンポーネントのstatesを設定したい場合は、必ず別ファイルに定義することになる。
こうなってくると、はじめから計画的に、状態を設計しておく必要が出てくる。
#別ファイルにするとidプロパティでのアクセスが出来なくなるので、eventを定義するなどしなければならなくなる。


まとめ


statesプロパティを使うとUIの状態遷移を管理出来る。
一方で複雑な状態を管理するには、それなりの準備が必要になる。
簡単な状態遷移の場合は率先して使っても良いかも。


今回説明していないがTransitionが使えるようになることが、最大のメリットな気がする。
Transitionについては後日、書きます。