Force RecyclerView to call onCreateViewHolder
Asked Answered
E

2

11

I have a RecyclerView that can show items as list, small grids or large grid and this can be change at runtime. Depending on what style user chooses i inflate different layout in onCreateViewHolder.

I also use layoutManger.setSpanSizeLookUp() to switch between styles. My code looks like this

layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
        @Override
        public int getSpanSize(int position) {
            if(showType == ProductAdapter.SHOW_TYPE_SMALL_GRID)
                return 1;
            else
                return columnCount; //show one item per row
        }
    });

@Override
public void onClick(View v) {
    if(showType == ProductAdapter.SHOW_TYPE_SMALL_GRID)
        showType = ProductAdapter.SHOW_TYPE_LARGE_GRID;
    else
        showType = ProductAdapter.SHOW_TYPE_SMALL_GRID;


    int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
    adapter = new ProductAdapter(getActivity(), productList, showType);
    recyclerView.setAdapter(adapter);
    layoutManager.scrollToPosition(firstVisibleItem);
}

The problem is to force onCreateViewHolder to be called I'm creating a new object every time user changes the style. Is there any other way?! to force onBindViewHolder() to be recalled. I simply use adapter.notifyDataSetChanged() How can i get something similar for onCreateViewHolder?

Any solution that doesn't uses multiple adapters is good enough!

Expedient answered 26/10, 2015 at 7:38 Comment(1)
It is good that you said "doesn't uses multiple adapters". Replacing adapters is not a good solution.Lowly
L
19

What you need to do is:

  1. Modify your Adapter:

  • Specify two types of Views that your Adapter can inflate:

private static final int LARGE_GRID_ITEM = -1;
private static final int SMALL_GRID_ITEM = -2;
  • Create a field that can store current type mCurrentType

  • Use your Adapter's getItemViewType. For example like this:

@Override
public int getItemViewType (int position) {
    return mCurrentType;
}
  • In your createViewHolder use the viewType to decide what type of ViewHolder you need to create.

public final RecyclerView.ViewHolder createViewHolder (ViewGroup parent, int viewType){
    if (viewType == LARGE_GRID_ITEM) {
        //return large grid view holder
    } else {
        //return small grid view holder
    }
}
  • Additionally you can create methods:

public void toggleItemViewType () {
    if (mCurrentType == LARGE_GRID_ITEM){
        mCurrentType = SMALL_GRID_ITEM;
    } else {
        mCurrentType = LARGE_GRID_ITEM;
    }
}

public boolean displaysLargeGrid(){
    return mCurrentType == LARGE_GRID_ITEM;
}
  1. Modify the code you posted:

layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
        @Override
        public int getSpanSize(int position) {
            if (adapter.displaysLargeGrid()) {
                return 1;
            } else {
                return columnCount; //show one item per row
            }
        }
    });

@Override
public void onClick(View v) {
    adapter.toggleItemViewType();
    adapter.notifyDataSetChanged();
}
Labourer answered 26/10, 2015 at 9:27 Comment(5)
I think you didn't get my problem. your code is fine but when you call toggle mCurrentType changes but createViewHolder will not be get called! notifyDataSetChanged() only force onBindView to be called not onCreateViewHolder(). So problem remains, how can also force onCreateViewHolder() to be recalled so the right item get inflated?Expedient
I did get your problem. You didn't get my answer. It takes your problem and gives you the proper approach. Did you even try my code? It will call onCreateViewHolder if there is no holder for the large grid. My code changes what getItemViewType returns. When you call notifyDataSetChanged, adapter will check itemViewTypes for positions and decide if it needs to call onCreateViewHolder (if there is no available holder for this type of View) or just onBindViewHolder (if it already has some holder left). You need to have two types of holders.Labourer
His code would work because he has purged the entire list by calling notifyDataSetChanged() this is bad for performance, when you should be calling notifyItemChanged(). calling notifyDataSetChanged() is like creating the list all over again. For a small list you can do that, but with a large number of items or heavy single row layout you shouldn't.Harmonie
This is not an answer! you changed the title completelyCureall
@mahdiazarm the SO question is more than just a title. It has a description as well. This is the correct answer for the problem that was presented in the question.Labourer
B
3

Its not the optimal choice but it's better to create a new Adapter, which will call onCreateViewHolder(). This way you can avoid your troubles, by the cost of very tiny performance issues.

Bolick answered 6/5, 2016 at 13:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.