Joe Dow
Joe Dow

Reputation: 1193

How to make text centered vertically in Android compose?

I am working on my first app fully designed by Compose.

I need to centralize vertically text with the Text() compose component. In traditional Android dev practice I did this over alignment property. Text() compose also has alignment property, but at this moment it has limited capacity (Alignment.Horizontal() is only allowed), despite on I noticed different alignment values within Text() when made a research in web. Similar case for Column() - it also has alignment property .wrapContentSize() and also has limitation on values that could be used here, despite on quick research in web shows it may receive CenterVertically too.

What is your way to achieve this visual effect? The full code piece is below

@ExperimentalUnitApi
@Composable
fun TripBookingContent(state: PassengerTripUiState.TripBookUiState) {
    Log.d(App.TAG, "[screen] TripBookingContent")
    val baselineGrid = dimensionResource(id = R.dimen.baseline_grid)
    val mainPadding = dimensionResource(id = R.dimen.main_margin_compact)
    var componentSpace = dimensionResource(id = R.dimen.component_space)
    Column(
        modifier = Modifier
            .wrapContentHeight()
            .fillMaxWidth()
            .padding(
                paddingValues = PaddingValues(
                    horizontal = mainPadding,
                    vertical = baselineGrid
                )
            )
    ) {
        TripViewItem(
            data = state.trip,
            {},
            modifier = Modifier.padding(vertical = baselineGrid)
        )
        Text(
            text = stringResource(id = R.string.booking_screen_driver),
            color = colorResource(id = R.color.white),
            style = TextStyle(textIndent = TextIndent(firstLine = TextUnit(16F, TextUnitType.Sp))),
            modifier = Modifier
                .height(componentSpace)
                .padding(start = baselineGrid)
                .fillMaxWidth()
                .background(color = colorResource(id = R.color.design_default_color_secondary_variant))
        )
        Log.d(App.TAG, "[state] state.driver - ${state}")
        Log.d(App.TAG, "[state] state.driver.toDriver() - ${state.driver}")
        DriverCardContent(data = state.driver)

        Text(
            text = stringResource(id = R.string.booking_screen_msg),
            color = colorResource(id = R.color.white),
            style = TextStyle(textIndent = TextIndent(firstLine = TextUnit(16F, TextUnitType.Sp))),
            textAlign = TextAlign.Center,
            modifier = Modifier
//                .height(componentSpace)
                .height(32.dp)
                .padding(start = baselineGrid)
                .fillMaxWidth()
                .background(color = colorResource(id = R.color.colorPrimaryDark))
        )
        /**
         * Disable book button at current (alfa version), for more details
         * */
        Button(
            onClick = { /* no op */ },
            modifier = Modifier
                .alpha(0F)
                .fillMaxWidth()
                .padding(baselineGrid)
                .height(dimensionResource(id = R.dimen.button_height)),
            colors = ButtonDefaults.buttonColors(
                backgroundColor = Color.Blue,
                contentColor = Color.White
            ),
        ) {
            Text(
                text = stringResource(id = R.string.booking_screen_confirm_button),
                modifier = Modifier.align(Alignment.CenterVertically),
                fontWeight = FontWeight.Bold
            )
        }
    }
}

UPDATE My final solution become this one below. I have to move padding and background to the Box() layer to achieve text in center both vertically and horizontally.

        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier
                .height(32.dp)
                .padding(start = baselineGrid)
                .fillMaxWidth()
                .background(color = colorResource(id = R.color.colorPrimaryDark))
        ) {
            Text(
                text = stringResource(id = R.string.booking_screen_msg),
                color = colorResource(id = R.color.white),
                style = TextStyle(textIndent = TextIndent(firstLine = TextUnit(16F, TextUnitType.Sp))),
                textAlign = TextAlign.Center,
/*                modifier = Modifier
//                .height(componentSpace)
                    .height(32.dp)
                    .padding(start = baselineGrid)
                    .fillMaxWidth()
                    .background(color = colorResource(id = R.color.colorPrimaryDark))*/
            )
        } 

Upvotes: 63

Views: 50430

Answers (6)

Manoj Behera
Manoj Behera

Reputation: 2835

Try this

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Surface(modifier.fillMaxSize(), color = Color.LightGray) {
        Text(
            text = "Hello $name!",
            modifier = modifier.wrapContentHeight(align = Alignment.CenterVertically),
            textAlign = TextAlign.Center,
            fontSize = 40.sp
        )
    }
}

Upvotes: 2

