RecyclerView 存储/恢复活动之间的状态

RecyclerView store / restore state between activities

我正在将我的 ListView 迁移到 RecyclerView。对于列表视图,我使用 here 描述的常用技术来存储和恢复活动之间的滚动位置。

如何对 RecyclerView 做同样的事情? RecyclerView.onSaveInstanceState() 似乎有 protected 访问权限,因此不能直接使用。

好的,所以回答我自己的问题。据我了解,由于他们已经分离了布局代码和视图回收代码(因此得名),负责保持布局状态(并恢复它)的组件现在是您的回收视图中使用的 LayoutManager

因此,要存储状态,您使用相同的模式,但在 布局管理器 而不是 recyclerview:

protected void onSaveInstanceState(Bundle state) {
     super.onSaveInstanceState(state);

     // Save list state
     mListState = mLayoutManager.onSaveInstanceState();
     state.putParcelable(LIST_STATE_KEY, mListState);
}

恢复 onRestoreInstanceState() 中的状态:

protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);

    // Retrieve list state and list/item positions
    if(state != null)
        mListState = state.getParcelable(LIST_STATE_KEY);
}

然后更新 LayoutManager(我在 onResume() 中做的):

@Override
protected void onResume() {
    super.onResume();

    if (mListState != null) {
        mLayoutManager.onRestoreInstanceState(mListState);
    }
}

onPause()onResume()中使用此代码保存和恢复滚动位置-

private Parcelable recyclerViewState;
recyclerViewState = mrecyclerView.getLayoutManager().onSaveInstanceState();//save
mrecyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);//restore

我找到了更好的解决方案 - 这个解决方案有以下好处:

  1. RecyclerView 状态在 phone
  2. 旋转时保存和恢复
  3. RecyclerView 状态在 返回 activity 时保存并恢复 RecyclerView(在显示另一个 activity 时未被破坏 - 这意味着onRestoreInstanceState() 没有被调用!!)

代码

public class ActivityItemList extends AppCompatActivity
{
    private final String KEY_RECYCLER_STATE = "recycler_state";
    private RecyclerView mRecyclerView;
    private static Bundle mBundleRecyclerViewState;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);//set to whatever layout name you have

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);//set to whatever view id you use
        // don't forget to set your adapter
    }

    @Override
    protected void onPause()
    {
        super.onPause();

        // save RecyclerView state
        mBundleRecyclerViewState = new Bundle();
        Parcelable listState = mRecyclerView.getLayoutManager().onSaveInstanceState();
        mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, listState);
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        // restore RecyclerView state
        if (mBundleRecyclerViewState != null) {
            Parcelable listState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
            mRecyclerView.getLayoutManager().onRestoreInstanceState(listState);
        }
    }
}

这是我的解决方案,它恢复了项目和 RecyclerView 位置

1) 在 onSaveInstanceState 方法中保存回收器视图状态

@Override
protected void onSaveInstanceState(Bundle outState) {
    Parcelable listState = myRecyclerView.getLayoutManager().onSaveInstanceState();
    // putting recyclerview position
    outState.putParcelable(SAVED_RECYCLER_VIEW_STATUS_ID, listState);
    // putting recyclerview items
    outState.putParcelableArrayList(SAVED_RECYCLER_VIEW_DATASET_ID,mDataset);
    super.onSaveInstanceState(outState);
}

2) 检查 onCreate 方法中的 savedInstanceState Bundle

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState==null){
            getRemoteData(); // No saved data, get data from remote
        }else{
            restorePreviousState(); // Restore data found in the Bundle
        }
    }

3) 如果屏幕已旋转,则恢复回收器视图数据

public void restorePreviousState(){
    // getting recyclerview position
    mListState = mSavedInstanceState.getParcelable(SAVED_RECYCLER_VIEW_STATUS_ID);
    // getting recyclerview items
    mDataset = mSavedInstanceState.getParcelableArrayList(SAVED_RECYCLER_VIEW_DATASET_ID);
    // Restoring adapter items
    mAdapter.setItems(mDataset);
    // Restoring recycler view position
    mRvMedia.getLayoutManager().onRestoreInstanceState(mListState);
}

