javaandroidandroid-recyclerviewscroll-position

Retain scroll position in RecyclerView


I've read many suggestions on how to retain the current scroll position in a RecyclerView when the device is rotated. I now found out that when I use com.android.support:recyclerview-v7:23.0.+, the current position is actually saved in the savedInstanceState. It also seems to be restored, but it is only restored "somewhat correctly".

What I'm experiencing this this: I scroll to the bottom of my list in portrait mode, then I switch to landscape. The current position is restored correctly (of course, some items are not visible - I'd be able to continue scrolling)!

Without any further scrolling, I rotate back to portrait and the position restored is several items above the position I'd actually thought I'd have. The list is no longer scrolled to the bottom.

My question is: Is there anything else I can do to actually store and restore the correct scroll position?

This is the layout for the RecyclerView:

<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/swipeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="...">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/itemsView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />

</android.support.v4.widget.SwipeRefreshLayout>

This is the shortened layout for the single items:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="8dp"
    android:paddingBottom="8dp"
    android:paddingLeft="12dp"
    android:paddingRight="12dp">

    <!-- This linear layout aligns type indicator to the left and text content to the right -->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        ...

    </LinearLayout>

</LinearLayout>

The fragment that hosts the RecyclerView has the following code:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    View v = inflater.inflate(R.layout.calendar_fragment, container, false);
    ButterKnife.bind(this, v);

    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            ...
        }
    });

    if (mLayoutManager == null)
    {
        mLayoutManager = new LinearLayoutManager(getActivity());
        mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    }

    // Get cached information or access storage
    if (savedInstanceState != null)
    {
        // Initialize the area and the adapter from the saved state
        mArea = savedInstanceState.getParcelable("AREA");
        Entry[] entries = (Entry[])savedInstanceState.getParcelableArray("ITEMS");
        mDataAdapter = new MyAdapter(this, mArea, entries);
    }
    else
    {
        // Initialize the area and the adapter form scratch
        mArea = getAreaByPreference();
        mDataAdapter = new MyAdapter(this, mArea);
    }

    // Initialize the RecyclerView
    mItemsView.setHasFixedSize(true);
    mItemsView.setAdapter(mDataAdapter);
    mItemsView.setLayoutManager(mLayoutManager);

    return v;
}

Solution

  • Turns out that after updating to the current API 24 and the latest support libraries everything works as desired by default with no further coding...