読者です 読者をやめる 読者になる 読者になる

ぱんくずリスト

気になる話をちらほらと。

サンプルから学ぶDataBinding その1

1ヶ月ぶりに記事を書いたパンクズです。
最近は就活で忙しかったのですが、少し時間が取れたので、 前から気になっていたAndroidのDataBindingについて備忘録としてまとめてみました。

DataBindingとは

レイアウトファイル(res/layout/*.xml)に直接データを定義することで、findViewByIdを使わずとも簡単にデータにアクセスできるようになる機能。

似たような機能をButterKnifeも持っていますが、それよりもさらに簡潔に書けるそうです。

導入方法

Android2.1(APIレベル7)以降、Gradle 1.5.0-alpha1以降であることが前提条件となります。

DataBindingを使えるようにするにはappフォルダ直下にあるbuild.gradleに以下の文を加えるだけで使えるようになります。

android {
    ...
    dataBinding {
       enabled = true
    }
}

サンプル

例に挙げているものはドキュメントのサンプルです。

まずはモデルを定義します。
モデルはpublicなフィールドか、getterがなければエラーを吐きます。

//User.java
public class User {
    private String firstName;
    private String lastName;
 
    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    public String getFirstName() {
        return this.firstName;
    }
    public String getLastName() {
        return this.lastName;
    }
}

or

public class User {
    public String firstName;
    public String lastName;
 
    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

レイアウトは<layout>で全体を囲い、読み込みたいデータを<data>内の<variable>で定義し、Viewの必要な箇所で"@{}"の中に処理を書きます。

<!-- main_activity.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <!-- データの定義 -->
        <variable name="user" type="com.example.User"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"/>
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"/>
    </LinearLayout>
</layout>

これでレイアウト内にデータが定義できました。 続いては、実際にデータをセットしてみます。

DataBindingUtil.setContentViewからBindingクラスを入手し、set***(variableで定義したname、ここではUser)を使ってデータをセットします。
Bindingクラスは [レイアウト名のパスカルケース] + Binding という名前で自動生成されます。
(例:main_activity.xml → MainActivityBinding)

//MainActivity.java
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
       User user = new User("Hoge", "Fuga");
       binding.setUser(user);
    }
}

以下のように表示されれば完了です。
f:id:pankuz:20161207152500p:plain

値の更新

続いては値の更新の仕方についてです。

オブジェクトを渡す

set***にモデルクラスをセットする方法です。
何も考えなくてよいので実装は簡単ですが、更新するたびにオブジェクトを生成しなければならないのであまり良い方法ではありませんね。

   binding.setUser(new User("hoge","fuga"));

出力
hoge
fugaと表示されます。

ObservableFieldを使う

モデルのフィールドをObservableFieldクラスで宣言する方法です。 データが変更されたら自動的に更新してくれます。

public class User {
    private ObservableField firstName = new ObservableField();
    private ObservableField lastName = new ObservableField();

    public User(String firstName, String lastName) {
        this.firstName.set(firstName);
        this.lastName.set(lastName);
    }
    public ObservableField getFirstName() {
        return firstName;
    }
    public ObservableField getLastName() {
        return lastName;
    }
}
//MainActivity.java
   ...
   User user = new User("Hoge","Fuga");
   binding.setUser(user);
   user.getFirstName().set("test");

出力
test
Fugaと表示されます。

モデルでBaseObservableを継承する

モデルクラスでBaseObservableを継承し、変更したいプロパティに@Bindableを付けます。 @Bindableのついたプロパティは、BRクラスのフィールドに追加され、notifyPropertyChangedに渡すとViewへ変更を通知します。

public class User extends BaseObservable {
    ...
    @Bindable
    public String getFirstName() {
        return firstName;
    }
    @Bindable
    public String getLastName() {
        return lastName;
    }
    
    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }
}
    ...
    User user = new User("Hoge","Fuga");
    binding.setUser(user);
    user.setFirstName("pan");
    user.setLastName("kuz");

出力
pan
kuzと表示されます。

補足

レイアウトで<data>を定義しなくても、各Viewの値を変更することができます。
ViewにIDを設定することでBindingクラスから直接アクセスできるようになります。

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- <data>の削除 -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/firstName" <!-- idの設定 -->
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""/>
        <TextView
            android:id="@+id/lastName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""/>
    </LinearLayout>
</layout>
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MainActivityBinding binding = DataBindingUtil.setContentView(this,R.layout.main_activity);
    //BindingクラスからViewの値を変更
    binding.firstName.setText("Hoge");
    binding.lastName.setText("Fuga");
}

出力
Hoge Fuga と表示されます。

その2へ

内容が長くなりそうだったので何回かに分けて書くことにしました。

次回はイベント処理について書いていきますのでよろしこ。

参考文献

Data Binding Guide Android Data Bindingを試してみた