Android Binding Markup Demoを読み解く 〜リストクリックからイベント駆動まで〜

ようやくスタートラインに立ったので早速スタートを切ります。リストをクリックしてからクリックイベントを駆動させるまでを読み解きます。

と、その前に。なんとこのデモ、これまで作成してきたApplicationクラス (のサブクラス) がありません。えぇ〜って感じなんですけど。実は別にApplicationクラスで初期化しなくてもよくって、このデモでは最初に起動するActivityのonCreateで初期化しています。そんなのありなんですね。私はApplicationクラスで初期化するのが好きですけどねっ!

話を元に戻します。どこから見ていこうかなぁ、ということで最初に起動するActivityのコードから見ていきます。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Binder.init(this.getApplication());

    Demos.add(new Demo("View", true));
    Demos.add(new Demo("TextView"));
    Demos.add(new Demo("ImageView"));
    Demos.add(new Demo("ProgressBar"));
    Demos.add(new Demo("SeekBar", true));
    Demos.add(new Demo("RatingBar"));
    Demos.add(new Demo("CompoundButton"));
    Demos.add(new Demo("SpinnerWithArraySource"));
    Demos.add(new Demo("ListViewWithCursorSource"));
    Demos.add(new Demo("MasterDetailListView"));
    Demos.add(new Demo("NestedCursor"));
    Demos.add(new Demo("MultipleAdapters"));

    Binder.setAndBindContentView(this, R.layout.select_demo, this);
}


Android Bindingフレームワーク初期化のところはいいとして、問題は次のブロックです。Demosというオブジェクトに何かしきりにaddしています。というわけでDemosの定義を見てみます。

public final ArrayListObservable<Demo> Demos = new ArrayListObservable<Demo>(Demo.class);


なるほど、ここでオブザーバが出てきました。それも勉強したばっかりのArrayListObservableです♪リストの中身の要素はDemoクラスのインスタンスです。とうわけでなし崩し的にDemoクラスを見てみます。

public static class Demo{
    public StringObservable Name = new StringObservable();
    public BooleanObservable NewAddition = new BooleanObservable();
    public Demo(String name, boolean newAddition){
        Name.set(name);
        NewAddition.set(newAddition);
    }
    public Demo(String name){
        this(name, false);
    }
}


こちらはObservableオブジェクトを二つ持ったクラスです。StringObservableとBooleanObservable。これらのオブジェクトはもちろんレイアウト定義 (View) から参照されますが、それはまた後ほど。

これで一応View Modelの側は理解出来ました。というわけで次はView (select_demo.xml) です。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:binding="http://www.gueei.com/android-binding/"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <ListView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        binding:itemSource="Demos"
        binding:itemTemplate="@layout/demo_list_item"
        binding:onItemClicked="ViewDemo"
        binding:clickedItem="SelectedDemo" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="About..."
        binding:onClick="ShowAbout" />

</LinearLayout>


ListViewとButtonが縦方向に配置されています。とりあえずButtonは置いておいて、ListViewに集中します。binding:itemSourcebinding:itemTemplateもまさに先ほど勉強した内容です。当然itemSourceには上で記載したDemosというView Modelオブジェクトが指定されています。

ここで新たに出て来たのがbinding:onItemClickedbindingo:clickedItem。この二つの属性が何を意味するのかきちんと書いてあるところが見つけられなかったのであくまで推測ですが、これらの属性の役割はずばり以下の通りでしょう。

属性 役割
binding:onItemClicked 任意の行がクリックされた時のCommand指定
binding:clickedItem クリックされた行に対応するsourceを入れておくオブジェクト指定


まずはonItemClickedにバインドされているView Modelオブジェクト (Command) を見てみましょう。

public final Command ViewDemo = new Command(){
    public void Invoke(View view, Object... args) {
        if (SelectedDemo.get() == null) return;
        Demo selection = (Demo)SelectedDemo.get();
        Intent intent = new Intent(Launch.this, ViewDemoActivity.class);
        intent.putExtra("DEMO", selection.Name.get());
        Launch.this.startActivity(intent);
    }
};


SelectedDemoはbinding:clickedItemにバインドされたView Modelオブジェクト、つまりタップされた行に対応するDemoオブジェクトを保持しています。

そこまで分かれば後は簡単。Demoオブジェクトから名前を取り出して、Intentの引数に設定して飛ばしています。つまり画面遷移させているんですね。

ではSelectedDemoの定義を見てみましょう。

public final Observable<Object> SelectedDemo = new Observable<Object>(Object.class);


何コレ〜!すごい単純な定義じゃない!!Android Bindingでは、選択された行の内容はこうやって取ってくるんですね。勉強になりました。

それでは最後にListViewの各行のレイアウト定義 (demo_list_item.xml) を見てみましょう。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:binding="http://www.gueei.com/android-binding/"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <TextView 
        android:id="@android:id/text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:gravity="center_vertical"
        android:paddingLeft="6dip"
        android:minHeight="?android:attr/listPreferredItemHeight"
        binding:text="Name" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="10dip"
        android:textColor="#f00"
        binding:visibility="NewAddition"
        binding:text="HTML('&lt;sup&gt;*new&lt;/sup&gt;')" />

</LinearLayout>


ここで面白いのはリストの各行のView Modelの指定の仕方です。それぞれ以下のように指定しています。

binding:text="Name"
binding:visibility="NewAddition"


つまり、リストの各行のView Modelは親のListViewがsourceにバインドしていたリストの対応する項目内に定義してあるObservableオブジェクトを指定するわけです。・・・と言葉で書くといまいち伝わらないので、図にしてみました。


階層関係が一致しているんですね。細かいところはさておき、大枠は掴めました。

次はListViewにCursorオブジェクトをバインドする方法を調べてみようかな。