RecyclerViewの実装が楽になるEpoxyライブラリを使ってみる

# 概要
EpoxyはAirbnb製のライブラリである。特に、複数のViewTypeを持つタイプのRecyclerViewの実装が楽になる。

# 導入
build.gradleに以下を追加。

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

dependencies {
    // epoxy
    compile 'com.airbnb.android:epoxy:2.7.3'
    kapt 'com.airbnb.android:epoxy-processor:2.7.3'
    compile 'com.airbnb.android:epoxy-databinding:2.7.3'

}

kapt {
    correctErrorTypes = true
}
```
# レイアウト
RecyclerViewを使用してheader２種類、contents２種類の計４種類のレイアウトが含まれた以下のような画面を作成する。


![device-2017-12-09-110718.png](https://qiita-image-store.s3.amazonaws.com/0/141937/2248258f-a5e6-7f70-d1e1-6f1909d8dc3d.png)


Epoxyを使わない方法だと、RecyclerViewに紐付けるAdapter内で４種類のViewTypeに応じて処理を分けるような実装が必要になると思われる。

## メインのレイアウト
```xml
<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">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            app:layoutManager="android.support.v7.widget.LinearLayoutManager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </RelativeLayout>

</layout>
```

## header1のレイアウト
```xml
<layout xmlns:android="http://schemas.android.com/apk/res/android" >

    <data>
        <variable name="title" type="String" />
    </data>

    <LinearLayout
        android:background="#ff0000"
        android:gravity="left|center_vertical"
        android:layout_width="match_parent"
        android:layout_height="100dp">

        <TextView
            android:text="@{title}"
            android:textStyle="bold"
            android:layout_marginLeft="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

</layout>
```

## header2のレイアウト
```xml
<layout xmlns:android="http://schemas.android.com/apk/res/android" >

    <data>
        <variable name="title" type="String" />
    </data>

    <LinearLayout
        android:background="#00ff00"
        android:gravity="right|center_vertical"
        android:layout_width="match_parent"
        android:layout_height="80dp">

        <TextView
            android:text="@{title}"
            android:textStyle="italic"
            android:layout_marginRight="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

</layout>
```

## contents1のレイアウト
```xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"　>

    <data>
        <variable name="text" type="String" />
    </data>

    <LinearLayout
        android:gravity="center"
        android:background="#eeeeee"
        android:layout_width="match_parent"
        android:layout_height="50dp">

        <TextView
            android:text="@{text}"
            android:textSize="35sp"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

</layout>
```

## contents2のレイアウト
```xml
<layout xmlns:android="http://schemas.android.com/apk/res/android" >

    <data>
        <variable name="text" type="String" />
    </data>

    <LinearLayout
        android:gravity="center"
        android:background="#888888"
        android:layout_width="match_parent"
        android:layout_height="50dp">

        <TextView
            android:text="@{text}"
            android:textSize="18sp"
            android:fontFamily="serif-monospace"
            android:textColor="#ffffff"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

</layout>
```

# package-info.java
EpoxyにレイアウトファイルからBindingModel_クラスを自動生成してもらうために、package-info.javaを作成し、以下のように記述する。

```java
@EpoxyDataBindingLayouts({
        R.layout.header1,
        R.layout.header2,
        R.layout.contents1,
        R.layout.contents2
})
package example.test.epoxytest;

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

作成したpackage-info.javaは以下のように配置する。
![スクリーンショット 2017-12-09 11.33.01.png](https://qiita-image-store.s3.amazonaws.com/0/141937/21f91be5-8882-d698-2636-462fb8d2382c.png)

ここまで作成したら、一旦ビルドすることでHeader1BindingModel_, Contents1BindingModel_, Header2BindingModel_, Contents2BindingModel_がEpoxyによって自動生成される。

# Dataクラス作成
コンテンツのデータを格納するクラスを作成する。

```kotlin
class Data(val header1: String, val contents1: List<String>, val header2: String, val contents2: List<String>)
```

# Controller作成
Adapterの代わりにControllerを作成する。

```kotlin
class TestController : TypedEpoxyController<Data>() {

    override fun buildModels(data: Data) {

        Header1BindingModel_()
                .title(data.header1)
                .id(modelCountBuiltSoFar)
                .addTo(this)

        for (item in data.contents1) {
            Contents1BindingModel_()
                    .text(item)
                    .id(modelCountBuiltSoFar)
                    .addTo(this)
        }

        Header2BindingModel_()
                .title(data.header2)
                .id(modelCountBuiltSoFar)
                .addTo(this)

        for (item in data.contents2) {
            Contents2BindingModel_()
                    .text(item)
                    .id(modelCountBuiltSoFar)
                    .addTo(this)
        }
    }
}
```
Adapterを実装するのと比べると簡潔である。

# MainActivity
```kotlin
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

        val controller = TestController()
        controller.setData(Data("header1", listOf("aa", "ab", "ac"),
                "header2", listOf("ba", "bb", "bc", "bd", "be", "bf")))
        binding.recyclerView.adapter = controller.adapter

        binding.recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
    }
}
```
ControllerにDataを設定し、Controller内部で生成されたadapterをRecyclerViewに紐付ける。

# まとめ
Epoxyを使うことでボイラープレートコードが減り、少ない記述で複数のviewTypeを持つRecyclerViewの実装ができた。:ok_woman_tone2:

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3134313933372f32323438323538662d613565362d376637302d643165312d3666313930396438646333642e706e67.png](https://qiita-image-store.s3.amazonaws.com/0/141937/24a49cab-ce19-e5b1-15d4-d36fe8e4815b.png)