RecyclerView存储/还原活动之间的状态


73

我正在将ListViews迁移到RecyclerViews。对于列表视图,我使用了此处介绍的通用技术来存储和恢复活动之间的滚动位置。

如何使用RecyclerViews进行相同操作?在RecyclerView.onSaveInstanceState()似乎有protected访问权限,因此无法直接使用。

Answers:


90

好吧,所以回答我自己的问题。据我了解,由于它们已将布局代码和视图回收代码(因此称为名称)分离,因此负责保持布局状态(并还原其状态)的组件现在已LayoutManager在您的recyclerview中使用。

因此,要存储状态,请使用相同的模式,但要在布局管理器而不是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);
    }
}

2
如果onRestoreInstanceState()永不称呼怎么办?
某处某人

10
但是适配器呢?
Artem Novikov

3
您可以通过消除空检查来进一步简化onRestoreInstanceState()。根据Android文档:“仅当存在要还原的保存状态时,系统才会调用onRestoreInstanceState(),因此您无需检查Bundle是否为空”。这不是我挑剔的东西。
Konaras

1
您不应该state.getParcelable使用参数LIST_STATE_KEY而不是参数进行调用"myState"吗?
乔恩·麦格伦

1
如果您使用的是Fragment,而不是恢复状态为ON onRestoreInstanceState,则可以放心地对其进行操作onActivityCreated
莫克

22

我找到了一个更好的解决方案-该解决方案具有以下优点:

  1. RecyclerView状态会在手机旋转时保存和恢复
  2. RecyclerView状态在通过RecyclerView返回活动时得以保存和恢复(在显示其他活动时未销毁该状态-这意味着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列表有帮助吗,其中活动之间没有保存instancestate?我在这里发布了以下问题: stackoverflow.com/questions/35413495/…
AJW

我没有运气将上面的代码添加到我的RecyclerView活动中。当转到上一个活动,然后返回到新创建的RecyclerView列表时,新项目的列表会丢失,并且我只会看到默认的列表布局。
AJW

5
为什么要保存状态onPause()?目前,该活动尚未销毁。状态完好无损。没有什么可恢复的。
WindRider

出于沮丧,因为我有片刻无法调用OnSaveInstanceState()的方法:/
某处某人

1
我进行了一些其他研究,并在Google的示例代码中发现,简单地保存和恢复滚动位置而不是序列化管理器的状态可能要好得多。参见\ sdk \ samples \ android-21 \ ui \ views \ RecyclerView \ Application \ src \ main \ java \ com \ example \ android \ recyclerview \ RecyclerViewFragment.java
Somewhere Somewhere

13

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

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

2
它似乎缺少了一些内容,因为recyclerViewState从未保存在任何地方
某处某人

您可以根据需要实现任何方式保存它。
Clive Jefferies

9

那是我的解决方案,它可以还原项目和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);
}

3
您不应该使用saveInstanceState保存数据集。developer.android.com/topic/libraries/architecture/…–
jakk

感谢@jakk的提示,我将尝试ViewModel类!
Nicola Gallazzi '17

ViewModel是个坏主意。如果要使用它,请小心。techyourchance.com/...
阿肖克Koyi

1
@Nicola Gallazzi在上面的#2中,mSavedInstanceState定义为什么?
AJW

1
@AJW谢谢您的提问,那是一个错误。我编辑了来自onCreate方法的mSavedInstanceState替换为saveInstanceState的答案
Nicola Gallazzi

1

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

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

但是它存在一些问题,例如不能与内部RecyclerView一起使用,以及一些其他问题,您可以在中篇文章的评论部分中查看。

或者你可以使用ViewModelSavedStateHandle该作品内RecyclerViews,屏幕旋转和工艺死亡

创建一个ViewModelsaveStateHandle

val scrollState=
    savedStateHandle.getLiveData<Parcelable?>(KEY_LAYOUT_MANAGER_STATE)

使用包裹 scrollState来保存和恢复状态,如在其他帖子中回答的那样,或者通过向RecyclerView添加滚动侦听器并

recyclerView.addOnScrollListener(object:RecyclerView.OnScrollListener(){

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

好的答案-比使用保存实例状态好得多。Recyclerview现在也处于beta版:recyclerview:1.2.0-alpha02
benzabill

0

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

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

    if (recyclerView != null) {
        outState.putParcelable(SCROLL_POSITION, recyclerView.getLayoutManager().onSaveInstanceState());
    }
}

-1
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
    }
}

-1

考虑到您已经在代码中定义了RecyclerView(mRecyclerView)和LayoutManager(mLayoutManager),并且到目前为止一切工作正常,因此保存mPositionRecyclerView的position()的解决方案如下所示:

  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);
    }
    
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.