なにこれ
- android-bindingという便利なフレームワークを使ってMVVCモデルでの開発をしてみようという趣旨
- C#やったことある人ならXAMLみたいに書けるよ、と言うとわかりやすいか
前提
- android-bindingのjarのある場所にビルドパスが通っていること
- mavenなどを使えば楽
Viewの定義
- AndroidではViewはActivityと等価と考えてしまって良いだろう
- ViewはXMLで定義することにする
- 下のXMLを見るとそれぞれのUIコンポーネントにidが振られていないことがわかる
- この部分がミソ
- 一般にAndroidではidを振っておいて以下の様なコードでUIコンポーネントのオブジェクトのインスタンスを手に入れて操作することが一般的
- その結果ViewであるActivityにプレゼンテーションロジックが溢れてActivityに書かれているコード量が多くなってしまう
1 mEditText = (EditText)findViewById(R.id.num1);
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:binding="http://www.gueei.com/android-binding/"
5 android:layout_width="fill_parent"
6 android:layout_height="fill_parent"
7 android:orientation="vertical">
8 <EditText
9 android:contentDescription="height"
10 android:layout_width="fill_parent"
11 android:layout_height="wrap_content"
12 android:inputType="number"
13 binding:text="num1"/>
14 <EditText
15 android:contentDescription="weight"
16 android:layout_width="fill_parent"
17 android:layout_height="wrap_content"
18 android:inputType="number"
19 binding:text="num2"/>
20 <EditText
21 android:contentDescription="weight"
22 android:layout_width="fill_parent"
23 android:layout_height="wrap_content"
24 android:inputType="number"
25 binding:text="answer"/>
26 <Button
27 android:contentDescription="calc"
28 android:layout_width="wrap_content"
29 android:layout_height="wrap_content"
30 android:layout_gravity="right"
31 android:text="calc"
32 binding:onClick="calculate"/>
33 </LinearLayout>
Activityの定義
- Activityには次のコードを書くだけで良い
- 最後の二行がポイント
意訳するとmain.xmlに書かれたActivityのデザイン設計を元にActivityのUIが実際に作られて、そこにMainViewModelというものがひもづけられた
- ActivityはViewだから見た目のことだけ気にしていればいい
1 public class MainActivity extends BindingActivity {
2 /**
3 * Called when the activity is first created.
4 */
5 @Override
6 public void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8
9 MainViewModel model = new MainViewModel();
10 setAndBindRootView(R.layout.main, model);
11 }
12 }
ViewModelの定義
- Activityは見た目の構造の事しか専念しなくなったので、実際にUIを変化させる仕事をする別のものが必要になる
最初のStringObservable型の3つのインスタンス変数がさきほどXMLに書いたEditTextに対応している
- Command型のインスタンス変数calculateは勘の良い人なら気づくだろうがbinding:onClick="calculate"と書かれているButtonに対応している。binding:onClickというからには後ろのInvokeメソッドがButtonをクリックすると実行される、という仕組みになっている。
Buttonのクリックされた時の仕事はCalcというクラスに定義されているcalcメソッドにEditTextの値を渡すことである。そして帰ってきた結果をanswerにセットしている。
ViewModelの仕事はあくまでViewから何か入力をとってきてそれをべつのクラスに投げ、結果をViewに通知することだけである。
- この入力を取ってくる、あるいはViewへ結果を通知する、という作業のためにデータバインドを使っているわけだ
1 public class BmiViewModel {
2
3 public final StringObservable num1 = new StringObservable();
4
5 public final StringObservable num2 = new StringObservable();
6
7 public final StringObservable answer = new StringObservable();
8
9 public final Command calculate = new Command() {
10 @Override
11 public void Invoke(View arg0, Object... arg1) {
12 double result = Calc.calc(Double.parseDouble(num1.get()), Double.parseDouble(num2.get()));
13 .set(String.format("%.1f", result));
14 }
15 };
16 }
Modelの定義
ViewModelから入力をもらって、実際に仕事をして結果を返すのがModelの役割である。
ViewModelからViewに仕事を移譲する
- Androidの場合、Contextがないとできない処理というのが結構ある
- この問題を解決するためのMessageパターンというデザインパターンを用いる
android-bindingではEventAggregatorというものが使える
まずViewでEventAggregatorを生成する
1 EventAggregator eventAggregator = EventAggregator.getInstance(this);
ViewModelにEventAggregatorのセッタを用意する
ViewからViewModelにEventAggregatorへの参照を渡す
1 model.setEventAggregator(eventAggregator);
- Viewのほうにsubscribeを実装する
ViewModelのほうにpublishを実装する
1 mEventAggregator.publish("hoge", MainActivityModel.this, null);
- このように記述するとpublishの第一引数が一致するようなsubscribeに対応するonEventTriggeredメソッドが呼び出される。
- onEventTriggeredメソッドはViewに実装されているのでここでContextを要するような処理を行えば良い