RecyclerView滚动事件分析与OnScrollListener的使用

KaelLi 2017年11月20日21:14:07221,01910

作为一个目前在Android开发中可能是应用最为广泛之一的组件,RecyclerView在诞生之初就广受好评。而随着对其研究的深入,我们会对它有很多使用需求,一个能滚动的组件,需要监控其滚动事件是非常正常的事情。

一、列表的滚动事件:

一个列表在滚动的时候,一般会有两种滚动形式:

  • 手指按下后,不离开屏幕,一直拖动着列表进行滚动,滚动到合适的位置后停下来,手指离开屏幕。
  • 手指按下,在很短时间内用较快的速度拖动列表,然后手指马上离开列表,列表随着惯性,继续滚动到一个位置(或者滚动到尾部)。

二、如何监听RecyclerView的滚动事件

稍有经验的开发者,都不难猜到会有一个Listener,在Android Studio里面直接用一个RecyclerView对象进行set或者add的时候,IDE就会提示相应的Listener。是的,Google给我们提供了一个RecyclerView下的OnScrollListener,你可以用addOnScrollListener或setOnScrollListener(已经deprecated了,不建议使用)的方式来监听,下面我们来看看它的源码:

    /**
     * An OnScrollListener can be added to a RecyclerView to receive messages when a scrolling event
     * has occurred on that RecyclerView.
     * <p>
     * @see RecyclerView#addOnScrollListener(OnScrollListener)
     * @see RecyclerView#clearOnChildAttachStateChangeListeners()
     *
     */
    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){}
    }

OnScrollListener提供了2个回掉方法,但由于OnScrollListener不是接口而是抽象类,所以你并不是必须同时实现这2个方法,可以根据实际需求来进行实现。下面来看一下这2个方法都有什么作用。

onScrollStateChanged(RecyclerView recyclerView, int newState)

很容易看出来,这个方法记录了RecyclerView的滚动状态的变化,2个变量中,第一个自然是当前的RecyclerView组件,第二个则表达了当前滚动状态,它有3个值:

    /**
     * The RecyclerView is not currently scrolling.
     * @see #getScrollState()
     */
    public static final int SCROLL_STATE_IDLE = 0;

    /**
     * The RecyclerView is currently being dragged by outside input such as user touch input.
     * @see #getScrollState()
     */
    public static final int SCROLL_STATE_DRAGGING = 1;

    /**
     * The RecyclerView is currently animating to a final position while not under
     * outside control.
     * @see #getScrollState()
     */
    public static final int SCROLL_STATE_SETTLING = 2

我特别欣慰,这次Google自己写的注释很详细了。当然如果你英语水平不高我可以给你翻译一下:

  • SCROLL_STATE_IDLE代表RecyclerView现在不是滚动状态。
  • SCROLL_STATE_DRAGGING代表RecyclerView处于被外力引导的滚动状态,比如手指正在拖着进行滚动。
  • SCROLL_STATE_SETTLING代表RecyclerView处于自动滚动的状态,此时手指已经离开屏幕,RecyclerView的滚动是自身的惯性在维持。

onScrolled(RecyclerView recyclerView, int dx, int dy)

此方法用来获取RecyclerView的滚动距离,dx和dy自然分别代表横向和纵向的滚动距离啦,这2个值都是可正可负的:

  • 当dx > 0 时,代表手指向左拖动,RecyclerView则从右向左滚动。
  • 当dx < 0时,代表手指向右拖动,RecyclerView则从左向右滚动。
  • 当dy > 0时,代表手指向上拖动,RecyclerView则从上向下滚动(就是我们最常见的,从顶部开始往下滚动)。
  • 当dy < 0时,代表手指向下拖动,RecyclerView则从下向上滚动(就是从列表底部往回挥动)。

这里再介绍2个很有用的方法:

  • public boolean canScrollVertically(int direction)
  • public boolean canScrollHorizontally(int direction)

这2个方法实际上并不是RecyclerView所特有的,而是View这个超级类的。这2个方法,可以判断当前View能否在横向或纵向上滚动。当返回false的时候,说明组件已经不能在给定的方向上进行滚动了,而这2个方法,也可以引出一个很常见的问题及解决办法。

三、如何判断RecyclerView是否滚动到底部或顶部?

以更常见的纵向来说,使用Recyclerview. canScrollVertically(1),当返回值是false的时候,代表你的RecyclerView不能继续往下滚动啦,也就是说已经滚动到底部了。同理,当Recyclerview. canScrollVertically(-1)返回false的时候代表RecyclerView不能继续网上滚动了,已经到顶部了。这个方法比通过计算当前item的位置以及总item数量的方法进行判断要好多了。

KaelLi
  • 本文由 发表于 2017年11月20日21:14:07
  • 转载请务必保留本文链接:https://www.kaelli.com/14.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

评论:2   其中:访客  1   博主  1
    • castlong castlong 0

      兄弟 关于 dy的方向问题,你应该是反过来

        • KaelLi KaelLi

          @ castlong 我特意弄了点代码试了试,没问题啊,是不是我表述的跟你理解的不太一样啊