如何检测NestedScrollView滚动到底部且用户正在执行下拉操作?
解决NestedScrollView到底部后下拉动作的检测方案
我之前也碰到过一模一样的需求!既要保留AppBarLayout和NestedScrollView的联动效果(就是你说的appbar_scrolling_view_behavior正常工作),又要精准判断用户滚动到底部后的下拉动作,确实踩了不少坑,分享下我最终用的方案:
核心思路
不需要重写CoordinatorLayout.Behavior,只需要自定义NestedScrollView,通过监听它的滚动状态、滚动方向以及是否到底部的条件组合,就能精准触发我们要的回调。这样完全不会破坏原有Behavior的联动效果。
具体实现代码
1. 自定义NestedScrollView类
这个类会帮我们监听滚动细节,精准判断到底部+下拉的动作:
class CustomNestedScrollView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : NestedScrollView(context, attrs, defStyleAttr) { private var scrollListener: OnBottomPullListener? = null private var lastScrollY = 0 // 定义回调接口,用于通知外部到底部下拉的事件 interface OnBottomPullListener { fun onBottomPulled() } // 设置监听器的方法 fun setOnBottomPullListener(listener: OnBottomPullListener) { this.scrollListener = listener } // 监听滚动位置变化,基础判断逻辑 override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) { super.onScrollChanged(l, t, oldl, oldt) // 判断是否滚动到底部:canScrollVertically(1)返回false表示无法再向下滚动 val isAtBottom = !canScrollVertically(1) // 判断是否是下拉动作:当前滚动位置t > 上一次位置lastScrollY val isPullingDown = t > lastScrollY if (isAtBottom && isPullingDown) { scrollListener?.onBottomPulled() } lastScrollY = t } // 结合嵌套滚动类型,过滤惯性滚动,只响应用户主动触摸下拉 override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) { super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type) // TYPE_TOUCH表示是用户主动触摸滚动,排除惯性滚动的误触发 if (type == ViewCompat.TYPE_TOUCH) { val isAtBottom = !canScrollVertically(1) // dyUnconsumed < 0:表示用户向下拉的动作没有被ScrollView消耗(已经到底了) val isPullingDown = dyUnconsumed < 0 if (isAtBottom && isPullingDown) { scrollListener?.onBottomPulled() } } } }
2. 布局中使用自定义ScrollView
完全保留原来的appbar_scrolling_view_behavior,只把原生NestedScrollView替换成我们自定义的:
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- 你的Toolbar、CollapsingToolbarLayout等内容 --> </com.google.android.material.appbar.AppBarLayout> <com.yourpackage.CustomNestedScrollView android:id="@+id/customNestedScrollView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <!-- 你的滚动内容,比如LinearLayout、RecyclerView等 --> </com.yourpackage.CustomNestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout>
3. 在Activity/Fragment中设置回调
customNestedScrollView.setOnBottomPullListener { // 这里就是你要处理的"到底部且下拉"的逻辑 Toast.makeText(this@MainActivity, "已滚动到底部,正在下拉!", Toast.LENGTH_SHORT).show() }
关键细节说明
canScrollVertically(1):这是Android官方推荐的判断是否能向下滚动的方法,返回false就说明已经到了ScrollView的底部。type == ViewCompat.TYPE_TOUCH:过滤掉惯性滚动的情况,确保只响应用户主动的触摸下拉动作,避免误触发回调。dyUnconsumed < 0:当ScrollView已经到底时,用户继续下拉的动作不会被ScrollView消耗,此时dyUnconsumed会是负数,以此精准判断用户正在执行下拉操作。
内容的提问来源于stack exchange,提问作者Jocky Doe
相关产品推荐
相关产品推荐

