首页 > 解决方案 > 如何告诉 RecyclerView 从特定项目位置开始

问题描述

我希望我的带有 LinearLayoutManager 的 RecyclerView 在适配器更新后显示特定项目的滚动位置。(不是第一个/最后一个位置)表示第一个(重新)布局,这个给定的位置应该在可见区域。它不应该在顶部放置位置 0,然后滚动到目标位置。

我的适配器从 itemCount=0 开始,在线程中加载其数据并稍后通知其实际计数。但是必须在 count 仍然为 0 时设置起始位置!

截至目前,我使用了某种post Runnable包含scrollToPosition,但这有副作用(从第一个 pos 开始并立即跳转到目标位置(0 -> 目标),并且似乎不适用于 DiffUtil(0 -> 目标 -> 0))

编辑:清除:我需要替代layoutManager.setStackFromEnd(true);,例如setStackFrom(position). ScrollToPosition 不起作用,如果我在 itemCount 仍然为 0 时调用它,那么它会被忽略。如果我在通知 itemCount 现在 > 0 时调用它,它将从 0 开始布局并在目标位置之后快速跳转。如果我使用 DiffUtil.DiffResult.dispatchUpdatesTo(adapter)`,它会完全失败。(从 0 开始显示,然后滚动到目标位置,然后再次回到位置 0)

标签: androidandroid-recyclerviewposition

解决方案


我自己找到了解决方案:

我扩展了 LayoutManager:

  class MyLayoutManager extends LinearLayoutManager {

    private int mPendingTargetPos = -1;
    private int mPendingPosOffset = -1;

    @Override
    public void onLayoutChildren(Recycler recycler, State state) {
        if (mPendingTargetPos != -1 && state.getItemCount() > 0) {
            /*
            Data is present now, we can set the real scroll position
            */
            scrollToPositionWithOffset(mPendingTargetPos, mPendingPosOffset);
            mPendingTargetPos = -1;
            mPendingPosOffset = -1;
        }
        super.onLayoutChildren(recycler, state);
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        /*
        May be needed depending on your implementation.

        Ignore target start position if InstanceState is available (page existed before already, keep position that user scrolled to)
         */
        mPendingTargetPos = -1;
        mPendingPosOffset = -1;
        super.onRestoreInstanceState(state);
    }

    /**
     * Sets a start position that will be used <b>as soon as data is available</b>.
     * May be used if your Adapter starts with itemCount=0 (async data loading) but you need to
     * set the start position already at this time. As soon as itemCount > 0,
     * it will set the scrollPosition, so that given itemPosition is visible.
     * @param position
     * @param offset
     */
    public void setTargetStartPos(int position, int offset) {
        mPendingTargetPos = position;
        mPendingPosOffset = offset;
    }
}

它存储我的目标位置。如果onLayoutChildren被 RecyclerView 调用,它会检查适配器 itemCount 是否已经 > 0。如果为 true,它会调用scrollToPositionWithOffset().

所以我可以立即知道哪个位置应该是可见的,但在位置存在于适配器之前不会告诉 LayoutManager。


推荐阅读