24

I am working on a Jetpack Compose based app which shows a simple list with 3 columns.

Screenshot which shows the 3 columns

Things are working in principle. What I am struggling with is how to automatically determine the size of the columns.

For example, the width requirement of the date column will differ significantly depending on the user's locale settings and font size.

24.12.2021 - 20:00 requires a lot less screen space than

12/14/2021 - 08:00 PM.

What I was hoping I can do is to work with a sample date, measure it up based on current locale settings and font size and then set the width for all list entries accordingly.

Something similar to this:

val d = Date(2021, 12, 30, 23, 59, 59) // Sample date
val t = dateFormat.format(d) + " - " + timeFormat.format(d) // Build the output string
val dateColumnWidth = measureTextWidth(t, fontSize) // This is what I need

…
LazyColumn {
…
Row {
    Text(text = t, Modifier.width(dateColumnWidth.dp), fontSize = fontSize.sp))
    Text(text = value)
    Text(text = comment)
    }
}   
…

I have been on this for weeks but a function like my "measureTextWidth" above doesn't seem to exist.

Is there any way to achieve this?

2
  • Can someone answer my related question at stackoverflow.com/questions/72509738/… ? Commented Jun 5, 2022 at 17:45
  • For my use-case, IntrinsicSize.Min was helpful, which can be used with a modifier for example as Modifier.width(IntrinsicSize.Min). In my case I had multiple elements in a column and I wanted to find the minimum size such that for example texts are displayed correctly. Commented Nov 18 at 22:41

2 Answers 2

43

You can use SubcomposeLayout like this:

@Composable
fun MeasureUnconstrainedViewWidth(
    viewToMeasure: @Composable () -> Unit,
    content: @Composable (measuredWidth: Dp) -> Unit,
) {
    SubcomposeLayout { constraints ->
        val measuredWidth = subcompose("viewToMeasure", viewToMeasure)[0]
            .measure(Constraints()).width.toDp()

        val contentPlaceable = subcompose("content") {
            content(measuredWidth)
        }[0].measure(constraints)
        layout(contentPlaceable.width, contentPlaceable.height) {
            contentPlaceable.place(0, 0)
        }
    }
}

Then use it in your view:

MeasureUnconstrainedViewWidth(
    viewToMeasure = {
        Text("your sample text")
    }
) { measuredWidth ->
    // use measuredWidth to create your view
}
Sign up to request clarification or add additional context in comments.

2 Comments

if somebody want to use this function to get height, it will not work properly if your text is multiline, it looks like it have infinit width if not specified. You should restrict max width of viewToMeasure composable like Modifier.widthIn(max = maxWidth.dp)
@DaniilPozdnyakov in this case you shouldn't override constraints during measurement - I'm doing it with Constraints(), you can use constraints.copy(maxHeight = Constraints.Infinity) instead
24

If all you want is a measureTextWidth function that measures the width of a given text according to a given TextStyle, here's the function:

@Composable
fun measureTextWidth(text: String, style: TextStyle): Dp {
    val textMeasurer = rememberTextMeasurer()
    val widthInPixels = textMeasurer.measure(text, style).size.width
    return with(LocalDensity.current) { widthInPixels.toDp() }
}

This function is inspired by Xam's answer here in a different thread.

Here is an example of how you would use this function:

val textStyle = TextStyle(fontSize = 16.sp)
val text = "12/14/2021 - 08:00 PM"
val dateColumnWidth = measureTextWidth(text, textStyle)

Row {
    Text(text = dateTime, Modifier.width(dateColumnWidth), style = textStyle)
    Text(text = value, style = textStyle)
    Text(text = comment, style = textStyle)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.