Save scroll state in nested RecyclerView

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().


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)
    }
}