(平滑)ScrollToPosition 不能与 RecyclerView 一起正常工作

(Smooth)ScrollToPosition doesn't work properly with RecyclerView

我正在使用带有 GridLayoutManager 的基本 RecyclerView。我观察到 smoothScrollToPosition 和 scrollToPosition 都不能正常工作。

a) 当使用 smoothScrollToPosition 时,我经常收到来自 RecyclerView

的错误

"RecyclerView﹕ Passed over target position while smooth scrolling."

RecyclerView 没有正确滚动(通常会错过目标行)。这主要是在我试图滚动到某行的第一项时观察到的

b) 当使用 scrollToPosition 时,它似乎工作得很好,但大多数时候我只能看到该行的第一项,其余的没有显示。

你能给我一些提示,告诉我如何至少使其中一种方法正常工作吗?

非常感谢!

调用 recyclerView smoothScroll 无效,因为 recyclerView 本身不处理其布局。

您应该改为调用布局管理器滚动方法。

这应该看起来像这样

mRecyclerView.getLayoutManager().scrollToPosition(desiredPosition);

我终于成功了! LinearLayoutManager.scrollToPositionWithOffset(int, int) 成功了。

单击 EditText 时从 RecyclerView 中的任意位置平滑向下滚动到底部。

edittext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                rv_commentList.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                      rv_commentList.scrollToPosition(rv_commentList.getAdapter().getItemCount() - 1);
                    }
                }, 1000);
            }
        });

我也有同样的问题,但通过自定义 SmoothScroller 设法解决了这个问题

让 Custom LayoutManager 如下所示

public class CustomLayoutManager extends LinearLayoutManager {
    private static final float MILLISECONDS_PER_INCH = 50f;
    private Context mContext;

    public CustomLayoutManager(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView,
        RecyclerView.State state, final int position) {

        LinearSmoothScroller smoothScroller = 
            new LinearSmoothScroller(mContext) {

            //This controls the direction in which smoothScroll looks
            //for your view
            @Override
            public PointF computeScrollVectorForPosition
            (int targetPosition) {
                return CustomLayoutManager.this
                    .computeScrollVectorForPosition(targetPosition);
            }

            //This returns the milliseconds it takes to 
            //scroll one pixel.
            @Override
            protected float calculateSpeedPerPixel
                (DisplayMetrics displayMetrics) {
                return MILLISECONDS_PER_INCH/displayMetrics.densityDpi;
            }
        };

        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }
}

(上面给出的代码里面注释的文档)请设置上面的LayoutManager 到 recyerview

CustomLayoutManagerlayoutManager = new CustomLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.smoothScrollToPosition(position);

通过使用自定义布局管理器

scrollToPosition also working well in my case u can use

recyclerView.scrollToPosition(position)

此外,如果您想调整 smoothScrollToPosition 的速度,请覆盖

private static final float MILLISECONDS_PER_INCH = 50f;

在 CustomLayoutManager 中。 因此,如果我们将值设置为 1f smoothScrollToPosition 会更快,就像 scrollToPosition.increasing 值一样,延迟和减少将提高滚动速度。 希望这会有用。

尝试测量项目的宽度或高度并调用 smoothScrollBy(int dx, int dy)。

就我而言,

mRecyclerView.scrollToPosition(10);

也没有用。 但是

mRecyclerView.smoothScrollToPosition(10);

适合我...

如果您尝试快速滚动到 RecyclerView 顶部的某个位置,只需使用 LinearLayoutManager.scrollToPositionWithOffset 并将 0 作为偏移量。

示例:

mLinearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(mLinearLayoutManager);

mLinearLayoutManager.scrollToPositionWithOffset(myPosition, 0);

smoothScrollToPosition 非常慢。如果你想要快速的东西,请使用 scrollToPositionWithOffset。

当您使用 scrollToPosition 时,它会显示在回收站视图的顶部。

但是如果你使用 smoothScrollToPosition 它会一直滚动到 Window 可见。这就是为什么当 smoothScrool 到下面的项目时,它会显示在底部

recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,new RecyclerView.State(),currentPosition);

如何在设备旋转后执行平滑滚动并保存 RecyclerView 垂直位置: 这是适用于我的情况的方法,

