Android 在recyclerview中检测开始滚动和结束滚动

Android 在recyclerview中检测开始滚动和结束滚动,android,android-recyclerview,Android,Android Recyclerview,我需要在recyclerview中检测滚动的开始/结束和方向。滚动侦听器有两种方法:onscroll()和onScrollStateChanged()。第一个方法在滚动启动后调用(实际上称为onscroll(),而不是onScrolling())。第二种方法提供了关于状态的信息,但我没有方向信息。如何实现我的目标?有关详细信息,请参阅文档。 三个可能的值是: SCROLL\u STATE\u IDLE:不进行滚动 SCROLL\u STATE\u drawing:用户在屏幕上拖动手指(或按编程

我需要在recyclerview中检测滚动的开始/结束和方向。滚动侦听器有两种方法:
onscroll()
onScrollStateChanged()
。第一个方法在滚动启动后调用(实际上称为onscroll(),而不是onScrolling())。第二种方法提供了关于状态的信息,但我没有方向信息。如何实现我的目标?

有关详细信息,请参阅文档。 三个可能的值是:

  • SCROLL\u STATE\u IDLE
    :不进行滚动
  • SCROLL\u STATE\u drawing
    :用户在屏幕上拖动手指(或按编程方式进行)
  • SCROLL\u STATE\u setting
    :用户已抬起手指,动画现在变慢
因此,如果要检测滚动的开始和结束时间,可以创建如下内容:

public void onScrollStateChanged(int state) {
    boolean hasStarted = state == SCROLL_STATE_DRAGGING;
    boolean hasEnded = state == SCROLL_STATE_IDLE;
}

步骤1您可以创建一个扩展RecyclerView.OnScrollListener的类并重写这些方法

public class CustomScrollListener extends RecyclerView.OnScrollListener {
    public CustomScrollListener() {
    }

    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        switch (newState) {
            case RecyclerView.SCROLL_STATE_IDLE:
                System.out.println("The RecyclerView is not scrolling");
                break;
            case RecyclerView.SCROLL_STATE_DRAGGING:
                System.out.println("Scrolling now");
                break;
            case RecyclerView.SCROLL_STATE_SETTLING:
                System.out.println("Scroll Settling");
                break;

        }

    }

    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        if (dx > 0) {
            System.out.println("Scrolled Right");
        } else if (dx < 0) {
            System.out.println("Scrolled Left");
        } else {
            System.out.println("No Horizontal Scrolled");
        }

        if (dy > 0) {
            System.out.println("Scrolled Downwards");
        } else if (dy < 0) {
            System.out.println("Scrolled Upwards");
        } else {
            System.out.println("No Vertical Scrolled");
        }
    }
}

最简单的方法是退出你的layoutManager

   private RecyclerView.OnScrollListener mListener = new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        GridLayoutManager layoutManager=GridLayoutManager.class.cast(recyclerView.getLayoutManager());
        int visibleItemCount = layoutManager.getChildCount();
        int totalItemCount = layoutManager.getItemCount();
        int pastVisibleItems = layoutManager.findFirstCompletelyVisibleItemPosition();

        if(pastVisibleItems+visibleItemCount >= totalItemCount){
            // End of the list is here.
            Log.i(TAG, "End of list");
        }
    }
}

希望有帮助!

这里有一个完整的解决方案,可以在停止滚动后执行操作(用于RecyclerView)

这已经纠正了我与recyclerView滚动相关的OutOfBounds异常

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        //Your action here
                    }
                });

我认为其他答案没有触及你的痛处

关于这个问题

正如您所说,RecycleView将在OnScroll()方法之前调用onScrollStateChanged()。但是,onScrollStateChanged()方法只能告诉您RecycleView的三种状态:

  • 滚动\u状态\u空闲
  • 滚动\u状态\u拖动
  • 滚动\u状态\u设置
这意味着,假设RecycleView现在位于其顶部,如果用户向上滚动,它只能触发
onScrollStateChanged()
方法,而不能触发
onScrolled()
方法。 如果用户向下滚动,它可以首先触发
onScrollStateChanged()
方法,然后触发
onScrolled()
方法

然后问题就来了,
SCROLL\u STATE\u drawing
只能告诉您用户正在拖动视图,而不能告诉他拖动的方向

您必须结合
onScrolled()
方法中的
dy
来检测拖动方向,但是
onScrolled()
不会触发
onScrollStateChanged()

我的解决方案:

class DraggingDirectionScrollListener(private val view: View)
        : RecyclerView.OnScrollListener() {
        private val DIRECTION_NONE = -1
        private val DIRECTION_UP = 0
        private val DIRECTION_DOWN = 1

        var scrollDirection = DIRECTION_NONE
        var listStatus = RecyclerView.SCROLL_STATE_IDLE
        var totalDy = 0

        override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
            listStatus = newState

            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                scrollDirection = DIRECTION_NONE
            }

            if (isOnTop() && newState == RecyclerView.SCROLL_STATE_DRAGGING) {
              view.postDelayed({
                    if (getDragDirection() == DIRECTION_DOWN){
                        // Drag down from top
                    }else if (getDragDirection() == DIRECTION_UP) {
                        // Drag Up from top
                    }
                }, 10)
            }
        }

        override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
            this.totalDy += dy
            scrollDirection = when {
                dy > 0 -> DIRECTION_UP
                dy < 0 -> DIRECTION_DOWN
                else -> DIRECTION_NONE
            }

        }

        /**
         * is on the top of the list
         */
        private fun isOnTop():Boolean{
            return totalDy == 0
        }

        /**
         * detect dragging direction
         */
        private fun getDragDirection():Int {
            if (listStatus != RecyclerView.SCROLL_STATE_DRAGGING) {
                return DIRECTION_NONE
            }

            return when (scrollDirection) {
                DIRECTION_NONE -> if (totalDy == 0){
                    DIRECTION_DOWN  // drag down from top
                }else{
                    DIRECTION_UP  // drag up from bottom
                }
                DIRECTION_UP -> DIRECTION_UP
                DIRECTION_DOWN -> DIRECTION_DOWN
                else -> DIRECTION_NONE
            }
        }
    }
