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

Jetpack Compose实现带可缩放头部的滚动列表

更优实现方案:原生嵌套滚动 + 动态高度控制

你可以用下面这种更简洁的方案,完全避开废弃的swipeable API和复杂的MotionLayout配置,核心通过NestedScrollConnection处理滚动事件,联动控制头部高度与列表的滚动行为:

核心思路

  • 定义头部的最大/最小高度,计算可折叠的高度差值
  • 通过嵌套滚动连接拦截滚动事件,计算头部的折叠偏移量
  • 动态调整头部高度,同时给列表设置对应的顶部内边距,实现“列表滑过头部”的视觉效果

完整实现代码

@Composable
fun CollapsibleHeaderList() {
    val maxHeaderHeight = 264.dp
    val minHeaderHeight = 115.dp
    val collapsibleHeight = maxHeaderHeight - minHeaderHeight

    // 转换为像素值便于计算
    val collapsibleHeightPx = with(LocalDensity.current) { collapsibleHeight.toPx() }
    val minHeaderHeightPx = with(LocalDensity.current) { minHeaderHeight.toPx() }

    // 列表滚动状态
    val scrollState = rememberScrollState()
    // 记录头部的折叠偏移量(0表示完全展开,collapsibleHeightPx表示完全折叠)
    val headerOffset = remember { mutableStateOf(0f) }

    val nestedScrollConnection = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                // 向下滚动(available.y > 0):尝试展开头部
                if (available.y > 0) {
                    val expandAmount = min(available.y, headerOffset.value)
                    headerOffset.value -= expandAmount
                    return Offset(0f, expandAmount) // 消费掉用于展开头部的滚动量
                }
                // 向上滚动(available.y < 0):尝试折叠头部
                else {
                    val collapseAmount = min(-available.y, collapsibleHeightPx - headerOffset.value)
                    headerOffset.value += collapseAmount
                    return Offset(0f, -collapseAmount) // 消费掉用于折叠头部的滚动量
                }
            }

            override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
                // 处理快速滑动后的惯性,让头部自动折叠/展开到位
                if (available.y > 0) {
                    headerOffset.value = max(0f, headerOffset.value - available.y * 0.2f)
                } else {
                    headerOffset.value = min(collapsibleHeightPx, headerOffset.value + available.y * -0.2f)
                }
                return super.onPostFling(consumed, available)
            }
        }
    }

    // 计算当前头部高度和列表顶部内边距
    val currentHeaderHeight = with(LocalDensity.current) {
        (minHeaderHeightPx + collapsibleHeightPx - headerOffset.value).toDp()
    }
    val listTopPadding = with(LocalDensity.current) {
        (collapsibleHeightPx - headerOffset.value).toDp()
    }

    Box(modifier = Modifier.fillMaxSize()) {
        // 列表区域:顶部内边距抵消头部折叠的高度,实现滑过效果
        LazyColumn(
            modifier = Modifier
                .fillMaxSize()
                .nestedScroll(nestedScrollConnection),
            state = scrollState,
            contentPadding = PaddingValues(top = listTopPadding)
        ) {
            // 列表项示例
            items(50) { index ->
                Text(
                    text = "列表项 $index",
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp)
                )
            }
        }

        // 头部区域:固定在顶部,动态调整高度
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(currentHeaderHeight)
                .background(Color(0xFF6200EE))
                .align(Alignment.TopCenter)
        ) {
            // 头部内容,可根据高度变化调整布局(比如标题位置)
            Text(
                text = "可折叠头部",
                color = Color.White,
                fontSize = 20.sp,
                modifier = Modifier.align(Alignment.Center)
            )
        }
    }
}

方案优势

  • 完全抛弃了废弃的swipeable API,符合Compose的最新规范
  • 逻辑直观,没有复杂的MotionLayout约束配置,维护成本更低
  • 滚动交互完全贴合Compose原生嵌套滚动机制,流畅度更好
  • 支持惯性滑动后的自动归位,交互体验更自然

内容的提问来源于stack exchange,提问作者Mehdi Satei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:26:18