https://kotlinlang.org logo
n

Nathan Castlehow

08/17/2021, 3:16 AM
Hi All, Wondering if anyone has thoughts on the best way to reserve space based on textSize (width wise)? Current use case is I have an area I which could have 0 to N characters in it (in reality itll be just 1,2, or 3 characters). I want the space to always be wide enough for the N characters based on the users current fontscale settings? My understanding is that it really depends on the font as to how much width each character will take?
I guess if its even possible itll only be possible on monospaced fonts. Such a rabbit hole 🤦. It’ll be 3 numbers that may eventually occupy the space so could work from there I guess?
c

Colton Idle

08/17/2021, 3:23 AM
Hacky I suppose but Box{ Text("MMM", color = Color.Transparent) Text(actualText) } The box will always measure to be at least those 3 chars long, but be transparent. Leaving your real text on top.
n

Nathan Castlehow

08/17/2021, 3:23 AM
Yeah, I normally go with a variation of that. Not great for accessibility though I guess but it gets the job done.
c

Colton Idle

08/17/2021, 3:39 AM
Yeah, someone else could definitely chime in, but I'm not sure how else you get around measuring something to be 3 chars long if you don't have 3 chars. I suppose you could abstract all of it in a layout, but I've added my 2 cents. Someone else with a better idea could probably chime in. Good luck!
❤️ 1
h

Halil Ozercan

08/17/2021, 5:55 AM
Recently there was a discussion on how to understand if text is longer than 2 lines before layout phase is completed. You can always use
TextLayoutResult
but it makes you lose a frame. I wanted to check whether it's possible with
SubcomposeLayout
. I'm not able to test it out right now but the idea is basically;
Copy code
SubcomposeLayout { constraints ->
  val measurable = subcompose("3chars") {
    Text("WWW", ...)
  }
  // get the supposed width from this subcomposition but don't place it in the final layout
  val actualPlaceable = subcompose("") {
    Text("..,", )
  }.single().measure(constraints.constrain(...))
  layout(a, b) {
    actualPlaceable.placeRelative...
  }
}
K 2
n

Nathan Castlehow

08/18/2021, 3:54 AM
@Halil Ozercan were you thinking something like this? (Not super familiar with custom layouts in compose) This works fairly well (currently setup to right align text). It reserves too much space for me but I assume its because its not using a monospaced font.
Copy code
@Composable
fun TextWithReservedSpace(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    style: TextStyle = LocalTextStyle.current,
    reservedCharLength: Int,
) {
    val reservedWidthString = remember(reservedCharLength) {
        "W".repeat(reservedCharLength)
    }


    SubcomposeLayout { constraints ->

        // get the supposed width from this subcomposition but don't place it in the final layout
        val measurable = subcompose("reserved_space") {
            Text(
                reservedWidthString,
                style = style,
                modifier = modifier,
            )
        }.single().measure(constraints)

        val actualPlaceable = subcompose("") {
            Text(
                text,
                color = color,
                style = style,
                modifier = modifier
            )
        }.single().measure(constraints)


        layout(measurable.width, measurable.height) {
            actualPlaceable.placeRelative(measurable.width - actualPlaceable.width, 0)
        }
    }
}
h

Halil Ozercan

08/18/2021, 6:30 AM
@Nathan Castlehow exactly! W is most of the time the widest character in a font, it's normal that it takes too much space.
👍 1
n

Nathan Castlehow

08/18/2021, 7:08 AM
Thanks for your help! 🙂