zoukankan      html  css  js  c++  java
  • RecyclerView的滚动事件分析

    列表的滚动一般分为两种:

    1. 手指按下 -> 手指拖拽列表移动 -> 手指停止拖拽 -> 抬起手指

    2. 手指按下 -> 手指快速拖拽后抬起手指 -> 列表继续滚动 -> 停止滚动

    从上面可以看出,滚动状态分为:

    |--静止
    |--滚动
        |--被迫拖拽移动
        |--自己滚动

    上面的过程的状态变化如下:

    1. 静止 -> 被迫拖拽移动 -> 静止

    2. 静止 -> 被迫拖拽移动 -> 自己滚动 -> 静止

    <!--more-->

    监听RecyclerView的滚动

    好了,我们分析完滚动的过程,再看看如何监听RecyclerView的滚动.查看源码是最好的方法.

    看源码

    查看RecyclerView的源码,我们可以看到以下代码:

    /**
     * Set a listener that will be notified of any changes in scroll state or position.
     * @param listener Listener to set or null to clear
     * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} and
     *             {@link #removeOnScrollListener(OnScrollListener)}
     */
    @Deprecated
    public void setOnScrollListener(OnScrollListener listener) {
        mScrollListener = listener;
    }
    
    /**
     * Add a listener that will be notified of any changes in scroll state or position.
     * <p>Components that add a listener should take care to remove it when finished.
     * Other components that take ownership of a view may call {@link #clearOnScrollListeners()}
     * to remove all attached listeners.</p>
     * @param listener listener to set or null to clear
     */
    public void addOnScrollListener(OnScrollListener listener) {
        if (mScrollListeners == null) {
            mScrollListeners = new ArrayList<>();
        }
        mScrollListeners.add(listener);
    }

    也就是说有两种方式可以监听滚动事件:

    1. 其中 setOnScrollListener 已经过时( 设置的监听器源码如下:

      public abstract static class OnScrollListener {
          /**
           * Callback method to be invoked when RecyclerView's scroll state changes.
           * @param recyclerView The RecyclerView whose scroll state has changed.
           * @param newState     The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
           *                     {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
           */
          public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
      
          /**
           * Callback method to be invoked when the RecyclerView has been scrolled. This will be
           * called after the scroll has completed.
           * <p>
           * This callback will also be called if visible item range changes after a layout
           * calculation. In that case, dx and dy will be 0.
           *
           * @param recyclerView The RecyclerView which scrolled.
           * @param dx The amount of horizontal scroll.
           * @param dy The amount of vertical scroll.
           */
          public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
      }

      在滚动过程中,此监听器会回调两个方法.

      onScrollStateChanged : 滚动状态变化时回调
      onScrolled : 滚动时回调

      这两者的区别在于: 状态与过程

      举例子

      注 : 以下源码可在最后的地址中找到.

      demoRv = (RecyclerView) findViewById(R.id.demo_rv);
      layoutManager = new LinearLayoutManager(this);
      demoRv.setLayoutManager(layoutManager);
      demoRv.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
      
      bookAdapter = new BookAdapter();
      bookAdapter.fillList(MockService.getBookList());
      demoRv.setAdapter(bookAdapter);
      
      demoRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
          @Override
          public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
              super.onScrollStateChanged(recyclerView, newState);
              Log.i(TAG, "-----------onScrollStateChanged-----------");
              Log.i(TAG, "newState: " + newState);
          }
      
          @Override
          public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
              super.onScrolled(recyclerView, dx, dy);
              Log.i(TAG, "-----------onScrolled-----------");
              Log.i(TAG, "dx: " + dx);
              Log.i(TAG, "dy: " + dy);
              Log.i(TAG, "CHECK_SCROLL_UP: " + recyclerView.canScrollVertically(TAG_CHECK_SCROLL_UP));
              Log.i(TAG, "CHECK_SCROLL_DOWN: " + recyclerView.canScrollVertically(TAG_CHECK_SCROLL_DOWN));
          }
      });

      以上代码中输出了主要个几个信息:

      1. newState : 目前的状态

      2. dx : 水平滚动距离

      3. dy : 垂直滚动距离

      onScrollStateChanged 方法

    2. recyclerView : 当前在滚动的RecyclerView

    3. newState : 当前滚动状态.

    其中newState有三种值:

    //停止滚动
    public static final int SCROLL_STATE_IDLE = 0;
    
    //正在被外部拖拽,一般为用户正在用手指滚动
    public static final int SCROLL_STATE_DRAGGING = 1;
    
    //自动滚动开始
    public static final int SCROLL_STATE_SETTLING = 2;

    onScrolled 方法

    • recyclerView : 当前滚动的view

    • dx : 水平滚动距离

    • dy : 垂直滚动距离

    真机实践

    运行代码

    运行以上代码,然后按照上面的滚动过程分别进行两种滚动.

    第一种方式缓慢滚动结果如下:

    I/MainActivity: -----------onScrollStateChanged-----------
    I/MainActivity: newState: 1
    I/MainActivity: -----------onScrolled-----------
    I/MainActivity: dx: 0
    I/MainActivity: dy: -6
    I/MainActivity: CHECK_SCROLL_UP: true
    I/MainActivity: CHECK_SCROLL_DOWN: true
    ------------------------n个onScrolled--------------------
    I/MainActivity: -----------onScrolled-----------
    I/MainActivity: dx: 0
    I/MainActivity: dy: -2
    I/MainActivity: CHECK_SCROLL_UP: true
    I/MainActivity: CHECK_SCROLL_DOWN: false
    I/MainActivity: -----------onScrollStateChanged-----------
    I/MainActivity: newState: 0

    第二种快速滚动结果如下:

    I/MainActivity: -----------onScrollStateChanged-----------
    I/MainActivity: newState: 1
    I/MainActivity: -----------onScrolled-----------
    I/MainActivity: dx: 0
    I/MainActivity: dy: 59
    I/MainActivity: CHECK_SCROLL_UP: true
    I/MainActivity: CHECK_SCROLL_DOWN: true
    --------------------------n个onScrolled-------------------
    I/MainActivity: -----------onScrolled-----------
    I/MainActivity: dx: 0
    I/MainActivity: dy: 54
    I/MainActivity: CHECK_SCROLL_UP: true
    I/MainActivity: CHECK_SCROLL_DOWN: true
    I/MainActivity: -----------onScrollStateChanged-----------
    I/MainActivity: newState: 2
    I/MainActivity: -----------onScrolled-----------
    I/MainActivity: dx: 0
    I/MainActivity: dy: 56
    I/MainActivity: CHECK_SCROLL_UP: true
    I/MainActivity: CHECK_SCROLL_DOWN: true
    --------------------------n个onScrolled-------------------
    I/MainActivity: -----------onScrolled-----------
    I/MainActivity: dx: 0
    I/MainActivity: dy: 14
    I/MainActivity: CHECK_SCROLL_UP: true
    I/MainActivity: CHECK_SCROLL_DOWN: true
    I/MainActivity: -----------onScrolled-----------
    I/MainActivity: dx: 0
    I/MainActivity: dy: 1
    I/MainActivity: CHECK_SCROLL_UP: true
    I/MainActivity: CHECK_SCROLL_DOWN: true
    I/MainActivity: -----------onScrollStateChanged-----------
    I/MainActivity: newState: 0

    分析结果

    且在滚动过程中发现:

    1.滚动方向

    dy > 0 时为向上滚动
    dy < 0 时为向下滚动

    2.回调过程

    缓慢拖拽回调过程:

    1. newState = RecyclerView.SCROLL_STATE_DRAGGING;
    2. dy 多次改变
    3. newState = RecyclerView.SCROLL_STATE_IDLE

    快速滚动回调过程:

    1. newState = RecyclerView.SCROLL_STATE_DRAGGING;
    2. dy 多次改变
    3. newState = RecyclerView.SCROLL_STATE_SETTLING;
    4. dy 多次改变
    5. newState = RecyclerView.SCROLL_STATE_IDLE;

    3.顶端与底部

    以上信息中还打印了

    RecyclerView.canScrollVertically(-1)的值表示是否滚动到顶部

    封装

    基于以上,我们可以封装一个可以回调滚动状态和方向的RecyclerView.

    先建立事件监听的接口public interface OnScrollCallback { void onStateChanged(ScrollRecycler recycler, int state); void onScrollUp(ScrollRecycler recycler, int dy); void onScrollDown(ScrollRecycler recycler, int dy); }

    再写一个类RecyclerView,在类中添加以下方法:

    public void setOnScrollCallback(final OnScrollCallback callback) {
        if (callback == null) {
            return;
        }
        addOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                callback.onStateChanged(ScrollRecycler.this, newState);
            }
    
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (dy > 0) {
                    callback.onScrollDown(ScrollRecycler.this, dy);
                } else {
                    callback.onScrollUp(ScrollRecycler.this, dy);
                }
            }
        });
    }
  • 相关阅读:
    338. Counting Bits
    78. Subsets
    MySQL读写分离
    AESEncryption Aes 加密
    LoopBox 用于包装循环的盒子
    ES 服务器 索引、类型仓库基类 BaseESStorage
    一键压缩脚本
    非常好用的一个分组扩展方法
    快递、拆分、合并、优选逻辑
    Git Extensions 使用小结
  • 原文地址:https://www.cnblogs.com/ldq2016/p/6645952.html
Copyright © 2011-2022 走看看