ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Android Paging Library with multiple view types

Tuan Kiet
ProAndroidDev
Published in
5 min readJun 17, 2019

Introduction

Android paging library helps you load and display small chunks of data at a time. In real work use cases, we might have multiple item view types, additional headers or separators between items.

However, android paging library works best if you only have 1 model type with 1 view type at a time (e.g. PageKeyedDataSource<Int, TaskItem>), model list modification is unsupported because that will lead to wrong diffing notifications to recycler view.

The android-architecture-components sample provide a workaround by passing a custom ItemCallback.

While this solution works for simple use cases like adding one header to top of the list or adding a loading item at the bottom. For more complex use cases, this approach becomes error-prone and doesn’t solve the core problem: multiple item view types, not just decoration items but items that represent actual data. Moreover, the number of item types and their order in the list is unknown due to A/B experimental.

What we’ll build:

Notice the scroll bar!
  • Each section (banner, deal, category) represents a trunk of data obtained by different rest API
  • A section might have multiple items (e.g. rest API for category section will have info for 3 blocks: category header, content, and footer)
  • Load one out of a time
  • Support placeholder (loading item) for fixed size list
  • Support infinity list
  • The number of items in the list and their order is unknown until run time

Approaches

For better readability, we will use a custom view to represent a section. These are just a combination of views using LinearLayout or ConstrainLayout. You can reuse these views anywhere else in your application.

  • BannerView
  • DealView
  • CategoryView
  • SeparatorView
  • LoadingView

E.g.

Each of these views will be an ItemView in your RecyclerView. It is good to have a data class that holds presentation data of each of them. To avoid confusion with AAC ViewModel, let call them ItemViewModel.

Now for the data source part, instead of implementing a data source that provides concrete model type like we usually do:
PageKeyedDataSource<Int, DealData>()
We will have a data source that provides ItemViewModel:
PageKeyedDataSource<Int, ItemViewModel>()

Note that in Clean Architecture world, this data source is not a part of the data layer, it belongs to the UI layer, consider it like a controller that helps us in load and paging UI components.

Data flow
Overall architecture

We choose PageKeyedDataSource because we treat each section of the screen as a page. Each page will return an inconsistent number of ItemViewModel due to additional separators that will lead to wrong page calculation. Fortunately for us, the paging library is flexible enough to handle this case. LoadCallback come with a handy parameter called adjacentPageKey that let you pass your own page calculation.

For the dynamic UI layer problem, we also need to model our view types in the screen:

Then, the UI layer is described in a simple list:

PageKeyedDataSource<Int, ItemViewModel>() implementation:

  • When load first page, we load the layout first
  • For each page request, check what view type it is from the layout, load data from the repository/API then use that to construct ItemViewHolder list, invoke paging callbacks with that list.
  • The total item is the sum of Views in the layout plus all addition views (separator, header), this should be equals to the number of ItemViewHolder
  • Next page is always currentPage + 1, pass it in the adjacentPageKey

RecyclerView.Adapter, there are not much to do here, just ordinary view inflation, binding, diffing. For unloaded items, you could have different placeholders for each view type if you want to, in this example, I just use a generic loading view.

Put the PagedList in your ViewModel, observe it in your Fragment/Activity, submit new values to the RecyclerView and you are good to go.

Beyond

Think about what we can do with this setup, from a simple to-do app to a complex messaging app.

Bonus for Epoxy user

Instead of ItemViewModel, you already have the generated EpoxyModel<*>, the data source is now acting like EpoxyController, In the data source, you construct models using their generated builder. Then use PagedListEpoxyController to handle delivering the EpoxyModel<*>.

Check out the epoxy branch for full Epoxy + MvRx integration.

Conclusion

The paging library team indeed did a great job when they design a library that is intended to use at the data layer but turns out to be a powerful tool in the UI layer. Now we can easily load data in small trunks which different API, loading views now can be replaced with shimmer holders. Bring user best experiences while maintaining performance.

Example source code:

What do you think? let me know in the comment section!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Responses (9)

Write a response