Android recyclerview nested recyclerview

Easily Adding Nested Recycler View in Android

Sep 8, 2018 · 6 min read

Recycler View is one of the most used view components in android. Also, the design of recycler views is getting complex day by day such as Recycler View with multiple view types, nested recycler view, nested recycler view with multiple view types. Today, we are going to talk about Nested Recycler View with one view type.

What will it look like?

Nested Recycler View would look like something shown below. A vertical infinite scrollable list with horizontal infinite carousels.

Key Idea

Before we begin with the c o de, let’s understand the basic key idea involved in designing nested recycler view. So, the key idea is that on the top level we will have a list of card view objects which we will show in recycler view. So at the top level, we will have single recycler view of card views. Now each card view, in turn, will have a recycler view as it’s one of the children and this child recycler view will act as a horizontal carousel.

If you directly want to jump straight to the full source code then visit here.
Otherwise, follow through the whole blog for a detailed step by step implementation of nested recycler view in Kotlin.

Cool, Show me the Code!

Okay, let’s directly jump to code now. Open a new project in Android Studio with default settings and Kotlin support checked in.

Adding Dependencies
Modify your build.gradle file for app module to add dependencies for recycler-view and card view as shown below:

Now create three packages in the main directory:

  1. Models — We will keep all our models, data factory and objects here.
  2. Views — We will keep all our views, activities and ui related stuff here
  3. ViewModel — ViewModels are like a bridge between models and views.

Let’s prepare UI for our App

This will be the final result of our this short activity. Our final view/UI will look like this with an infinite scrollable list of card views with each card view holding another child infinite horizontal carousel.

So let’s follow a top-down approach and start with the activity_main.xml file. Modify it to have a recycler view as shown:

Now let’s prepare our card view layout which will be a single item in the above-created recycler view as shown below:

Now let’s create View for the recycler view present in card view. As shown in the above figure, the card view contains a list of image view with text view and thus that is going to be our layout structure as shown below. Let’s call this child_recycler.xml.

As clear from the layout file, this view is Constraint layout with text view for title and image view for the image.

Now Let’s create the Models

We will have two models:
1. ParentModel — This will store data for card view or card object.
2. ChildModel — This will store data for Item for recycler-view present in card.

ParentModel.kt
This is a Kotlin data class that stores data for a single card view and so contains one text view for the title of the card and List of ChildModel to show in the recycler view present in card view.

ChildModel.kt
This is also a Kotlin data class that stores data for items present in the recycler view of the card view. That’s why it contains one text view for title of the movie and one image view for the image.

Читайте также:  Не удалось сохранить изменения контакта андроид

Now Ideally, the data should come from some database either remote or local but for keeping the simplicity of our blog, I have created ParentDataFactory and ChildDataFactory class to get respective data.

As shown above, in the ParentDataFactory object (Equivalent to Singleton class in Java), it has two private functions to generate random title and random image and one public function for getting a list of parent models with a passed size.
Similarly, we will also create the ChildDataFactory as shown below:

Let’s add View and Recycler view Adapters

Now let’s complete our view and for that, we will first create two RecyclerView Adapters for two recycler views. We will start with ChildAdapter which is just like any other RecyclerView adapter as shown below:

As you can see here, we are passing the data to the Adapter. We can also delegate this to some view model but again is left out to have our focus on creating nested recycler view. Also, the view in ViewHolder is directly accessed without view binding as it is default in Kotlin.

Now let’s add ParentAdapter which will have text view and a recycler view and thus in this adapter in its onBindViewHolder method we will be setting up the values for recycler view as shown below:

As seen above in onBindViewHolder, we are setting up our child recycler view by passing layout manager, adapter by creating a new ChildAdapter and also you can see that we are setting the recycledViewPool for each child recycler view to the same view pool that is created earlier. Also, the initial prefetch amount for child recycler view is set to 4 for our use case. These are done to optimize the nested recycler view. You can read more about it here.

Now let’s modify our MainActivity to have the final setup ready. Modify the MainActivity.kt as follows:

All is Well, Hit Run!

Now everything seems good, hit run and see your beautiful final result. So you can see how easy it is to add nested recycler views and also using Kotlin for development make development much more fun and easy.