考虑到您在代码中定义了一个 RecyclerView (mRecyclerView) 和一个 LayoutManager (mLayoutManager),并且到目前为止一切正常,保存位置的解决方案 (mPosition) 你的 RecyclerView 看起来像这样:

  1. 使用的变量和常量:

    private final String RECYCLER_POSITION_KEY = "recycler_position";
    private int mPosition = RecyclerView.NO_POSITION;
    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLayoutManager;
    private static Bundle mBundleState;
    
  2. onPause方法中:

    @Override
    protected void onPause()
    {
        super.onPause();
    
        // Save RecyclerView state
        mBundleState = new Bundle();
        mPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
        mBundleState.putInt(RECYCLER_POSITION_KEY, mPosition);
    }
    
  3. onResume方法中:

    @Override
    protected void onResume()
    {
        super.onResume();
    
        // Restore RecyclerView state
        if (mBundleState != null) {
            mPosition = mBundleState.getInt(RECYCLER_POSITION_KEY);
            if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
            // Scroll the RecyclerView to mPosition
            mRecyclerView.smoothScrollToPosition(mPosition);
        }
    }
    
  4. onSaveInstanceState方法中:

    @Override
    public void onSaveInstanceState(Bundle outState) {
        // Save RecyclerView state
        outState.putInt(RECYCLER_POSITION_KEY,  mLayoutManager.findFirstCompletelyVisibleItemPosition());
    
        super.onSaveInstanceState(outState);
    }
    
  5. onRestoreInstanceState方法中:

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        // Restore RecyclerView state
        if (savedInstanceState.containsKey(RECYCLER_POSITION_KEY)) {
            mPosition = savedInstanceState.getInt(RECYCLER_POSITION_KEY);
            if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
            // Scroll the RecyclerView to mPosition
            mRecyclerView.smoothScrollToPosition(mPosition);
        }
    
        super.onRestoreInstanceState(savedInstanceState);
    }
    

当您使用 recyclerView.getLayoutManager().onSaveInstanceState() 时,不要忘记检查 null:

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

    if (recyclerView != null) {
        outState.putParcelable(SCROLL_POSITION, recyclerView.getLayoutManager().onSaveInstanceState());
    }
}
public class MainActivity extends AppCompatActivity {
Parcelable recyclerViewState;
.......
@Override
    protected void onPause() {
        super.onPause();
        recyclerViewState = MainAnecdotesView.getLayoutManager().onSaveInstanceState();//save
    }
    @Override
    protected void onResume()
    {
        super.onResume();
        if(recyclerViewState!=null)
            MainAnecdotesView.getLayoutManager().onRestoreInstanceState(recyclerViewState);//restore
    }
}

您可以使用 adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY recyclerview:1.2.0-alpha02

中介绍的

https://medium.com/androiddevelopers/restore-recyclerview-scroll-position-a8fbdc9a9334

但它有一些问题,例如无法使用内部 RecyclerView,还有一些其他问题,您可以在 medium post 的评论部分查看。

或者您可以将 ViewModelSavedStateHandle 结合使用,这适用于内部 RecyclerView、屏幕旋转和 进程死亡

saveStateHandle

创建一个 ViewModel
val scrollState=
    savedStateHandle.getLiveData<Parcelable?>(KEY_LAYOUT_MANAGER_STATE)

使用 Parcelable scrollState 来保存和恢复状态,如其他 post 中的回答或通过向 RecyclerView 添加滚动侦听器和

recyclerView.addOnScrollListener(对象:RecyclerView.OnScrollListener() {

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
           // save here
        }
    }

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

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

以下是您的操作方法

首先创建一个成员变量

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 方法,你就可以开始了!