【问题标题】:Detect start scroll and end scroll in recyclerview在recyclerview中检测开始滚动和结束滚动
【发布时间】:2016-01-31 23:28:34
【问题描述】:

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

【问题讨论】:

    标签: android android-recyclerview


    【解决方案1】:

    请参阅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;
    }
    

    【讨论】:

    • 好的,但是如何同时检测方向呢?
    • 您可以覆盖onScrolled(int dx, int dy) 并检查dy 是正数还是负数,从而为您提供一个或另一个方向。
    【解决方案2】:

    第 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 最好使用 addOnScrollListener

     mRecyclerView.addOnScrollListener(new CustomScrollListener());
    

    【讨论】:

    • RecyclerView.OnScrollListener 不是一个界面 /facepalm @ 谷歌开发者决定这样做。
    • 在这种情况下我更喜欢一个类,因为您不需要重写这两种方法。
    【解决方案3】:

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

    希望对您有所帮助!

    【讨论】:

      【解决方案4】:

      这是在滚动停止后执行操作的完整解决方案(用于 RecyclerView)

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

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

      【讨论】:

      • 此方法已弃用,在上述版本23或M中无法使用。
      • 可能,但评论来自 2018 年的家伙
      • 我只是通知您根据当前情况更新您的答案。
      【解决方案5】:

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

      关于问题

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

      • SCROLL_STATE_IDLE
      • SCROLL_STATE_DRAGGING
      • SCROLL_STATE_SETTLING

      也就是说,假设 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
                  }
              }
          }
      

      【讨论】:

        【解决方案6】:

        使用此代码避免重复调用

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

        【讨论】:

          【解决方案7】:
          @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
             }
          }
          

          【讨论】:

            【解决方案8】:

            您可以使用RecyclerView.getScrollState() 来检测RecyclerView.SCROLL_STATE_IDLE 或另一种状态。

            我的情况:

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

            【讨论】:

              猜你喜欢
              • 2011-04-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-10-30
              • 1970-01-01
              相关资源
              最近更新 更多