Home > Software engineering >  What is the simplest way to align center on first component of two ordered component in jetpack comp
What is the simplest way to align center on first component of two ordered component in jetpack comp

Time:04-29

I want to align the red surface to the center of the page, how can i do that?

@Composable
fun Screen() {

    Row(
        modifier = Modifier.fillMaxSize(),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Surface(
            color = Color.Red,
            modifier = Modifier.size(100.dp, 50.dp)
        ){}
        Surface(
            color = Color.Blue,
            modifier = Modifier.size(100.dp, 50.dp)
        ) {}
    }
}

View:

enter image description here

Want this:

enter image description here

CodePudding user response:

If your blue view has a static size, just add Spacer of the same size on the other side.

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    Spacer(Modifier.width(100.dp))
    Surface(
        color = Color.Red,
        modifier = Modifier.size(100.dp, 50.dp)
    ){}
    Surface(
        color = Color.Blue,
        modifier = Modifier.size(100.dp, 50.dp)
    ) {}
}

If it is dynamically sized, you can use the blue view instead of Spacer and apply Modifier.alpha(0) - this will be less performant than creating a custom layout, but should be perfectly fine, unless your view has a really huge layout to measure.

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    Surface(
        color = Color.Blue,
        modifier = Modifier.size(100.dp, 50.dp).alpha(0f)
    ) {}
    Surface(
        color = Color.Red,
        modifier = Modifier.size(100.dp, 50.dp)
    ){}
    Surface(
        color = Color.Blue,
        modifier = Modifier.size(100.dp, 50.dp)
    ) {}
}

CodePudding user response:

You could just use a Layout if things need to be precisely positioned.

Layout(
 content = { Red(); Blue() } // Add Composables here
) { measurables, constraints ->
 layout(width = constraints.maxWidth, height = constraints.maxHeight) {
  measurables[0].measure(constraints).apply { //Red
   place(
    (constraints.maxWidth - width) / 2,
    (constraints.maxHeight - height) / 2  
   )
  }
  measurables[1].measure(constraints).apply {
   place(
    (constraints.maxWidth   width) / 2,
    (constraints.maxHeight - height) / 2
   )
  }
 }
}

That should just about do it, y'know provided that both Red AND Blue are of the same width. If not, you'll have to store the measurables' measure results in a variable instead of just using apply. But that is no hard job -- It's actually super easy, barely an inconvenience.

EXPLANATION:

We call a simple Layout Composable and pass our desired Composables to its content block, since those are what we have to position. Then, we are given access to those same composables in the form of measurables, which we can use to measure and place wherever we want. We also have a constraints parameter which are just like the dimensions of the screen in this case. We return a layout block, which is essentially a container for all the desired Composables, and we specify a width and a height, in this case max values for both of these dimensions, which corresponds to full screen here.

measurables is an array containing all the Composables in order they were passed to the content block. Hence, measurables[0] is the Red block and blue is on index 1. Now, we position the red block at precisely the center of the screen. Try tweaking the x and y values for a bit to see how the UI reacts. Afterwards we place Blue at an x equal to the x of Red Width of Red (or blue, since they're the same here). We do this since we wish for blue to be placed exactly after Red. x is the distance from the edge of screen to the start (or left edge) of the Composable while y is from its top.

CodePudding user response:

You can use the Layout composable.
Something like:

Layout( content = {

    Surface(
        color = Color.Red,
        modifier = Modifier
            .size(100.dp, 50.dp)
            .layoutId("red")
    ) {}
    Surface(
        color = Color.Blue,
        modifier = Modifier
            .size(100.dp, 50.dp)
            .layoutId("blue")
    ) {}

}){ measurables, incomingConstraints ->

    val constraints = incomingConstraints.copy(minWidth = 0, minHeight = 0)
    val redPlaceable =
        measurables.find { it.layoutId == "red" }?.measure(constraints)
    val bluePlaceable =
        measurables.find { it.layoutId == "blue" }?.measure(constraints)

    //align red and blue
    layout(
        width = constraints.maxWidth, constraints.maxHeight)
        {
            val redPositionx = (constraints.maxWidth - widthOrZero(redPlaceable)) /2
            val redPositiony = (constraints.maxHeight - heightOrZero(redPlaceable)) /2
            redPlaceable?.placeRelative(redPositionx, redPositiony)
            bluePlaceable?.placeRelative(redPositionx widthOrZero(redPlaceable), redPositiony)

    }
}

with:

internal fun widthOrZero(placeable: Placeable?) = placeable?.width ?: 0
internal fun heightOrZero(placeable: Placeable?) = placeable?.height ?: 0

enter image description here

  • Related