Источник

How to easily add Nested RecyclerView

Jan 9, 2018 · 6 min read

Last time we optimized the way we worked with RecyclerView, and we also learned how to reuse cells in different lists and easily add new cells.

Today I’ll be explaining two things:

· simplifying DiffUtil support in this implementation;

· adding support for nesting RecyclerView.

If you liked the previous post, then you’ll like this one, too.

DiffUtil

I think there’s no need to explain what Dif f Util is. Every Android developer has probably used it in projects and took advantage of nice extras like animation and improved performance.

A few days after the first article has gone live, I’ve got a pull request with a DiffUtil implementation. Let’s see how it’s implemented. You may remember that we ended up with an adapter with public method setItems(ArrayList items). Here it’s not very convenient for us to use DiffUtil, and we’ll need to save the old copy of the list somewhere. So, we would be getting something like this:

Classic implementation of DiffUtil.Callback:

And an extended ItemModel interface:

This is quite implementable and not complicated. However, if we use this several times in a project, then let us think if we really need so much of the same code. Let us carry over the reusable elements into our own DiffUtil.Callback implementation:

All in all, we managed to make a rather versatile implementation. We avoided unnecessary lines of code and were able to focus on the important methods: areItemsTheSame() and areContentsTheSame(). Those must be implemented and those may differ.

I’m omitting the implementation of getChangePayload() and you can see it in the source.

Now we can add one more method with DiffUtil support in our adapter:

That’s about all with DiffUtil, and now we can use our abstract class DiffCallback if necessary. And we’ll be implementing just two methods.

Читайте также:  Gopro quick для android

I think now that we’ve warmed up and refreshed our memories, we can move on to more interesting things.

One way or another, nested lists find their ways into our applications, at customer request or at GUI designers discretion,. Until recently I disregarded them as I faced the following problems:

· complex implementation of the cell that contains RecyclerView;

· complex data updating in nesting cells;

· non-reusable nesting cells;

· too complex click forwarding from nesting cells into the root location — Fragment/Activity;

Some of these problems are questionable and easily addressable. Some will go away if we plug in our optimized adapter from the first article 🙂 However, we’ll have to face the complexity of implementation at least. Let us articulate our requirements:

· easy addition of new types of nested cells;

· reusability of a cell type for both nested and parent list items;

It’s important to note that I have divided the concepts of a cell and a list item:
list item — the entity used in RecyclerView.
cell — a set of classes that allow for showing one list item type. In our case, this is an implementation of already known classes and interfaces: ViewRenderer, ItemModel, ViewHolder.

Let’s sum up what we have at our disposal. ItemModel is the key interface, and it’s obvious it will be convenient for us to work with it later on. Our composite model must include child models, so we add a new interface:

Looks decent. Accordingly, composite ViewRenderer must know about child renderers, so we add:

Here I’ve added two ways of adding child renderers, and I’m sure we’ll put them to good use.
Also, please note generic CompositeViewHolder. This will be a separate class for composite ViewHolder, and I don’t know yet what it will contain. Now let us continue working with CompositeViewRenderer. We have two required methods left — bindView() and createViewHolder(). We should initialize the adapter in createViewHolder() and then familiarize it with the renderers. As for bindView(), we’ll do a simple default item updating:

Almost there! As it turned out, for such an implementation we’ll need viewHolder itself in createViewHolder(). We cannot initialize it here, so we create a separate abstract method. Here, we may want to familiarize our adapter with RecyclerView. We could borrow it from CompositeViewHolder. We haven’t implemented it yet, so let’s do it:

That’s right! I’ve added the default implementation with LinearLayoutManager 🙁 I thought this would bring more profit, and we’ll overload the method if necessary and set another LayoutManager.

It seems that’s about it. Now we have specific classes to implement, and we’ll see what we get:

We register our composite renderer:

As seen from the last sample, to subscribe to clicks we just pass the necessary interface into the renderer’s constructor. Thus, our root location implements this interface and is aware of all the required clicks.

Click forwarding example:

We’ve achieved sufficient versatility and flexibility while working with nested lists. We’ve essentially simplified the process of adding composite cells. Now we can easily add new composite cells and easily combine individual cells in nested and parent lists.

