A RecyclerView with multiple item types in Kotlin

Ruud Jansen
2 min readNov 11, 2022

--

It's been already 7 years ago that I wrote this article about multiple RecyclerView item types.

It got quite some views and responses, so I thought it would be nice to write a Kotlin version of this mini tutorial. You will see that it's a lot more compact than the old Java version.

So, here we go… Let's start with an interface for all the data types we have!

interface ListItem {
enum class Type(value: Int) {TypeA(0), TypeB(1) }
fun getListItemType(): Int
}

I'm still a bit in doubt if a sealed class would be a more elegant solution, but let's use an enum for now. We have a TypeA and a TypeB implementation and they look like this.

data class ItemA(val textA: String): ListItem {
override fun getListItemType(): Int {
return ListItem.Type.TypeA.ordinal
}
}

And here is our Adapter!

class ItemAdapter(private val items: List<ListItem>) : RecyclerView.Adapter<BaseViewHolder>() {
override fun getItemViewType(position: Int): Int {
return items[position].getListItemType()
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return when (viewType) {
ListItem.Type.TypeA.ordinal -> {
val view = LayoutInflater.from(parent.context).inflate(R.layout.itemview_a, parent, false)
return ViewHolderA(view)
}
ListItem.Type.TypeB.ordinal -> {
val view = LayoutInflater.from(parent.context).inflate(R.layout.itemview_b, parent, false)
return ViewHolderB(view)
}
else -> {
val view = LayoutInflater.from(parent.context).inflate(R.layout.itemview_a, parent, false)
return ViewHolderB(view)
}
}
}

override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
holder.bind(items[position])
}

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

I am not very happy with the fact that the onCreateViewHolder() function needs an else-statement which never will be reached. But since the RecyclerView is implemented with viewType as an Int I think we don't really have another option. But please leave a comment if you have a better solution for this!

Last but not least we need a ViewHolder abstraction. I made a BaseViewHolder with an abstract bind() function.

abstract class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(item: ListItem)
}

And here's an example ViewHolder implementation.

class ViewHolderA(itemView: View) : BaseViewHolder(itemView) {
private val textView: TextView = itemView.findViewById(R.id.text)

override fun bind(item: ListItem) {
val itemA = item as ItemA
textView.text = itemA.textA
}
}

Let me know what you think!

--

--