Home > Blockchain >  Android Compose: Place an element in the center and other elements above it
Android Compose: Place an element in the center and other elements above it

Time:07-24

I need to insert a Text element Text("text center") in the center of the screen (both horizontally and vertically). Also, I want to insert the Text("text") element above Text("text center") with a blank space of 32dp: enter image description here

So for example the following code is not correct because the whole block is centered and not just "text center":

 Column(
    modifier = Modifier
        .fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.Center,
) {
    Text(text = "text")
    Spacer(modifier = Modifier.height(32.dp))
    Text(text = "text center")
}

CodePudding user response:

You can use the ConstraintLayout since this type of layout would be a bit cumbersome to do properly with just the composables from the standard library.

ConstraintLayout can help place composables relative to others on the screen, and is an alternative to using multiple nested Row, Column, Box and custom layout elements.

Add this to your app.gradle file

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"

and sync the project so that Gradle downloads the dependency. As of July 2022 the stable version is 1.0.1.

Your layout needs the following constraints:

  1. the "center text" needs to center horizontally and vertically to the parent
  2. the "top text" needs to center horizontally to the parent
  3. there should be a margin of 32 dp between the bottom of "top text" and the top of "center text"

In code these constraints are specified like this

ConstraintLayout(
    modifier = Modifier.fillMaxSize() // copied from your example  
) {
    // Create references for the composables to constrain
    val (topText, centerText) = createRefs()

    Text(
        text = "text",
        modifier = Modifier.constrainAs(topText) {
            // the "top text" needs to center horizontally to the parent
            centerHorizontallyTo(parent)
            // there should be a margin of 32 dp between the bottom
            // of "top text" and the top of "center text"
            bottom.linkTo(centerText.top, margin = 32.dp)
        }
    )

    Text(
        text = "text center",
        modifier = Modifier.constrainAs(centerText) {
            // the "center text" needs to center horizontally and
            // vertically to the parent
            centerHorizontallyTo(parent)
            centerVerticallyTo(parent)
        }
    )
}

Since the official documentation on the compose version of ConstraintLayout is really limited, you can check the compose samples that are using it if you need more examples.

The official documentation for the Widget (non-compose) version of ConstraintLayout is good though, so to learn about everything that it can do, you can check https://developer.android.com/reference/androidx/constraintlayout/widget/ConstraintLayout.

Maybe some of the functionality might not be available in the compose version yet. As of July 2022 the latest alpha version is 1.1.0-alpha03

implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha03"

CodePudding user response:

This can be done in many ways.

Option one using a custom Layout

@Composable
private fun TextLayout(
    modifier: Modifier,
    padding: Dp = 32.dp,
    content: @Composable () -> Unit
) {
    val density = LocalDensity.current
    val paddingInPx = with(density) { padding.roundToPx() }

    Layout(modifier = modifier, content = content) { measurables, constraints ->

        require(measurables.size == 2)
        val placeables: List<Placeable> = measurables.map { measurable: Measurable ->
            measurable.measure(constraints.copy(minWidth = 0, minHeight = 0))
        }


        val topPlaceable = placeables.first()
        val centerPlaceable = placeables.last()
        val height = centerPlaceable.height

        val totalHeight = constraints.maxHeight
        val verticalCenter = (totalHeight - height)/2

        layout(constraints.minWidth, totalHeight) {
            centerPlaceable.placeRelative(0, verticalCenter)
            topPlaceable.placeRelative(0, verticalCenter - height - paddingInPx)
        }
    }
}

Option 2 Using a Box and getting height of text using onTextLayout callback of Text.

Box(
    modifier = Modifier
        .fillMaxHeight()
        .border(3.dp, Color.Red),
    contentAlignment = Alignment.CenterStart
) {


    val density = LocalDensity.current
    var textHeight by remember { mutableStateOf(0.dp) }
    Text(
        "TextCenter",
        fontSize = 20.sp,
        onTextLayout = {
            textHeight = with(density) {
                it.size.height.toDp()
            }
        }
    )

    Text(
        text = "Text",
        modifier = Modifier.padding(bottom = 64.dp   textHeight * 2),
        color = Color.Blue,
        fontSize = 20.sp
    )
}

But this option, it's only for demonstration, will have another recomposition since we update var textHeight by remember { mutableStateOf(0.dp) }

Option 3 is using a ConstraintLayout. For a simple layout that like this i don't think anyone counter any performance issues using ConstraintLayout but i always refrain using ConstraintLayout because it uses MultiMeasureLayout that comes with a serious warning

@Deprecated(
    "This API is unsafe for UI performance at scale - using it incorrectly will lead "  
        "to exponential performance issues. This API should be avoided whenever possible."
)
fun MultiMeasureLayout(
    modifier: Modifier = Modifier,
    content: @Composable @UiComposable () -> Unit,
    measurePolicy: MeasurePolicy
) {

}
  • Related