Asking for intrinsics measurements doesn't measure the children twice.
Children are queried for their intrinsic measurements before they're
measured and then, based on that information the parent calculates the
constraints to measure its children with.
https://developer.android.com/jetpack/compose/layouts/intrinsic-measurements#intrinsics-in-action
In the case of intrinsics, they are more of a tentative calculation in
order to perform real measuring using the obtained values. Imagine a
row with 3 children. In order to make its height match the height of
the tallest child, it would need to get the intrinsic measures of all
its children, and finally measure itself using the maximum one.
https://newsletter.jorgecastillo.dev/p/introducing-lookaheadlayout
Modifier.onSizeChanged
or Modifier.globallyPositioned
to get size to effect another Composable has risk of infinite recompositions. If you have Text you can use TextMeasured, as in this question if you know which one of the siblings need to be use as reference you can use Layout if you don't know which one should be bigger or lower you should use SubcomposeLayout
.
https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/package-summary#(androidx.compose.ui.Modifier).onSizeChanged(kotlin.Function1)
How to get exact size without recomposition?
For the scope of this question i will post first 2.
With TextMeasurer
val textMeasurer = rememberTextMeasurer()
val text = "Yum Yum"
val textLayoutResult = remember {
textMeasurer.measure(AnnotatedString(text))
}
val density = LocalDensity.current
val height = with(density) {
textLayoutResult.size.height.toDp()
}
Row(
modifier = Modifier
.border(1.dp, Color.Red)
.height(height)
.width(200.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painterResource(id = R.drawable.placeholder),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.border(2.dp, Color.Black)
.padding(end = 8.dp)
)
Text(
modifier = Modifier,
text = text,
color = MaterialTheme.colors.onBackground,
)
}
With Layout that measures Text first and then measures Image with Text's height
@Composable
private fun ImageTextLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content
) { measurables: List<Measurable>,
constraints: Constraints ->
require(measurables.size == 2)
val textPlaceable = measurables.last().measure(constraints = constraints)
val imagePlaceable = measurables.first().measure(
constraints.copy(
minWidth = 0,
minHeight = textPlaceable.height,
maxHeight = textPlaceable.height
)
)
val hasFixedWidth = constraints.hasFixedWidth
val width = if (hasFixedWidth) constraints.maxWidth
else imagePlaceable.width + textPlaceable.width
val height = imagePlaceable.height.coerceAtMost(textPlaceable.height)
layout(width, height) {
imagePlaceable.placeRelative(0, 0)
textPlaceable.placeRelative(imagePlaceable.width, 0)
}
}
}
Usage
ImageTextLayout(
modifier = Modifier
.border(1.dp, Color.Green)
.width(200.dp),
) {
Image(
painterResource(id = R.drawable.placeholder),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.border(2.dp, Color.Black)
.padding(end = 8.dp)
)
Text(
modifier = Modifier,
text = text,
color = MaterialTheme.colors.onBackground,
)
}
Full Demo
@OptIn(ExperimentalTextApi::class)
@Preview
@Composable
fun Test(
) {
Column {
Spacer(modifier = Modifier.height(40.dp))
val textMeasurer = rememberTextMeasurer()
val text = "Yum Yum"
val textLayoutResult = remember {
textMeasurer.measure(AnnotatedString(text))
}
val density = LocalDensity.current
val height = with(density) {
textLayoutResult.size.height.toDp()
}
Row(
modifier = Modifier
.border(1.dp, Color.Red)
.height(height)
.width(200.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painterResource(id = R.drawable.placeholder),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.border(2.dp, Color.Black)
.padding(end = 8.dp)
)
Text(
modifier = Modifier,
text = text,
color = MaterialTheme.colors.onBackground,
)
}
ImageTextLayout(
modifier = Modifier
.border(1.dp, Color.Green)
.width(200.dp),
) {
Image(
painterResource(id = R.drawable.placeholder),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.border(2.dp, Color.Black)
.padding(end = 8.dp)
)
Text(
modifier = Modifier,
text = text,
color = MaterialTheme.colors.onBackground,
)
}
}
}