Master Disaster
Master Disaster

Reputation: 769

Save scroll state in nested RecyclerView

I have a problem in my application. I have main list (recycler view). This list keeps 20 horizontal recycler views. It looks really similar like Netflix app. I need to keep state of every "carousel" in main list. If I move first item to right, next to left, scroll down (first and second items are recycled), rotate the screen, scroll up, I need to have restored position of first and second element.

I know that I should use linearLayout.saveOnInstanceState and onRestoreState but my problem is that every carousel is in main list.

configChanges in AndroidManifest.xml can not come into play...

Thank you!

Upvotes: 16

Views: 8869

Answers (3)

Narendra_Nath
Narendra_Nath

Reputation: 5173

This is the solution i implemented:

1:Use layout manager to find the lastVisible element by using recyclerView.layoutmanager.findLastVisibleItemPosition()

  1. use an interface to send this value to your calling activity or fragment

  2. store this value in a hashmap and pass it to the outer recyclerViewAdapter

  3. pass it to the inner recyclerViewAdapter to update it to the position stored by using recyclerview.scrollToPosition[hashmapofpositions[YOUR_KEY]]

This way your state that you used in the last run is conserved.

Upvotes: 0

Evgeniy
Evgeniy

Reputation: 559

As @Ixx mentioned scrollX is always 0. You have to use layoutManager to save and restore scroll position

 val scrollStates = mutableMapOf<Int, Parcelable?>()
override fun onViewRecycled(holder: VH) {
    super.onViewRecycled(holder)

    val key = holder.layoutPosition
    scrollStates[key] = holder.childRecycler.layoutManager.onSaveInstanceState()
}

override fun onBindViewHolder(holder: VH, position: Int) {
    val key = holder.layoutPosition
    val state = scrollStates[key]
    if(state != null){
        holder.childRecycler.layoutManager.onRestoreInstanceState(state)
    }else{

        holder.childRecycler.layoutManager.scrollToPosition(0)
    }
}

Upvotes: 18

azizbekian
azizbekian

Reputation: 62189

You can save scrollX position when you item of RecyclerView is being recycled, and then scroll that much when that item is being bind again:

int[] scrollXState = new int[20]; // assuming there are 20 carousel items

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
    // setup recycler here, then post scrollX after recycler has been laid out
    holder.nestedRecyclerView.post(() ->
            holder.nestedRecyclerView.setScrollX(scrollXState[holder.getAdapterPosition()]));
}

@Override
public void onViewRecycled(MyViewHolder holder) {
    scrollXState[holder.getAdapterPosition()] = holder.nestedRecyclerView.getScrollX();
    super.onViewRecycled(holder);
}

class MyViewHolder extends RecyclerView.ViewHolder {

    RecyclerView nestedRecyclerView;

    public MyViewHolder(View itemView) {
        super(itemView);
    }
}

Only thing you should take care of is that you do not want parent adapter to be destroyed and created after orientation change, make it survive orientation change. Otherwise save int[] into Bundle and then get it back from onRestoreInstanceState().

Upvotes: 6

Related Questions