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

Jetpack Compose动态按钮加载器尺寸适配及高度优化问题

解决按钮加载状态下尺寸变化的问题

针对按钮在加载状态下尺寸变大,且使用尺寸记录方法后按钮高度超出预期的问题,我们可以通过以下优化实现保持wrap content布局同时固定尺寸:

问题分析

原代码存在几个关键问题:

  1. 尺寸记录逻辑未区分加载/非加载状态,可能错误记录加载时的按钮尺寸
  2. Modifier组合方式错误,导致重复应用传入的Modifier,引发不必要的布局叠加
  3. 未处理文本内容变化时的尺寸重置,动态文本场景下会导致尺寸不匹配

优化后的代码

@Composable
fun GreenCTA(
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
    title: String,
    isShowLoader: Boolean
) {
    // 当title变化时重置尺寸,确保动态文本适配新尺寸
    var buttonSize by remember(title) {
        mutableStateOf(DpSize.Zero)
    }
    val density = LocalDensity.current

    Button(
        modifier = modifier
            // 仅在记录到有效尺寸后,强制设置按钮尺寸
            .run {
                if (buttonSize != DpSize.Zero) size(buttonSize) else this
            }
            // 仅在非加载状态下记录尺寸,避免错误记录加载时的尺寸
            .onSizeChanged { newSize ->
                if (!isShowLoader && buttonSize == DpSize.Zero) {
                    buttonSize = with(density) {
                        newSize.toSize().toDpSize()
                    }
                }
            },
        onClick = onClick,
        border = BorderStroke(1.dp, color = colorResource(id = R.color.pass_color_c2e6cd)),
        colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(id = R.color.pass_color_e3f6ec)),
        shape = RoundedCornerShape(6.dp),
        contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
        elevation = ButtonDefaults.elevation(0.dp),
    ) {
        if (isShowLoader) {
            BlueLoader(
                modifier = Modifier
                    .fillMaxHeight()
                    .aspectRatio(3f)
                    // 让加载器在按钮内居中,避免填充时出现布局偏移
                    .align(Alignment.CenterVertically)
            )
        } else {
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                Image(
                    painter = painterResource(id = R.drawable.ic_add_tag),
                    contentDescription = "按钮图标"
                )
                Text(
                    text = title,
                    style = MaterialTheme.typography.body2.copy(lineHeight = 20.sp),
                    fontWeight = FontWeight.SemiBold,
                )
            }
        }
    }
}

关键优化点

  • 条件记录尺寸:仅在isShowLoader为false时记录按钮尺寸,确保记录的是文本状态下的真实wrap content尺寸
  • 文本变化时重置尺寸:使用remember(title)绑定尺寸状态,当按钮文本改变时自动重置尺寸,重新记录新的文本布局尺寸
  • 正确组合Modifier:用run替代then,避免重复应用传入的Modifier,确保仅在需要时添加固定尺寸约束
  • 加载器居中对齐:给BlueLoader添加align(Alignment.CenterVertically),保证加载器在按钮内居中显示,避免填充高度时出现视觉偏移

替代方案:使用Box固定容器尺寸

如果上述方法仍有布局问题,还可以用Box作为容器,先渲染文本内容测量尺寸,再在加载状态下复用该尺寸:

@Composable
fun GreenCTA(
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
    title: String,
    isShowLoader: Boolean
) {
    val content = @Composable {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            Image(
                painter = painterResource(id = R.drawable.ic_add_tag),
                contentDescription = "按钮图标"
            )
            Text(
                text = title,
                style = MaterialTheme.typography.body2.copy(lineHeight = 20.sp),
                fontWeight = FontWeight.SemiBold,
            )
        }
    }

    // 测量文本内容的尺寸
    val contentSize = remember(title) {
        mutableStateOf(DpSize.Zero)
    }
    val density = LocalDensity.current

    Box(modifier = modifier.clickable { onClick() }) {
        // 不可见的文本内容用于测量尺寸
        content().apply {
            Modifier
                .onSizeChanged {
                    contentSize.value = with(density) { it.toSize().toDpSize() }
                }
                .alpha(0f)
        }

        Button(
            modifier = Modifier.size(contentSize.value),
            onClick = onClick,
            border = BorderStroke(1.dp, color = colorResource(id = R.color.pass_color_c2e6cd)),
            colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(id = R.color.pass_color_e3f6ec)),
            shape = RoundedCornerShape(6.dp),
            contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
            elevation = ButtonDefaults.elevation(0.dp),
        ) {
            if (isShowLoader) {
                BlueLoader(
                    modifier = Modifier
                        .fillMaxHeight()
                        .aspectRatio(3f)
                        .align(Alignment.CenterVertically)
                )
            } else {
                content()
            }
        }
    }
}

这个方案通过渲染不可见的文本内容来提前获取尺寸,确保按钮始终使用文本状态下的wrap content尺寸,完全避免布局抖动问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:45:17