DEV Community

Bigyan Thapa
Bigyan Thapa

Posted on • Updated on

Generic RecyclerViewAdapter

Trying to make one adapter fit all RecyclerViews in the application.

While this is not an absolute use-case as we might want different list to behave differently. But the core purpose of RecyclerView is to display the data and be able to scroll through it. Also, when we click on the list-item we possible want to navigate to details for that list-item. While doing this, there will be a lot of redundant code that will be duplicated in all RecyclerView adapters in our codebase.

Below is a basic example of abstracting away the core functionality of a RecyclerView adapter and have the sub-classes take care of the rest. This articles assumes that the readers will have basic understanding of RecyclerView, databinding and kotlin.

abstract class BaseRecyclerViewAdapter<T> : Adapter<ViewHolder>(), {

  var items: List<T> = emptyList()
    set(value) {
      field = value
      notifyDataSetChanged()
    }

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = getViewHolder(parent, viewType)

  override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    if(position in items.indices) {
      (holder as Binder<T>).bind(items[position])
    }

  abstract fun getViewHolder(parent: ViewGroup, viewType: Int): ViewHolder

  interface Binder<in T> {
    fun bind(item: T)
  }
}

Usage

The above abstract RecyclerView adapter can be used in a fragment as follows:

class SomeListFragment {

  private lateinit var adapter: SomeAdapter

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) : View? {
    super.onCreateView(inflater, container, savedInstanceState)
    // initialization code here ...
    adapter = SomeAdapter() // initialize adapter here
    // ...
  }

  // rest of fragment implementation

  inner class SomeAdapter : BaseRecyclerViewAdapter<SomeData>() {
    override fun getViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
      val viewBinding: SomeListItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.some_list_item, parent, false)
      return SomeViewHolder(viewBinding)
    }
  }

  inner class SomeViewHolder(private val viewBinding: SomeListItemBinding) : ViewHolder(viewBinding.root), BaseRecyclerViewAdapter.Binder<SomeData> {

   override fun bind(item: SomeData) {
     /*
       bind data here, we can also assign click listeners
       viewBinding.root.setOnClickListener { ... }
     */
   }
  }
}

Please provide feedback in comments.
Thank you for reading.

Oldest comments (3)

Collapse
 
funkymuse profile image
FunkyMuse

Or you can use listadapter and abstract over it, it offers diff utils with nice animations.

Collapse
 
bigyan4424 profile image
Bigyan Thapa

@crazylegend Thank you for the feedback. I will try to put something together for ListAdapter in my future post.

Collapse
 
bigyan4424 profile image
Bigyan Thapa

That is correct. We don't need to override that in the above use case.
I will update the post. Thanks @Stavro Xhardha