多层视图下ScrollView滚动时Toolbar隐藏后无法恢复的问题
你遇到的问题在嵌套布局场景里很常见——当CoordinatorLayout和滚动View之间隔了多层布局时,普通ScrollView的滚动事件没法传递给AppBarLayout,导致设置了scroll|enterAlways的Toolbar只能上滑隐藏,却无法随下滑动作恢复。下面给你两种可行的解决方案:
方案一:替换为NestedScrollView(推荐,最简单)
NestedScrollView是AndroidX提供的支持嵌套滚动的组件,它会自动把滚动事件传递给CoordinatorLayout,完美适配AppBarLayout的滚动行为。只需要做几个小修改:
替换ScrollView为NestedScrollView
把布局里的<ScrollView>换成<androidx.core.widget.NestedScrollView>,注意导入正确的AndroidX包。给外层LinearLayout设置Behavior
找到和AppBarLayout同级的那个LinearLayout(也就是CoordinatorLayout的直接子View),添加app:layout_behavior="@string/appbar_scrolling_view_behavior"属性,让CoordinatorLayout识别这个布局是滚动容器的父容器。保留Toolbar的scrollFlags设置
保持Toolbar的app:layout_scrollFlags="scroll|enterAlways"不变。
修改后的完整布局示例:
<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"> <androidx.appcompat.widget.Toolbar android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_scrollFlags="scroll|enterAlways"/> </com.google.android.material.appbar.AppBarLayout> <!-- 必须给这个LinearLayout设置behavior --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@drawable/your_image"/> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 你的滚动内容容器 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!-- 这里放你的数字列表等滚动内容 --> </LinearLayout> </androidx.core.widget.NestedScrollView> </LinearLayout> </LinearLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
这样修改后,下拉NestedScrollView时,Toolbar就会平滑滑入,完全符合你想要的scroll|enterAlways效果。
方案二:自定义ScrollView传递滚动事件(如果必须用ScrollView)
如果因为某些限制不能替换成NestedScrollView,可以自定义一个ScrollView,手动把滚动事件传递给AppBarLayout的Behavior:
- 创建自定义ObservableScrollView
class ObservableScrollView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ScrollView(context, attrs, defStyleAttr) { private var scrollListener: OnScrollChangeListener? = null override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) { super.onScrollChanged(l, t, oldl, oldt) scrollListener?.onScrollChanged(this, l, t, oldl, oldt) } fun setOnScrollChangeListener(listener: OnScrollChangeListener) { this.scrollListener = listener } interface OnScrollChangeListener { fun onScrollChanged(v: ObservableScrollView, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) } }
- 在Activity中监听滚动并通知Behavior
在你的Activity里,找到自定义ScrollView、AppBarLayout和CoordinatorLayout,手动转发滚动事件:
class YourActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.your_layout) val scrollView = findViewById<ObservableScrollView>(R.id.observable_scroll_view) val appBarLayout = findViewById<AppBarLayout>(R.id.app_bar_layout) val coordinatorLayout = findViewById<CoordinatorLayout>(R.id.coordinator_layout) scrollView.setOnScrollChangeListener { v, _, scrollY, _, oldScrollY -> // 获取AppBarLayout的Behavior实例 val appBarParams = appBarLayout.layoutParams as CoordinatorLayout.LayoutParams val behavior = appBarParams.behavior as AppBarLayout.Behavior? // 手动触发嵌套滚动事件 behavior?.onNestedScroll( coordinatorLayout, appBarLayout, v, 0, scrollY - oldScrollY, // 滚动差值 intArrayOf(0, 0), 0 ) } } }
- 同样设置外层LinearLayout的Behavior
和方案一一样,确保和AppBarLayout同级的LinearLayout设置了app:layout_behavior="@string/appbar_scrolling_view_behavior",Toolbar的scrollFlags保持不变。
为什么之前的设置无效?
普通ScrollView没有实现嵌套滚动接口,它的滚动事件只会在自身内部处理,不会传递给上层的CoordinatorLayout。而你中间的两层LinearLayout也没有配置对应的滚动Behavior,导致CoordinatorLayout无法感知到ScrollView的下滑动作,自然不会触发Toolbar的enterAlways效果。
内容的提问来源于stack exchange,提问作者Cactus

