Categorizing in RecyclerViews
April 3, 2018We are all constantly categorizing bits of information that we receive without even realizing it. This allows us to think about things practically; such as understanding which tasks need to be completed in the next hour, day, or week. But, when it comes to displaying information in organized groups on a device’s screen, architecturally it can be a bit of a pain. So, how can we more effectively group data in a RecyclerView
?
Let’s make it happen #
With a basic RecyclerView
, we can categorize data by overriding functions in our implementation of RecyclerView.Adapter
. By overriding getItemViewType
, we can create a different type of RecyclerView.ViewHolder
in our onCreateViewHolder
. This is great! It allows us to easily implement many different types of views/view holders all within the same RecyclerView
. But remember, with great power comes great responsibility. Managing a data structure to handle grouping can quickly become error prone as you are likely referencing a Map<Any, List<Any>>
. Referencing an item at position 101 cannot be done with a simple index, but instead will require iterating through the groups to count their children. In the end, you will have a basic structure like this.
class SomeRecyclerAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
/*
* Remember, your logic would likely be more complicated to handle
* a nested data structure. You would probably want to pull this out into its own
* function so that it could also be used here, in getItemViewType,
* and in onBindViewHolder as well.
*/
override fun getItemCount(): Int {
return data.size
}
override fun getItemViewType(position: Int): Int {
return when (position) {
0 -> TYPE_HEADER
else -> TYPE_ITEM
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = parent.context.layoutInflater
return when (viewType) {
TYPE_HEADER -> HeaderViewHolder(inflater.inflate(R.layout.header, parent, false))
else -> ItemViewHolder(inflater.inflate(R.layout.item, parent, false))
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is HeaderViewHolder -> {
// do some Header things
}
is ItemViewHolder -> {
// do some Item things
}
}
}
}
But wait, I wanted animations #
You may be thinking that what has been described so far is pretty straightforward. However, I challenge you to consider how you would expand and collapse your categories. If you’re like me, your answer likely requires you to manipulate your data structure, possibly add new variables to manage visibility, convert these changes into notifyItemRangeInserted
and notifyItemRangeRemoved
events, and in general, make things complicated.
At that point, if you’re anything like me you will have tried a few libraries or maybe even attempted to roll your own. For me, Groupie was the clear front-runner in terms of functionality and simplicity but just had one issue; the collapse animation was just not right ( full disclosure, I created an issue only to realize the error was in my code ). So, I wanted to understand how it worked and hopefully, provide the fix.
Let’s reconsider what we would like in our code:
- We want a data structure that is easy to understand and manipulate.
- We want to avoid handling multiple types when creating/binding views
- We want to remain easily manageable.
With that in mind, I dedicated a weekend to crafting the perfect solution. What I ended up with was a functional solution (MacGrouper), and the understanding that I should probably have looked at my own code before blaming Groupie for my problems. The premise of Groupie, and MacGrouper for that matter, is that a Group
(or category) is really just an Item
that contains a List<Item>
. When the GroupAdapter
(displaying a List<Item>
) reports its size, it is able to ask all of the Items
for their size (and they report back 1+).
if (isExpanded) { children.size + 1 } else 1
This means your RecyclerView.Adapter
only really cares about Groups
. A Group
knows its number of children and handles visibility changes. This means it can make the appropriate item range change calls automatically for you.
The magic of it all is very clean code that is easy to understand. For the full example refer to my gist to create an ExpandableListFragment.
val phones = mapOf(
"Google" to listOf(
"Pixel",
"Pixel XL",
"Pixel 2",
"Pixel 2 XL"
),
"HTC" to listOf(
"Nexus One"
),
"Huawei" to listOf(
"Nexus 6P"
)
)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val layoutManager = GridLayoutManager(activity, groupAdapter.spanCount)
layoutManager.spanSizeLookup = groupAdapter.spanSizeLookup
phones.forEach { (brand, phones) ->
val brandItem = BrandItem(brand)
val expandableGroup = ExpandableGroup(brandItem)
phones.forEach { phone ->
expandableGroup.add(PhoneItem(phone))
}
groupAdapter.add(expandableGroup)
}
recyclerView.layoutManager = layoutManager
recyclerView.adapter = groupAdapter
}
All this is to say, I highly recommend giving Groupie a shot for anything that might require a complex RecyclerView
layout. The provided examples are an excellent way to get started or check out my example gist for an ExpandableListFragment.
Stay in the loop with our latest content!
Select the topics you’re interested to receive our new relevant content in your inbox. Don’t worry, we won’t spam you.

Press Release: University of Michigan partnership
March 19, 2019Michigan Software Labs announces new University of Michigan partnership to expand talent pipeline
Read more
Being Intentional, Flexible and People-Focused in 2021
October 19, 2020MichiganLabs co-founder and managing partner, Joshua Hulst, sat down with Chelsea Dubey (Guest Writer) to share his insights for a strong start to the new year and the exciting technology trends that his team is tracking for 2021.
Read more
Clutch Names Michigan Software Labs as a 2019 Top Developer in U.S.A.
November 14, 2019Clutch is a B2B research, ratings, and reviews firm located in downtown Washington, D.C.. Clutch connects businesses with the best-fit agencies, software, and consultants they need to tackle business challenges together and with confidence. Clutch’s methodology compares business service providers and software in a specific market based on verified client reviews, services offered, work quality, and market presences.
Read more