Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refreshing data in RecyclerView and keeping its scroll position

People also ask

How do I stop refreshing RecyclerView data scroll to top position Android?

Just set your LayoutManager and adapter for the first time. Make a setDataList method in your adapter class. And set your updated list to adapter list. And then every time of calling API set that list to setDataList and call adapter.

Does RecyclerView scroll?

To help you build apps with lists, Android provides the RecyclerView . RecyclerView is designed to be very efficient, even with large lists, by reusing, or recycling, the views that have scrolled off the screen.

What is ViewHolder in RecyclerView?

A ViewHolder describes an item view and metadata about its place within the RecyclerView. RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive View. findViewById(int) results.


I use this one.^_^

// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();

// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);

It is simpler, hope it will help you!


I have quite similar problem. And I came up with following solution.

Using notifyDataSetChanged is a bad idea. You should be more specific, then RecyclerView will save scroll state for you.

For example, if you only need to refresh, or in other words, you want each view to be rebinded, just do this:

adapter.notifyItemRangeChanged(0, adapter.getItemCount());

EDIT: To restore the exact same apparent position, as in, make it look exactly like it did, we need to do something a bit different (See below how to restore the exact scrollY value):

Save the position and offset like this:

LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager();
int firstItem = manager.findFirstVisibleItemPosition();
View firstItemView = manager.findViewByPosition(firstItem);
float topOffset = firstItemView.getTop();

outState.putInt(ARGS_SCROLL_POS, firstItem);
outState.putFloat(ARGS_SCROLL_OFFSET, topOffset);

And then restore the scroll like this:

LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager();
manager.scrollToPositionWithOffset(mStatePos, (int) mStateOffset);

This restores the list to its exact apparent position. Apparent because it will look the same to the user, but it will not have the same scrollY value (because of possible differences in landscape/portrait layout dimensions).

Note that this only works with LinearLayoutManager.

--- Below how to restore the exact scrollY, which will likely make the list look different ---

  1. Apply an OnScrollListener like so:

    private int mScrollY;
    private RecyclerView.OnScrollListener mTotalScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            mScrollY += dy;
        }
    };
    

This will store the exact scroll position at all times in mScrollY.

  1. Store this variable in your Bundle, and restore it in state restoration to a different variable, we'll call it mStateScrollY.

  2. After state restoration and after your RecyclerView has reset all its data reset the scroll with this:

    mRecyclerView.scrollBy(0, mStateScrollY);
    

That's it.

Beware, that you restore the scroll to a different variable, this is important, because the OnScrollListener will be called with .scrollBy() and subsequently will set mScrollY to the value stored in mStateScrollY. If you do not do this mScrollY will have double the scroll value (because the OnScrollListener works with deltas, not absolute scrolls).

State saving in activities can be achieved like this:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(ARGS_SCROLL_Y, mScrollY);
}

And to restore call this in your onCreate():

if(savedState != null){
    mStateScrollY = savedState.getInt(ARGS_SCROLL_Y, 0);
}

State saving in fragments works in a similar way, but the actual state saving needs a bit of extra work, but there are plenty of articles dealing with that, so you shouldn't have a problem finding out how, the principles of saving the scrollY and restoring it remain the same.


Keep scroll position by using @DawnYu answer to wrap notifyDataSetChanged() like this:

val recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState() 
adapter.notifyDataSetChanged() 
recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)