当触发
onScrollStateChanged()
时,记录滚动状态,然后删除一段时间,如10ms,检查是否有Y轴移动,并得出拖动方向的结论

Kotlin代码:

class DraggingDirectionScrollListener(private val view: View)
        : RecyclerView.OnScrollListener() {
        private val DIRECTION_NONE = -1
        private val DIRECTION_UP = 0
        private val DIRECTION_DOWN = 1

        var scrollDirection = DIRECTION_NONE
        var listStatus = RecyclerView.SCROLL_STATE_IDLE
        var totalDy = 0

        override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
            listStatus = newState

            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                scrollDirection = DIRECTION_NONE
            }

            if (isOnTop() && newState == RecyclerView.SCROLL_STATE_DRAGGING) {
              view.postDelayed({
                    if (getDragDirection() == DIRECTION_DOWN){
                        // Drag down from top
                    }else if (getDragDirection() == DIRECTION_UP) {
                        // Drag Up from top
                    }
                }, 10)
            }
        }

        override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
            this.totalDy += dy
            scrollDirection = when {
                dy > 0 -> DIRECTION_UP
                dy < 0 -> DIRECTION_DOWN
                else -> DIRECTION_NONE
            }

        }

        /**
         * is on the top of the list
         */
        private fun isOnTop():Boolean{
            return totalDy == 0
        }

        /**
         * detect dragging direction
         */
        private fun getDragDirection():Int {
            if (listStatus != RecyclerView.SCROLL_STATE_DRAGGING) {
                return DIRECTION_NONE
            }

            return when (scrollDirection) {
                DIRECTION_NONE -> if (totalDy == 0){
                    DIRECTION_DOWN  // drag down from top
                }else{
                    DIRECTION_UP  // drag up from bottom
                }
                DIRECTION_UP -> DIRECTION_UP
                DIRECTION_DOWN -> DIRECTION_DOWN
                else -> DIRECTION_NONE
            }
        }
    }
class DraggingDirectionScrollListener(私有val视图:视图)
:RecyclerView.OnScrollListener(){
私有值方向_NONE=-1
专用val方向_向上=0
专用val方向_向下=1
var scrollDirection=方向\u无
var listStatus=RecyclerView.SCROLL\u STATE\u IDLE
var totalDy=0
覆盖CrollStateChanged(recyclerView:recyclerView?,newState:Int){
listStatus=newState
if(newState==RecyclerView.SCROLL\u STATE\u IDLE){
滚动方向=方向\无
}
if(isOnTop()&&newState==RecyclerView.SCROLL\u STATE\u drawing){
view.postdayed({
如果(getDragDirection()==方向\u向下){
//从顶部向下拖动
}else if(getDragDirection()==向上的方向){
//从顶部向上拖动
}
}, 10)
}
}
覆盖已克隆的乐趣(recyclerView:recyclerView?,dx:Int,dy:Int){
this.totalDy+=dy
滚动方向=何时{
dy>0->向上方向
dy<0->向下的方向
其他->方向\u无
}
}
/**
*在列表的顶部
*/
private fun isOnTop():布尔值{
返回totalDy==0
}
/**
*检测拖动方向
*/
private fun getDragDirection():Int{
如果(listStatus!=RecyclerView.SCROLL\u STATE\u Draging){
返回方向\u无
}
返回时间(滚动方向){
方向\u无->如果(totalDy==0){
方向\向下//从顶部向下拖动
}否则{
方向_向上//从底部向上拖动
}
方向向上->方向向上
方向向下->方向向下
其他->方向\u无
}
}
}

使用此代码避免重复呼叫

使用-1检测顶部

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

            if (!recyclerView.canScrollVertically(1) && newState==RecyclerView.SCROLL_STATE_IDLE) {
                Log.d("-----","end");

            }
        }
    });

您可以使用
RecyclerView.getScrollState()
来检测
RecyclerView.SCROLL\u STATE\u IDLE
或另一种状态

我的情况是:

if (recyclerView?.scrollState != RecyclerView.SCROLL_STATE_IDLE) {
    return
}

好的,但是怎么能同时检测到方向呢?你可以覆盖
onScrolled(intdx,intdy)
并检查
dy
是否为正或负,这为您提供了一个或另一个方向。
RecyclerView.OnScrollListener
不是一个界面/facepalm@google开发人员决定的。在这种情况下,我更喜欢一个类,因为您不需要重写这两个方法。此方法已被弃用,无法在上述v中工作可能是23版或M版,但评论来自2018年,我只是通知您用当前场景更新您的答案。
if (recyclerView?.scrollState != RecyclerView.SCROLL_STATE_IDLE) {
    return
}