RecyclerView - 如何在某个位置平滑滚动到项目顶部?

Tia*_*ago 100 android scroll position smooth android-recyclerview

在RecyclerView上,我可以使用以下命令突然滚动到所选项目的顶部:

((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(position, 0);
Run Code Online (Sandbox Code Playgroud)

但是,这突然将项目移动到顶部位置.我想顺利地移动到项目的顶部.

我也尝试过:

recyclerView.smoothScrollToPosition(position);
Run Code Online (Sandbox Code Playgroud)

但它不能很好地工作,因为它不会将项目移动到选定的顶部位置.它只是滚动列表,直到位置上的项目可见.

Pau*_*hek 175

RecyclerView被设计为可扩展的,因此不需要子类化LayoutManager(如droidev建议)只是为了执行滚动.

相反,只需SmoothScroller使用首选项创建一个SNAP_TO_START:

RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(context) {
  @Override protected int getVerticalSnapPreference() {
    return LinearSmoothScroller.SNAP_TO_START;
  }
};
Run Code Online (Sandbox Code Playgroud)

现在,您可以设置要滚动到的位置:

smoothScroller.setTargetPosition(position);
Run Code Online (Sandbox Code Playgroud)

并将SmoothScroller传递给LayoutManager:

layoutManager.startSmoothScroll(smoothScroller);
Run Code Online (Sandbox Code Playgroud)

  • 感谢这个更短的解决方案.在我的实现中我还需要考虑另外两件事:因为我有一个水平滚动视图,我必须设置`protected int getHorizo​​ntalSnapPreference(){return LinearSmoothScroller.SNAP_TO_START; }`.此外,我不得不实现抽象方法`public PointF computeScrollVectorForPosition(int targetPosition){return layoutManager.computeScrollVectorForPosition(targetPosition); }`. (7认同)
  • 有没有办法让它快速启动,但偏移量为X dp? (6认同)
  • 有可能减慢速度吗? (3认同)
  • 还想知道是否可以修改滚动发生的速度(减慢)@AlirezaNoorali (3认同)
  • RecyclerView可能被设计为可扩展的,但是这样的简单事情只是让人感觉懒得丢失.好的答案!谢谢. (2认同)
  • 完全是@Alessio,这将破坏RecyclerView的默认“ smoothScrollToPosition”功能 (2认同)

dro*_*dev 109

为此,您必须创建自定义LayoutManager

public class LinearLayoutManagerWithSmoothScroller extends LinearLayoutManager {

    public LinearLayoutManagerWithSmoothScroller(Context context) {
        super(context, VERTICAL, false);
    }

    public LinearLayoutManagerWithSmoothScroller(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class TopSnappedSmoothScroller extends LinearSmoothScroller {
        public TopSnappedSmoothScroller(Context context) {
            super(context);

        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return LinearLayoutManagerWithSmoothScroller.this
                    .computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将它用于RecyclerView并调用smoothScrollToPosition.

例如:

 recyclerView.setLayoutManager(new LinearLayoutManagerWithSmoothScroller(context));
 recyclerView.smoothScrollToPosition(position);
Run Code Online (Sandbox Code Playgroud)

这将滚动到指定位置的RecyclerView项目的顶部.

希望这可以帮助.

  • 只有经过几个小时的痛苦才能得到有用的答案, (3认同)

小智 10

我们可以这样尝试

    recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,new RecyclerView.State(), recyclerView.getAdapter().getItemCount());
Run Code Online (Sandbox Code Playgroud)


vov*_*ost 10

这是我在Kotlin中编写的扩展函数,可以直接在RecyclerView上调用(基于@Paul Woitaschek的回答):

fun RecyclerView.smoothSnapToPosition(position: Int, snapMode: Int = LinearSmoothScroller.SNAP_TO_START) {
    val smoothScroller = object: LinearSmoothScroller(this.context) {
        override fun getVerticalSnapPreference(): Int {
            return snapMode
        }

        override fun getHorizontalSnapPreference(): Int {
            return snapMode
        }
    }
    smoothScroller.targetPosition = position
    layoutManager?.startSmoothScroll(smoothScroller)
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

myRecyclerView.smoothSnapToPosition(itemPosition)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢@vovahost,你让我免于自杀。哈哈哈哈哈 (3认同)
  • 这有效!平滑滚动的问题是当你有很大的列表,然后滚动到底部或顶部需要很长很长的时间 (2认同)

Cri*_*iss 8

我这样使用:

recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, new RecyclerView.State(), 5);
Run Code Online (Sandbox Code Playgroud)


Bad*_*ser 7

我想更全面地解决滚动持续时间的问题,如果您选择任何早期的答案,滚动持续时间实际上会根据从当前位置到达目标位置所需的滚动量而发生巨大变化(并且令人无法接受)。

为了获得统一的滚动持续时间,速度(每毫秒像素数)必须考虑每个单独项目的大小 - 当项目具有非标准尺寸时,就会增加一个全新的复杂程度。

这可能就是为什么RecyclerView开发人员为平滑滚动这一重要方面部署了太硬的篮子的原因。

假设您想要半均匀的滚动持续时间,并且您的列表包含半均匀的项目,那么您将需要这样的东西。

/** Smoothly scroll to specified position allowing for interval specification. <br>
 * Note crude deceleration towards end of scroll
 * @param rv        Your RecyclerView
 * @param toPos     Position to scroll to
 * @param duration  Approximate desired duration of scroll (ms)
 * @throws IllegalArgumentException */
private static void smoothScroll(RecyclerView rv, int toPos, int duration) throws IllegalArgumentException {
    int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;     // See androidx.recyclerview.widget.LinearSmoothScroller
    int itemHeight = rv.getChildAt(0).getHeight();  // Height of first visible view! NB: ViewGroup method!
    itemHeight = itemHeight + 33;                   // Example pixel Adjustment for decoration?
    int fvPos = ((LinearLayoutManager)rv.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
    int i = Math.abs((fvPos - toPos) * itemHeight);
    if (i == 0) { i = (int) Math.abs(rv.getChildAt(0).getY()); }
    final int totalPix = i;                         // Best guess: Total number of pixels to scroll
    RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(rv.getContext()) {
        @Override protected int getVerticalSnapPreference() {
            return LinearSmoothScroller.SNAP_TO_START;
        }
        @Override protected int calculateTimeForScrolling(int dx) {
            int ms = (int) ( duration * dx / (float)totalPix );
            // Now double the interval for the last fling.
            if (dx < TARGET_SEEK_SCROLL_DISTANCE_PX ) { ms = ms*2; } // Crude deceleration!
            //lg(format("For dx=%d we allot %dms", dx, ms));
            return ms;
        }
    };
    //lg(format("Total pixels from = %d to %d = %d [ itemHeight=%dpix ]", fvPos, toPos, totalPix, itemHeight));
    smoothScroller.setTargetPosition(toPos);
    rv.getLayoutManager().startSmoothScroll(smoothScroller);
}
Run Code Online (Sandbox Code Playgroud)

PS:我诅咒我开始不加选择地将ListView转换为RecyclerView 的那一天。


小智 6

覆盖LinearSmoothScroller中的calculateDyToMakeVisible/calculateDxToMakeVisible函数来实现偏移Y/X位置

override fun calculateDyToMakeVisible(view: View, snapPreference: Int): Int {
    return super.calculateDyToMakeVisible(view, snapPreference) - ConvertUtils.dp2px(10f)
}
Run Code Online (Sandbox Code Playgroud)