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

Jetpack Compose中如何正确测量父视图高度并实现视图动画?

Compose视图测量与动画修复方案

问题根源

你的代码存在循环依赖问题:初始时columnHeightDp为0.dp,你给子Column设置了.height(heightInDp),这会强制视图以0高度布局,导致onGloballyPositioned回调拿到的高度就是0,最终动画始终无法获取到视图的真实高度,出现图中高度为0的异常情况。

修复方案

要先让视图完成一次正常布局,获取到真实高度后再应用高度动画。核心调整点:

  • 初始状态不强制设置高度,用wrapContentHeight()让视图自然测量自身高度;
  • 仅当columnHeightDp不为0时,才将动画高度应用到视图上;
  • 限制高度更新逻辑,避免重复触发不必要的布局刷新。

修改后的代码

@Preview(showBackground = true)
@Composable
fun MoveText() {
    val density = LocalDensity.current
    var columnHeightDp by remember { mutableStateOf(0.dp) }
    var visible by remember { mutableStateOf(true) }
    
    // 动画状态定义
    val iconOffsetAnimation: Dp by animateDpAsState(
        targetValue = if (visible) 13.dp else 0.dp,
        animationSpec = tween(1000)
    )
    val textOffsetAnimation: Dp by animateDpAsState(
        targetValue = if (visible) 6.dp else 0.dp,
        animationSpec = tween(1000)
    )
    val viewAlpha: Float by animateFloatAsState(
        targetValue = if (visible) 1f else 0f,
        animationSpec = tween(1000)
    )
    val heightInDp: Dp by animateDpAsState(
        targetValue = if (visible) columnHeightDp else 0.dp,
        animationSpec = tween(1000)
    )

    ScrollComposeTheme {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(start = 16.dp, top = 16.dp)
        ) {
            Column(
                modifier = Modifier
                    .onGloballyPositioned { coordinates ->
                        // 仅首次获取真实高度时更新,避免重复触发布局
                        if (columnHeightDp == 0.dp) {
                            with(density) {
                                columnHeightDp = coordinates.size.height.toDp()
                            }
                        }
                    }
                    // 动态切换高度设置:初始自然布局,获取高度后应用动画
                    .run {
                        if (columnHeightDp != 0.dp) height(heightInDp) else wrapContentHeight()
                    }
                    .background(Color.LightGray)
            ) {
                Image(
                    modifier = Modifier.padding(top = iconOffsetAnimation),
                    alpha = viewAlpha,
                    imageVector = Icons.Default.ShoppingCart,
                    contentDescription = null,
                )
                Text(
                    modifier = Modifier.padding(top = textOffsetAnimation),
                    text = "Hello, Anna",
                    fontSize = 20.sp,
                    color = Color.Black.copy(alpha = viewAlpha),
                )
            }
            Button(
                modifier = Modifier.padding(top = 10.dp),
                onClick = { visible = !visible }
            ) {
                Text(text = "Move Text")
            }
        }
    }
}

关键修改说明

  1. 在onGloballyPositioned中增加判断,仅在columnHeightDp为0时更新高度,避免视图布局变化时重复触发更新;
  2. 使用Modifier.run动态切换高度设置:初始阶段用wrapContentHeight()让视图正常测量自身高度,获取到真实高度后再应用动画高度;
  3. 这样就能保证第一次布局时拿到视图的真实高度,后续动画可以基于这个高度展开,实现预期的收起/展开效果。

内容的提问来源于stack exchange,提问作者Compose Learner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 08:25:07