mKa
mKa

Reputation: 23

Constraint Layout items overlapping: Smart wrap text does not work when two opposing items intertwine

I want to make a list item which contains two texts , one anchored to the start of parent and one to the end of parent, I want the space to be dynamically handled each time and if too long it should wrap text and break line, my problem is that I'm using this item everywhere, sometimes Text1 takes up more space than Text2 and vice versa, this makes using Row with weight not useful, I tried using constraint layout, with top, bottom and start/end constraints, but this makes the texts overlap when too long.

item using Row and weight

This is my constraint layout approach:

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)
                }
        )
    }
}

And the row approach which does not give me flexibility, when wanting different text having the bigger weight each time:

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)
        )
    }
}

What I am expecting :

enter image description here

Upvotes: 1

Views: 110

Answers (1)

BenjyTec
BenjyTec

Reputation: 10867

Please try to apply the weight Modifier to the first Text Composable as follows:

@Composable
fun MyComposable() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            modifier = Modifier.weight(1f),
            text = "ABC DEF"
        )
        Text(text = "1000 1000 1000 1000 1000 1000 1000 1000 1000")
    }
}

Jetpack Compose measures the unweighted children first, then it distributes the remaining space between the children that have the weight Modifier set.

The parent will divide the horizontal space remaining after measuring unweighted child elements and distribute it according to this weight.

Output:

screenshot


Note that if the second Text might fill the whole screen width, you need to specify a max width of the second Text to prevent it from filling the whole screen width and pushing the first Text off. You can try this as follows:

val configuration = LocalConfiguration.current
val screenWidth = configuration.screenWidthDp.dp

Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.SpaceBetween
) {

    Text(
        modifier = Modifier.weight(1f),
        text = "ABC DEF"
    )

    Text(
        modifier = Modifier.widthIn(max = screenWidth - 150.dp),  // first Text will always have 150dp space
        Text(text = "1000 1000 1000 1000 1000 1000 1000 1000 1000")
    )

}

Upvotes: 0

Related Questions