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

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 地址

写在最后

简诗

推荐阅读更多精彩内容

  • 简介: 提供一个让有限的窗口变成一个大数据集的灵活视图。 术语表: Adapter:RecyclerView的子类...
    酷泡泡阅读 3,349评论 0 16
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 152,909评论 22 671
  • 这篇文章分三个部分,简单跟大家讲一下 RecyclerView 的常用方法与奇葩用法;工作原理与ListView比...
    LucasAdam阅读 3,528评论 0 26
  • 起深入浅出这名字的时候我是慎重又慎重的,生怕被人骂标题党,写的什么破玩意还敢说深入浅出。所以还是请大家不要抱着太高...
    渔农阅读 4,627评论 6 32
  • 人一生中最难写的作文是什么?答案也许就是“遗嘱”了。一段日子之前,我在视频上看到一位女士。正当壮年,不幸丧夫。怀着...
    晓天狼星阅读 147评论 3 4