Android Data Bindingを試してみた

最近かなり来そうな、Data Bindingを軽めに試してみたので備忘録です。

#Data Binding
Butter Knife等使わない場合いままではxmlからviewを作り出し、javaのコード上でfindViewByIdして値を設定していました。
Data bindingはxmlにデータの設定を書けるのでviewとコードの分離が楽になるのと、xmlのレイアウトを使いまわした場合に複数の箇所でで同じようなセットするコードを書かなくていいのですごく楽になります。
現在はbeta版です。
公式の[Data Binding Guide](http://developer.android.com/intl/ja/tools/data-binding/guide.html#studio_support)を参考にしてます。
#準備
Android 2.1以降、Gradle 1.5.0-alpha1以上が必要になります。
build.gradleに下記を記述します。ちょっと前は```classpath "com.android.databinding:dataBinder:```のような記述が必要だったらしいですが、Android Studio13からはいらないです。

``` build.gradle
android {
    dataBinding {
        enabled = true
    }
}
```

#viewにデータをセットする
とりあえずViewにデータをいれるところまでやってみます。
layoutで全体を囲い、<data></data>の中にセットするjavaコードを書いて紐付けて、Viewの必要な箇所で```"@{}"```の中にデータ取得処理などを書きます。


```xml:xml
<?xml version="1.0" encoding="utf-8"?>
<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>
```

```java:model
public class User {
   private final String firstName;
   private final 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;
   }
}
```
viewを表示するActivityで```DataBindingUtil.setContentView```を使ってBidingクラスを取得してxmlで定義してある必要なデータをセットします。
setContentViewをしたxmlの名前を[キャメルケースにしたもの+Binding]がbindingのクラス名になります。

```java:Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user = new User("Test", "User");
   binding.setUser(user);
}
```
結果
<img width="200" alt="実行結果1" src="https://qiita-image-store.s3.amazonaws.com/0/34084/85900c2e-4b7a-57c4-2487-559d42f9c8d7.png">

#onClick
```android:onClick``` にviewがクリックされた時に呼び出すイベントを設定できます。


```xml
<data>
       <variable name="handlers" type="com.example.MainActivity.MyHandlers"/>
       <variable name="user" type="com.example.User"/>
</data>
<TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers.onClickFriend}"/>       
```

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user = new User("Test", "User");
   binding.setUser(user);
   binding.setHandlers(new MyHandlers());
}
 public class MyHandlers{
        public void onClickFriend(View view) {
            Toast.makeText(MainActivity.this, "friend click", Toast.LENGTH_LONG).show();
        }
}
```
#条件分岐
簡単な条件分岐を試してみました。
モデルに条件判定メソッドを追加してそれをxml側で読んでVisibleを設定します。
まずモデルに年齢の変数と、isAgeという年齢が20歳以上だったらtrueを返すメソッドを追加してそれをxml側で呼び出します。

```java:model
public class User {
   private final String firstName;
   private final String lastName;
   private final int age;
   public User(String firstName, String lastName, int age) {
       this.firstName = firstName;
       this.lastName = lastName;
       this.age = age;
   }
   public String getFirstName() {
       return this.firstName;
   }
   public String getLastName() {
       return this.lastName;
   }

    public boolean isAdult() {
        return age >= 20;
    }
}
```
値の読み出しと同じようにUserもでるのisAdultを呼び出して三項演算子のように書けます。
View.VISIBLEの値を取得するために```<data>```に ```<import type="android.view.View"/>``` を追加してます。


```xml:xml
<data>
       <variable name="handlers" type="com.example.MainActivity.MyHandlers"/>
       <variable name="user" type="com.example.User"/>
       <import type="android.view.View"/>
</data>
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{handlers.onClickUser}"
            android:text="@{user.firstName}"
            android:textSize="20sp" />

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

        <ImageView
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:src="@mipmap/ic_launcher"
            android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}" />
        
</LinearLayout>
```

#値の更新
DataBindingUtilで作成したインスタンスに再度必要なオブジェクトを渡すとviewも当然更新されます。
※Observableも使う方法もあります(機会があれば別途書きます)

```java
binding.setUser(new User("hoge", "fuga", 21));
```
xmlにidを付けておくとbindingオブジェクトでも直接Viewにアクセスできる変数ができます。

```xml:xml
<TextView
            android:id="@+id/firstName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{handlers.onClickUser}"
            android:text="@{user.firstName}"
            android:textSize="20sp" />
```

```java:java
binding.firstName.setText("hogehoge");
```

※変数名はidと同じ名前になるようですがスネークケースで書いた場合はキャメルケースになるようです。上記の場合にidをfirst_nameにしても変数はfirstNameになります。