如何在屏幕旋转时保持 recyclerView 的滚动位置

How to Maintain Scroll position of recyclerView on Screen Rotation

我正在使用 gridlayoutManager 填充 recyclerView。现在我想保存屏幕旋转的滚动位置。

我已经尝试使用 onSaveInstanceState 和 onRestoreInstanceState() 来这样做 post :

下面是我的代码:

@Override
protected void onSaveInstanceState(Bundle outState) {
    Log.e(TAG, "onSaveInstanceState");
    super.onSaveInstanceState(outState);
    outState.putParcelable(KEY_INSTANCE_STATE_RV_POSITION, 
    gridLayoutManager.onSaveInstanceState());
 }


@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {

    Log.e(TAG, "onRestoreInstanceState");
    super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState!= null){
        Parcelable savedState = 
    savedInstanceState.getParcelable(KEY_INSTANCE_STATE_RV_POSITION);

        movieAdapter.addAll(movieItemList);
        if (savedState!= null){
            gridLayoutManager.onRestoreInstanceState(savedState);
        }
    }
}


//this is my movieAdapter.addAll() method 

public void addAll(List<MovieItem>items){

    movieItems = items;
}


//This is the method to get lists of movies from ViewModel Class

private void loadFavMovies() {
    FavViewModel favViewModel = 
    ViewModelProviders.of(MainActivity.this).get(FavViewModel.class);
    favViewModel.getFavListLiveData().observe(MainActivity.this, new 
    Observer<List<FavlistItem>>() {
        @Override
        public void onChanged(List<FavlistItem> favlistItems) {
            if (!favlistItems.isEmpty()) {
                loadingIndicator.setVisibility(View.GONE);
                movieRecycler.setVisibility(View.GONE);
                favRecycler.setVisibility(View.VISIBLE);
                favAdapter.setFavlistItems(favlistItems);
                Toast.makeText(getApplicationContext(), "Swipe Left Or 
       Right To remove Item",Toast.LENGTH_SHORT).show();
               }else {
                loadingIndicator.setVisibility(View.GONE);
                Toast.makeText(getApplicationContext(), "No Favorite 
      Movies",Toast.LENGTH_SHORT).show();
            }
        }
    });
}

这是这个项目的 link 到 GitHub https://github.com/harshabhadra/Movies-Mela

一种方法是在方向改变时停止 re-creation of activity。为此,您可以将其添加到清单中的 activity。

android:configChanges="orientation|screenSize"

但这并不总是一个好的做法,因此另一种选择是在 onSaveInstanceState 中退出之前保存适配器位置,然后使用 [=14= 滚动到 onCreate 中的那个位置].因此,例如,您可以按照以下几行进行操作。

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
      savedInstanceState.putInt("position", mRecyclerView.getAdapterPosition()); // get current recycle view position here.
      //your other code
      super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      //your other code
      if(savedInstanceState != null){
        // scroll to existing position which exist before rotation.

  mRecyclerView.scrollToPosition(savedInstanceState.getInt("position"));
      }
   }

你应该看看这个

基本上,您想创建一个新的 class 扩展 RecyclerView(记住您必须使用新的 class 而不是 RecyclerView)并覆盖 onSaveInstanceState()onRestoreInstanceState()。在 onSaveInstanceState() 中,您将保存第一个可见元素的索引并滚动到 onRestoreInstanceState() 中的该元素。

他们在链接 post 的已接受答案中采用的方式不同,但使用 LinearLayoutManager 代替,因此我将定制代码以使用 GridLayoutManager

public class CustomRecyclerView extends RecyclerView {
    private int mScrollPosition;
    public VacationsRecyclerView(@NonNull Context context) {
        super(context);
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        super.onRestoreInstanceState(state);
        if(state != null && state instanceof SavedState){
            mScrollPosition = ((SavedState) state).mScrollPosition;
            LayoutManager layoutManager = getLayoutManager();
            if(layoutManager != null){
                int count = layoutManager.getItemCount();
                if(mScrollPosition != RecyclerView.NO_POSITION && mScrollPosition < count){
                    layoutManager.scrollToPosition(mScrollPosition);
                }
            }
        }
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        LayoutManager layoutManager = getLayoutManager();
        if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
            mScrollPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();
        }
        SavedState newState = new SavedState(superState);
        newState.mScrollPosition = mScrollPosition;
        return newState;
    }

    static class SavedState extends android.view.View.BaseSavedState {
        public int mScrollPosition;
        SavedState(Parcel in) {
            super(in);
            mScrollPosition = in.readInt();
        }
        SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(mScrollPosition);
        }
        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}

实现您想要实现的目标的最简单方法是在 Fragment 中使用 RecyclerView,而不是直接在 Activity.

中使用

与活动不同,片段在默认情况下不会 re-created 旋转,但会保留其状态。这样,recyclerview 将始终保持在相同的滚动位置。

我在我的一个应用程序中以这种方式使用 RecyclerView,它显示包含 > 200 个元素的列表。

如果您使用 GridLayoutManager 子类 LinearLayoutManager,滚动位置应该已经自动保存。你可以看看 SavedState inside LinearLayoutManager:

public static class SavedState implements Parcelable {

    int mAnchorPosition;

    int mAnchorOffset;

    boolean mAnchorLayoutFromEnd;

mAnchorPosition 实际上是您的滚动位置,它会在旋转过程中保存。如果这对您不起作用,则可能是其他地方出了问题:例如,您可能正在错误地重新加载/重新应用数据。这可能是您需要问的真正问题。您可能需要将该数据缓存在某处,以便您可以在配置更改后立即自动重新应用它。

我还可以确认在我使用过 LinearLayoutManagerGridLayoutManager 的每个项目中,滚动位置在旋转期间正确保持,我没有额外的工作。

我已经尝试了所有人建议的所有方法,但最终,这个要点对我有用

https://gist.github.com/FrantisekGazo/a9cc4e18cee42199a287

我只是在我的项目中导入这个,并用这个替换默认提供的recyclerView "StateFulRecyclerView" 它解决了我的问题并在屏幕旋转或任何配置更改期间自动处理滚动位置。无需使用 onSaveInstanceState 来保持滚动位置。

现在,它有简单的solution.use这个依赖

    implementation "androidx.recyclerview:recyclerview:1.2.0-alpha05"

然后像下面这样设置 stateRestorationPolicy

myAdapter.stateRestorationPolicy=RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY

我知道我迟到了,但如果它有帮助的话!

存储回收站视图位置比其他答案看起来更简单

以下是您的操作方法

首先创建一个成员变量

    Parcelable state;

现在,

@Override
protected void onPause() {
    super.onPause();
    state = recyclerView.getLayoutManager().onSaveInstanceState();
}

@Override
protected void onResume() {
    super.onResume();
        recyclerView.getLayoutManager().onRestoreInstanceState(state);
}

使用上面的代码覆盖 on pause 和 on resume 方法,你就可以开始了!

很简单。暂停时保存位置,恢复时恢复。

暂停

lastVisitPosition = ((LinearLayoutManager)recycleView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();

恢复

((LinearLayoutManager) recycleView.getLayoutManager()).scrollToPosition(lastVisitPosition);

((LinearLayoutManager) recycleView.getLayoutManager()).scrollToPositionWithOffset(lastVisitPosition,0)