检测循环视图中的开始滚动和结束滚动



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

步骤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");
        }
    }
}

步骤2-由于setOnScrollListener已弃用,因此最好使用addOnScrollLister

 mRecyclerView.addOnScrollListener(new CustomScrollListener());

请参阅onScrollStateChanged(int state)的文档。三个可能的值是:

  • SCROLL_STATE_IDLE:不进行滚动
  • SCROLL_STATE_DRAGGING:用户正在屏幕上拖动他的手指(或者这是通过程序进行的)
  • SCROLL_STATE_SETTLING:用户已经举起手指,动画现在正在减速

因此,如果你想检测滚动的开始和结束时间,那么你可以创建这样的东西:

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

使用此代码来避免重复调用

使用-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");
            }
        }
    });

最简单的方法是退出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");
        }
    }
}

希望它能有所帮助!

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
   super.onScrollStateChanged(recyclerView, newState);
   if (!recyclerView.canScrollVertically(1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
              //reached end
   }
   if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
              //reached top
   }
   if(newState == RecyclerView.SCROLL_STATE_DRAGGING){
           //scrolling
   }
}

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

关于问题

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

  • 滚动状态IDLE
  • SCROLL_STATE_DRAGGING
  • 滚动状态设置

也就是说,假设RecycleView现在处于顶部,如果用户向上滚动,它只能触发onScrollStateChanged()方法,而不能触发onScrolled()方法。如果用户向下滚动,可以先触发onScrollStateChanged()方法,然后再触发onScrolled()方法。

那么问题来了,SCROLL_STATE_DRAGGING只能告诉你用户正在拖动视图,而不能告诉他拖动的方向。

您必须与onScrolled()方法中的dy结合才能检测拖动方向,但onScrolled()未与onScrollStateChanged()触发。

我的解决方案:

记录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
            }
        }
    }

您可以使用RecyclerView.getScrollState()来检测RecyclerView.SCROLL_STATE_IDLE或其他状态。

我的案例:

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

其他不适合我的答案,它们要么做了其他事情,要么工作不完美。这适用于

binding.filters.addOnScrollListener(object : OnScrollListener() {
    private var layoutManager: LinearLayoutManager? = null
    var totalItemCount = 0
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        if (layoutManager == null) {
            layoutManager = recyclerView.layoutManager as LinearLayoutManager
            totalItemCount = layoutManager?.itemCount ?: 0
        }
        if(layoutManager?.findFirstCompletelyVisibleItemPosition() == 0) //reached start
        if(layoutManager?.findLastCompletelyVisibleItemPosition() == totalItemCount - 1) //reached end
    }
})

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

已经更正了我与回收器相关的异常越界查看滚动

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

最新更新