10

I am using nested RecyclerView. Means inside a vertical RecyclerView I have multiple horizontal recycler view

I am attaching adapter to horizontal recylerviews inside onBindViewHolder method of parent RecyclerView as follows.

@Override
public void onBindViewHolder(final MainViewHolder holder, final int position) {
    switch (holder.getItemViewType()) {
        case TYPE_PRODUCT:
            ((ListHolderProduct) holder).itemTitle.setText(browseCategoryHomePageItems.get(position).displayName.toUpperCase());
            ((ListHolderProduct) holder).recyclerView.setAdapter(new CarouselProductsRecyclerAdapter(context
                    , browseCategoryHomePageItems.get(position).products
                    , R.layout.activity_categoryhome_products_grid_item
                    , nestedRecyclerItemClickedListener
                    , position));
            break;
        case TYPE_DEAL:
            ((ListHolderDeal) holder).itemTitle.setText(browseCategoryHomePageItems.get(position).displayName.toUpperCase());
            ((ListHolderDeal) holder).recyclerView.setAdapter(new CarouselDealsRecyclerAdapter(context
                    , browseCategoryHomePageItems.get(position).dealItems
                    , R.layout.activity_categoryhome_deals_grid_item
                    , nestedRecyclerItemClickedListener
                    , position));
            break;
            //few more types like this
    }
}

Now whenever I scroll page it is lagging a bit since I am attaching adapter to horizontal RecyclerView on OnBindViewHolder

And there can be N Number of TYPE_PRODUCT or any type of horizontal lists. Means there can be more that one horizontal lists of same type.

Any idea how can I optimize this thing and improve the scroll speed.

It is lagging since setAdapter is called every time for list previously.

Update on this I am extending LinearLayoutManager and in that I am setting extraLayout space which has fixed my issue but I don't know this is right way or not I am setting extra space as below.

 layoutManager.setExtraLayoutSpace(2 * this.getResources().getDisplayMetrics().heightPixels);

and follwoing is custom layout manager class

public class PreCachingLayoutManager extends LinearLayoutManager {
private static final int DEFAULT_EXTRA_LAYOUT_SPACE = 600;
private int extraLayoutSpace = -1;
private Context context;

public PreCachingLayoutManager(Context context) {
    super(context);
    this.context = context;
}

public PreCachingLayoutManager(Context context, int extraLayoutSpace) {
    super(context);
    this.context = context;
    this.extraLayoutSpace = extraLayoutSpace;
}

public PreCachingLayoutManager(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
    this.context = context;
}

public void setExtraLayoutSpace(int extraLayoutSpace) {
    this.extraLayoutSpace = extraLayoutSpace;
}

@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
    if (extraLayoutSpace > 0) {
        return extraLayoutSpace;
    }
    return DEFAULT_EXTRA_LAYOUT_SPACE;
}

}

amodkanthe
  • 4,345
  • 6
  • 36
  • 77
  • check this http://stackoverflow.com/questions/26649406/nested-recycler-view-height-doesnt-wrap-its-content – Dmitriy Puchkov Mar 16 '16 at 14:23
  • 3
    no my question different I am facing lag while scroll – amodkanthe Mar 17 '16 at 05:35
  • Thanks for solution with getExtraLayoutSpace! I have not found better solution, but i add one improvement - i return extra space only first time, and return zero in other cases. – Anton Oct 18 '17 at 09:06

6 Answers6

5

Do not create horizontal adapter every time in onBindViewHolder, instead in each ViewHolder class(ListHolderDeal, ListHolderProduct) create appropriate adapter. Then in onBindViewHolder of vertical Recyclerview just replace the data of that adapter and if the horizontal RecyclerView does not have an adapter set it with the viewholder adapter. If it does, replace the data set of that adapter and call notifyDataSetChange. With this approach, you can use adapter pool implicitly so the GC may less bother you.

I hope it helps.

Mohamed Salah
  • 868
  • 1
  • 15
  • 34
2

I had the same problem with scrolling, because of loading images while scrolling. So you need to use Picasso library for loading your images, and to make pause_tag if you are using onScrollListener.

In your onBindViewHolder Picasso.with(context) .load(foodData.getRecipe_resize_image_url()) .resize(width, height) .placeholder(R.drawable.empty_image) .tag("resume_tag") .into(mainViewHolder.food_picture);

In your onScrollListener

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    super.onScrollStateChanged(recyclerView, newState);
    final Picasso picasso = Picasso.with(context);
    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
        picasso.resumeTag("resume_tag");
    } else {
        picasso.pauseTag("resume_tag");
    }

}
Peter Parker
  • 560
  • 1
  • 5
  • 17
2

Create the Adapter at your ViewHolder creation, not binding. Also, be aware that sometimes, initial scroll lag happens due to the build variant you are testing on. Release APKs have better performance since the entire debugging tools and profilers activated on a Debug Build are not activated anymore. Most of my performance issues are solved on the release build.

M'aiq the Coder
  • 762
  • 6
  • 18
0

Please check whether you have overrided getItemViewType(int position) method, if not please override this method and say how many type of views your recyclerview is going to handle, from the above code there are two types of view one for TYPE_PRODUCT and other for TYPE_DEAL.

This particular method if overriden and returns the type count correctly, it prevents unwanted layout inflation for the already available typed view(in recycler cache) which increases the performance drastically.

For example of implementing multiple typed recyclerview, please refer this.

Reference : http://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType%28int%29

Hope this helps!!! Please let me know if you have any issues with it. Thanks.

Community
  • 1
  • 1
Dinash
  • 3,027
  • 4
  • 32
  • 45
0

RecyclerView uses view pool to prevent recreation of views which improve performance but when we use RecyclerView inside RecyclerView the inner RecyclerView has its own view pool so every time outer RecyclerView scrolls each view of the inner RecyclerView inflated because it has a separate view pool.

So to use same view we need to add this code in outer RecyclerView adapter:

public OuterRecyclerViewAdapter(List<Item> items) {
    //Constructor stuff
    viewPool = new RecyclerView.RecycledViewPool();
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    //Create viewHolder etc
    holder.innerRecyclerView.setRecycledViewPool(viewPool);

}

I found this info from this helpful article

Suraj Vaishnav
  • 7,777
  • 4
  • 43
  • 46
0

The other answers are focusing on the recycling part of the RecyclerView. If you look carefully, the problem is not about the recycling process at all. In order to have smooth behavior on the first scroll, you should preload the items when the outer adapter created. This can be done by extending LinearLayoutManager and overriding calculateExtraLayoutSpace() method.

// Utility to convert DP to PX
val Int.dp: Float get() = (this * Resources.getSystem().displayMetrics.density).toInt()

// Assign LayoutManager to RecyclerView
recyclerView.layoutManager = object : LinearLayoutManager(this) {
   override fun calculateExtraLayoutSpace(
      state: RecyclerView.State,
      extraLayoutSpace: IntArray
   ) {
      extraLayoutSpace[0] = 500.dp // Extra space for the top/left items
      extraLayoutSpace[1] = 500.dp // Extra space for the bottom/right items
   }
}

The extra space needed is depend on the size of your items. Choose wisely as this will cause RecyclerView to add extra items in the ViewPool. If the LayoutManager space is greater then your overall layout size, the items will not get recycled at all (because all items are visible by LayoutManager).

Wachid Susilo
  • 496
  • 4
  • 11