点击RecyclerView条目时如何保存其滚动状态(ViewPager+Fragment场景)
解决方案
核心问题说明
现有实现的问题主要有3个:
- 仅保存横向滚动偏移值,且用固定100ms延迟触发滚动,RecyclerView未完成布局时滚动操作会失效,导致状态恢复失败
- 临时状态存在SharedPreferences中,读写效率低且没有必要,跨Fragment的临时状态直接在宿主Activity中维护即可
- 仅在Fragment初始化时传入一次滚动值,后续用户手动滑动RecyclerView的状态没有更新保存,切换页面再返回就会重置
具体修改步骤
1. 宿主Activity修改
首先在TvShowEpisodeDetails中新增成员变量,维护全局状态:
// 存储每个页面对应的RecyclerView滚动状态 private SparseArray<Parcelable> mRvScrollStates = new SparseArray<>(); // 存储全局共享的滚动值,供新建Fragment使用 private int mGlobalScrollValue = 0;
然后实现Fragment回调的sendText方法:
@Override public void sendText(int selectedPos, int scrollValue) { // 保存最新滚动值 mGlobalScrollValue = scrollValue; // 切换ViewPager到选中的页面 mViewPager.setCurrentItem(selectedPos); } // 新增供Fragment调用的状态存取方法 public void saveRvScrollState(int pagePosition, Parcelable state) { mRvScrollStates.put(pagePosition, state); } public Parcelable getRvScrollState(int pagePosition) { return mRvScrollStates.get(pagePosition); }
同时调整ViewPager的缓存策略,避免页面频繁销毁重建:
// 页面数量不多的情况下直接缓存全部 mViewPager.setOffscreenPageLimit(mEpisodes.size());
2. Fragment修改
调整onViewCreated中的状态恢复逻辑,去掉不可靠的延迟滚动:
// 原有初始化RecyclerView、LayoutManager、Adapter的代码保留 // ... // 替换原有的延迟滚动逻辑 Parcelable scrollState = ((TvShowEpisodeDetails)requireActivity()).getRvScrollState(currentPosition); if (scrollState != null) { layoutManager.onRestoreInstanceState(scrollState); } else { int scrollValue = getArguments().getInt("scrollValue", 0); mEpisodesList.scrollBy(scrollValue, 0); }
新增Fragment销毁时的状态保存逻辑,重写onPause方法:
@Override public void onPause() { super.onPause(); if (mEpisodesList != null && mEpisodesList.getLayoutManager() != null) { int currentPagePos = getArguments().getInt("position"); Parcelable state = mEpisodesList.getLayoutManager().onSaveInstanceState(); ((TvShowEpisodeDetails)requireActivity()).saveRvScrollState(currentPagePos, state); } }
3. 可选优化
如果PlanetAdapter的选中状态需要全局同步,可以把选中位置也存在宿主Activity中,每次Fragment初始化的时候从Activity取最新的选中位置传参给Adapter,避免每个Adapter各自维护选中状态导致不同步。
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

