Search code examples
androidkotlinandroid-jetpack-compose

How to change the orientation of a composable?


I'm working on a 3-column layout, and I want to rotate the content of one of the columns by 90 degrees:

I've tried swapping the width and height and adjusting the placement of the content, but without success.

Here’s the issue: when the box I want to rotate occupies 50% of the screen width, it works as expected. However, if the box is larger or smaller than 50%, I encounter a gap at the bottom.

Can anyone suggest a solution to ensure the rotated content fills the available space without causing a gap?

Here is my complete file:

@Composable
fun Layout() {
    Row(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.Red)
    ) {
        Row(
            modifier = Modifier
                .fillMaxSize()
        ) {
            Box(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxSize()
                    .rotate(90f)
                    .swapDimensions()
                    .background(Color.Blue)
                    .background(Color.LightGray)
            ) {

                Column(
                    modifier = Modifier
                ) {
                    Column(modifier = Modifier.background(Color.Magenta).fillMaxWidth()) {
                        Text(text = "All content in this box has to be rotated 90deg")
                    }
                    Column(modifier = Modifier.background(Color.LightGray)) {
                        Text(text = "Text 2")
                    }
                }
            }
            Column(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxSize()
            ) {
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxSize()
                        .background(Color.Green)
                ) {
                    Text(text = "This content is normal")
                }
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxSize()
                        .background(Color.Yellow)
                ) {
                    Text(text = "This content is normal")
                }
            }
            Column(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxSize()
            ) {
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxSize()
                        .background(Color.Magenta)
                ) {
                    Text(text = "This content is normal")
                }
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxSize()
                        .background(Color.Blue)
                ) {
                    Text(text = "This content is normal")
                }
            }
        }
    }
}
fun Modifier.swapDimensions() = layout { measurable, constraints ->
    val placeable = measurable.measure(constraints)

    val originalWidth = placeable.width
    val originalHeight = placeable.height

    val swappedWidth = originalHeight
    val swappedHeight = originalWidth

    layout(
        swappedWidth,
        swappedHeight
        ) {
        placeable.place(
            x = (swappedWidth - originalHeight) / 2,
            y = (swappedHeight - originalWidth) / 2
        )
    }
}
@Preview(
    name = "Landscape Preview",
    device = "spec:width=800dp, height=400dp",
    showBackground = true
)
@Composable
fun PreviewLayout() {
    Layout()
}

Solution

  • You should calculate your Placeable dimensions by measuring available height for width calculation and height for height while change layout which do not abide parent Constraints.

    Remove rotate and use only

    fun Modifier.swapDimensions() = layout { measurable, constraints ->
    
        val placeable = measurable.measure(
            Constraints.fixed(constraints.maxHeight, constraints.maxWidth)
        )
    
        val originalWidth = placeable.width
        val originalHeight = placeable.height
    
        layout(
            originalWidth,
            originalHeight
        ) {
            placeable.placeWithLayer(
                x = 0,
                y = 0,
                layerBlock = {
                    rotationZ = 90f
                }
            )
        }
    }
    

    enter image description here

    @Composable
    fun Layout() {
        Row(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.Red)
        ) {
    
            Row(
                modifier = Modifier
                    .fillMaxSize()
            ) {
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxSize()
                        .swapDimensions()
                        .border(4.dp, Color.Cyan)
                        .background(Color.Blue)
                        .background(Color.LightGray)
                ) {
    
                    Column(
                        modifier = Modifier
                    ) {
                        Column(
                            modifier = Modifier.border(2.dp, Color.Black).background(Color.Magenta)
                                .fillMaxWidth()
                        ) {
                            Text(text = "All content in this box has to be rotated 90deg")
                        }
                        Column(
                            modifier = Modifier.border(2.dp, Color.Red).background(Color.LightGray)
                        ) {
                            Text(text = "Text 2")
                        }
                    }
                }
    
                Column(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxSize()
                ) {
                    Box(
                        modifier = Modifier
                            .weight(1f)
                            .fillMaxSize()
                            .background(Color.Green)
                    ) {
                        Text(text = "This content is normal")
                    }
                    Box(
                        modifier = Modifier
                            .weight(1f)
                            .fillMaxSize()
                            .background(Color.Yellow)
                    ) {
                        Text(text = "This content is normal")
                    }
                }
                Column(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxSize()
                ) {
                    Box(
                        modifier = Modifier
                            .weight(1f)
                            .fillMaxSize()
                            .background(Color.Magenta)
                    ) {
                        Text(text = "This content is normal")
                    }
                    Box(
                        modifier = Modifier
                            .weight(1f)
                            .fillMaxSize()
                            .background(Color.Blue)
                    ) {
                        Text(text = "This content is normal")
                    }
                }
            }
        }
    }