'Jetpack Compose, centering text without font padding?

I'm struggling with vertically centering text in Jetpack Compose version alpha-11. It appears that my font has a significant amount of padding and I'm unable to find a way to disable it. This has come up only once before on SO, as far as I can tell, here, but their answer of using a constraint layout seems to suggest that they simply positioned it absolutely, which isn't exactly a solution as much as a workaround, and something I'd like to avoid.

You can see it clearly in the screenshot below.

enter image description here

The code for that looks like this:

                   Column(verticalArrangement = Arrangement.Center) {
                        Text(
                            text = "Let's Go",
                            color = Color.White,
                            fontSize = 120.sp,
                            fontFamily = oswaldLightFontFamily(),
                            textAlign = TextAlign.Center,
                            modifier = Modifier.background(Color.Blue)
                        )
                    }

The arguments you would expect to position it -- verticalArrangement and textAlign -- do not do anything here but I'm including them to demonstrate what I've tried.

My workaround so far has been to use Modifier.graphicsLayer(translationY = -25f) to shift it up but that seems like a terrible hack for something that should be so straightforward. It appears that in classic Android layouts, one could set android:includeFontPadding="false" and that would bypass this behavior but there doesn't seem to be a similar option in Jetpack Compose.

Anyone encounter this?



Solution 1:[1]

According to https://issuetracker.google.com/issues/171394808, It seems this is one of the limitations of the current JetPack Compose.

This is also deal breaker for my app because the font used rely heavily with the includeFontPadding. For current workaround, I create a CoreText that wraps TextView inside my compose.

Here's example of my wrapper, its not perfect but it does the job for my current use case:

@Composable
fun CoreText(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    overflow: TextOverflow = TextOverflow.Clip,
    maxLines: Int = Int.MAX_VALUE,
    style: TextStyle = Typography.body2,
    onClick: (() -> Unit)? = null,
) {
    AndroidView(
        modifier = modifier,
        factory = { context ->
            TextView(context)
        },
        update = {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                it.setTextAppearance(style.fontWeight.toStyle())
            } else {
                it.setTextAppearance(it.context, style.fontWeight.toStyle())
            }

            if (overflow == TextOverflow.Ellipsis) {
                it.ellipsize = TextUtils.TruncateAt.END
            }

            if (textDecoration != null) {
                it.paintFlags = when (textDecoration) {
                    TextDecoration.Underline -> {
                        Paint.UNDERLINE_TEXT_FLAG
                    }
                    TextDecoration.LineThrough -> {
                        Paint.STRIKE_THRU_TEXT_FLAG
                    }
                    else -> 0
                }
            }

            if (onClick != null) {
                it.setOnClickListener { onClick.invoke() }
            }

            if (color != Color.Unspecified || style.color != Color.Unspecified) {
                it.setTextColor(if (color == Color.Unspecified) style.color.toArgb() else color.toArgb())
            }

            it.textSize = style.fontSize.value
            it.text = text
            it.background = ColorDrawable(style.background.toArgb())
            it.maxLines = maxLines
            it.includeFontPadding = false
            it.textAlignment = textAlign?.toStyle() ?: style.textAlign.toStyle()
        }
    )
}

// Replace with your style
fun FontWeight?.toStyle(): Int {
    return when (this) {
        FontWeight.Bold -> R.style.TextStyle_Bold
        FontWeight.Normal -> R.style.TextStyle_Regular
        FontWeight.Medium, FontWeight.SemiBold -> R.style.TextStyle_Medium
        else -> -1
    }
}

fun TextAlign?.toStyle(): Int {
    return when (this) {
        TextAlign.Left -> TEXT_ALIGNMENT_TEXT_START
        TextAlign.Right -> TEXT_ALIGNMENT_TEXT_END
        TextAlign.Center -> TEXT_ALIGNMENT_CENTER
        TextAlign.Start -> TEXT_ALIGNMENT_TEXT_START
        TextAlign.End -> TEXT_ALIGNMENT_TEXT_END
        else -> -1
    }
}

Solution 2:[2]

Just got around this same issue.

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

Solution 3:[3]

(Temporary) custom solution:

fun Modifier.baselinePadding(
    firstBaselineToTop: Dp,
    lastBaselineToBottom: Dp
) = layout { measurable, constraints ->
    val placeable = measurable.measure(constraints)

    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    check(placeable[LastBaseline] != AlignmentLine.Unspecified)
    val lastBaseline = placeable[LastBaseline]

    val lastBaselineToBottomHeight = placeable.height - lastBaseline

    val lastBaselineToBottomDelta = lastBaselineToBottom.roundToPx() - lastBaselineToBottomHeight

    val totalHeight = placeable.height +
            (firstBaselineToTop.roundToPx() - firstBaseline)

    val placeableY = totalHeight - placeable.height
    layout(placeable.width, totalHeight + lastBaselineToBottomDelta) {
        placeable.placeRelative(0, placeableY)
    }
}

Solution 4:[4]

Compose now have TextStyle.platformStyle.incudeFontPadding that is set to true by default for version 1.2. you can set it to false in your TextStyle

Making the default false is something that Compose wants to do in v1.3 or later.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 DTechnlogy
Solution 2 Richard Onslow Roper
Solution 3 Agnieszka Janicka
Solution 4 Siyamed