P Kuijpers
P Kuijpers

Reputation: 1653

How to use the ConstraintLayout's layout_constraintWidth_percent in Jetpack Compose?

My layout consists of 2 texts with an arrow image between them. All elements are placed in a composable Row and aligned to the left, right behind eachother. When the texts are short it looks like this:

|Origin → destination |

When the texts are too long, I'd like them to ellipsise with max 1 line. If the 'destination' is too long, it works as expected:

|Origin → looooooooongDest...|

But what if the 'origin' text is too long?

|LooooooooooooooongOrigin → .| <<< wrong!

In xml ConstraintLayout I'd use a layout_constraintWidth_percent=0.5 for the first text, which results in the following:

|Loooooooo... → destination |

Simply put, this is my request:

How can I achieve this in compose?


Note: I've tried using combinations of .weight(0.5F) and .weight(0.5F, fill = false) on either the first or both texts. These are some of the results I got with these solutions, which is not what want either, but I think I'm getting closer to the desired result with these modifiers:

|Origin → destination | <<< wrong!

|Or.. → looooooongDestination| <<< wrong!

My latest code is getting close to the desired solution:

Row {
    Text(
        modifier = Modifier.weight(0.5F, fill = false),
        text = "Origin",
        maxLines = 1,
        overflow = TextOverflow.Ellipsis,
    )
    Icon(
        modifier = Modifier.padding(horizontal = 4.dp),
        ...
    )
    Text(
        modifier = Modifier.weight(0.5F),
        text = "Destination",
        maxLines = 1,
        overflow = TextOverflow.Ellipsis,
    )
}

Although this seems to work as expected for the 1st text, it always truncates the 2nd text to 50% width:

|Origin → loooongDest... | <<< wrong!

Upvotes: 1

Views: 871

Answers (2)

Manishoaham
Manishoaham

Reputation: 812

You can similarly use fillMaxWidth(faction:Float) along with constraintAs {...} on a child in ConstraintLayout in Compose as below where faction:Float ranges from 0f to 1f:

                modifier = Modifier
                    .fillMaxWidth(.5f) // fraction = .5f for half width of screen
                    .constrainAs(soochak) {
                        start.linkTo(parent.start)
                        ...
                    }

Upvotes: 0

hoford
hoford

Reputation: 5323

If you know ConstraintLayout you can implement directly in ConstraintLayout for compose. Here is approximately what you described in ConstraintLayout Compose: It will clip the first text when it grows past 50% It will use the rest and clip if it fills the rest of the ConstraintLayout container.

It has 2 syntaxes. JSON is a little easier to read the constraints as it is separated. The DSL is a little more compact and more in the style of the rest of Compose.

Here are the examples in both formats:

JSON

@OptIn(ExperimentalMotionApi::class)
@Preview
@Composable
fun DemoCLText() {
    val c = ConstraintSet("""{ 
      midpoint: { type: 'vGuideline', percent: 0.50 },
      origin: {
        width:  { value: 'preferWrap', max: 'wrap' },
        start: ['parent', 'start', 0],
        end: ['midpoint', 'start', 0],
        centerVertically: 'parent',
        hBias:0
      },
      arrow: {
        width: 'wrap',
        centerVertically: 'parent',
        start: ['origin', 'end', 0],
      },
      destination: {
        width:  { value: 'preferWrap', max: 'wrap' },
        start: ['arrow', 'end', 0],
        end: ['parent', 'end', 2],
        hBias: 0,
        centerVertically: 'parent'
      },
    }
"""
    );
    ConstraintLayout(
        constraintSet = c,
        modifier = Modifier.width(200.dp).background(Color.LightGray)
    ) {
        Text(
            modifier = Modifier.layoutId("origin"),
            text = "Origin",
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
        Text(
            modifier = Modifier.layoutId("arrow"),
            text = "→",
        )
        Text(
            modifier = Modifier.layoutId("destination"),
            text = "Destination",
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
    }
}

DSL

@OptIn(ExperimentalMotionApi::class)
@Preview
@Composable
fun DemoCLText2() {

    ConstraintLayout(
        modifier = Modifier.width(140.dp).background(Color.LightGray)
    ) {
        val origin = createRef()
        val midpoint = createGuidelineFromAbsoluteRight(0.5f)
        Text(
            modifier = Modifier.constrainAs(origin) {
                centerVerticallyTo(parent)
                start.linkTo(parent.start)
                end.linkTo(midpoint)
                width = Dimension.preferredWrapContent.atMostWrapContent
                horizontalBias = 0f
            },
            text = "Origin",
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
        var arrow = createRef();
        Text(
            modifier = Modifier.constrainAs(arrow) {
                centerVerticallyTo(parent)
                start.linkTo(origin.end)
                width = Dimension.wrapContent
            },
            text = "→",
        )
        Text(
            modifier = Modifier.constrainAs(createRef()) {
                centerVerticallyTo(parent)
                start.linkTo(arrow.end)
                end.linkTo(parent.end)
                width = Dimension.preferredWrapContent.atMostWrapContent
                horizontalBias = 0f
            },
            text = "Destination",
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
    }
}

Upvotes: 0

Related Questions