RecyclerViewのヘッダ、フッタ、セクション

# ヘッダ、フッタ、セクション
RecyclerViewでは、ヘッダ、フッタ、セクションのViewを自前で用意し、設定してあげる必要がある。

ベースはこちら
[RecyclerViewの実装](http://qiita.com/ijichi_y/items/7a45140d075b2466073f "RecyclerViewの実装")

# ゴール
ヘッダ、フッタ、セクションをそれぞれカスタムビュー化し、別レイアウトとする。
ヘッダを列の頭に設定
フッタを列の後ろに設定
セクションは列の値の途中追加

# 実装
Adapterのデータクラス
RecyclerAdapterでのヘッダ、フッタ、セクション、ボディ部分の判定もtypeにて行う
今回はカスタムビューでも使用する

```RecyclerState.kt
class RecyclerState(){

  constructor(type: RecyclerType, text: String): this(){
    this.type = type
    this.text = text
  }

  // RcyclerAdapterにて追加するレコードのタイプ
  var type: RecyclerType = RecyclerType.BODY
  var text: String = ""
}
```

## Type
RecyclerAdapterでの判定で使用するenum

```RecyclerType.kt
enum class RecyclerType(val int: Int){
  HEADER(0),
  FOOTER(1),
  SECTION(2),
  BODY(3);

  companion object {
    // Intからenumへの変換
    fun fromInt(int: Int): RecyclerType{
      return values().firstOrNull { it.int == int }
        ?: RecyclerType.BODY
    }
  }
}
```

## Activity
ヘッダ、ボディ、ボディの間にセクション、フッタの順にテストデータが入るようにする

```MainActivity.kt
class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val states = arrayListOf<RecyclerState>()
    // ヘッダ追加
    val headerState = RecyclerState(RecyclerType.HEADER, "へっだ")
    states.add(headerState)

    var secCounter = 0
    for(i in 1..5){

      // 2 件目 と 3 件目　の上にセクションを追加
      if(i == 2 || i == 3){
        secCounter++
        val sectionState = RecyclerState(RecyclerType.SECTION, "セクション（区切り） No. $secCounter")
        states.add(sectionState)
      }

      val state = RecyclerState(RecyclerType.BODY, "$i 件目")
      states.add(state)
    }

    // フッタ追加
    val footerState = RecyclerState(RecyclerType.FOOTER, "ふった")
    states.add(footerState)

    val adapter = RecyclerAdapter(this, states)

    val recycler = findViewById<RecyclerView>(R.id.mainRecycler)
    recycler.adapter = adapter
  }
}
```

##Adapter
RecyclerViewのアダプタ
タイプを判定してデータ格納

```RecyclerAdapter.kt
class RecyclerAdapter(
  private val context: Context,
  private val states: List<RecyclerState>) :
  RecyclerView.Adapter<RecyclerView.ViewHolder>() {

  override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    when(RecyclerType.fromInt(viewType)){
      RecyclerType.HEADER -> {
        val view = RecyclerItemHeaderView(context)
        return RecyclerItemHeaderViewHolder(view)
      }
      RecyclerType.FOOTER -> {
      val view = RecyclerItemFooterView(context)
      return RecyclerItemFooterViewHolder(view)
    }
      RecyclerType.SECTION -> {
      val view = RecyclerItemSectionView(context)
      return RecyclerItemSectionViewHolder(view)
    }
      RecyclerType.BODY -> {
        val view = RecyclerItemView(context)
        return RecyclerItemViewHolder(view)
      }
    }
  }

  override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
    when(viewHolder){
      is RecyclerItemHeaderViewHolder ->{
        viewHolder.update(states[position])
      }
      is RecyclerItemFooterViewHolder ->{
        viewHolder.update(states[position])
      }
      is RecyclerItemSectionViewHolder ->{
        viewHolder.update(states[position])
      }
      is RecyclerItemViewHolder ->{
        viewHolder.update(states[position])
      }
    }
  }

  override fun getItemViewType(position: Int): Int {
    return states[position].type.int
  }

  override fun getItemCount(): Int {
    return states.count()
  }
}
```

##ViewHolder
レイアウトを操作するクラス

```RecyclerItemViewHolder.kt
class RecyclerItemViewHolder(private val view: RecyclerItemView) : RecyclerView.ViewHolder(view) {
  fun update(state: RecyclerState){
    view.update(state)
  }
}
```

##Custom View
今回もヘッダ、フッタ、セクション、ボディ　全てをカスタムビューにしている
セクションだけ色つけたので載せておく

```recycler_item_section_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@android:color/darker_gray">

    <TextView
        android:id="@+id/recyclerItemSectionText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="セクション"
        android:textColor="@android:color/holo_blue_dark"
        android:textSize="20sp"
        android:textStyle="bold"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_gravity="bottom"
        android:background="@android:color/darker_gray"/>

</FrameLayout>
```

```RecyclerItemSectionView.kt
class RecyclerItemSectionView constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr){
  private val textView: TextView

  init {
    val rootView = LayoutInflater.from(context).inflate(R.layout.recycler_item_section_view, this)
    textView = rootView.findViewById(R.id.recyclerItemSectionText)
    setOnClickListener {
      // クリック処理
    }
  }

  fun update(state: RecyclerState){
    textView.text = state.text
  }
}
```

# できあがり
<img width="341" alt="スクリーンショット 2017-08-31 19.41.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/199764/a731abcb-8633-dcb3-1792-26083291def0.png">


# 注意点
getItemViewTypeをoverrideするのを忘れないようにずっと０で小パニックになります。

```RecyclerAdapter.kt
  override fun getItemViewType(position: Int): Int {
    return states[position].type.int
  }
```

今回はstateにAdapterで種類を判定できるtype追加して判定を行ったけど、
Adapter自体にaddFooter()のようなメソッドを追加して、
これ以上データが無いかどうか判定してから、addFooter()を呼び出すのが実務に近いかも。

# まとめ
カスタムビューとViewHolderをそれぞれ作るのに時間かかるのが難点
ヘッダ、フッタは１つずつなので、カスタムビュー作らない可能性も考慮してもいいかも。
セクションは使いまわす可能性高いのでカスタムビューが良いかと。