'Dynamically shorten text to avoid ellipsizes in jetpack compose

I'm looking to implement a calendar-like application including a day-detail screen in Jetpack Compose. This screen has a TopAppBar, in which i'd like to show the date like Friday, March 12th 2022

However, some screensizes i'm targeting can't display that completely, so i would like to gradually shorten the text if necessary:

Friday, March 12th 2022
Fr. March 12th 2022
Fr. March 12th '22
Fr. March 12th
March 12th

How can i achieve this? I already know about Text's onTextLayout: (TextLayoutResult) → Unit, however i don't seem able to change the text from there?

Please note:

  • For now, the app is german only, and i tried to "translate" the date formats for this question, so please excuse if i used uncommon ones.
  • There are a few similar questions using traditional xml-views, but i'm looking for a compose-solution


Solution 1:[1]

When you need to display different content depending other content size, you can use SubcomposeLayout: it allows measuring content without waiting view to be displayed and drawn.

Here's a basic example of how it can solve your problem:

@Composable
fun Component(modifier: Modifier) {
    val items = listOf(
        "Friday, March 12th 2022",
        "Fr. March 12th 2022",
        "Fr. March 12th '22",
        "Fr. March 12th",
        "March 12th",
    )
    SubcomposeLayout(modifier) { constraints ->
        val noWidthLimitConstraints = constraints.copy(maxWidth = Int.MAX_VALUE)
        var i = 0
        var placeable: Placeable
        do {
            placeable = subcompose(i) {
                Text(items[i])
            }[0].measure(
                if (i < items.indices.last) {
                    noWidthLimitConstraints
                } else {
                    constraints
                }
            )
        } while (placeable.width > constraints.maxWidth && items.indices.contains(++i))
        layout(placeable.width, placeable.height) {
            placeable.place(0, 0)
        }
    }
}

Code to test:

var width by remember { mutableStateOf(1f) }
Component(Modifier.padding(100.dp).fillMaxWidth(width))
Slider(width, { width = it })

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pylyp Dukhov