Android:自定义RecyclerView+CollapsingToolbarLayout的伸缩高度行为实现
实现自定义折叠行为的CollapsingToolbarLayout
要实现你需要的自定义折叠逻辑(初始高度为屏幕1/3、上滚折叠至15%、下滚展开至全屏),默认的CollapsingToolbarLayout无法直接满足需求,需要通过自定义滚动监听或扩展AppBarLayout.Behavior来实现,以下是两种可行方案:
方案一:监听NestedScrollView滚动动态调整高度
这种方式简单直接,适合快速实现需求:
1. 布局结构
确保使用CoordinatorLayout包裹AppBarLayout和可滚动内容(如NestedScrollView):
<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsingToolbar" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll"> <!-- 自定义头部内容,比如背景图 --> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/header_bg"/> <androidx.appcompat.widget.Toolbar android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"/> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.core.widget.NestedScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <!-- 滚动内容区域 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!-- 你的内容 --> </LinearLayout> </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout>
2. 代码实现滚动监听
在Activity/Fragment中初始化高度值并监听滚动:
val screenHeight = resources.displayMetrics.heightPixels val initialHeight = screenHeight / 3 val collapsedHeight = (screenHeight * 0.15).toInt() val expandedHeight = screenHeight val collapsingToolbar = findViewById<CollapsingToolbarLayout>(R.id.collapsingToolbar) val scrollView = findViewById<NestedScrollView>(R.id.scrollView) // 设置初始高度 collapsingToolbar.layoutParams.height = initialHeight collapsingToolbar.requestLayout() var lastScrollY = 0 scrollView.setOnScrollChangeListener { _, _, scrollY, _, _ -> val scrollDelta = scrollY - lastScrollY lastScrollY = scrollY val currentHeight = collapsingToolbar.layoutParams.height var newHeight = currentHeight when { scrollDelta > 0 -> { // 向上滚动:折叠至最小高度 newHeight = max(collapsedHeight, currentHeight - scrollDelta) } scrollDelta < 0 -> { // 向下滚动:展开至全屏 newHeight = min(expandedHeight, currentHeight + Math.abs(scrollDelta)) } } if (newHeight != currentHeight) { // 平滑更新高度(可选,提升体验) ValueAnimator.ofInt(currentHeight, newHeight).apply { duration = 80 addUpdateListener { anim -> collapsingToolbar.layoutParams.height = anim.animatedValue as Int collapsingToolbar.requestLayout() } start() } } }
方案二:自定义AppBarLayout.Behavior(更优雅的联动方式)
通过扩展AppBarLayout.Behavior,可以更好地融入CoordinatorLayout的滚动体系,避免直接监听滚动的耦合问题:
1. 自定义Behavior类
class CustomAppBarBehavior : AppBarLayout.Behavior() { private var screenHeight = 0 private var initialHeight = 0 private var collapsedHeight = 0 private var expandedHeight = 0 override fun onLayoutChild(parent: CoordinatorLayout, child: AppBarLayout, layoutDirection: Int): Boolean { // 初始化高度参数 screenHeight = parent.resources.displayMetrics.heightPixels initialHeight = screenHeight / 3 collapsedHeight = (screenHeight * 0.15).toInt() expandedHeight = screenHeight // 设置初始高度 child.layoutParams.height = initialHeight return super.onLayoutChild(parent, child, layoutDirection) } override fun onNestedScroll( coordinatorLayout: CoordinatorLayout, child: AppBarLayout, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int, consumed: IntArray ) { super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed) val currentHeight = child.layoutParams.height var newHeight = currentHeight when { dyConsumed > 0 -> { // 向上滚动,折叠高度 newHeight = max(collapsedHeight, currentHeight - dyConsumed) } dyConsumed < 0 -> { // 向下滚动,展开高度 newHeight = min(expandedHeight, currentHeight + Math.abs(dyConsumed)) } } if (newHeight != currentHeight) { // 平滑更新高度 ValueAnimator.ofInt(currentHeight, newHeight).apply { duration = 80 addUpdateListener { anim -> child.layoutParams.height = anim.animatedValue as Int child.requestLayout() } start() } // 标记滚动事件已被消费,避免重复处理 consumed[1] = dyConsumed } } }
2. 布局中应用自定义Behavior
在AppBarLayout中指定自定义Behavior:
<com.google.android.material.appbar.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="com.yourpackage.CustomAppBarBehavior"> <!-- 内部的CollapsingToolbarLayout和内容保持不变 --> </com.google.android.material.appbar.AppBarLayout>
注意事项
- 确保
CollapsingToolbarLayout的layout_height设置为match_parent,让其高度由AppBarLayout控制。 - 如果使用Toolbar,设置
app:layout_collapseMode="pin"可以让Toolbar在折叠时固定在顶部。 - 头部图片建议使用
scaleType="centerCrop",避免高度变化时出现拉伸变形。 - 平滑动画的
duration可以根据需求调整,平衡流畅度和响应速度。
内容的提问来源于stack exchange,提问作者Uriel Frankel
相关产品推荐
相关产品推荐

