Jetpack Compose如何实现支持手势控制的Prominent Top App Bar
Jetpack Compose 可折叠顶部应用栏手势交互实现问题
我希望使用Jetpack Compose创建类似下方截图的顶部应用栏,并为其添加对应手势交互:
我已经参考Android官方嵌套滚动相关文档实现了可折叠顶部栏,但无法通过手势控制其展开、折叠以及同步修改布局,以下是我尝试编写的可折叠工具栏代码:
val toolbarHeight = 48.dp val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() } // our offset to collapse toolbar val toolbarOffsetHeightPx = remember { mutableStateOf(0f) } // now, let's create connection to the nested scroll system and listen to the scroll // happening inside child LazyColumn val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { // try to consume before LazyColumn to collapse toolbar if needed, hence pre-scroll val delta = available.y val newOffset = toolbarOffsetHeightPx.value + delta toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f) // here's the catch: let's pretend we consumed 0 in any case, since we want // LazyColumn to scroll anyway for good UX // We're basically watching scroll without taking it return Offset.Zero } } }
我想要实现标准的顶部栏手势滑动折叠、展开效果,目前缺少完整的手势控制逻辑实现,恳请大家提供相关参考资料或解决方案。
解决方案
问题原因
你当前代码的核心问题是onPreScroll方法始终返回Offset.Zero,意味着你没有消费任何滚动事件,嵌套滚动系统不会将顶部栏的偏移变更和列表滚动做联动;另外你缺少了松手后的吸顶/吸底惯性逻辑,也没有将偏移量应用到顶部栏的布局属性上。
完整实现步骤
1. 修正嵌套滚动连接逻辑
你需要根据滚动方向合理消费滚动偏移,同时添加松手后的自动折叠/展开逻辑:
val toolbarHeight = 48.dp val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() } val toolbarOffsetHeightPx = remember { mutableFloatStateOf(0f) } // 定义阈值,滚动超过一半高度松手就自动切换折叠/展开状态 val collapseThreshold = toolbarHeightPx * 0.5f val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val delta = available.y return when { // 向上滚动、顶部栏未完全折叠:消费滚动偏移折叠顶部栏 delta < 0 && toolbarOffsetHeightPx.value > -toolbarHeightPx -> { val newOffset = (toolbarOffsetHeightPx.value + delta).coerceIn(-toolbarHeightPx, 0f) val consumed = newOffset - toolbarOffsetHeightPx.value toolbarOffsetHeightPx.value = newOffset Offset(0f, consumed) } // 向下滚动、列表已到顶部:消费滚动偏移展开顶部栏 delta > 0 && toolbarOffsetHeightPx.value < 0f -> { val newOffset = (toolbarOffsetHeightPx.value + delta).coerceIn(-toolbarHeightPx, 0f) val consumed = newOffset - toolbarOffsetHeightPx.value toolbarOffsetHeightPx.value = newOffset Offset(0f, consumed) } else -> Offset.Zero } } // 处理松手后的惯性滚动 override suspend fun onPreFling(available: Velocity, source: NestedScrollSource): Velocity { val currentOffset = toolbarOffsetHeightPx.value return when { currentOffset < -collapseThreshold && available.y < 0 -> { animateToOffset(-toolbarHeightPx) Velocity(0f, available.y) } currentOffset > -collapseThreshold && available.y > 0 -> { animateToOffset(0f) Velocity(0f, available.y) } else -> Velocity.Zero } } private suspend fun animateToOffset(target: Float) { toolbarOffsetHeightPx.animateTo( targetValue = target, animationSpec = tween(durationMillis = 200) ) } } }
2. 将偏移应用到实际布局
你需要给顶部栏添加偏移修饰符,同时给列表添加顶部padding避免内容被遮挡:
Box( Modifier .fillMaxSize() .nestedScroll(nestedScrollConnection) ) { // 列表内容 LazyColumn( contentPadding = PaddingValues(top = toolbarHeight), modifier = Modifier.fillMaxSize() ) { // 此处添加你的列表项逻辑 } // 顶部应用栏 TopAppBar( title = { Text("示例标题") }, modifier = Modifier .height(toolbarHeight) .offset { IntOffset(0, toolbarOffsetHeightPx.value.roundToInt()) } ) }
优化建议
如果需要实现Material Design风格的折叠工具栏,可以直接使用Material3库中提供的TopAppBarScrollBehavior相关API,无需手动处理嵌套滚动逻辑;你也可以根据顶部栏的折叠进度添加标题缩放、背景透明度渐变等自定义动画效果。
内容的提问来源于stack exchange,提问作者prat
相关产品推荐
相关产品推荐

