55

I'm using a RecyclerView with heterogeneous views inside, as seen in this tutorial.

I have some items inside that RecyclerView that are RecyclerViews too. Too hard to imagine? Let's say I want to copy the Play Store's layout: One big RecyclerView with vertical linear layout and filled by many elements: Single apps and carousel of apps.

If the item to add is the layout for a single app then ID 1 will be used and I will add the layout for a single item. Else, if I need to add a Carousel then I will add one element to the main RecyclerView: Another RecyclerView with its own adapter.

That works very well. Except when you scroll the main RecyclerView. Why? Because some views are being destroyed when no more visible and then recreated in the onBindViewHolder() method. Why here? Because the Adapter of the main RecyclerView is passing for the position X and then calls OnBindViewHolder(). The latter then creates a new RecyclerView with its own adapter and assigns it to it. I'd like to keep those views just because they are heavy to reinflate every time.

Is this possible? If so, can you tell me how?

Adinia
  • 3,722
  • 5
  • 40
  • 58
Joaquin Iurchuk
  • 5,499
  • 2
  • 48
  • 64

7 Answers7

95

Use this:

recyclerView.getRecycledViewPool().setMaxRecycledViews(TYPE_CAROUSEL, 0);

This will not recycle any view of viewType TYPE_CAROUSEL but if the item count of this type is very high, then it will reduce your performance significantly, maybe even cause OOMEs

And if you adapter only have one type of view and therefore you do not override the getItemViewType(position: Int): Int method, you can pass 0 as the firts parameter of the setMaxRecycledViews function.


EDIT

After reading MML13's answer, I think it might work for you. You are worried about items of your carousel being reinflated when that view is rebinded in outer RecyclerView. If all those carousel's are of same type, i.e., they all use same adapter, you can keep the adapter inside outer RecyclerView's ViewHolder, and just swap out data and call notifyDataSetChanged() or notifyItemRangeChanged(...) on this adapter when it's rebinded. This will recycle all carousel views and all views inside those carousels.

Leonardo Sibela
  • 1,613
  • 1
  • 18
  • 39
Sourabh
  • 8,243
  • 10
  • 52
  • 98
  • 2
    what is the default value to be passed for `int viewType` parameter?? i couldnt find any documentation for it? – Sanoop Surendran Nov 21 '16 at 05:19
  • 4
    @Sanoop Unless you Override `getItemViewType`, the default viewType is `0` (see [this](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java#5818)) – Sourabh Nov 21 '16 at 06:36
  • how do you go about declaring TYPE_CAROUSEL in the adapter? do you have any examples of initialization and assignment etc –  May 17 '18 at 00:58
  • [Read this](https://stackoverflow.com/questions/26245139/how-to-create-recyclerview-with-multiple-view-type) – Sourabh Jun 07 '18 at 19:59
  • Note: TYPE_CAROUSEL can be any integer " private static final int TYPE_CAROUSEL = 1;" is declared at the top of my adapter Then I override getItemViewType and only return TYPE_CAROUSEL under a certain condition and i return 0 for all other conditions. Thanks for the help @Sourabh!! – Jacob Kaddoura Jan 24 '20 at 21:55
63

You can also set below code in your onBindViewholder() method of adapter.

holder.setIsRecyclable(false);

I have taken reference from this link

http://aphoe.com/blog/prevent-androids-recyclerview-recycling-views/

Yyy
  • 2,285
  • 16
  • 29
  • YOU ARE MY HERO!! That saved me from a huge headache! Thank you so much!!! – Leonardo Sibela Mar 15 '23 at 20:23
  • That link you provided is dead – Jcorretjer Apr 14 '23 at 02:48
  • This stops it from being recycled, but it doesn't even reuse it for the item that was bound when it was set to false. If ItemA is set to recyclable false, then scrolled away and gone back to, ItemA gets a new view, it's old view that was set to false is just taken out of circulation. How can the recycler be told to use that specific view when it comes back to itemA? – phazei May 01 '23 at 21:14
10

Because some views are being destroyed when no more visible and then recreated in the onBindViewHolder() method.

It is not true, they are not created again, they just rebind. if you have a view for position X (in scrap or recycler) RecyclerView is going to use that and rebind it.

I'd like to keep those views just because they are heavy to reinflate every time.

The main RecyclerView keeps those for you. You only need to change the adapter data of your second RecyclerView and call notifyDataSetChanged. Also store your second RecyclerView adapter inside the viewHolder of your main RecyclerView.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
6

Set the below line in your activity

recyclerView.getRecycledViewPool().setMaxRecycledViews(1,0);

set Below Code in RecyclerViewAdapter class

@Override
    public int getItemViewType(int position) {
        return 1;
    }
1

My code is working for reset UI in recycleview.

public class BooleanViewHolder extends BaseViewHolder<ConfigItemBoolean> {
    private SwitchCompat configBoolean;

    BooleanViewHolder(View view) {
        super(view);
        configBoolean = view.findViewById(R.id.enable_config_boolean);
        configBoolean.setOnCheckedChangeListener((buttonView, isChecked) -> {
            ((ConfigItemBoolean) getItem(getAdapterPosition())).setValue(isChecked);
            DataSet.mConfigDataSet.getConfigItemBooleanMap().put(configPairList.get(getAdapterPosition()).first, ((ConfigItemBoolean) getItem(getAdapterPosition())));
        });
    }

    @Override
    void bind(ConfigItemBoolean item, String key) {
        configBoolean.setText(item.getDisplayName());
        configBoolean.setChecked(item.isValue());
    }

}
Joaquin Iurchuk
  • 5,499
  • 2
  • 48
  • 64
0

try this

recyclerView.getRecycledViewPool().setMaxRecycledViews(youViewHolder type,0);

if you use viewHolder.setIsRecyclable(false); will ocur multi same viewholder in recyclerview

0

The only solution worked for me was:

recyclerView.setItemViewCacheSize(INT)
recyclerView.setDrawingCacheEnabled(true)

In that case RecyclerView items will not be recycled, but can be replaced or deleted.

vladd
  • 43
  • 6