JoeHz
JoeHz

Reputation: 2196

I acknowledge I am suggesting is an utterly hideous solution from the perspective of following best practices, but in this situation, I felt certain I was tempting fate when I tried "correct" approach mentioned in other answers, ultimately giving up when I had to tweak dp numbers to fix alignment issues. This struck me as the "future-volatile" (the opposite of future proof). I didn't trust JetpackCompose to not change how it was going to handle this problem, and I didn't exactly trust myself (A Jetpack newbie at the time) to anticipate every possible way a user could break it.

That, and sooner or later there was going to be a "right and proper" way to handle this that was going to be complete different.

So I went with the string manipulation approach, creating a function that generated a newline char after each one in the string that is to be displayed vertically. It can even be made sensitive to languages and screen-reading utilities for the visually impaired where this shouldn't happen. The i18n and disability aspects of that seemed far more straightforward than trying to second guess this.

Upvotes: 0

Inderjeet Singh Sra
Inderjeet Singh Sra

Reputation: 1001

It is possible to center text vertically IF your Text widget has a height modifier for example - Text(text="example", modifier=Modifier.height(100.dp) You can use the wrapContentHeight modifier to align text vertically for example

Text(
    text = "Example",
    modifier = Modifier.width(100.dp)
        .height(100.dp)
        .background(color = Color.Cyan)
        .wrapContentHeight(align = Alignment.CenterVertically),
    color = Color.Black,
    fontSize = 42.sp,
    textAlign = TextAlign.Right
)

Upvotes: 100

Oskar Gustavsson
Oskar Gustavsson

Reputation: 15

For centering the text in a Text you can also use TextStyle, example:

val body = TextStyle(
        fontSize = 16.sp,
        lineHeight = 22.sp,
        lineHeightStyle = LineHeightStyle(
            alignment = LineHeightStyle.Alignment.Proportional,
            trim = LineHeightStyle.Trim.None
        )
    )

LineHeightStyle you can set its alignment and trim.

TextAlign doesnt work with aligning Vertically, however in here you can.

Upvotes: 1

Thracian
Thracian

Reputation: 67268

This can be done in several ways. As mentioned in other answer you can use Modifier.wrapContentSize(), Modifier.layout which is very similar to how wrapContentSize modifier implemented , it's how size Modifers are implemented, they basically change, update or limit Constraints and measure and layout their contents.

Modifier.wrapContentSize changes minimum width and height to 0, and because of that you would be able measure content in that range instead of size Modifier or Constraints that return fixed min,max width range. such as min=200.dp, max=200.dp. If you change min range to 0 your content gets measured with it's actual content dimensions and it gets placed in any aligment.

@Preview
@Composable
private fun Test() {
    Text(
        text = "Hello world",
        modifier = Modifier
            .border(2.dp, Color.Red)
            .size(200.dp)
            .layout { measurable, constraints ->
                val placeable =
                    measurable.measure(
                        // This is how wrapContent works
                        constraints.copy(minWidth = 0, minHeight = 0)
                    )
                layout(constraints.maxWidth, constraints.maxHeight) {
                 // This is how wrapContent alignment works
                    val x = (constraints.maxWidth - placeable.width) / 2
                    val y = (constraints.maxHeight - placeable.height) / 2
                    placeable.placeRelative(x, y)
                }
            },
        textAlign = TextAlign.Center
    )
} 

or use Box() with contentAlignment = Alignment.Center. There are also CenterStart and CenterEnd options for alignment either.

   Box(
        contentAlignment = Alignment.Center,
    ) {
        Text(
            text = "Text",
            textAlign = TextAlign.Center
        )
    }

Upvotes: 48

Róbert Nagy
Róbert Nagy

Reputation: 7690

Alignment modifiers depend on the parent, which is how it supposed to be, in a vertical list (column) vertical alignment is handled by the parent (which is great that compose can enforce this)

Soo in your case you probably need a different hierarchy. For ex: Box instead of Column. Alternatively you can play around with the size of the Text, make it 200dp in height for example and use textAlign, to center it's text.

For reference:

  • Box(contentAlignment = Alignment.Center) -> Specifies alignment of children
  • Text(text = "example", modifier = Modifier.wrapContentSize().align()) -> Specifies alignment of Text composable under parent
  • Text(text = "example", textAlign = TextAlign.Center) -> specifies alignment of text, inside composable, using this with wrapContentSize() won't have any effect, since the Composable has the same size as it's content text

Upvotes: 11

Related Questions