Groupie をつかってみる

## はじめに
[VALU Advent Calendar 2018](https://qiita.com/advent-calendar/2018/valu) の17日目です。
Advent Calendar なるものに初めて参加したのですが…ちょっとドキドキです！

今回は今更感が満載ですが RecyclerView のあるある実装が簡単になる [Groupie](https://github.com/lisawray/groupie) を使ってヘッダーのあるリストを作成したいと思います。

## 手順
[Groupie](https://github.com/lisawray/groupie) のサンプルプロジェクト [groupie/example-databinding](https://github.com/lisawray/groupie/tree/master/example-databinding) を参考にして実装していきます。

1. レイアウトを作成
2. ViewHolder を作成
3. RecyclerView に add

## Step1
RecyclerView に表示するレイアウトを作成します。

### ヘッダー
```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"  
  >  
  
  <data>  
    <variable  name="viewModel"
      type="me.aluceps.practicerecyclerview.ItemViewModel"  
      />  
  </data>  

  <androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    >  
  
    <TextView  android:id="@+id/item_text"  
      android:layout_width="0dp"  
      android:layout_height="wrap_content"  
      android:padding="16dp"  
      android:text="@{viewModel.text}"  
      app:layout_constraintBottom_toBottomOf="parent"  
      app:layout_constraintEnd_toEndOf="parent"  
      app:layout_constraintStart_toStartOf="parent"  
      app:layout_constraintTop_toTopOf="parent"  
      tools:text="ヘッダー1"  
      />  
  </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```

### アイテム
```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"  
  >  
  
  <data>  
    <variable  name="viewModel"  
      type="me.aluceps.practicerecyclerview.ItemViewModel"  
      />  
  </data>  

  <androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    android:orientation="vertical"  
    >  
  
  <TextView  android:id="@+id/item_text"  
    android:layout_width="0dp"  
    android:layout_height="wrap_content"  
    android:background="@android:color/white"  
    android:padding="16dp"  
    android:text="@{viewModel.text}"  
    app:layout_constraintBottom_toBottomOf="parent"  
    app:layout_constraintEnd_toEndOf="parent"  
    app:layout_constraintStart_toStartOf="parent"  
    app:layout_constraintTop_toTopOf="parent"  
    tools:text="アイテム1"  
    />  
  </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
```

Binding させる ViewModel は ObservableField を1つ持つだけのクラスです。

```kotlin
class ItemViewModel : BaseObservable() {  
    val text: ObservableField<String> = ObservableField()  
}
```

## Step2
ヘッダーとアイテムの ViewHolder を作成します。

### ヘッダー
```kotlin
class ItemHeaderViewHolder(id: Long) : BindableItem<ItemHeaderBinding>(id) {  

  private val viewModel = ItemViewModel()  

  override fun getLayout(): Int = R.layout.item_header  

  override fun bind(viewBinding: ItemHeaderBinding, position: Int) {  
    viewBinding.viewModel = viewModel  
  }  

  fun setText(value: String) {  
    viewModel.text.set(value)  
    viewModel.notifyChange()  
  }  
}
```

### アイテム
```kotlin
class ItemNormalViewHolder(id: Long) : BindableItem<ItemNormalBinding>(id) {  
  
  private val viewModel = ItemViewModel()  

  override fun getLayout(): Int = R.layout.item_normal  

  override fun bind(viewBinding: ItemNormalBinding, position: Int) {  
    viewBinding.viewModel = viewModel  
  }  

  fun setText(value: String) {  
    viewModel.text.set(value)  
    viewModel.notifyChange()  
  }  
}
```

## Step3
これまで作成したヘッダーとアイテムを使って RecyclerView に追加する処理を書きます。

```kotlin
private var groupAdapter: GroupAdapter<ViewHolder>? = null  
  
override fun onCreate(savedInstanceState: Bundle?) {  
  super.onCreate(savedInstanceState)  
  groupAdapter = GroupAdapter()  
  initializeRecyclerView()  
  for (i in 1..5) addItems(i)  
}  
  
private fun initializeRecyclerView() {  
  binding.recyclerView.run {  
    adapter = groupAdapter  
    layoutManager = LinearLayoutManager(this@MainActivity)  
    addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL))  
  }  
}  
  
private fun addItems(index: Int) {  
  val section = Section()  
  section.setHeader(ItemHeaderViewHolder(index.toLong()).apply { setText("Section No.$index") })  
  for (i in 1..20) section.add(ItemNormalViewHolder(i.toLong()).apply { setText("Section No:$index Row:$i") })  
  groupAdapter?.add(section)  
}
```

[Groupie](https://github.com/lisawray/groupie) は Section という単位でヘッダーとアイテムを追加していきます。`Section.setHeader()` でヘッダーを追加し `Section.add()` でアイテムを追加しています。そして GroupAdapter に追加することでヘッダーとアイテムの組み合わせを RecyclerView で表現できるようです。とても簡単ですね。

### イメージ
<img src="https://qiita-image-store.s3.amazonaws.com/0/185323/2a844c22-eec6-713a-d7b2-90a74e029e31.png" width="300"/>

## まとめ
[Groupie](https://github.com/lisawray/groupie) を使うことでわずか3ステップでヘッダーとリストのある RecyclerView ができました。[Groupie](https://github.com/lisawray/groupie) を使わずに手作りした場合は Adapter の作成が必要になる上に ViewType に応じた処理を書く必要があります。難しいわけではないのですが ViewType を追加するたびに書くのは結構面倒なんですよね…！

そういった手間を省くことができるので [Groupie](https://github.com/lisawray/groupie) はとてもオススメです！