Merge adapters sequentially with MergeAdapter
MergeAdapter is a new class available in recyclerview:1.2.0-alpha02 which enables you to sequentially combine multiple adapters to be displayed in a single RecyclerView. This enables you to better encapsulate your adapters rather than having to combine many data sources into a single adapter, keeping them focused and re-usable.
To learn android course visit:android app development course
One use case for this is displaying a list loading state in a header or footer: when the list is retrieving data from the network, we want to show a progress spinner; in case of error, we want to show the error and a retry button.

Introducing MergeAdapter
MergeAdapter allows us to display the contents of multiple adapters, in a sequence. For example, let’s say that we have the following 3 adapters:
val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …
val mergeAdapter = MergeAdapter(firstAdapter, secondAdapter,
thirdAdapter)
recyclerView.adapter = mergeAdapter
The recyclerView will display the items from each adapter sequentially.
Having different adapters allows you to better separate the concerns of each sequential part of a list. For example, if you want to display a header, you don’t need to put the logic related to the header display in the same adapter that handles the list display, rather you can encapsulate it in its own adapter.

Displaying load state in a header and footer
Our header/footer displays either a progress indicator or reports an error. When the list has successfully finished loading, the header/footer shouldn’t display anything. Therefore they can be represented as a list with 0 or 1 items, with their own adapter:
val mergeAdapter = MergeAdapter(headerAdapter, listAdapter,
footerAdapter)
recyclerView.adapter = mergeAdapter
If both the header and the footer use the same layout, ViewHolder and UI logic (e.g when progress is displayed and how), you can implement just one Adapter class and create 2 instances of it: one for the header and one for the footer.
For a complete implementation, check out this pull request, which adds:
- A
LoadState, exposed from theViewModel - A load state header and footer layout
- A
ViewHolderobject for the header and footer - A
ListAdapterthat displays 0 or 1 items based on theLoadState. Every time theLoadStatechanges, we notify that the item needs to be changed, inserted or removed (see code).
ViewHolders
By default, each adapter maintains their own pool of ViewHolders, with no re-use in between adapters. If multiple adapters display the same ViewHolder, we may want to reuse instances between them. We can achieve this by constructing our MergeAdapter with a MergeAdapter.Config object, where isolateViewTypes = false. Like this, all the adapters merged will use the same view pool. In the loading status header and footer example, both ViewHolders will actually display the same content so we could reuse them.
To support different ViewHolder types, you should implement Adapter.getItemViewType. When you’re reusing ViewHolders, make sure that the same view type doesn’t point to different ViewHolders! One best practice for this is to return the layout ID as the view type.
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> class HeaderAdapter() : RecyclerView.Adapter<LoadingStateViewHolderHeaderViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return LoadingStateViewHolder(parent)}
override fun getItemViewType(position: Int): Int {- return 0+ return R.layout.list_loading }}
class FooterAdapter() : RecyclerView.Adapter<LoadingStateViewHolderFooterViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return LoadingStateViewHolder(parent)}
override fun getItemViewType(position: Int): Int {- return 0+ return R.layout.list_loading }}
Using stable ids
Instead of using stable ids along with notifyDataSetChanged, it is recommended to use the specific notify events of the adapter that give the RecyclerView more information about the changes in the data set. This allows the RecyclerView to update the UI more efficiently and with better animations. If you’re using ListAdapter then the notify events are handled for you, under the hood, with the help of the DiffUtil callback. But if you do need to use stable ids, the MergeAdapter.Config provides 3 different configurations for stable ids: NO_STABLE_IDS, ISOLATED_STABLE_IDS and SHARED_STABLE_IDS. The last two require you to handle stable ids in your adapter. Check out the StableIdMode documentation for more information on how they work.
Data changes notifications
When an adapter part of a MergeAdapter calls one of the notify functions, the MergeAdapter computes the new item positions before updating the RecyclerView.
From the RecyclerView’s perspective, notifyItemRangeChanged means items are the same, just their contents changed. notifyDataSetChanged means there is no relation between before and after. Hence, we cannot map notifyDataSetChanged into notifyItemRangeChanged.
If an adapter calls Adapter.notifyDataSetChanged, MergeAdapter will also call Adapter.notifyDataSetChanged, rather than Adapter.notifyItemRangeChanged. As usual with RecyclerViews avoid calling Adapter.notifyDataSetChanged(), prefer more granular updates or use an Adapter implementation that does this automatically, like ListAdapter or SortedList.
To know more information visit:best android development course
Finding ViewHolder position
You might have used ViewHolder.getAdapterPosition in the past to get the position of a ViewHolder in the adapter. Now, because we’re merging multiple adapters, use ViewHolder.getBindingAdapterPosition(). If you want to get the adapter that last bound a ViewHolder, in the case where you’re sharing ViewHolders, use ViewHolder.getBindingAdapter().
That’s all! If you want to sequentially show different types of data that would benefit from being encapsulated in their own adapters, start using MergeAdapter. For advanced control of ViewHolder pool and stable ids, use MergeAdapter.Config.
Comments
Post a Comment