刷新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,这可能会使列表看起来不同

  1. 像这样应用一个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中始终存储确切的滚动位置。

  1. 将这个variables存储在你的Bundle中,并将其恢复到另一个variables的状态 ,我们将其称为mStateScrollY。

  2. 状态恢复后,您的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); }