I want to set androidx.compose.foundation.Canvas
size dynamically after measuring measurables and placing placeables with layout function but Canvas requires a Modifier with specific size how can achieve this with Layout and Canvas and in efficient way.
What i want to achieve is as in the image to set content and also add some space and draw an arrow in red area to create speech bubble as can be seen here.
First Container in image is the correct one with Modifier.layout and Modifier.drawbackground as
@Composable
private fun MyComposable(modifier: Modifier = Modifier) {
Row(
modifier = Modifier
) {
var totalSize = Size.Zero
val layoutModifier = modifier
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
totalSize = Size(placeable.width + 50f, placeable.height.toFloat())
println("MyComposable() LAYOUT placeable: $placeable")
layout(totalSize.width.roundToInt(), placeable.height) {
placeable.place(0, 0)
}
}
.drawBehind {
println("MyComposable() drawBehind size: $size")
drawRect(Color.Red, size = totalSize, style = Stroke(2f))
drawRect(Color.Blue, size = size, style = Stroke(2f))
}
Column(modifier = layoutModifier) {
Text("First Text")
Text("Second Text")
}
}
}
And logs
I: MyComposable() LAYOUT placeable width: 250, height: 124
I: MyComposable() drawBehind size: Size(250.0, 124.0)
With Layout and Canvas
@Composable
private fun CanvasLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
var widthInDp by remember { mutableStateOf(0.dp) }
var heightInDp by remember { mutableStateOf(0.dp) }
Layout(content = content, modifier = modifier) { measurables, constraints ->
val placeables = measurables.map { measurable ->
measurable.measure(constraints)
}
val maxWidth = placeables.maxOf { it.width } + 50
val height = placeables.sumOf { it.height }
widthInDp = (maxWidth / density).toDp()
heightInDp = (height / density).toDp()
println(" CanvasLayout() LAYOUT maxWidth: $maxWidth, height: $height placeables: ${placeables.size}")
var yPos = 0
layout(maxWidth, height) {
placeables.forEach { placeable: Placeable ->
placeable.placeRelative(0, yPos)
yPos += placeable.height
}
}
}
androidx.compose.foundation.Canvas(
modifier = Modifier.size(widthInDp, heightInDp),
onDraw = {
println(" CanvasLayout() CANVAS size: $size")
drawRect(Color.Blue, size = size, style = Stroke(2f))
}
)
}
As can be seen in image bottom rectangle is drawn below Text composables instead of around them and calls layout
twice
I: CanvasLayout() LAYOUT maxWidth: 300, height: 124 placeables: 2
I: CanvasLayout() CANVAS size: Size(0.0, 0.0)
I: CanvasLayout() LAYOUT maxWidth: 300, height: 124 placeables: 2
I: CanvasLayout() CANVAS size: Size(114.0, 47.0)
What's the correct and efficient way to use Layout
and Canvas
together?