LoginSignup
4
5

More than 1 year has passed since last update.

【RecyclerView × ViewDataBinding】なるべく頭を使わずに最短で複数レイアウトを使う

Last updated at Posted at 2022-11-19

やりたいこと

なるべく頭を使わずに最短でRecyclerViewで複数レイアウトを使う。
いつも忘れる + ググった時にやり方色々出てきてややこしいのでテンプレート作っておく。

実装方法の大枠

基本的に最後に添付したコードをまるっとコピペして少し修正すればOK。
データやレイアウトは自由に作って試してみてください。

① ViewTypeのEnumを作る
onCreateViewHolderはコピペ
onBindViewHolderもほぼコピペ
getItemViewTypeもほぼコピペ
getItemCountもほぼコピペ
⑥ ViewHolderのメソッドでデータとレイアウトを紐づける

※ 上記の順番で実装していく場合はViewHolderがないよとエラーになりますが、最後までいけば解消されるのでスルーしてください。

元となるAdapterファイル

data class Layout1Data(
    val country: String,
    val city: String
)

data class Layout2Data(
    val name: String,
    val number: Int
)

class MultiLayoutRecyclerAdapter :
    RecyclerView.Adapter<MultiLayoutRecyclerAdapter.ViewHolder>() {

    private val list = listOf(
        Layout1Data("Japan", "Tokyo"),
        Layout2Data("Alice", 1),
        Layout2Data("Bob", 2),
        Layout2Data("Cathy", 3),
        Layout2Data("Daniel", 4),
        Layout2Data("Elza", 5),
        Layout2Data("Fiona", 6),
        Layout2Data("Gina", 7),
        Layout2Data("Helen", 8),
    )
}

色々な実装方法があってややこしいので、下記の$クラスだけ変更すればOKになるように実装していきます。
class $クラス: RecyclerView.Adapter<$クラス.ViewHolder>()

① ViewTypeのEnumを作る

private enum class ViewType(val layoutId: Int) {
    LAYOUT_1(R.layout.layout_1),
    LAYOUT_2(R.layout.layout_2),
}

viewTypeに応じてレイアウトを出し分けたいのでとプロパティにレイアウトIDを設定しておきます。
なおprivateにしているのでクラス名("ViewType")をファイルごとに変更する必要はありません。

onCreateViewHolderはコピペ

override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
    val view: ViewDataBinding = DataBindingUtil.inflate(
        LayoutInflater.from(viewGroup.context),
        ViewType.values()[viewType].layoutId,
        viewGroup,
        false
    )

    return ViewHolder(view)
}

①で作成したEnumを使用してコピペを実現します。
ViewType.values()[viewType].layoutIdでviewTypeに応じたレイアウトIDを指定しています。

onBindViewHolderもほぼコピペ

override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
    when(val data = list[position]) {
        is Layout1Data -> viewHolder.bind(data)
        is Layout2Data -> viewHolder.bind(data)
        else -> return
    }
}

listに渡したデータに応じてそれぞれのレイアウトにデータをバインドしていきます。

getItemViewTypeもほぼコピペ

override fun getItemViewType(position: Int): Int {
    return when(list[position]) {
        is Layout1Data -> ViewType.LAYOUT_1.ordinal
        is Layout2Data -> ViewType.LAYOUT_2.ordinal
        else -> ViewType.LAYOUT_2.ordinal
    }
}

listに渡したデータに応じてItemViewのViewTypeを指定します。

getItemCountもコピペ

override fun getItemCount() = list.size

listのサイズを指定します

⑥ ViewHolderのメソッドでデータとレイアウトを紐づける

inner class ViewHolder(private val binding: ViewDataBinding) :
    RecyclerView.ViewHolder(binding.root) {

    fun bind(data: Layout1Data) {
        (binding as Layout1Binding).run {
            country.text = data.country
            city.text = data.city
        }
    }

    fun bind(data: Layout2Data) {
        (binding as Layout2Binding).run {
            name.text = data.name
            name.setTextColor(Color.BLUE)
            number.text = data.number.toString()
        }
    }
}

fun bind(data: $データクラス)をレイアウトの数だけ用意します。
各bindメソッド内でレイアウトとデータを紐づけていけば完了です。
なお、innerにしてるのでクラス名("ViewHolder")をファイルごとに変更する必要はありません。

コピペ用

data class Layout1Data(
    val country: String,
    val city: String
)

data class Layout2Data(
    val name: String,
    val number: Int
)

class MultiLayoutRecyclerAdapter :
    RecyclerView.Adapter<MultiLayoutRecyclerAdapter.ViewHolder>() {

    private val list = listOf(
        Layout1Data("Japan", "Tokyo"),
        Layout2Data("Alice", 1),
        Layout2Data("Bob", 2),
        Layout2Data("Cathy", 3),
        Layout2Data("Daniel", 4),
        Layout2Data("Elza", 5),
        Layout2Data("Fiona", 6),
        Layout2Data("Gina", 7),
        Layout2Data("Helen", 8),
    )

    private enum class ViewType(val layoutId: Int) {
        LAYOUT_1(R.layout.layout_1),
        LAYOUT_2(R.layout.layout_2),
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        val view: ViewDataBinding = DataBindingUtil.inflate(
            LayoutInflater.from(viewGroup.context),
            ViewType.values()[viewType].layoutId,
            viewGroup,
            false
        )

        return ViewHolder(view)
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        when(val data = list[position]) {
            is Layout1Data -> viewHolder.bind(data)
            is Layout2Data -> viewHolder.bind(data)
            else -> return
        }
    }

    override fun getItemViewType(position: Int): Int {
        return when(list[position]) {
            is Layout1Data -> ViewType.LAYOUT_1.ordinal
            is Layout2Data -> ViewType.LAYOUT_2.ordinal
            else -> ViewType.LAYOUT_2.ordinal
        }
    }

    override fun getItemCount() = list.size

    inner class ViewHolder(private val binding: ViewDataBinding) :
        RecyclerView.ViewHolder(binding.root) {

        fun bind(data: Layout1Data) {
            (binding as Layout1Binding).run {
                country.text = data.country
                city.text = data.city
            }
        }

        fun bind(data: Layout2Data) {
            (binding as Layout2Binding).run {
                name.text = data.name
                name.setTextColor(Color.BLUE)
                number.text = data.number.toString()
            }
        }
    }
}
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5