1

Consider the following Composable:

enter image description here

    setContent {
            Column(
                modifier = Modifier.fillMaxHeight(),
                verticalArrangement = Arrangement.Bottom,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(fontSize = 30.sp, text = "Hello")
                Box {
                    Canvas(
                        modifier = Modifier
                            .size(100.dp)
                    ) {
                        drawRect(Color.Red)
                    }
                }
            }
        }

If we add a rotation() modifier on the Canvas, the square is drawn on top of the other Text element.

        setContent {
            Column(
                modifier = Modifier.fillMaxHeight(),
                verticalArrangement = Arrangement.Bottom,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(fontSize = 30.sp, text = "Hello")
                Box {
                    Canvas(
                        modifier = Modifier
                            .size(100.dp)
                            .rotate(45f)
                    ) {
                        drawRect(Color.Red)
                    }
                }
            }
        }

enter image description here

I understand this is because the transformation append in the drawing phase, after everything has been sized.

If we want the column to adjust to this, we have to add a padding to the box:

        setContent {
            Column(
                modifier = Modifier.fillMaxHeight(),
                verticalArrangement = Arrangement.Bottom,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(fontSize = 30.sp, text = "Hello")
                Box(
                    modifier = Modifier
                        .padding(21.dp)

                ) {
                    Canvas(
                        modifier = Modifier
                            .size(100.dp)
                            .rotate(45f)
                    ) {
                        drawRect(Color.Red)
                    }
                }
            }
        }

enter image description here

My problem is that this padding have to be calculated by taking into account the transformed foot print.

Is there something like Modifier.wrapContentSize()(which is not working here) or another way of arranging things that could provide this result?

Nico
  • 2,570
  • 1
  • 9
  • 17

1 Answers1

3

Modifier.rotation() uses Modifier.graphicsLayer() which doesn't effect physical place relative to siblings. Order of graphicsLayer matter but not for measurement and placeable placement. You can consider this as zooming or moving image doesn't change its original bounds or push siblings in any directions.

What you can do is calculate size based on rotation as you said

Or write your own Layout with no fixed Size, this is important, calculate rotated height(hypot of two sides) + height of text then place Text Composable with placeable.placeRelative() while second one with

placeable.placeRelativeWithLayer{rotationz=your angle}

@Composable
private fun MyCanvasText(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(modifier = modifier, content = content) { measurables, constraints ->

        require(measurables.size == 2)

        val placeables: List<Placeable> = measurables.map { measurable: Measurable ->
            measurable.measure(constraints)
        }

        val text = placeables.first()
        val canvas = placeables.last()
        val rotatedSize = hypot(canvas.width.toDouble(), canvas.height.toDouble()).toInt()
        val maxWidth = text.width.coerceAtLeast(rotatedSize)
        val maxHeight = text.height + rotatedSize

        layout(maxWidth, maxHeight) {
            text.placeRelative((maxWidth - text.width) / 2, 0)
            canvas.placeRelativeWithLayer(
                x = (maxWidth - canvas.height) / 2,
                y = text.height + (rotatedSize-canvas.height)/2
            ) {
                rotationZ = 45f
            }
        }
    }
}

Usage

Column(modifier = Modifier.fillMaxSize()) {
    MyCanvasText(modifier = Modifier.border(3.dp, Color.Green)) {
        Text(fontSize = 30.sp, text = "Hello")
        Box {
            Canvas(
                modifier = Modifier.size(100.dp)
            ) {
                drawRect(Color.Red)
            }
        }
    }
}

enter image description here

Some insights and tips about Modifier.graphicLayer

I have another answer that explains rotation in detail here, you can check it out if you wish, another one i asked about rotation depending on order of Modifier.graphicsLayer. Also question and answer that visual change using graphicsLayer as you can see other siblings are not effected when i scale and translate Composable.

Thracian
  • 43,021
  • 16
  • 133
  • 222