ページ

2016年11月23日水曜日

Android DataBinding を使ってみる3 - MVVMパターン -


前回はイベントのBindingの基本的な所を
やりましたが、今回はDataBindingを使ってMVVM(Model-View-ViewModel)をやってみます。
サンプルとしては、EditTextに入力した内容を
リアルタイムにTextViewに反映させる定番のやつです。
実際の動作は↓のようになります。
gif1

環境


Android Studio 2.2.2
Gradle plugin 2.2.2

実装方法


まずはレイアウトファイルですが、↓のようになってます。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="field" type="com.example.slowhand.databindingsample.EditField"/>
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="20dp"
        android:orientation="vertical">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@={field.text}"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{field.text}"/>

    </LinearLayout>
</layout>
ここでEditText@={field.text}が通常の@{field.text}とは違って
=が付いています。これはAndroid Studio 2.1 Preview 3から使える双方向のDataBindingです。
双方向というのが、getterだけでなくEditTextでユーザーが変更した場合setterも呼ばれます。
こうする事で入力した内容を即座にtextプロパティにセットしTextViewで表示させる事が出来ます。

  • EditFieldクラス
双方向を実現する為の実装としては2通りあります。
まずObservableFieldを使う方法。
public class EditField {

    public final ObservableField<String> text =
            new ObservableField<String>();
}
こうする事でtextが変更されると自動でUIに通知してくれます。
次にBaseObservableクラスを継承しオブザーバブル オブジェクトを作成する場合。
public class EditField extends BaseObservable {

   @Bindable
   private String text;

   public String getText() {
       return text;
   }

   public void setText(String text) {
       this.text = text;
       notifyPropertyChanged(BR.text);
   }
}
@BindableアノテーションをGetterまたはプロパティにセットし、
setTextが呼ばれる(EditTextが更新された)時にnotifyPropertyChanged(BR.text);
通知をしています。

2016年11月19日土曜日

Android DataBinding を使ってみる2 - イベント処理 -


今回はイベント処理のBindingです。
シンプルなクリック時のイベントをBindingさせる方法は以下の2通りです。
  • メソッド参照
    クラス内のメソッドを参照し、イベントとしてBindingさせます。
  • リスナーバインディング
    イベント発生時に実行するBinding

メソッド参照


ボタンをクリックしたら↓のHandlersクラスのonClickEventメソッドが呼ばれるようにします。
package com.example.slowhand.databindingsample;

import android.util.Log;
import android.view.View;

public class Handlers {

    private static final String TAG = "Handlers";

    public void onClickEvent(View view) {
        Log.d(TAG, "onClickEvent");
    }
}
メソッドの書式としては引数にViewを取るメソッドを定義します。
ちなみにViewを引数に取らなかったり、そもそも無いメソッドを定義すると
以下のようなエラーが発生します。
Error:(16, 32) Listener class android.view.View.OnClickListener with method onClick did not match signature of any method handlers.XXXX
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
レイアウトファイル(activity_main.xml)は以下のようになります。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="handlers" type="com.example.slowhand.databindingsample.Handlers"/>
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="20dp"
        android:orientation="vertical">
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:onClick="@{handlers.onClickEvent}"/>

    </LinearLayout>
</layout>
Activity側ではレイアウトファイル(activity_main.xml)用のBindingクラスを取得し、
<variable>で定義したhandlersをセットしてやります。
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setHandlers(new Handlers());
    }
}
実行結果
gif1

リスナーバインディング


メソッド参照と似てますが、レイアウトファイル内にラムダ式で表現できます。
↑と同じ事をリスナーバインディングで書くと、
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="handlers" type="com.example.slowhand.databindingsample.Handlers"/>
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="20dp"
        android:orientation="vertical">

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:onClick="@{(v) -> handlers.onClickEvent(v)}"/>

    </LinearLayout>
</layout>
このように書けます。
式内では、特定の演算子が使えたりするので、
<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/app_name"
    android:onClick="@{(v) -> XXXX ? handlers.onClickEvent(v) : void}"/>
このように何らかの条件でイベント発生の切り替えが出来たりします。
イベントを発生したく無い場合はvoidを指定します。

2016年11月13日日曜日

Android DataBinding を使ってみる


Data Binding以前解説したButterknifeと同等の機能を持つ
公式のライブラリーです。

インストール


まずは、サンプルのプロジェクトを作成したいと思います。
プロジェクトの構成は以下の様にしました。
  • 起動時のActivityクラス
    MainActivity
  • レイアウトファイル
    activity_main.xml
  • パッケージ名
    com.example.slowhand.databindingsample
プロジェクトが作成されたら、appのbuild.gradleに以下を追加します。
android {
    ....
    dataBinding {
        enabled = true
    }
}
あとはGradle Syncして完了です。簡単

実装と動作確認


例えば、↓のようなレイアウトファイル(activity_main.xml)の場合
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text_view1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/text_view2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/text_view3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
  • ButterKnifeの場合
@Bind(R.id.text_view1)
TextView mTextView1;

@Bind(R.id.text_view2)
TextView mTextView2;

@Bind(R.id.text_view3)
TextView mTextView3;
このように実装してリソースIDと変数を結びつけます。
  • Data Bindingの場合
まずレイアウトファイルの要素を<layout>で囲ってやります。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical">
      ・・・省略・・・
  </LinearLayout>
</layout>
次にActivity側でレイアウトファイル(activity_main.xml)に対応した
ActivityMainBindingクラスが作成されるので、それを使って
値を設定します。※レイアウトファイル名に依存
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.textView1.setText("hoge1");
        binding.textView2.setText("hoge2");
        binding.textView3.setText("hoge3");
    }
}
動作結果
img1
TextViewに値が反映されています。
次はデータクラス(POJO)をバインドさせてみたいと思います。
public class Item {

    private String mId;
    private String mName;
    private String mValue;

    public void setId(String id) {
        mId = id;
    }

    public String getId() {
        return mId;
    }

    public void setName(String name) {
        mName = name;
    }

    public String getName() {
        return mName;
    }

    public void setValue(String value) {
        mValue = value;
    }

    public String getValue() {
        return mValue;
    }
}
例えば上の様なクラスの値をTextViewに反映させたい場合、
レイアウトファイルを以下の様に変更します。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="item" type="com.example.slowhand.databindingsample.Item"/>
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/text_view1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{item.id}"/>

        <TextView
            android:id="@+id/text_view2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{item.name}"/>

        <TextView
            android:id="@+id/text_view3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{item.value}"/>

    </LinearLayout>
</layout>
<data>
    <variable name="item" type="com.example.slowhand.databindingsample.Item"/>
</data>
<variable>で作成したデータクラスを定義し、<data>で囲みます。
すると@{item.value}としてレイアウト内で使える様になります。
動作結果
img2