Kotlin × Data BindingでonItemClickListener付きListViewアプリを作る

KotlinとData Bindingを使ってListViewのアプリを作ります。
サンプルプロジェクトは、以下repositoryに格納しています。
https://github.com/shanonim/Kotlin-DataBinding-ListView

## KotlinでData Bindingを使うための設定
`build.gradle`に以下を追記します。

```build.gradle
apply plugin: 'kotlin-kapt'

android {
    dataBinding {
        enabled = true
    }
}

dependencies {
    kapt 'com.android.databinding:compiler:2.2.3'
}
```

`kapt 'com.android.databinding:compiler:`のversionは以下サイトを参考にしました。
https://bintray.com/android/android-tools/com.android.databinding.compiler

2017.02.11現在、stableの最新は`2.2.3`だったので、そちらを採用しています。
![スクリーンショット 2017-02-11 14.21.41.png](https://qiita-image-store.s3.amazonaws.com/0/38442/5618ed21-b7a8-83ab-2129-fa9f3840dd56.png)

## 表示するデータ
APIから取得するデータではなく、enumで定義したサンプルデータを表示します。

```KemonoData.kt
enum class KemonoData(val kemonoName: String, val kemonoWords: String) {
    SERVAL("サーバル", "サーバルキャットのサーバルだよ！"),
    FENNEC("フェネック", "君、やっぱイケてるねー"),
    COMMON_RACCOON("アライグマ", "アライさんにお任せなのだー！"),
    KABAN("かばん", "(・８・)")
}
```

```KemonoModel.kt
data class KemonoModel(val kemonoName: String, val kemonoWords: String)
```

## ソースコード
### `Activity`
```MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val binding : ActivityMainBinding
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        val listAdapter = KemonoAdapter(applicationContext)
        val list = mutableListOf<KemonoModel>()
        for (value in KemonoData.values()) {
            list.add(KemonoModel(value.kemonoName, value.kemonoWords))
        }
        listAdapter.kemonoFriends = list
        binding.listview.adapter = listAdapter

        binding.setOnItemClick { adapterView, view, position, l ->
            Toast.makeText(this, listAdapter.kemonoFriends[position].kemonoName, Toast.LENGTH_SHORT).show()
        }
    }
}
```

対応するxmlファイルは`activity_main.xml`です。
`<variable>`タグ内の`type`に`AdapterView.OnItemClickListener`をセットし、`ListView`の子要素に対する`onItemClick`を設定しています。

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

    <data>

        <variable
            name="onItemClick"
            type="android.widget.AdapterView.OnItemClickListener" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ListView
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            bind:onItemClickListener="@{onItemClick}" />

    </LinearLayout>
</layout>
```

### `item_list_view`
`ListView`の子要素のxmlファイルです。

```item_list_view.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="kemonoFriends"
            type="com.example.shanonim.kotlin_databinding_listview.model.KemonoModel" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="10dp"
        android:paddingLeft="20dp"
        android:paddingTop="10dp">

        <TextView
            android:id="@+id/text_view_kemono_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:width="500dp"
            android:height="30dp"
            android:gravity="center_vertical"
            android:text="@{kemonoFriends.kemonoName}"
            android:textColor="@android:color/black"
            android:textSize="20dp"
            tools:text="サーバル" />

        <TextView
            android:id="@+id/text_view_kemono_words"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/text_view_kemono_name"
            android:width="500dp"
            android:height="30dp"
            android:gravity="center_vertical"
            android:text="@{kemonoFriends.kemonoWords}"
            android:textColor="@android:color/black"
            tools:text="サーバルキャットのサーバルだよ！" />
    </RelativeLayout>
</layout>
```

### `Adapter`
```KemonoAdapter.kt
class KemonoAdapter(private val context: Context) : BaseAdapter() {
    var kemonoFriends: List<KemonoModel> = emptyList()

    override fun getCount(): Int = kemonoFriends.size

    override fun getItem(position: Int): Any? = kemonoFriends[position]

    override fun getItemId(position: Int): Long = 0

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val binding: ItemListViewBinding
        if (convertView == null) {
            binding = ItemListViewBinding.inflate(LayoutInflater.from(context), parent, false)
            binding.root.tag = binding
        } else {
            binding = convertView.tag as ItemListViewBinding
        }
        binding?.kemonoFriends = getItem(position) as KemonoModel

        return binding.root
    }
}
```

## 動作結果

<img src="https://qiita-image-store.s3.amazonaws.com/0/38442/aaa9e631-fb8a-0571-7da4-663c33f960d7.png" width="320px">

`ListView`の子要素をタップすると、それぞれのデータをToast表示します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/38442/214089ab-d620-3629-4ffd-e843ab2b6740.png" width="320px">

## まとめ
ButterKnifeからData Bindingに乗り換える目的で今回のサンプルアプリを作りましたが、少ないコード量で求める機能を実現できたので、Data Bindingの有用性を確認することができました。

もっと良い方法やここはこうしたほうがいいなどありましたら、コメントやGitHubのPullRequestで教えて頂けると嬉しいです。

## 参考URL
参考にさせていただきました。ありがとうございます。:bow:

- KotlinでData Bindingを使う時の設定  
http://qiita.com/umetsu/items/487d38be86c31ff59075
- KotlinでData Bindingをする - ListView編  
http://qiita.com/kykomi/items/cb9020d37ae2b04af800
- DataBindingを使ったLayoutの作成(ソース編)  
http://tiro105.hateblo.jp/entry/2016/03/23/000500