刷新RecyclerView中的数据并保持滚动位置
如何刷新RecyclerView
(在其适配器上调用notifyDataSetChanged
)中显示的数据,并确保滚动位置被重置为原来的位置?
在好的情况下, ListView
只需要检索getChildAt(0)
,检查其getTop()
然后用相同的确切数据调用setSelectionFromTop
。
在RecyclerView
情况下似乎不可能。
我想我应该使用它的LayoutManager
,它的确提供了scrollToPositionWithOffset(int position, int offset)
,但是检索位置和偏移量的正确方法是什么?
layoutManager.findFirstVisibleItemPosition()
和layoutManager.getChildAt(0).getTop()
?
还是有更好的方法来完成这项工作?
我用这个。^ _ ^
// Save state private Parcelable recyclerViewState; recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState(); // Restore state recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
这是更简单的,希望它会帮助你!
我有相当类似的问题。 我想出了以下解决scheme。
使用notifyDataSetChanged
是一个坏主意。 你应该更具体,然后RecyclerView
将为您保存滚动状态。
例如,如果您只需刷新,换句话说,您希望每个视图都被重新绑定,只需执行以下操作:
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
编辑:要恢复完全相同的明显的位置,使其看起来完全一样,我们需要做一些不同的东西(见下面如何恢复确切的scrollY值):
保存位置并像这样偏移:
LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager(); int firstItem = manager.findFirstVisibleItemPosition(); View firstItemView = manager.findViewByPosition(firstItem); float topOffset = firstItemView.getTop(); outState.putInt(ARGS_SCROLL_POS, firstItem); outState.putFloat(ARGS_SCROLL_OFFSET, topOffset);
然后像这样恢复滚动:
LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager(); manager.scrollToPositionWithOffset(mStatePos, (int) mStateOffset);
这恢复清单的确切位置。 显然,因为它看起来与用户相同,但不会有相同的scrollY值(因为可能在横向/纵向布局尺寸上存在差异)。
请注意,这只适用于LinearLayoutManager。
— 下面如何恢复确切的scrollY,这可能会使列表看起来不同 —
-
像这样应用一个OnScrollListener:
private int mScrollY; private RecyclerView.OnScrollListener mTotalScrollListener = new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); mScrollY += dy; } };
这将在mScrollY中始终存储确切的滚动位置。
-
将这个variables存储在你的Bundle中,并将其恢复到另一个variables的状态 ,我们将其称为mStateScrollY。
-
状态恢复后,您的RecyclerView已重置其所有数据重置滚动与此:
mRecyclerView.scrollBy(0, mStateScrollY);
而已。
请注意,您将滚动恢复到不同的variables,这很重要,因为OnScrollListener将被调用.scrollBy(),随后将mScrollY设置为存储在mStateScrollY中的值。 如果你不这样做mScrollY将有两倍的滚动值(因为OnScrollListener与增量变化,而不是绝对滚动)。
国家在活动中的节约可以这样实现:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(ARGS_SCROLL_Y, mScrollY); }
并在你的onCreate()中恢复调用:
if(savedState != null){ mStateScrollY = savedState.getInt(ARGS_SCROLL_Y, 0); }
状态保存碎片的工作方式类似,但是实际的状态保存需要一些额外的工作,但是有大量的文章处理这个问题,所以你不应该有一个问题,找出如何保存scrollY的原则并恢复它保持不变。
我没有使用Recyclerview,但我做了ListView。 Recyclerview中的示例代码:
setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { rowPos = mLayoutManager.findFirstVisibleItemPosition();
当用户滚动时,它是监听者。 性能开销并不显着。 而第一个可见的位置就是这样准确的。
也许这太简单了,但我只是打电话来做这件事情
recreate();
我的回收站视图位置被保存。
是的,你可以通过使适配器构造函数只有一次解决这个问题,我在这里解释编码部分:
if (appointmentListAdapter == null) { appointmentListAdapter = new AppointmentListAdapter(AppointmentsActivity.this); appointmentListAdapter.addAppointmentListData(appointmentList); appointmentListAdapter.setOnStatusChangeListener(onStatusChangeListener); appointmentRecyclerView.setAdapter(appointmentListAdapter); } else { appointmentListAdapter.addAppointmentListData(appointmentList); appointmentListAdapter.notifyDataSetChanged(); }
现在你可以看到我已经检查过适配器是否为空,只有当它为空时才初始化。
如果适配器不是null,那么我确信我已经初始化了我的适配器至less一次。
所以我只是将列表添加到适配器并调用notifydatasetchanged。
RecyclerView始终保持滚动的最后一个位置,因此您不必存储最后一个位置,只需调用notifydatasetchanged,回收器视图总是刷新数据而不会返回顶部。
感谢快乐编码
如果oldPosition和position是相同的,就返回。
private int oldPosition = -1; public void notifyItemSetChanged(int position, boolean hasDownloaded) { if (oldPosition == position) { return; } oldPosition = position; RLog.d(TAG, " notifyItemSetChanged :: " + position); DBMessageModel m = mMessages.get(position); m.setVideoHasDownloaded(hasDownloaded); notifyItemChanged(position, m); }
我有这个问题,每个项目列表中有一个分钟的时间,直到他们“到期”,需要更新。 我会更新数据,然后再打电话
orderAdapter.notifyDataSetChanged();
每次都会滚动到顶部。 我用了
for(int i = 0; i < orderArrayList.size(); i++){ orderAdapter.notifyItemChanged(i); }
这很好。 这个线程中的其他方法都没有为我工作。 在使用这个方法的时候,它使每个单独的项目在更新的时候都是闪存的,所以我也必须把它放在父片段的onCreateView
RecyclerView.ItemAnimator animator = orderRecycler.getItemAnimator(); if (animator instanceof SimpleItemAnimator) { ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false); }