博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SwipeRefreshLayout 在 RecyclerView 空白时下拉失效分析
阅读量:6133 次
发布时间:2019-06-21

本文共 3380 字,大约阅读时间需要 11 分钟。

为一个旧页面添加空白页后验证时出现了意想不到的情况,SwipeRefreshLayoutRecyclerView 空白时下拉失效。布局是 SwipeRefreshLayout 里面有一个 RecyclerView 还有一个 ViewStub 用来显示空白页面。出现情况就是当 RecyclerView 没有数据,展示出空白页之后,SwipeRefreshLayout 就无法下拉。

一开始以为是空白页挡住 SwipeRefreshLayout ,后面迅速又否定来自己,因为小圆圈是有阴影的,在 Z 轴它不应该被覆盖的。而且进来那一瞬间,也的确看到小圆圈 loading 然后才消失。

那就是触摸事件没有传递到的原因咯。为什么在没有数据时事件就会有问题呢?看来这个问题还是要回到 RecyclerView 本身。接着我打开 RecyclerView 的源码,这里简单提一下,SwipeRefreshlayoutRecyclerView 都实现了 NestedScroll ,默认大家都比较了解这一机制。不熟悉的话,阔以先看看我之前写的

其实这篇文章讲的就是上篇文章忽略的那几行代码。在 RecyclerView 开始消费事件后,RecyclerView 最终调用了 scrollByInternal() 开始消费事件。接着看看这个方法的详细代码。

boolean scrollByInternal(int x, int y, MotionEvent ev) {    int unconsumedX = 0, unconsumedY = 0;    int consumedX = 0, consumedY = 0;    consumePendingUpdateOperations();    if (mAdapter != null) {        ...        if (x != 0) {            consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);            unconsumedX = x - consumedX;        }        if (y != 0) {            consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);            unconsumedY = y - consumedY;        }        ...    }    ...    if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,            TYPE_TOUCH)) {        ...        mNestedOffsets[0] += mScrollOffset[0];        mNestedOffsets[1] += mScrollOffset[1];    }复制代码

如果 Adapter 为空的话,那么 unconsumedX、Y consumedX、Y 都是默认值 0 ,接着再看看 dispatchNestedScroll() 这个方法,最后会到 NestedScrollingChildHelper 方法中:

public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,        int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,        @NestedScrollType int type) {    if (isNestedScrollingEnabled()) {        final ViewParent parent = getNestedScrollingParentForType(type);        if (parent == null) {            return false;        }        if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {            int startX = 0;            int startY = 0;            if (offsetInWindow != null) {                mView.getLocationInWindow(offsetInWindow);                startX = offsetInWindow[0];                startY = offsetInWindow[1];            }            ViewParentCompat.onNestedScroll(parent, mView, dxConsumed,                    dyConsumed, dxUnconsumed, dyUnconsumed, type);            if (offsetInWindow != null) {                mView.getLocationInWindow(offsetInWindow);                offsetInWindow[0] -= startX;                offsetInWindow[1] -= startY;            }            return true;        } else if (offsetInWindow != null) {            // No motion, no dispatch. Keep offsetInWindow up to date.            offsetInWindow[0] = 0;            offsetInWindow[1] = 0;        }    }    return false;}复制代码

那因为默认值都是0 ,所以这个方法直接返回false,内部

ViewParentCompat.onNestedScroll(parent, mView, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)复制代码

肯定走不到,自然而然,Parent 肯定收不到相关事件。这里的 Parent 就是 SwipeRefreshLayout , 对应的就是我提到的那个现象,没法执行下拉刷新的操作。

结合刚刚分析,发现事件能传递给 parent 有一个前置条件就是一定要设置过 Adapter,这个就想使用 RecyclerView 一定要先设置 LayoutManager 一样

再回到项目中看看具体代码,才发现同事写的代码里 Adapter 是懒加载的形式,且数据集合是通过构造函数传递进去的,那么问题就清晰了。因为没有数据,所以没有初始化 Adapter,接着就是分析代码中因为没有 Adapter,所以 NestedScroll 这一套都失效了。因为我自己的习惯都是先创建出 AdapterLayoutManager 数据都是动态添加,所以以前没有出现这个情况,还有,以前空白页都是做到 RecyclerView 内部,这样更没机会出现这个情况。

所以题目并不是最准确的描述,而应该描述为 SwipeRefreshLayoutRecyclerView 没有设置 Adapter 时下拉失效。一次简单快速的源码分析以及问题定位。

转载地址:http://gaeua.baihongyu.com/

你可能感兴趣的文章
数据类型的一些方法
查看>>
Mindjet MindManager 2019使用教程:
查看>>
游戏设计的基本构成要素有哪些?
查看>>
详解 CSS 绝对定位
查看>>
AOP
查看>>
我的友情链接
查看>>
NGUI Label Color Code
查看>>
.NET Core微服务之基于Polly+AspectCore实现熔断与降级机制
查看>>
vue组件开发练习--焦点图切换
查看>>
浅谈OSI七层模型
查看>>
Webpack 2 中一些常见的优化措施
查看>>
移动端响应式
查看>>
python实现牛顿法求解求解最小值(包括拟牛顿法)【最优化课程笔记】
查看>>
js中var、let、const的区别
查看>>
腾讯云加入LoRa联盟成为发起成员,加速推动物联网到智联网的进化
查看>>
从Python2到Python3:超百万行代码迁移实践
查看>>
Windows Server已可安装Docker,Azure开始支持Mesosphere
查看>>
简洁优雅地实现夜间模式
查看>>
react学习总结
查看>>
微软正式发布PowerShell Core 6.0
查看>>