Search code examples
javaandroidandroid-recyclerviewmvplinearlayoutmanager

Unable to Restore the Scroll Position in RecyclerView using LayoutManager


So, I have an EditText on which I have set onEditorActionListener, i.e after the user enters the text and presses enter/search it will fetch the details and populate the recycler view accordingly.

Now in order to save the state on a configuration change I have wrote the following code -

Parcelable stateList;

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    //Saving instance of the state in Parcelable stateList
    stateList = recyclerView.getLayoutManager().onSaveInstanceState();
    outState.putParcelable(RETAIN_STATE, stateList);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    if(savedInstanceState!=null) {
        stateList = savedInstanceState.getParcelable(RETAIN_STATE);
        recyclerView.getLayoutManager().onRestoreInstanceState(stateList);
    }
}

But when I run this, and rotate the screen the recycler view does not restore the state from the stateList parcelable.

I am using MVP, so I'm setting the adapter in the callback of the presenter.

I was able to retain the state when we click on enter/search on the keyboard after the screen was rotated, so I tried this hack in the onRestoreInstanceState(), but I think there should be a better way to go about this.

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    if(savedInstanceState!=null) {
        //The hack!
        et_search.onEditorAction(EditorInfo.IME_ACTION_SEARCH);
        stateList = savedInstanceState.getParcelable(RETAIN_STATE);
        recyclerView.getLayoutManager().onRestoreInstanceState(stateList);
    }
}

Let me know if there is more information needed. Thanks in advance.


Solution

  • You can let android handle the orientation changes for you if don't want to any custom stuff by overriding config change.e.g hiding some view etc

    <activity android:name=".mainpage.view.MainActivity" android:configChanges="orientation|screenSize|screenLayout" >
    

    Include this in your manifest for that activity Further to save scroll position in recyler view on rotation you can use following gist to avoid duplicate code as you can use this custom recyclerview in your project wherever same feature required

    import android.content.Context;
    import android.os.Bundle;
    import android.os.Parcelable;
    import android.support.annotation.Nullable;
    import android.support.v7.widget.RecyclerView;
    import android.util.AttributeSet;
    
    /**
     * Class {@link StatefulRecyclerView} extends {@link RecyclerView} and adds position management on configuration changes.
     *
     * @author FrantisekGazo
     * @version 2016-03-15
     */
    public final class StatefulRecyclerView
            extends RecyclerView {
    
        private static final String SAVED_SUPER_STATE = "super-state";
        private static final String SAVED_LAYOUT_MANAGER = "layout-manager-state";
    
        private Parcelable mLayoutManagerSavedState;
    
        public StatefulRecyclerView(Context context) {
            super(context);
        }
    
        public StatefulRecyclerView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public StatefulRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        protected Parcelable onSaveInstanceState() {
            Bundle bundle = new Bundle();
            bundle.putParcelable(SAVED_SUPER_STATE, super.onSaveInstanceState());
            bundle.putParcelable(SAVED_LAYOUT_MANAGER, this.getLayoutManager().onSaveInstanceState());
            return bundle;
        }
    
        @Override
        protected void onRestoreInstanceState(Parcelable state) {
            if (state instanceof Bundle) {
                Bundle bundle = (Bundle) state;
                mLayoutManagerSavedState = bundle.getParcelable(SAVED_LAYOUT_MANAGER);
                state = bundle.getParcelable(SAVED_SUPER_STATE);
            }
            super.onRestoreInstanceState(state);
        }
    
        /**
         * Restores scroll position after configuration change.
         * <p>
         * <b>NOTE:</b> Must be called after adapter has been set.
         */
        private void restorePosition() {
            if (mLayoutManagerSavedState != null) {
                this.getLayoutManager().onRestoreInstanceState(mLayoutManagerSavedState);
                mLayoutManagerSavedState = null;
            }
        }
    
        @Override
        public void setAdapter(Adapter adapter) {
            super.setAdapter(adapter);
            restorePosition();
        }
    }

    Link to gist