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

Constraint Layout文本重叠:双向文本自适应换行实现方案咨询

两端对齐动态文本列表项实现方案

问题背景

需要实现可复用的列表项组件,满足以下需求:

  • 两段文本分别锚定父布局的起始端和结束端
  • 空间动态分配,文本过长时自动换行
  • 复用场景中,两段文本的空间占比不固定(有时第一段占比大,有时第二段占比大)

原实现存在的问题:

  • 固定权重的Row布局无法灵活适配不同文本的空间需求
  • ConstraintLayout仅设置首尾约束时,文本过长会出现重叠

原实现代码

ConstraintLayout版本

val offset: Dp = 8.dp
val modifier = if (onClick != null) {
    Modifier
        .fillMaxWidth()
        .background(backgroundColor)
        .height(rowHeight)
        .clickable(
            onClick = onClick
        )
} else {
    Modifier
        .fillMaxWidth(widthPercentage)
        .background(backgroundColor)
        .defaultMinSize(minHeight = rowHeight)
}

ConstraintLayout(
    modifier = modifier
) {
    val (titleRef, infoRef, disclosureRef) = createRefs()

    Text(
        text = title,
        color = titleColor,
        style = titleStyle,
        modifier = Modifier
            .constrainAs(titleRef) {
                start.linkTo(parent.start, margin = horizontalPadding)
                top.linkTo(parent.top)
                bottom.linkTo(parent.bottom)
            }
    )

    Text(
        text = infoText,
        color = infoTextColor,
        style = infoStyle,
        textAlign = TextAlign.End,
        modifier = Modifier
            .constrainAs(infoRef) {
                if (showDisclosure) {
                    end.linkTo(disclosureRef.start, margin = offset)
                } else {
                    end.linkTo(parent.end, margin = horizontalPadding)
                }
                top.linkTo(parent.top)
                bottom.linkTo(parent.bottom)
            }
    )

    if (showDisclosure) {
        Image(
            painter = painterResource(id = R.drawable.ic_disclosure_gray),
            modifier = Modifier
                .constrainAs(disclosureRef) {
                    end.linkTo(parent.end, margin = horizontalPadding - offset)
                    top.linkTo(parent.top)
                    bottom.linkTo(parent.bottom)
                }
        )
    }
}

固定权重Row版本

val offset: Dp = 8.dp
val modifier = if (onClick != null) {
    Modifier
        .defaultMinSize(minHeight = 35.dp)
        .fillMaxWidth()
        .background(backgroundColor)
        .padding(vertical = verticalPadding)
        .clickable(
            onClick = onClick
        )
} else {
    Modifier
        .fillMaxWidth(widthPercentage)
        .background(backgroundColor)
        .padding(vertical = verticalPadding)
        .defaultMinSize(minHeight = rowHeight)
}

Row(
    verticalAlignment = Alignment.CenterVertically,
    modifier = modifier
) {
    Text(
        text = title,
        color = titleColor,
        style = titleStyle,
        maxLines = 2,
        modifier = Modifier
            .weight(1f)
            .padding(start = horizontalPadding)
    )

    val trailingPadding = if (showDisclosure) {
        offset
    } else {
        horizontalPadding
    }

    Text(
        text = infoText,
        color = infoTextColor,
        style = infoStyle,
        textAlign = TextAlign.End,
        modifier = Modifier
            .weight(1.5f)
            .padding(start = horizontalPadding, end = trailingPadding)
    )

    if (showDisclosure) {
        Image(
            painter = painterResource(id = R.drawable.ic_disclosure_gray),
            modifier = Modifier
                .padding(end = offset)
                // .align(Alignment.CenterVertically)
        )
    }
}

优化解决方案

方案1:改进ConstraintLayout实现

通过添加双向约束链和宽度限制,解决文本重叠问题,同时支持动态空间分配:

val offset: Dp = 8.dp
val modifier = if (onClick != null) {
    Modifier
        .fillMaxWidth()
        .background(backgroundColor)
        .defaultMinSize(minHeight = rowHeight)
        .clickable(onClick = onClick)
        .padding(vertical = verticalPadding)
} else {
    Modifier
        .fillMaxWidth(widthPercentage)
        .background(backgroundColor)
        .defaultMinSize(minHeight = rowHeight)
        .padding(vertical = verticalPadding)
}

