Epoxy をつかってみる

## はじめに
[VALU Advent Calendar 2018](https://qiita.com/advent-calendar/2018/valu) の23日目です。
明日はクリスマス・イヴですね…そんなの関係ねぇ！！

今回は airbnb が提供している [Epoxy](https://github.com/airbnb/epoxy) というライブラリを使ってみます。[前回](https://qiita.com/aluceps/items/86208987f4c07331bfa0)と同様に RecyclerView の実装を楽にできるライブラリです。

では [Epoxy](https://github.com/airbnb/epoxy) の [Installation](https://github.com/airbnb/epoxy#installation) に従ってすすめます。

## build.gradle
```groovy
dependencies {
	...
    implementation 'com.airbnb.android:epoxy:3.1.0'
    implementation 'com.airbnb.android:epoxy-databinding:3.1.0'
    kapt 'com.airbnb.android:epoxy-processor:3.1.0'
}

kapt {
    correctErrorTypes = true
}
```

DataBinding を使いたいので`epoxy-databinding` ライブラリを追加しています。また、kotlin を使うので `correctErrorTypes = true` も加えています。

- [Data Binding Support · airbnb/epoxy Wiki](https://github.com/airbnb/epoxy/wiki/Data-Binding-Support)

## モデルの作成

モデルは CustomView から作成する方法と、DataBinding (レイアウト) から作成する方法があります。ここでは DataBinding から作成する方法ですすめていきます。

```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="name"
            type="String"
            />
    </data>

    <TextView
        style="@style/TextAppearance.AppCompat.Large"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:text="@{name}"
        tools:text="header"
        />
</layout>
```

このレイアウトでファイル名とスタイルを少し変えてヘッダーと普通のアイテムの2種類を作成します。

DataBinding からモデルを作成する場合は `package-info.java` が必要になります。`File > New > File` で package-info.java を作成し、次のように記述します。

```java
@EpoxyDataBindingLayouts({
        R.layout.item_header,
        R.layout.item_normal
})
package me.aluceps.practicerecyclerview;

import com.airbnb.epoxy.EpoxyDataBindingLayouts;
```

## Controller の実装

Epoxy は `EpoxyController` を継承した `TypedEpoxyController` を持っています。これはデータ型を指定することができるクラスです。ここではデータ型を1つ指定することができる  `TypedEpoxyController<T>` を使用して実装します。

まずはデータ型です。

```kotlin
data class ItemData(
        val header: String,
        val normal: List<String>
)
```

次に Controller です。

```kotlin
class SampleTypedEpoxyController : TypedEpoxyController<ItemData>() {

    override fun buildModels(data: ItemData?) {
        data?.let {
            itemHeader {
                id(it.header)
                name(it.header)
            }
            it.normal.forEachIndexed { i, s ->
                itemNormal {
                    id(i)
                    name(s)
                }
            }
        }
    }
}
```

## RecyclerView の実装

基本的には RecyclerVIew の adapter に Controller を渡して、`Controller.setData` するだけです。`layoutManager` と `addItemDecoration ` も追加します。

```kotlin
class MainActivity : AppCompatActivity() {

    private val binding by lazy {
        DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
    }

    private val controller by lazy {
        SampleTypedEpoxyController()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding.recyclerView.run {
            adapter = controller.adapter
            layoutManager = LinearLayoutManager(context)
            addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
        }
        controller.setData(ItemData("header1", listOf(
                "item: 1",
                "item: 2",
                "item: 3",
                "item: 4",
                "item: 5",
                "item: 6",
                "item: 7",
                "item: 8",
                "item: 9",
                "item: 10",
                "item: 11",
                "item: 12",
                "item: 13",
                "item: 14",
                "item: 15",
                "item: 16",
                "item: 17",
                "item: 18",
                "item: 19",
                "item: 20"
        )))
    }
}
```

ここでは RecyclerView を使いましたが、EpoxyRecyclerView を使用することもできます。

## イメージ

<img src="https://qiita-image-store.s3.amazonaws.com/0/185323/00619b49-13ae-c2ea-0b22-132fd9e786fe.png" width="500"/>

## まとめ

[Epoxy](https://github.com/airbnb/epoxy) の実装は[前回](https://qiita.com/aluceps/items/86208987f4c07331bfa0)の Groupie と比べると少しややこしい印象です。しかし、[Epoxy](https://github.com/airbnb/epoxy) の [Wiki](https://github.com/airbnb/epoxy/wiki/Basic-Usage) はとてもしっかり作られているので、[Wiki](https://github.com/airbnb/epoxy/wiki/Basic-Usage) をよく読めば比較的簡単に導入できるのではと思いました。