在RecyclerView中刷新数据并保持其滚动位置

Kon*_*ski 79 android android-recyclerview

如何刷新显示的数据RecyclerView(调用notifyDataSetChanged其适配器)并确保滚动位置重置到它的确切位置?

在好的情况下,ListView所有需要的是检索getChildAt(0),检查它getTop()setSelectionFromTop在之后使用相同的确切数据进行调用.

在这种情况下似乎不可能RecyclerView.

我想我应该使用它LayoutManager确实提供的scrollToPositionWithOffset(int position, int offset),但是检索位置和偏移的正确方法是什么?

layoutManager.findFirstVisibleItemPosition()layoutManager.getChildAt(0).getTop()

或者是否有更优雅的方式来完成工作?

Daw*_*nYu 126

我用这个.^ _ ^

// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();

// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
Run Code Online (Sandbox Code Playgroud)

它更简单,希望它能帮到你!

  • 我应该在哪里使用此代码?在onResume方法? (3认同)
  • 它可以仅用于包围 notifyDataSetChanged 调用:`val recyclerViewState = recyclerView.layoutManager.onSaveInstanceState() adapter.notifyDataSetChanged() recyclerView.layoutManager.onRestoreInstanceState(recyclerViewState)` (2认同)

小智 45

我有类似的问题.我提出了以下解决方案.

使用notifyDataSetChanged是一个坏主意.您应该更具体,然后RecyclerView将为您保存滚动状态.

例如,如果您只需要刷新,或者换句话说,您希望每个视图都被重新绑定,只需执行以下操作:

adapter.notifyItemRangeChanged(0, adapter.getItemCount());
Run Code Online (Sandbox Code Playgroud)

  • 这有助于我的情况.我在位置0处插入了几个项目,并且使用`notifyDataSetChanged`,滚动位置将更改显示新项目.但是,如果使用`notifyItemRangeInserted(0,newItems.size() - 1)``RecyclerView`保持滚动状态. (3认同)
  • 我尝试了adapter.notifyItemRangeChanged(0,adapter.getItemCount());,这也适用于.notifyDataSetChanged() (2认同)
  • 它不起作用,即使我添加了recyclerview,也刷新到顶部 (2认同)

A. *_*gen 20

编辑:要恢复完全相同的明显位置,如同使其看起来完全一样,我们需要做一些不同的事情(请参阅下面的如何恢复确切的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);
Run Code Online (Sandbox Code Playgroud)

然后像这样恢复滚动:

LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager();
manager.scrollToPositionWithOffset(mStatePos, (int) mStateOffset);
Run Code Online (Sandbox Code Playgroud)

这将列表恢复到其确切的明显位置.显而易见,因为它看起来与用户相同,但它不会具有相同的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;
        }
    };
    
    Run Code Online (Sandbox Code Playgroud)

这将在mScrollY中始终存储精确的滚动位置.

  1. 将此变量存储在Bundle中,并在状态恢复中将其恢复为另一个变量,我们将其称为mStateScrollY.

  2. 在状态恢复之后,您的RecyclerView重置其所有数据后,重置滚动,使用:

    mRecyclerView.scrollBy(0, mStateScrollY);
    
    Run Code Online (Sandbox Code Playgroud)

而已.

请注意,将滚动恢复为另一个变量,这很重要,因为将使用.scrollBy()调用OnScrollListener,然后将mScrollY设置为存储在mStateScrollY中的值.如果你不这样做,mScrollY将使滚动值加倍(因为OnScrollListener使用增量,而不是绝对滚动).

国家节约活动可以像这样实现:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(ARGS_SCROLL_Y, mScrollY);
}
Run Code Online (Sandbox Code Playgroud)

并在你的onCreate()中恢复调用它:

if(savedState != null){
    mStateScrollY = savedState.getInt(ARGS_SCROLL_Y, 0);
}
Run Code Online (Sandbox Code Playgroud)

片段中的状态保存以类似的方式工作,但实际的状态保存需要一些额外的工作,但是有很多文章处理这个,所以你应该没有问题找出如何,保存scrollY的原则并恢复它保持不变.


Mor*_*ard 12

通过使用 @DawnYu 回答来保持滚动位置,notifyDataSetChanged()如下所示:

val recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState() 
adapter.notifyDataSetChanged() 
recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)
Run Code Online (Sandbox Code Playgroud)


小智 7

是的,你可以通过使适配器构造函数只有一次来解决这个问题,我在这里解释编码部分:

if (appointmentListAdapter == null) {
        appointmentListAdapter = new AppointmentListAdapter(AppointmentsActivity.this);
        appointmentListAdapter.addAppointmentListData(appointmentList);
        appointmentListAdapter.setOnStatusChangeListener(onStatusChangeListener);
        appointmentRecyclerView.setAdapter(appointmentListAdapter);

    } else {
        appointmentListAdapter.addAppointmentListData(appointmentList);
        appointmentListAdapter.notifyDataSetChanged();
    }
Run Code Online (Sandbox Code Playgroud)

现在您可以看到我已经检查了适配器是否为null,并且仅在它为null时进行初始化.

如果适配器不为null,那么我确信我已经初始化了我的适配器至少一次.

所以我只是将列表添加到适配器并调用notifydatasetchanged.

RecyclerView始终保持滚动的最后位置,因此您不必存储最后位置,只需调用notifydatasetchanged,Recycler视图始终刷新数据而不会返回顶部.

谢谢快乐编码


小智 5

@DawnYu 的最佳答案有效,但 recyclerview 将首先滚动到顶部,然后返回到预期的滚动位置,导致“闪烁状”反应,这是不愉快的。

要刷新recyclerView,尤其是从另一个activity来之后,不闪烁,并且保持滚动位置,需要做如下操作。

  1. 确保您正在使用 DiffUtil 更新回收站视图。在此处阅读更多相关信息:https : //www.journaldev.com/20873/android-recyclerview-diffutil
  2. 在您的活动恢复时,或者在您想要更新您的活动时,将数据加载到您的回收站视图。使用 diffUtil,仅在 recyclerview 上进行更新,同时保持其位置。

希望这可以帮助。


The*_*oid 1

我没有使用过Recyclerview,但是我在ListView上使用过。Recyclerview 中的示例代码:

setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        rowPos = mLayoutManager.findFirstVisibleItemPosition();
Run Code Online (Sandbox Code Playgroud)

它是用户滚动时的监听器。性能开销并不大。这样第一个可见位置就准确了。