ConstraintLayout(modifier = modifier) {
    val (titleRef, infoRef, disclosureRef) = createRefs()

    Text(
        text = title,
        color = titleColor,
        style = titleStyle,
        modifier = Modifier
            .constrainAs(titleRef) {
                start.linkTo(parent.start, margin = horizontalPadding)
                top.linkTo(parent.top)
                bottom.linkTo(parent.bottom)
                end.linkTo(infoRef.start, margin = horizontalPadding)
                // 允许文本自适应宽度,超出可用空间时自动换行
                width = Dimension.wrapContent
                constrainedWidth = true
            }
    )

    Text(
        text = infoText,
        color = infoTextColor,
        style = infoStyle,
        textAlign = TextAlign.End,
        modifier = Modifier
            .constrainAs(infoRef) {
                val endAnchor = if (showDisclosure) disclosureRef.start else parent.end
                end.linkTo(endAnchor, margin = offset)
                top.linkTo(parent.top)
                bottom.linkTo(parent.bottom)
                start.linkTo(titleRef.end, margin = horizontalPadding)
                width = Dimension.wrapContent
                constrainedWidth = true
            }
    )

    if (showDisclosure) {
        Image(
            painter = painterResource(id = R.drawable.ic_disclosure_gray),
            contentDescription = "展开图标",
            modifier = Modifier
                .constrainAs(disclosureRef) {
                    end.linkTo(parent.end, margin = horizontalPadding)
                    top.linkTo(parent.top)
                    bottom.linkTo(parent.bottom)
                }
        )
    }
}

关键优化点:

  • 移除固定height,改用defaultMinSize保证最小高度,支持文本换行后高度自适应
  • 给两段文本添加双向首尾约束,设置间距形成水平约束链,避免重叠
  • 开启constrainedWidth = true,限制文本宽度不超出可用空间,触发自动换行
  • 添加垂直内边距,优化文本显示的上下间距

方案2:灵活权重Row实现

通过动态权重参数和fill=false的weight配置,适配不同场景的空间需求:

@Composable
fun AdaptiveTwoTextRow(
    title: String,
    infoText: String,
    onClick: (() -> Unit)? = null,
    titleWeight: Float = 1f,
    infoWeight: Float = 1f,
    showDisclosure: Boolean = false,
    // 其他样式参数...
) {
    val offset: Dp = 8.dp
    val modifier = if (onClick != null) {
        Modifier
            .fillMaxWidth()
            .background(backgroundColor)
            .defaultMinSize(minHeight = rowHeight)
            .clickable(onClick = onClick)
            .padding(vertical = verticalPadding)
    } else {
        Modifier
            .fillMaxWidth(widthPercentage)
            .background(backgroundColor)
            .defaultMinSize(minHeight = rowHeight)
            .padding(vertical = verticalPadding)
    }

    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween,
        modifier = modifier
    ) {
        Text(
            text = title,
            color = titleColor,
            style = titleStyle,
            modifier = Modifier
                .weight(titleWeight, fill = false)
                .padding(start = horizontalPadding)
        )

        Row(
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier.weight(infoWeight, fill = false)
        ) {
            Text(
                text = infoText,
                color = infoTextColor,
                style = infoStyle,
                textAlign = TextAlign.End,
                modifier = Modifier.padding(end = if (showDisclosure) offset else horizontalPadding)
            )
            if (showDisclosure) {
                Image(
                    painter = painterResource(id = R.drawable.ic_disclosure_gray),
                    contentDescription = "展开图标",
                    modifier = Modifier.padding(end = horizontalPadding)
                )
            }
        }
    }
}

关键优化点:

  • 新增titleWeight和infoWeight参数,复用组件时可根据场景灵活调整两段文本的空间占比
  • 使用weight(..., fill = false),让文本先占据自身所需宽度,再分配剩余空间,避免固定权重导致的空间浪费
  • 将右侧文本和图标封装在子Row中,保证它们始终在父布局的结束端对齐
  • 设置horizontalArrangement = Arrangement.SpaceBetween,确保两段文本分别锚定首尾

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 04:35:59