RecyclerView的滾動事件分析

列表滾動過程

開始以前,咱們想一下一個列表的滾動過程是怎樣的?java

列表的滾動通常分爲兩種:git

  1. 手指按下 -> 手指拖拽列表移動 -> 手指中止拖拽 -> 擡起手指github

  2. 手指按下 -> 手指快速拖拽後擡起手指 -> 列表繼續滾動 -> 中止滾動微信

從上面能夠看出,滾動狀態分爲:ide

|--靜止
|--滾動
    |--被迫拖拽移動
    |--本身滾動

上面的過程的狀態變化以下:測試

  1. 靜止 -> 被迫拖拽移動 -> 靜止this

  2. 靜止 -> 被迫拖拽移動 -> 本身滾動 -> 靜止spa

<!--more-->.net

監聽RecyclerView的滾動

好了,咱們分析完滾動的過程,再看看如何監聽RecyclerView的滾動.查看源碼是最好的方法.code

看源碼

查看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()

  2. addOnScrollListener()

其中 setOnScrollListener 已通過時(@deprecated),建議使用 addOnScrollListener.若是在源碼中沒有 addOnScrollListener 方法,可能你的版本過舊,請升級recyclerview包.

設置的監聽器源碼以下:

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 方法

void onScrollStateChanged(RecyclerView recyclerView, int newState)中回調兩個變量:

  1. recyclerView : 當前在滾動的RecyclerView

  2. 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 方法

void onScrolled(RecyclerView recyclerView, int dx, int dy)方法中回調了三個變量:

  1. recyclerView : 當前滾動的view

  2. dx : 水平滾動距離

  3. 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.頂端與底部

以上信息中還打印了canScrollVertically的信息,其中:

RecyclerView.canScrollVertically(1)的值表示是否滾動到底部
RecyclerView.canScrollVertically(-1)的值表示是否滾動到頂部

封裝

基於以上,咱們能夠封裝一個能夠回調滾動狀態和方向的RecyclerView.

先創建事件監聽的接口OnScrollCallback,代碼以下:

public interface OnScrollCallback {

    void onStateChanged(ScrollRecycler recycler, int state);

    void onScrollUp(ScrollRecycler recycler, int dy);

    void onScrollDown(ScrollRecycler recycler, int dy);
}

再寫一個類ScrollRecycler繼承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);
            }
        }
    });
}

測試如下是否可行,下面直接給出測試結果:

I/MainActivity: onStateChanged: state:1
 I/MainActivity: onScrollDown: 7
 I/MainActivity: onScrollDown: 4
 I/MainActivity: onScrollDown: 4
 I/MainActivity: onScrollDown: 4
 I/MainActivity: onStateChanged: state:0
 I/MainActivity: onStateChanged: state:1
 I/MainActivity: onScrollUp: -11
 I/MainActivity: onScrollUp: -8
 I/MainActivity: onScrollUp: -9
 I/MainActivity: onStateChanged: state:0

完畢,以上就是對RecyclerView的滾動的簡單研究,項目代碼地址以下:Dev-Wiki/RecyclerScroll: RecyclerView滾動的Demo

更多文章請移步個人博客:DevWiki Blog

重要說明

想隨時獲取最新博客文章更新,請關注公共帳號DevWiki,或掃描下面的二維碼:

微信公共號

相關文章
相關標籤/搜索