【问题标题】:How to save scroll position of RecyclerView in Android?如何在 Android 中保存 RecyclerView 的滚动位置?
【发布时间】:2016-04-12 08:52:29
【问题描述】:

我有位于 SwipeRefreshLayout 内部的 Recycler 视图。此外,有能力在另一个活动中打开每个项目。 返回 Recycler 后,我需要滚动到所选项目或上一个 Y。 该怎么做?

是的,我在 Google 上搜索过,在 StackOverFlow 中找到了有关保存布局管理器实例的文章,例如:RecyclerView store / restore state between activities。 但是,它对我没有帮助。

更新

现在我有这种解决问题,但是,当然,它也不起作用。

private int scrollPosition;

...//onViewCreated - it is fragment
recyclerView.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(llm);
data = new ArrayList<>();
adapter.setData(getActivity(), data);
recyclerView.setAdapter(adapter);
...

@Override
public void onResume() {
    super.onResume();
    recyclerView.setScrollY(scrollPosition);
}

@Override
public void onPause() {
    super.onPause();
    scrollPosition = recyclerView.getScrollY();
}

是的,我尝试过 scrollTo(int, int) - 不起作用。

现在我尝试只滚动到 Y = 100,但它根本不滚动。

【问题讨论】:

标签: android scroll android-recyclerview


【解决方案1】:

保存回收视图位置的当前状态@onPause:

    positionIndex= llManager.findFirstVisibleItemPosition();
    View startView = rv.getChildAt(0);
    topView = (startView == null) ? 0 : (startView.getTop() - rv.getPaddingTop());

恢复滚动位置@onResume:

    if (positionIndex!= -1) {
        llManager.scrollToPositionWithOffset(positionIndex, topView);
    }

或者其他方式可以是@onPause:

long currentVisiblePosition = 0;
currentVisiblePosition = ((LinearLayoutManager)rv.getLayoutManager()).findFirstCompletelyVisibleItemPosition();

恢复@onResume:

((LinearLayoutManager) rv.getLayoutManager()).scrollToPosition(currentVisiblePosition);
currentVisiblePosition = 0;

【讨论】:

  • 很好的解决方案..!!
  • 你能分享一个完整的例子吗
  • 在我的情况下,必须添加 marginTop 才能准确恢复位置 topView = (startView == null) ? 0 : (startView.getTop() - startView.getMarginTop() - rv.getPaddingTop());
【解决方案2】:

其中很多答案似乎过于复杂了。

LayoutManager 支持onRestoreInstanceState 开箱即用,因此无需保存滚动位置等。内置方法已经保存了像素完美位置。

示例片段代码(为清楚起见删除了空值检查等):

private Parcelable listState;
private RecyclerView list;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    listState=savedInstanceState.getParcelable("ListState");

}

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

    outState.putParcelable("ListState", list.getLayoutManager().onSaveInstanceState());

}

然后打电话

list.getLayoutManager().onRestoreInstanceState(listState);

一旦您的数据重新附加到您的RecyclerView

【讨论】:

  • 哇!简单易行的解决方案:)
  • 我认为这是最好的解决方案!
  • 我必须将 android:configChanges="orientation|screenSize" 添加到 AndroidManifest.xml。比上面的代码有效。谢谢
  • @LeonardoAssunção 那你做错了。这不是必需的,也不推荐。
  • 嗨,我们可以调用这个方法 list.getLayoutManager().onRestoreInstanceState(listState);
【解决方案3】:

从 androidx recyclerView 库的 1.2.0-alpha02 版本开始,它现在是自动管理的。只需添加:

implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"

并使用:

adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY

StateRestorationPolicy 枚举有 3 个选项:

  • ALLOW — 默认状态,在下一个布局过程中立即恢复 RecyclerView 状态
  • PREVENT_WHEN_EMPTY — 仅在适配器不为空时恢复 RecyclerView 状态(adapter.getItemCount() > 0)。如果您的数据是异步加载的,则 RecyclerView 会等到数据加载完毕,然后才会恢复状态。如果您有默认项目,如标题或加载进度指示器作为适配器的一部分,那么您应该使用 PREVENT 选项,除非使用 MergeAdapter 添加默认项目。 MergeAdapter 等待其所有适配器准备就绪,然后才恢复状态。
  • PREVENT — 延迟所有状态恢复,直到您设置 ALLOW 或 PREVENT_WHEN_EMPTY。

请注意,在此回答时,recyclerView 库仍处于 alpha03,但 alpha 阶段不适合生产目的

【讨论】:

  • 这让恢复状态变得容易了 100 倍。感谢您指出了这一点!工作得很好,我在生产中使用1.2.0-alpha05
【解决方案4】:

使用您的回收站视图 linearlayoutmanager 获取滚动位置

int position = 0;
if (linearLayoutManager != null) {
   scrollPosition = inearLayoutManager.findFirstVisibleItemPosition();
}

并且在恢复时使用以下代码

if (linearLayoutManager != null) {
  cardRecyclerView.scrollToPosition(mScrollPosition);
}

希望对你有帮助

【讨论】:

  • 抱歉,这个答案在我的问题下是 cmets 中的第一个。
【解决方案5】:

要将位置保存到首选项,请将其添加到您的 onStop()

 int currentVisiblePosition = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
 getPreferences(MODE_PRIVATE).edit().putInt("listPosition", currentVisiblePosition).apply();

然后像这样恢复位置

 if (getItemCount() == 0) {
     int savedListPosition = getPreferences(MODE_PRIVATE).getInt("listPosition", 0);
     recyclerView.getLayoutManager().scrollToPosition(savedListPosition); }

最后的代码应该添加到适配器的事件中(不确定女巫事件,但在我的例子中是 onEvent() - com.google.firebase.firestore.EventListener)

【讨论】:

    【解决方案6】:

    由于某些原因,关于如何在方向更改时保存和恢复 your_scrolling_container 中的滚动位置,有很多非常具有误导性的提示/建议。

    获取当前滚动位置并将其保存在 Activity 的 onSaveInstanceState 中 扩展某个可滚动视图以在那里做同样的事情 防止 Activity 在旋转时被破坏 是的,它们工作得很好,但是……

    但实际上,一切都简单得多,因为 Android 已经在为你做这件事了!

    如果你仔细看看 RecyclerView/ListView/ScrollView/NestedScrollView 源,您会看到它们每个都在 onSaveInstanceState 中保存其滚动位置。在第一次布局过程中,他们试图在 onLayout 方法中滚动到这个位置。

    您只需要做两件事,以确保它能够正常工作:

    1. 为您的可滚动视图设置一个 ID,这可能已经完成。否则 Android 将无法自动保存 View 状态。

    2. 在第一次布局传递之前提供数据,以具有与旋转之前相同的滚动边界。这就是开发人员通常会遇到一些问题的步骤。

    【讨论】:

      【解决方案7】:

      我发现的最简单且兼容过渡的方式是:

         @Override
      public void onPause() {
          super.onPause();
          recyclerView.setLayoutFrozen(true);
      }
      
      @Override
      public void onResume() {
          super.onResume();
          recyclerView.setLayoutFrozen(false);
      }
      

      【讨论】:

        【解决方案8】:

        您可以使用scrollToPositionsmoothScrollToPosition 滚动到RecyclerView 中的任何项目位置

        如果您想滚动到适配器中的项目位置,则必须使用适配器的scrollToPositionsmoothScrollToPosition

        【讨论】:

        猜你喜欢
        • 2015-03-05
        • 2017-01-23
        • 2015-10-08
        • 1970-01-01
        • 1970-01-01
        • 2022-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多