×

RecyclerView,你为什么不回调 onScrolled 方法了?

96
HelloVass
2015.11.20 16:56* 字数 410

RecyclerView

这个控件带来的插件化编程体验良好的性能都在不断吸引大家使用 ta,github 上已经有很多基于 RecyclerView 的开源项目,并且为 RecyclerView 增加了很多牛逼的 feature。这次,我也实现了一个自己的 RecyclerView。

feature-1 滑动到底部加载更多

其实,实现 LoadMore 功能并不难,网上也一堆分享,这里我主要参考了秋百万的 cube-sdk 中的 recycler-load-more 分支

我的疑惑

实现后的效果.gif

当 RecyclerView 中只有一条数据时,无论我怎么 scroll(滚动),OnScrollListener 里 onScrolled(RecyclerView recyclerView, int dx, int dy) 这个重要的回调方法 似乎是被谁吃掉了一样,再也没有被触发了,这是为什么?

不科学,断点调试大法启动

同事带我深入了 RecyclerView 源码寻找这个方法被吃掉的原因。

// 省略在源码中的艰辛寻找过程...

最后,定位到了 LinearLayoutManager 里的这个方法

int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getChildCount() == 0 || dy == 0) {
            return 0;
        }
        mLayoutState.mRecycle = true;
        ensureLayoutState();
        final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
        final int absDy = Math.abs(dy);
        updateLayoutState(layoutDirection, absDy, true, state);
        final int freeScroll = mLayoutState.mScrollingOffset;
        final int consumed = freeScroll + fill(recycler, mLayoutState, state, false);
        if (consumed < 0) {
            if (DEBUG) {
                // 没有更多元素可以 scroll !!!
                Log.d(TAG, "Don't have any more elements to scroll");
            }
            return 0;
        }
        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
        mOrientationHelper.offsetChildren(-scrolled);
        if (DEBUG) {
            Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
        }
        mLayoutState.mLastScrollDelta = scrolled;
        return scrolled;
    }

分析

看到这里,已经清楚很多了,注释里已经说明,consumed < 0 表明没有更多元素可以滚动(其实已经被内部消化了)。在这种情况下,onScrolled(RecyclerView recyclerView, int dx, int dy) 是不会被调用的!那什么情况下算是没有更多元素可以滚动呢?

做个实验好咯

刷新时加载的数据分别设置为 1、2、3 条,在 logcat 观察onScrolled(RecyclerView recyclerView, int dx, int dy) 是否被调用。

  1. 当屏幕中的 item 数量多到超出屏幕的时候,这时候的滚动是会触发 onScrolled(RecyclerView recyclerView, int dx, int dy) 方法的。

  2. 屏幕中的 item 完全显示在屏幕中时,onScrolled(RecyclerView recyclerView, int dx, int dy) 是不会被触发的。

附上 HVEndlessRecyclerView 地址

写在最后

简诗
Android
Web note ad 1