public class MainFragment extends Fragment { //OR activity it's //fragment in my case
    ....
 @Override
    public void onLoadFinished(@NonNull Loader<List<Report>> loader, List<Report> objects) { // or other method of your choice, in my case it's a Loader 
    RecyclerView recyclerViewRv = findViewById(........;
         .....
    recyclerViewRv.setAdapter(.....Your adapter);

            recyclerViewRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                }

                @Override
                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    recyclerScrollY = recyclerViewRv. computeVerticalScrollOffset();
                }
            });

    //Apply smooth vertical scroll
    recyclerViewRv.smoothScrollBy(0,recyclerScrollY);
}
    //Save vertical scroll position before rotating devices
    @Override
        public void onSaveInstanceState(@NonNull Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putInt("recyclerScrollY",recyclerScrollY);
        }

    //BackUp vertical scroll position after rotating devices 
    @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if(savedInstanceState != null) {
                recyclerScrollY = savedInstanceState.getInt("recyclerScrollY");
            }
        }
//If you want to perform the same operation for horizontal scrolling just add a variable called recyclerScrollX = recyclerScrollY = recyclerViewRv. computeHorizontalScrollOffset(); then save in bundle

前面提到的任何解决方案可能不起作用的另一个原因是,如果您的 RecyclerView 嵌入到 NestedScrollView 中。在这种情况下,您必须在 NestedScrollView 上调用滚动操作。

例如:

nestedScrollview.smoothScrollTo(0,0)

实际上,如果您在 NestedScrollView 中有一个 RecyclerView,则每次您想要转到 RecyclerView 的开头时都必须使用这两行:

nestedScrollView.smoothScrollTo(0, 0);
layoutManager.scrollToPositionWithOffset(0, 0);

这完全适合我。

我遇到了一个奇怪的问题,其中 smoothScrollToPosition 只是偶尔工作。

After putting the smoothScrollToPosition inside Handler Post Delayed with 1 second delay, it worked fine.

参考下面的Kotlin例子:

Handler().postDelayed({

   recyclerViewObject.smoothScrollToPosition(0) // mention the position in place of 0

}, 1000) // 1000 indicates the 1 second delay.

这对我有用

    Handler().postDelayed({
                    (recyclerView.getLayoutManager() as LinearLayoutManager).scrollToPositionWithOffset( 0, 0)

}, 100)

这个扩展非常有用,请试试。

fun RecyclerView.smoothSnapToPosition(position: Int, snapMode: Int = LinearSmoothScroller.SNAP_TO_START) {
        val smoothScroller = object : LinearSmoothScroller(this.context) {
            override fun getVerticalSnapPreference(): Int = snapMode
            override fun getHorizontalSnapPreference(): Int = snapMode
        }
        smoothScroller.targetPosition = position
        layoutManager?.startSmoothScroll(smoothScroller)
    }

所以我一直在寻找一种解决方案,以在另一个布局中使用 recyclerview 返回顶部,该布局顶部有一个视图(在我的例子中,我有一个带有 TextView 的 LinearLayout 和我的 recyclerview 内部)。因此,平滑滚动只会转到第一项的一半。

这是我所做的,效果非常好(Kotlin 解决方案):

back_to_top.setOnClickListener {
        GlobalScope.launch(Dispatchers.Main) {
            GlobalScope.launch(Dispatchers.Main) {
                recyclerview.layoutManager?.smoothScrollToPosition(recyclerview, RecyclerView.State(), 0)
                delay((recyclerview.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() * 100L)
            }.join()
            recyclerview.scrollToPosition(0)
        }
        back_to_top.visibility = View.GONE
    }
}

这里我做的是平滑滚动到第一个元素并将滚动延迟 100 毫秒乘以最后一个可见项,然后调用 scrollToPosition(0)(转到 top.correctly)

None 这些答案对我有用。我需要 smoothScrollToPosition 但@Ramz 的回答无效。我发现它会一直过度滚动,但只会朝一个方向滚动。我发现这似乎是物品装饰者把它扔掉了。我有一个水平布局,我想在除最后一项之外的每个项目之后添加一个 space,它不喜欢这种不对称。只要我在每个项目后面加上一个 space,它就起作用了!