Android + Kotlin + DataBinding

この前、`Kotlin Android Extensions`を使って感動してたら、友達から、`DataBinding`使うのが昨今のAndroidプロジェクトでは一般的と教わったので、使ってみます。

## 環境
Android Studio 3.1.2

## DataBindingとは
`ViewModel`を作っておいて、そこの値を変えれば、`xml`上のプロパティに自動でアクセスしてくれるものらしい。`Activity`に、`View`へのアクセスをごちゃごちゃ書かなくてスッキリ管理しやすくなる模様。逆にプロパティが変わったら`ViewModel`の値を変えるということもできるらしい。

## 簡単なサンプルを作ってみる
今回は、`View`である`TextView`の`text`と、`ViewModel`の`name`を紐付けしてみたいと思います。

`MyAppA`という名前でプロジェクトを作ったら、`build.gradle`に2箇所追加します。

```kotlin:app/build.gradle
apply plugin: 'kotlin-kapt' // ここを追記

android {
    ...
    // ここを追記
    dataBinding {
        enabled = true
    }
}

```

続いて、viewmodelフォルダを作成し、その中に `MainViewModel` クラスを作成。

```kotlin:MainViewModel.kt
package com.funeasysoft.myappa.viewmodel

class MainViewModel {
    var name = "funeasy"
}
```
紐づけるxmlファイルを編集。

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.funeasysoft.myappa.viewmodel.MainViewModel" />
    </data>

    <android.support.constraint.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.name}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </android.support.constraint.ConstraintLayout>
</layout>
```

元々のLayoutを、`<Layout>` で囲み、`<data>`を追加し、ViewModelクラスを指定している。また、今回は`TextView`の`text`とdataBindingするために、`android:text="@{viewModel.name}"`とします。

ここで注意が必要で、元々`ConstraintLayout`に付いている
`xmlns:android="http://schemas.android.com/apk/res/android"`
`xmlns:tools="http://schemas.android.com/tools"`
は、消しておかないといけない。
消さないと以下のようなエラーになってしまう。

```
error: duplicate attribute.
Message{kind=ERROR, text=error: duplicate attribute., sources=[/Users/masa/DevAndroid/MyAppA/app/src/main/res/layout/activity_main.xml:13], original message=, tool name=Optional.of(AAPT)}
```

最後に`Activity`へ、関連付けします。

```kotlin:MainActivity.kt
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // setContentView(R.layout.activity_main) // コメントアウト

        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.viewModel = MainViewModel()
    }

```

実行すると、画面に、 `MainViewModel` クラスの `name` に入れた `funeasy` が表示されました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/68684/c887b434-0e6a-7fae-0114-d9454c387314.png)

## ボタンをクリック -> TextViewを変える

`buttonClicked()`を追加します。また、viewModel.name が変更されたら、表示もかわるように、`ObservableField("[初期値]")`に変更します。

```kotlin:MainViewModel.kt
package com.funeasysoft.myappa.viewmodel

import android.databinding.ObservableField

class MainViewModel {
    var name = ObservableField("funeasy")

    fun buttonClicked() {
        name.set("Button clicked!")
    }
}
```

`Button`を追加、`android:onClick="@{() -> viewModel.buttonClicked()}"`を設定しておく。


```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.funeasysoft.myappa.viewmodel.MainViewModel" />
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/textView"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="@{viewModel.name}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="Button"
            android:onClick="@{() -> viewModel.buttonClicked()}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

    </android.support.constraint.ConstraintLayout>
</layout>
```

ボタンをタップすると、確かにテキストが変更されました！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/68684/8fab5c31-2620-0ecc-a714-85ce0783e715.png)

今後色々と`MainViewModel`をカスタムして、どれだけ楽になるのかみていこうと思います。