You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BottomSheet嵌套滚动异常:RecyclerView无法滚动与无法拖拽问题

解决BottomSheet完全展开后RecyclerView滚动与拖拽冲突问题

根据你的描述,核心矛盾在于BottomSheet完全展开时,RecyclerView的滚动事件与BottomSheet的拖拽收起事件互相抢占——要么RecyclerView无法滚动,要么BottomSheet不能被拖拽。结合你的布局结构(嵌套CoordinatorLayout、自定义圆角FrameLayout根视图、behavior_fitToContents=false),这里提供两种可行的解决方案:


方案一:自定义BottomSheetBehavior,精准控制嵌套滚动逻辑

这是最可靠的方案,能从Behavior层面直接处理事件分发逻辑,适配你的特殊布局需求。

步骤1:创建自定义BottomSheetBehavior类

继承BottomSheetBehavior,重写onStartNestedScroll和onNestedPreScroll方法,判断BottomSheet状态和滚动方向,决定是否让子视图(RecyclerView)处理滚动:

class CustomBottomSheetBehavior<V : View>(context: Context, attrs: AttributeSet) : BottomSheetBehavior<V>(context, attrs) {

    override fun onStartNestedScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        directTargetChild: View,
        target: View,
        axes: Int,
        type: Int
    ): Boolean {
        // 只处理垂直滚动事件
        return axes and ViewCompat.SCROLL_AXIS_VERTICAL != 0
    }

    override fun onNestedPreScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        target: View,
        dx: Int,
        dy: Int,
        consumed: IntArray,
        type: Int
    ) {
        val currentState = state
        // 当BottomSheet完全展开时,区分滚动逻辑:
        // 1. 向上滚动:交给RecyclerView处理
        // 2. 向下滚动:如果RecyclerView已滚到顶部,交给BottomSheet处理拖拽收起
        if (currentState == STATE_EXPANDED) {
            val recyclerView = target as? RecyclerView
            if (recyclerView != null) {
                val canScrollUp = recyclerView.canScrollVertically(-1)
                if (!canScrollUp && dy < 0) {
                    // RecyclerView已到顶,向下滚动时让BottomSheet处理拖拽
                    super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
                } else {
                    // 否则让RecyclerView自行处理滚动
                    consumed[1] = 0
                }
            } else {
                super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
            }
        } else {
            // 非完全展开状态,保持BottomSheet默认拖拽逻辑
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
        }
    }
}

步骤2:替换布局中的Behavior

在你的BottomSheet根视图(自定义FrameLayout)的布局属性中,替换为自定义Behavior:

<com.yourpackage.CustomFrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="com.yourpackage.CustomBottomSheetBehavior"
    app:behavior_fitToContents="false"
    ...>
    <!-- 内部ConstraintLayout和RecyclerView -->
</com.yourpackage.CustomFrameLayout>

步骤3:开启RecyclerView的嵌套滚动

在Fragment初始化时,仅开启RecyclerView的嵌套滚动(无需开启根视图的嵌套滚动):

binding.bottomSheetEvents.events.setNestedScrollingEnabled(true)

方案二:升级BottomSheet依赖版本(推荐优先尝试)

你使用的1.1.0-alpha07是较早的测试版本,存在一些嵌套滚动的bug。升级到com.google.android.material:material:1.2.0及以上的稳定版本,很多此类冲突问题已被官方修复:

  1. 修改gradle依赖:
implementation 'com.google.android.material:material:1.6.1' // 或最新稳定版
  1. 移除之前手动设置的ViewCompat.setNestedScrollingEnabled(bottomSheetRoot, true),仅保留RecyclerView的嵌套滚动开启:
binding.bottomSheetEvents.events.setNestedScrollingEnabled(true)
  1. 测试BottomSheet的拖拽和RecyclerView滚动是否正常,大部分情况下官方修复已经能解决问题。

关键注意事项

  • 不要用NestedScrollView包裹RecyclerView:这会导致RecyclerView失去复用特性,且不符合你“上方视图固定”的需求。
  • 自定义FrameLayout仅处理圆角,不要干扰触摸事件:确保你的自定义FrameLayout没有重写onTouchEvent或onInterceptTouchEvent方法,避免影响事件分发。
  • 确认behavior_fitToContents=false的配置:这个设置是正确的,配合setExpandedOffset能阻止BottomSheet顶到顶部,保持你的布局需求。

内容的提问来源于stack exchange,提问作者Mackovich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:17:51