SwipeRefreshLayout内嵌RecyclerView仅顶部第一个条目可点击异常问询
问题原因
这个问题是SwipeRefreshLayout和RecyclerView的触摸事件分发冲突导致的,核心触发点有两个:
- 当RecyclerView滚动偏移量为0时,SwipeRefreshLayout默认会判定用户可能触发下拉刷新操作,会在触摸事件分发阶段优先拦截事件,只有极小范围、无滑动趋势的点击事件会透传给下层RecyclerView。你给RecyclerView设置了
android:paddingVertical="8dp"且开启了android:clipToPadding="false",除了第一个条目外的其他条目触摸区域都会被SwipeRefreshLayout误拦截,所以只有第一个条目可以响应点击。 - 当你手动把滚动偏移量设为1后,RecyclerView调用
canScrollVertically(-1)(判断是否可以向上滚动)会返回true,SwipeRefreshLayout就会放弃所有触摸事件的拦截权,所有条目的点击都能正常响应。但此时下拉时SwipeRefreshLayout会判定子View还能向上滚动,不会触发下拉刷新逻辑,所以刷新功能失效。
解决方案
你可以通过以下任意一种方法修复问题:
- 优先方案:重写SwipeRefreshLayout的子View滚动判断逻辑,避免默认逻辑的误判,代码示例如下:
// Kotlin 实现 exchanges_swipe_refresh.setOnChildScrollUpCallback { _, _ -> rv_exchanges.canScrollVertically(-1) }
// Java 实现 exchanges_swipe_refresh.setOnChildScrollUpCallback(new SwipeRefreshLayout.OnChildScrollUpCallback() { @Override public boolean canChildScrollUp(@NonNull SwipeRefreshLayout parent, @Nullable View child) { return rv_exchanges.canScrollVertically(-1); } });
- 依赖升级方案:将你的
androidx.swiperefreshlayout依赖升级到最新稳定版,旧版本的默认滚动判断逻辑对开启了clipToPadding=false的RecyclerView适配存在缺陷,新版本已经修复了该问题。 - 布局调整方案:如果不需要让条目穿透RecyclerView的padding区域显示,可以删除
android:clipToPadding="false"属性,或者把垂直padding移到条目布局中设置,也可以解决事件拦截冲突。
内容的提问来源于stack exchange,提问作者Oscar Berggren
相关产品推荐
相关产品推荐

