如何让CoordinatorLayout识别代码触发的scrollTo滚动事件并触发AppBarLayout的隐藏行为?
如何让CoordinatorLayout识别代码触发的scrollTo滚动事件并触发AppBarLayout的隐藏行为?
兄弟,我之前也踩过这个一模一样的坑!直接调用NestedScrollView的scrollTo或者smoothScrollTo确实不会触发CoordinatorLayout控制的AppBarLayout隐藏/展开,因为这些原生滚动方法相当于“偷偷”修改了滚动位置,完全没走CoordinatorLayout依赖的NestedScrolling嵌套滚动通知机制——它根本感知不到你代码触发的滚动。
要解决这个问题,我们得手动帮NestedScrollView把滚动事件“上报”给CoordinatorLayout,让它参与到滚动流程里。具体怎么做呢?
核心思路
CoordinatorLayout是通过NestedScrollingChild接口的回调来监听子View滚动的,所以我们需要在代码触发滚动前后,手动调用NestedScrollView的嵌套滚动相关方法,把滚动意图和结果通知给它。
修改你的scrollToAnchor函数
我给你调整了代码逻辑,在找到锚点并计算出目标滚动位置后,手动触发嵌套滚动的通知流程,让CoordinatorLayout能实时响应:
fun scrollToAnchor(view: View, anchor: String, nestedScrollView: NestedScrollView) { val textView = view as TextView textView.setTextIsSelectable(false) val spanned = textView.text as Spanned val spans = spanned.getSpans(0, spanned.length, AnchorSpan::class.java) if (spans.isNotEmpty()) { for (span in spans) { if (anchor == span.anchor) { val start = spanned.getSpanStart(span) // 确保TextView已经完成布局,避免获取不到布局信息 textView.post { val layout = textView.layout ?: return@post val line = layout.getLineForOffset(start) // 计算锚点在TextView内的Y坐标 val lineTopY = layout.getLineTop(line) // 转换为NestedScrollView需要滚动到的目标Y值 val targetScrollY = nestedScrollView.scrollY + lineTopY - textView.paddingTop // 1. 启动嵌套滚动,告诉CoordinatorLayout:我要开始滚了! nestedScrollView.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL) // 2. 分发滚动前的事件,让CoordinatorLayout提前响应(比如开始隐藏AppBar) val consumed = intArrayOf(0, 0) val dy = targetScrollY - nestedScrollView.scrollY nestedScrollView.dispatchNestedPreScroll(0, dy, consumed, null) // 3. 执行实际的滚动操作 nestedScrollView.scrollTo(0, targetScrollY) // 4. 分发滚动完成的事件,告诉CoordinatorLayout:我滚完了! nestedScrollView.dispatchNestedScroll(0, dy - consumed[1], 0, 0, null) // 5. 停止嵌套滚动,结束这次流程 nestedScrollView.stopNestedScroll() } break } } } }
关键步骤解释
- startNestedScroll:告诉CoordinatorLayout,NestedScrollView要开始滚动了,建立两者的嵌套滚动关联
- dispatchNestedPreScroll:把即将滚动的偏移量通知给CoordinatorLayout,它会根据这个信息提前调整AppBarLayout的位置(比如开始隐藏)
- scrollTo:执行实际的滚动操作
- dispatchNestedScroll:把滚动完成的结果通知给CoordinatorLayout,完成后续的动画收尾
- stopNestedScroll:结束嵌套滚动关联,避免不必要的资源占用
额外注意点
- 一定要用
textView.post{}包裹布局计算的逻辑,确保TextView已经完成布局,不然layout.getLineForOffset会空指针或者计算出错误的位置 - 如果用的是
smoothScrollTo而不是scrollTo,那你需要监听NestedScrollView的滚动变化,在滚动过程中持续分发嵌套滚动事件,不过一般锚点跳转用直接scrollTo就足够了
备注:内容来源于stack exchange,提问作者Viewed
相关产品推荐
相关产品推荐

