netimen
netimen

Reputation: 4427

Dynamically adjust the weighting of columns in a row based on string length

I have a layout with two texts, one on left side and one on right side. If both texts are long, left one should occupy 60%, and right one 40% of the width. But if right text is shorter than 40%, the left one should take all the available space.

Here are the examples: long right part and: enter image description here So I would like to write something like this:

Row {
    Text(text = left, modifier = modifier.padding(8.dp).weight(<min 0.6f>))
    Text(text = right, modifier = modifier.padding(8.dp).weight(<max 0.4f>))
}

Is there any way to achieve this?

Upvotes: 4

Views: 1615

Answers (2)

netimen
netimen

Reputation: 4427

Finally, I figured it out. Here is the modifier:

fun Modifier.maxWidth(
    fraction: Float = 1f,
) = layout { measurable, constraints ->
    val maxWidth = (constraints.maxWidth * fraction).roundToInt()
    val width = measurable.maxIntrinsicWidth(constraints.maxHeight).coerceAtMost(maxWidth)

    val placeable = measurable.measure(Constraints(constraints.minWidth, width, constraints.minHeight, constraints.maxHeight))
    layout(width, placeable.height) {
        placeable.placeRelative(0, 0)
    }
}

So we can use it like this:

Row {
    Text(text = left, modifier = modifier.padding(8.dp).weight(1f)) // left text uses all the available space
    Text(text = right, modifier = modifier.padding(8.dp).maxWidth(fraction = 0.4f)) // right text can be 40% of the parent or less
}

Upvotes: 8

Johann
Johann

Reputation: 29885

Measure the length of each string and calculate their weights. Adjust the weights based on the maximum allowed for the right column:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        startActivity(intent)

        val paint = android.graphics.Paint()

        // First row
        val text1 = "This long text should take up maximum 60% of the space with wrapping"
        val text2 = "This is not very long"

        val width1 = paint.measureText(text1)
        val width2 = paint.measureText(text2)

        var w2 = width2 / (width1 + width2)

        if (w2 > 0.4f) {
            w2 = 0.4f
        }

        val w1 = 1f - w2

        // Second row
        val text3 = "This text should take up more than 60% of the space"
        val text4 = "Short"

        val width3 = paint.measureText(text3)
        val width4 = paint.measureText(text4)

        var w4 = width4 / (width3 + width4)

        if (w4 > 0.4f) {
            w4 = 0.4f
        }

        val w3 = 1f - w4

        setContent {
            Column(modifier = Modifier.fillMaxSize()) {
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .wrapContentHeight()
                        .padding(bottom = 20.dp)
                ) {
                    Text(text1, modifier = Modifier.weight(w1))
                    Text(text2, modifier = Modifier.weight(w2), color = Color.Red)
                }

                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .wrapContentHeight()
                        .padding(bottom = 20.dp)
                ) {
                    Text(text3, modifier = Modifier.weight(w3))
                    Text(text4, modifier = Modifier.weight(w4), color = Color.Red)
                }
            }
        }
    }
}

Upvotes: 0

Related Questions