Источник

Nested recycler in Android done right!

TL;DR: This article solves the problem of horizontal scroll position lost when scrolling vertically and horizontal scroll gestures being registered as vertical. Refer to this GitHub repo with sample app to see solution.

Many apps including Netflix and Play Store use the nested recycler pattern of having multiple horizontal scrollable views embedded inside a vertical one. Implementing such structure in Android with RecyclerView seemed quite straightforward. And it is!

BUT… there are a couple of things that need to be tweaked for everything to be smooth and behave as expected. Let’s jump straight into it.

Let’s create an app that shows sections with animals. Each section has a title and each animal has a name and an image. The result should look something like this:

Читайте также:  Android словарь русского языка

The data structure is following:

Layouts

We need a layout for the individual animal. The layout item_animal.xml contains a CardView with an ImageView, a TextView and a gradient to make the text pop.

Next, we need the layout for the section with the title and the nested RecyclerView: item_animal_section.xml

The activity_main.xml contains just a recycler.

Adapters

Next, we will need our adapters. For the sake of this tutorial, I am using a simple RecyclerView.Adapter. In real life, you’d probably wanna go for RecyclerView.ListAdapter.

The AnimalAdapter.kt uses item_animal.xml and onBindViewHolder method sets the name and loads the image (using Coil library):

The AnimalSectionAdapter.kt uses item_animal_section.xml and onBindViewHolder method sets the title of the section and the adapter of the nested recycler:

Putting it all together

For the parent recycler (vertical), we’ll be using a ConcatAdapter. This is technically not necessary, but is a good habit as it will make your life easier if you decide to build on this. The structure is illustrated in the following image (MS Paint skills: 10/10):

All we need to do now is create some fake data (this is not really the point of this tutorial; check out my DataSource at the GitHub repo to see how I came around creating the lists, or create your own logic) and populate the adapters.

And voilà, we’ve created working nested RecyclerViews!

But have we really?

The issues

1. Recycling the recyclers

The first issue is caused by the fact that the individual views in our AnimalSectionAdapter (meaning the whole rows) are being recycled. This results in the horizontal scroll position being lost when scrolling vertically:

To resolve this, we need to manually save and restore horizontal scroll state of each row when it gets recycled or bound respectively.

To achieve this, we need to to save the state in the onViewRecycled method inside of our AnimalSectionAdapter and restostore the state in the onBindViewHolder method .

To persist the states, will use a MutableMap with the key being the ID of the corresponding row.

2. Horizontal swipes registered as vertical

The second issue comes from the fact that we have two views with opposite scroll directions inside of each other. This has already been tackled by Christophe Beyls in his post (read it!) and instead of reinventing the wheel, we’ll use his brilliant solution.

Based on Christophe’s solution, we’ll create a Kotlin extension to solve this issue.

And call it in our MainActivity.kt’s initViews method :

Bonus: Optimize!

View pool

Each nested RecyclerView will by default have its own pool of views to recycle. This is not optimal, as we know the views are always identical so one pool would be enough for all of them.

We can easily achieve this by creating one pool inside of our AnimalSectionAdapter and setting it to each of the nested RecyclerViews.

Initial prefetch item count

As pointed out by Christophe Beyls himself:

On the nested RecyclerView’s LinearLayoutManager , you should call setInitialPrefetechItemCount() with the estimated number of horizontal items that will be visible. When prefetching the ViewHolder containing the horizontal RecyclerView, the parent RecyclerView will ask the child RecyclerView to pre-bind a full row of items and since the child RecyclerView can’t know how many items will be displayed until it’s laid out, you need to provide that information yourself. If you don’t, only 2 items will be pre-bound by default, and the rest will be bound after prefetch when the row becomes laid out and visible which causes slower performance.

We will do exactly that inside the AnimalSectionAdapter’s onBindViewHolder:

Conclusion

Full code available at the this GitHub repo.

Building a fairly complex nested RecyclerView layout is such a common task that one would expect it to be working perfectly out-of-the box. In the Android world, however, this is unfortunately still not the case. I tackled the issue in this post, creating a sample app you can use to solve a similar issue of your own. Good luck!

Источник

Оцените статью