subvertallchris
subvertallchris

Reputation: 5482

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?

Upvotes: 29

Views: 19654

Answers (6)

Alejandra
Alejandra

Reputation: 882

This happens due to uneven font padding on https://fonts.google.com/specimen/Oswald, plus the text you're using in lowercase makes the discrepancy more obvious.

As @Siyamed mentioned below, the API to turn the default includeFontPadding behaviour off in Compose was released with Compose 1.2 beta and you use it like so:

Text(
...
   style = TextStyle(
      platformStyle = PlatformTextStyle(
     includeFontPadding = false
   ),
)

https://android-developers.googleblog.com/2022/05/whats-new-in-jetpack-compose.html

give it a try, might help? Btw, if you notice PlatformTextStyle is "deprecated" this only wants to inform that this is a compatibility API. We removed the deprecation warning in more recent compose versions, from 1.5 and above.

Upvotes: 22

Dương Minh
Dương Minh

Reputation: 2421

By using Compose 1.2.0-alpha07 and above, you can use PlatformTextStyle api to set includeFontPadding.

Try to the below code:

private val NotoSans = FontFamily(
    Font(R.font.noto_san_jp_black, FontWeight.Black),
    Font(R.font.noto_san_jp_light, FontWeight.Light),
    Font(R.font.noto_san_jp_bold, FontWeight.Bold),
    Font(R.font.noto_san_jp_thin, FontWeight.Thin),
    Font(R.font.noto_san_jp_medium, FontWeight.Medium),
    Font(R.font.noto_san_jp_regular, FontWeight.Normal),
)

val Typography = Typography(
    headlineLarge = Typography().headlineLarge.copy(
        fontFamily = NotoSans,
    )
)

@OptIn(ExperimentalTextApi::class)
/* ... */

Text(
    text = "地域のお得は\nすべてここに",
    style = MaterialTheme.typography.headlineLarge.copy(
        platformStyle = PlatformTextStyle(
            includeFontPadding = false
        )
    /* ... */
    )
)

The result when includeFontPadding = false:

includeFontPadding = false

The result when includeFontPadding = true or no using it:

includeFontPadding = true or not using

More information:

Fixing Font Padding in Compose Text - Medium

Upvotes: 3

Siyamed
Siyamed

Reputation: 515

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.

Upvotes: 1

Agnieszka Janicka
Agnieszka Janicka

Reputation: 1

(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)
    }
}

Upvotes: -1

DTechnlogy
DTechnlogy

Reputation: 345

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
    }
}

Upvotes: 8

Richard Onslow Roper
Richard Onslow Roper

Reputation: 6863

Just got around this same issue.

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

Upvotes: -3

Related Questions