Beginner's Guide on ConcatAdapter in Android

Instead of making a number of RecyclerViews for each adapter in our app, a class named as ConcatAdapter is introduced in recyclerview:1.2.0-alpha02. This allows you to combine multiple adapters which display sequentially in a single RecyclerView. In this article, you will learn about ConcatAdapter and see how you can merge 3 adapters for a single RecyclerView like the image mockup below.

The Mockup for ConcatAdapter example The Mockup for ConcatAdapter example

If we use the manual approach, there will be 3 RecyclerViews and 3 adapters for Header, Footer and the List. Managing those with separate data, and especially handling the scrolling of all RecyclerViews through NestedScrollView often becomes tricky and less performant. Pagination in these cases brings another challenge too.



But the ConcatAdapter makes it easier to handle cases like this. We will make an app that will show a list which have a Header, some text in Footer, and finally, a List that displays some random users’ info.

Creating Adapters for Header, Footer and The List

Starting with a HeaderAdapter that will show a simple static text, let’s create a RecyclerView.Adapter like the code below:

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

   inner class ViewHolder(val binding: HeaderLayoutBinding) :
       RecyclerView.ViewHolder(binding.root) {   }

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):    ViewHolder {
       return ViewHolder(
           HeaderLayoutBinding.inflate(
               LayoutInflater.from(parent.context),
               parent,
               false
           )
       )
   }
   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
       holder.binding.apply {
       }
   }
   override fun getItemCount(): Int {
       return 1
   }
}

Then we have FooterAdapter that contains a dynamic text with the variable name remarks.

class FooterAdapter(val remarks: String) :
   RecyclerView.Adapter<FooterAdapter.ViewHolder>() {


   inner class ViewHolder(val binding: FooterLayoutBinding) :
       RecyclerView.ViewHolder(binding.root) {  }

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
       return viewHolder(
           FooterLayoutBinding.inflate(
               LayoutInflater.from(parent.context),
               parent,
               false
           )
       )
   }
   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
       holder.binding.apply {
           this.remarks.text = remarks
       }
   }
   override fun getItemCount(): Int {
       return 1
   }
}

And the last one is UserListAdapter which contains some users’ info like pictures, names, and achievements.

class UserListAdapter() : RecyclerView.Adapter<UserListAdapter.ViewHolder>() {
   private var items= emptyList<User>()
   
   inner class ViewHolder(val binding: UserListItemLayoutBinding) :
       RecyclerView.ViewHolder(binding.root) {    }

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
       return ViewHolder(
           UserListItemLayoutBinding.inflate(
               LayoutInflater.from(parent.context),
               parent,
               false
           )
       )
   }
   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
       holder.binding.apply {
           this.image.setImageResource(items[position].image)
           this.name.text=items[position].name.toString()
           this.achivements.text = items[position].achievement.toString()
       }
   }
   override fun getItemCount(): Int {
       return items.size
   }
   @SuppressLint("NotifyDataSetChanged")
   fun setUsersListData(itemsList:List<User>){
      this.items=itemsList
      this.notifyDataSetChanged()
   }
}

So that’s it for adapters. Now we will move toward the main work of our Activity or Fragment.



Merging Adapters with ConcatAdapter

Inside your Activity or Fragment class, you need to create the instances of all these adapters and populate data into them.

fun setupRecyclerViewAdapters() {
   val headerAdapter = HeaderAdapter()
   val footerAdapter = FooterAdapter("Remarks from your data source")
   val usersAdapter = UsersListAdapter()
   usersAdapter.setUsersListData(someUsersList)
}

In the normal case, you would have to add 3 separate RecyclerViews, but now you only need to add a single RecyclerView in your XML file.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">

   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/mainRecyclerView"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</LinearLayout>

Now, you need to create an instance of ConcatAdapter and pass all the references of the previous adapters. So add these lines in the above setupRecyclerViewAdapters method.

val concatAdapter = ConcatAdapter(headerAdapter, usersAdapter, footerAdapter)
binding.mainRecyclerView.layoutManager = LinearLayoutManager(this)
binding.mainRecyclerView.adapter = concatAdapter

You can see in the above code that the mainRecyclerView only is setting the concatAdapter. It doesn’t know about the instances of headerAdapter, footerAdapter etc. Those will be managed by ConcatAdapter itself.

Also, note that the order in the constructor of ConcatAdapter is important. If you want to show Footer on top, then pass footerAdapter before headerAdapter and so on. Now, if you call any data update methods of RecyclerView Adapters such as notifyItemChanged, notifyDatasetChanged(), notifyItemAdded() etc, then the whole ConcatAdpter will also be notified. So, you can easily update the data of any adapter at any point, and ConcatAdapter will make sure that updated data is shown in the combined list.

So, in case of pagination, at first you will only add headerAdapter and userListAdapter in the ConcatAdapter. Then you have a paginated list of users. As you reach the last page, then you can add any new Adapter programmatically by calling concatAdapter.addAdapter() etc. You can also remove the adapters with concatAdapter.removeAdapter() method as well.

This is how it will show:

A single RecyclerView merging 3 adapters A single RecyclerView merging 3 adapters

Here’s the final code of the above header/footer/list case.

class MainActivity : AppCompatActivity() {

   private lateinit var binding: ActivityMainBinding
   private lateinit var headerAdapter: HeaderAdapter
   private lateinit var footerAdapter: FooterAdapter
   private lateinit var listAdapter: ListAdapter

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupRecyclerViewAdapters()
   }

   fun setupRecyclerViewAdapters() {
        val headerAdapter = HeaderAdapter()
        val footerAdapter = FooterAdapter("Remarks from your data source")
        val usersAdapter = UsersListAdapter()
        usersAdapter.setUsersListData(someUsersList)

        val concatAdapter = ConcatAdapter(headerAdapter, usersAdapter, footerAdapter)
        binding.recyclerViewRv.layoutManager = LinearLayoutManager(this)
        binding.recyclerViewRv.adapter = adapter
   }
}

What’s Next?

I hope you understood the concept of how ConcatAdapter works and how you can merge multiple adapters to create a single list. I would love to hear your feedback, so leave a comment below and share with others!

At the end, please Subscribe to my newsletter DroidUp to learn learn about the latest things, tips, and skills in Android development manually handcrafted and curated by Wajahat Karim.


If you liked this article, you can read my new articles below:


profile card
Wajahat Karim
🌍 Making the world a better place, one app at a time.
🔥 Google Developer Expert (GDE) in Android . 📱 Professional Android Developer with ~10 years experience. 💻 Creator of various Open Source libraries on Android . 📝 Author of two technical books and 100+ articles on Android. 🎤 A passionate Public Speaker giving talks all over the world.
Author's picture

Wajahat Karim

🔥 Google Dev Expert (GDE) in Android .
📱 Android Dev. 💻 Open Source Contributor . 📝 Technical Writer . 🎤 Public Speaker

Senior Android Developer

Karachi, Pakistan