Merge Adapters sequentially with MergeAdapter

MergeAdapter is a new class provided in RecyclerView 1.2.0-alpha02 that enables you to combine multiple Adapters in sequence to display in a single RecyclerView. This allows you to better encapsulate adapters without having to combine many data sources into a single Adapter, allowing them to be centralized and reused.

One use case is to display list loading status in Header or Footer: We want to display a progress bar when the list retrieves data from the network. If an error occurs, we display the error and retry buttons.

Introducing the MergeAdapter

MergeAdapter allows us to display the contents of multiple Adapters in sequence. For example, suppose we have the following three Adapters:

valFirstAdapter: firstAdapter =...valSecondAdapter: secondAdapter =...valThirdAdapter: thirdAdapter =...val mergeAdapter = MergeAdapter(firstAdapter, secondAdapter, 
     thirdAdapter)
recyclerView.adapter = mergeAdapter
Copy the code

RecyclerView displays the items in each adapter in sequence

Using different Adapters allows you to better compartmentalize each sequential part of the table. For example, if you want to display titles, you don’t have to put the logic associated with title display in the same Adapter that handles list display, but you can encapsulate it in its own Adapter

Display load status in Header and Footer

Our Header/Footer shows progress or an error. Header/Footer should not display any content after the list has successfully loaded. Therefore, you can use your own Adapter to represent it as a list of 0 or 1 items:

val mergeAdapter = MergeAdapter(headerAdapter, listAdapter, footerAdapter)

recyclerView.adapter = mergeAdapter
Copy the code

If the Header/Footer uses the same layout, ViewHolder, and UI logic (for example, to display progress and how to display it), you can implement just one Adapter class and create two instances: one for the Header and one for the Footer

For a complete implementation, look at the pull Request, which adds:

  • LoadState exposed from ViewModel
  • Header and Footer loading status
  • ViewHolder objects for Header and Footer
  • aListAdapter, according to theLoadStateDisplays 0 or 1 item. Every timeLoadStateWhen we change, we notify you of the need to change, insert or delete the item (See code)

More information about MergeAdapter

ViewHolders

By default, each Adapter maintains its own ViewHolder pool and does not reuse it between adapters. If multiple Adapters display the same ViewHolder, we might want to reuse the same instance between them. We can do this by creating a MergeAdapter using the MergeAdapter.config object, where isolateViewTypes = false. In this way, all the merged Adapters will use the same view pool. In the loading state Header and Footer example, the two ViewHolder will actually display the same content, so we can reuse them

⚠️ To support different ViewHolder types, implement adapter.getitemViewType. When you reuse ViewHolder, make sure that the same view type does not point to different ViewHolder! A best practice is to return the layout ID as a View type.

Use a fixed ID

It is recommended not to use a fixed ID with notifyDataSetChanged, but rather to use the Adapter’s specific notification event, which provides the RecyclerView with more information about data set changes. This allows RecyclerView to update the UI more efficiently and have better animations. If you are using a ListAdapter, the Notify event will be handled for you in the background with the help of the DiffUtil callback. However, if you do need to use fixed ids, mergeAdapter.config provides three different configurations for fixed ids: NO_STABLE_IDS, ISOLATED_STABLE_IDS, and SHARED_STABLE_IDS. The last two require you to deal with fixed ids in the Adapter. See the StableIdMode documentation for more information on how it works

Data change notification

When the Adapter part of MergeAdapter calls the notification function, MergeAdapter calculates the location of the new item before updating the RecyclerView.

From the RecyclerView perspective, a notifyItemRangeChanged means that the item is the same, but the content is changed. NotifyDataSetChanged indicates that there is no relationship between before and after. Therefore, we cannot map notifyDataSetChanged to notifyItemRangeChanged

If adapter. The adapter call notifyDataSetChanged, MergeAdapter will also be calling adapter. The notifyDataSetChanged, Instead of Adapter. NotifyItemRangeChanged. Like RecyclerView, usually avoid calling Adapter. NotifyDataSetChanged (), but more detailed update, or using the automatically do this Adapter implementation, such as ListAdapter or SortedList

Find the ViewHolder location

. You may have used past ViewHolder getAdapterPosition to obtain ViewHolder position in the adapter. Now, because we want to merge multiple adapter, so please use the ViewHolder. GetBindingAdapterPosition (). If you want to get the final binding ViewHolder adapter, in the case of sharing ViewHolder, use ViewHolder. GetBindingAdapter ()

If you want to display sequentially different types of data that would benefit from being encapsulated in their own Adapter, start using MergeAdapter. For advanced control over ViewHolder pools and fixed ids, use mergeAdapter.config

Translators supplement

demo

About me

I am Flywith24, and my blog content has been classified here. You can click Watch in the upper right corner to get updates of my articles in time, oh 😉

  • The Denver nuggets
  • Jane’s book
  • Github