(平滑) 滚动到特定位置的功能在 RecyclerView 中无法正常工作。

59

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

a) 使用smoothScrollToPosition时,我经常会从RecyclerView收到错误提示:

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

RecyclerView没有正确滚动(通常是错过目标行)。这种情况在我尝试滚动到某一行的第一项时最常见。

b) 当使用scrollToPosition时,它似乎运行得很好,但大多数情况下,我只能看到该行的第一项,其余内容未显示。

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

非常感谢!


如果您有一个负位置(例如,您想在空适配器中到达最后位置),则会出现错误:https://dev59.com/nrbna4cB1Zd3GeqPY1G3。 - CoolMind
18个回答

82

6
我有类似的问题,那个方法对我也很有用……但我实际上正在尝试实现平滑滚动。你也是吗? - Phiat
5
我也尝试使用 gridLayoutManager.scrollToPositionWithOffset(index, 0),效果很好。 - droppin_science
5
是的,LayoutManager.scrollToPosition()可能效果不佳,但是LinearLayoutManager.scrollToPositionWithOffset()很好用 :D 它很神奇。 - krosshj
3
典型用法为:LinearLayoutManager llm = (LinearLayoutManager) recyclerView.getLayoutManager(); llm.scrollToPositionWithOffset(10, 0); 但请注意,您不再获得 smoothScrollToPosition() 所具有的平滑滚动动画。 - Mr-IDE
3
无法工作!与 mRecyclerView.scrollToPosition(position) 相同。 - Abu Nayem
显示剩余9条评论

18

我也遇到了同样的问题,但通过自定义SmoothScroller来解决了这个问题。

以下是自定义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 在我的情况下也很好用,您可以使用

recyclerView.scrollToPosition(position)

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

private static final float MILLISECONDS_PER_INCH = 50f;

在CustomLayoutManager中。 因此,如果我们将值设置为1f,smoothScrollToPosition将比scrollToPosition更快。增加值会使延迟增加,而减少值会使滚动速度加快。 希望这对您有用。


2
scrollToPositionWithOffset可以工作,但为了实现平滑滚动,我需要使用Ramz的答案。 - Christian

13

根据我的情况,

mRecyclerView.scrollToPosition(10);

也没有起作用。 但是

mRecyclerView.smoothScrollToPosition(10);

对我来说很好用...


8

在单击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);
            }
        });

7

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

例如:

nestedScrollview.smoothScrollTo(0,0)

4

这个扩展非常实用,请试用一下。

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)
    }

3

我遇到了一个奇怪的问题,smoothScrollToPosition只有偶尔能正常工作。

smoothScrollToPosition放在Handler Post Delayed里,并延迟1秒后,就能正常工作。

参考以下 Kotlin 示例:

Handler().postDelayed({

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

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

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

2
recyclerView.smoothScrollToPosition(position) 是你在评论中描述的方法调用。你能解释一下为什么要设置新状态吗?recyclerView已经有状态了吧? - Rahul Mandaliya

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

1
如何实现平滑滚动并在设备旋转后保存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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接