很久之前的一个博客,写了关于RecyclerView滚动事件的研究,最近又用到RecyclerView的滚动事件,发现之前的方法无法满足现在的需要,所以又有了这篇文章.

Not Talk, Let's Code!

0x01 回调函数

根据需求,目前确定了回调函数如下:

    interface OnScrollCallback {

        /**
         * 自己往列表顶部方向滚动
         */
        void onSettlingScrollUp();

        /**
         * 自己往列表底部方向滚动
         */
        void onSettlingScrollDown();

        /**
         * 列表停止滚动
         */
        void onScrollIdle();

        /**
         * 滚动到列表底部
         */
        void onScrollToBottom();

        /**
         * 滚动到列表顶部
         */
        void onScrollToTop();

        /**
         * 向列表顶部方向拖动滚动
         */
        void onDraggingScrollUp();

        /**
         * 向列表底部方向拖动滚动
         */
        void onDraggingScrollDown();

        /**
         * 在列表顶部往下拖拽
         */
        void onTopDraggingDown();

        /**
         * 在列表底部往上拖拽
         */
        void onBottomDraggingUp();
    }

回调方法基本包含了所有需要的场景,下面是如何实现上述的回调

0x02 自定义RecyclerView

单单依赖 onScrollListener 无法满足上述的需求,所以需要继承 RecyclerView 并覆写 onTouchEvent() 方法.
代码如下:

public class TouchRecyclerView extends RecyclerView {

    private static final String TAG = "TouchRecyclerView";

    private OnScrollCallback mScrollCallback;
    private boolean isDown = false;
    private boolean isMoved = false;
    private boolean isScrolled = false;
    private int draggingCount = 0;

    public TouchRecyclerView(Context context) {
        super(context);
        init();
    }

    public TouchRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TouchRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        OnScrollListener scrollListener = new OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
//                LogUtil.d(TAG, "newState:" + newState);
                if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
                    //自己在滚动
                } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                    //随手指滚动
                    draggingCount = draggingCount + 1;
                } else {
                    //停止滚动
                    if (isScrolled && mScrollCallback != null) {
                        mScrollCallback.onScrollIdle();
                        if (!canScrollVertically(1)) {
                            mScrollCallback.onScrollToBottom();
                        }
                        if (!canScrollVertically(-1)) {
                            mScrollCallback.onScrollToTop();
                        }
                    }
                    isDown = false;
                    isScrolled = false;
                    isMoved = false;
                    draggingCount = 0;
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//                LogUtil.d(TAG, "state:" + recyclerView.getScrollState());
//                LogUtil.d(TAG, "dy:" + dy);
                isScrolled = (dy != 0);
                if (recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) {
                    if (dy > 0) {
                        if (mScrollCallback != null) {
                            mScrollCallback.onSettlingScrollDown();
                        }
                    } else {
                        if (mScrollCallback != null) {
                            mScrollCallback.onSettlingScrollUp();
                        }
                    }
                } else if (recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
                    if (dy > 0) {
                        if (mScrollCallback != null) {
                            mScrollCallback.onDraggingScrollDown();
                        }
                    } else {
                        if (mScrollCallback != null) {
                            mScrollCallback.onDraggingScrollUp();
                        }
                    }
                } else {
                    if (!isDown) {
                        isDown = false;
                        mScrollCallback.onScrollIdle();
                        if (!canScrollVertically(1)) {
                            mScrollCallback.onScrollToBottom();
                        }
                        if (!canScrollVertically(-1)) {
                            mScrollCallback.onScrollToTop();
                        }
                    }
                }
                super.onScrolled(recyclerView, dx, dy);
            }
        };
        addOnScrollListener(scrollListener);
    }

    public void setScrollCallback(@NonNull OnScrollCallback scrollCallback) {
        mScrollCallback = scrollCallback;
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
//        LogUtil.d(TAG, "action:" + e.getAction());
        if (e.getAction() == MotionEvent.ACTION_DOWN) {
            isDown = true;
        }
        if (e.getAction() == MotionEvent.ACTION_MOVE) {
            isMoved = true;
        }
        if (e.getAction() == MotionEvent.ACTION_UP) {
            if (isMoved && draggingCount == 1) {
                if (!canScrollVertically(1)) {
                    mScrollCallback.onBottomDraggingUp();
                }
                if (!canScrollVertically(-1)) {
                    mScrollCallback.onTopDraggingDown();
                }
            }
        }
        return super.onTouchEvent(e);
    }
}

0x03 测试

测试代码如下:

touchRecycler = (TouchRecyclerView) findViewById(R.id.touch_recycler);
dataList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
    dataList.add(String.valueOf(i));
}
mTouchAdapter = new TouchAdapter(this);
LinearLayoutManager manager = new LinearLayoutManager(this);
touchRecycler.setLayoutManager(manager);
touchRecycler.setAdapter(mTouchAdapter);
mTouchAdapter.fillList(dataList);
ItemClickSupport.addTo(touchRecycler).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
    @Override
    public void onItemClicked(RecyclerView recyclerView, int i, View view) {
        Toast.makeText(MainActivity.this, "Click:" + i, Toast.LENGTH_SHORT).show();
    }
});
touchRecycler.setScrollCallback(new TouchRecyclerView.OnScrollCallback() {
    @Override
    public void onSettlingScrollUp() {
        DevLog.d("onSettlingScrollUp");
    }

    @Override
    public void onSettlingScrollDown() {
        DevLog.d("onSettlingScrollDown");
    }

    @Override
    public void onScrollIdle() {
        DevLog.d("onScrollIdle");
    }

    @Override
    public void onScrollToBottom() {
        DevLog.d("onScrollToBottom");
    }

    @Override
    public void onScrollToTop() {
        DevLog.d("onScrollToTop");
    }

    @Override
    public void onDraggingScrollUp() {
        DevLog.d("onDraggingScrollUp");
    }

    @Override
    public void onDraggingScrollDown() {
        DevLog.d("onDraggingScrollDown");
    }

    @Override
    public void onTopDraggingDown() {
        DevLog.d("onTopDraggingDown");
    }

    @Override
    public void onBottomDraggingUp() {
        DevLog.d("onBottomDraggingUp");
    }
});

实际运行符合预期结果.

0x04 后记

以上代码如果你再使用过程中发现什么问题,请留言讨论,谢谢~