RecyclerViewでセルごとに異なるレイアウトを適用する

あんまりRecyclerView使う機会が無かったんですけど仕事で触る機会があったのでめも。

AdapterのgetItemViewTypeをオーバーライドしてデータに応じたViewTypeを返してあげるとレイアウトを使い分けることが出来ます。enum使うと綺麗に書ける感じですね。

下の例はItemEntityを継承した3種類のクラスに応じたレイアウトを表示するサンプルです。なお仕事では今のとこKotlinで書いてるのでサンプルもKotlinです。

```kotlin:CustomAdapter.kt
class CustomAdapter(context: Context,
                    val itemList: MutableList<ItemEntity>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    enum class ViewType(val id: Int) {
        HOGE(0) {
            override fun createViewHolder(inflater: LayoutInflater, viewGroup: ViewGroup?): RecyclerView.ViewHolder {
                // タイプに応じたViewHolderを返す
                return HogeViewHolder (inflater.inflate(R.layout.adapter_row_hoge, viewGroup, false))
            }

            override fun bindViewHolder(holder: RecyclerView.ViewHolder, itemEntity: ItemEntity) {
                holder as HogeViewHolder
                // Viewにそれぞれのデータを表示（以下略）
            }
        },
        FUGA(1) {
            override fun createViewHolder(inflater: LayoutInflater, viewGroup: ViewGroup?): RecyclerView.ViewHolder {
                return FugaViewHolder (inflater.inflate(R.layout.adapter_row_fuga, viewGroup, false))
            }

            override fun bindViewHolder(holder: RecyclerView.ViewHolder, itemEntity: ItemEntity) {
                holder as FugaViewHolder
                // Viewにそれぞれのデータを表示（以下略）
            }
        },
        PIYO(2) {
            override fun createViewHolder(inflater: LayoutInflater, viewGroup: ViewGroup?): RecyclerView.ViewHolder {
                return PiyoViewHolder (inflater.inflate(R.layout.adapter_row_piyo, viewGroup, false))
            }

            override fun bindViewHolder(holder: RecyclerView.ViewHolder, itemEntity: ItemEntity) {
                holder as PiyoViewHolder
                // Viewにそれぞれのデータを表示（以下略）
            }
        };

        companion object {
            fun forId(id: Int): ViewType {
                for (viewType: ViewType in values()) {
                    if (viewType.id == id) {
                        return viewType
                    }
                }
                throw AssertionError("no enum found for the id. you forgot to implement?")
            }
        }

        abstract fun createViewHolder(inflater: LayoutInflater, viewGroup: ViewGroup?): RecyclerView.ViewHolder

        abstract fun bindViewHolder(holder: RecyclerView.ViewHolder, itemEntity: ItemEntity)
    }

    companion object {

        // レイアウトに応じたViewHolderを作る。KotterKnifeを使うとBindが楽
        private class HogeViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
            val imageHoge: ImageView by bindView(R.id.adapter_row_hoge_image)
            val textHoge: TextView by bindView(R.id.adapter_row_hoge_text)
        }

        private class FugaViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
            val imageFuga: ImageView by bindView(R.id.adapter_row_fuga_image)
            val textFuga: TextView by bindView(R.id.adapter_row_fuga_text)
        }

        private class PiyoViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
            val imagePiyo: ImageView by bindView(R.id.adapter_row_piyo_image)
            val textPiyo: TextView by bindView(R.id.adapter_row_piyo_text)
        }
    }

    private val inflater = LayoutInflater.from(context)

    // Viewを作る時に呼ばれる
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder? {
        return ViewType.forId(viewType).createViewHolder(inflater, parent)
    }

    // ViewにデータをBindする時に呼ばれる
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
        holder ?: return
        val item = itemList[position]
        ViewType.forId(holder.itemViewType).bindViewHolder(holder, item)
    }

    override fun getItemCount(): Int {
        return itemList.size
    }

    // 型に応じたViewTypeを返す
    override fun getItemViewType(position: Int): Int {

        val item = itemList[position]

        return when (item) {
            is HogeItemEntity -> {
                ViewType.HOGE.id
            }
            is FugaItemEntity -> {
                ViewType.FUGA.id
            }
            is PiyoItemEntity -> {
                ViewType.PIYO.id
            }
            else -> {
                throw AssertionError("no enum found for the id. you forgot to implement?")
            }
        }
    }
}
```