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

自定义CoordinatorLayout Behavior如何响应NestedScrollView滚动事件?

这个问题我之前也遇到过!核心原因是CoordinatorLayout的layoutDependsOn只能识别直接同级的View作为依赖,你的NestedScrollView藏在ViewPager的Fragment里面,属于深层子View,所以Behavior根本检测不到它,只会把同级的ViewPager当成依赖对象。

给你两种靠谱的解决方案,推荐第一种,更符合CoordinatorLayout的设计逻辑:


方案一:利用CoordinatorLayout的嵌套滚动Behavior直接监听滚动

修改你的NavigationViewBehavior,放弃依赖NestedScrollView,转而通过嵌套滚动回调来接收所有子层级的滚动事件:

package ui.behavior
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.support.v4.widget.NestedScrollView
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import ui.home.navigation.NavigationView

class NavigationViewBehavior(
    context: Context,
    attributeSet: AttributeSet
) : CoordinatorLayout.Behavior<NavigationView>(context, attributeSet) {

    private var originalHeight = 0
    private val minHeight: Int by lazy {
        // 定义导航栏的最小高度,这里设为48dp,可按需修改
        TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            48f,
            context.resources.displayMetrics
        ).toInt()
    }

    override fun onLayoutChild(parent: CoordinatorLayout, child: NavigationView, layoutDirection: Int): Boolean {
        // 保存导航栏的原始高度,用于后续恢复
        originalHeight = child.height
        return super.onLayoutChild(parent, child, layoutDirection)
    }

    override fun onStartNestedScroll(
        coordinatorLayout: CoordinatorLayout,
        child: NavigationView,
        directTargetChild: View,
        target: View,
        axes: Int,
        type: Int
    ): Boolean {
        // 只关心垂直方向的滚动,且滚动源是NestedScrollView
        return axes == View.SCROLL_AXIS_VERTICAL && target is NestedScrollView
    }

    override fun onNestedScroll(
        coordinatorLayout: CoordinatorLayout,
        child: NavigationView,
        target: View,
        dxConsumed: Int,
        dyConsumed: Int,
        dxUnconsumed: Int,
        dyUnconsumed: Int,
        type: Int
    ) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)
        
        // dyConsumed:已被消耗的滚动距离,正数=向上滚,负数=向下滚
        val newHeight = child.height - dyConsumed
        // 限制高度在最小高度和原始高度之间
        val finalHeight = newHeight.coerceIn(minHeight, originalHeight)
        
        child.layoutParams.height = finalHeight
        child.requestLayout()
    }
}

然后确保你的布局不变,NavigationView已经绑定了这个Behavior,不需要额外修改ViewPager的属性。


方案二:在Fragment中监听NestedScrollView,通过接口回调通知Activity调整高度

如果第一种方案不符合你的需求,也可以用这种更直接的方式:

  1. 在Fragment中定义滚动回调接口:
interface ScrollCallback {
    fun onVerticalScroll(dy: Int)
}

class YourFragment : Fragment() {
    private var scrollCallback: ScrollCallback? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val nestedScrollView = view.findViewById<NestedScrollView>(R.id.your_nested_scroll_id)
        
        nestedScrollView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
            val dy = scrollY - oldScrollY
            scrollCallback?.onVerticalScroll(dy)
        }
    }

    fun setScrollCallback(callback: ScrollCallback) {
        this.scrollCallback = callback
    }
}
  1. 在Activity中实现接口,调整NavigationView高度:
class MainActivity : AppCompatActivity(), ScrollCallback {
    private lateinit var navigationView: NavigationView
    private var originalNavHeight = 0
    private val minNavHeight = 48.dpToPx()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.your_layout)
        
        navigationView = findViewById(R.id.navigation_view)
        originalNavHeight = navigationView.height
        
        // 给Fragment设置回调
        val fragment = supportFragmentManager.findFragmentById(R.id.viewpager)?.getChildFragmentManager()?.fragments?.get(0) as? YourFragment
        fragment?.setScrollCallback(this)
    }

    override fun onVerticalScroll(dy: Int) {
        val newHeight = navigationView.height - dy
        val finalHeight = newHeight.coerceIn(minNavHeight, originalNavHeight)
        
        navigationView.layoutParams.height = finalHeight
        navigationView.requestLayout()
    }

    // 扩展函数:dp转px
    private fun Int.dpToPx(): Int {
        return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            this.toFloat(),
            resources.displayMetrics
        ).toInt()
    }
}

关键注意点

  • 方案一的优势是完全基于CoordinatorLayout的原生机制,耦合度低,不需要Fragment和Activity之间做额外通信;
  • 确保你的NestedScrollView设置了android:fillViewport="true",否则可能出现滚动异常;
  • 可以根据需求调整minHeight的值,或者添加平滑动画让高度变化更自然(比如用ValueAnimator来过渡高度)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:59:57