Jetpack ComposeのTextの各バージョンによる動作の違いまとめ

Jetpack ComposeのTextですがComposeバージョンによってincludeFontPaddingのデフォルト値が異なる、またはTargetSdkVersionによってfallbackLineSpacingの挙動が違うなど、ハマりポイントが多いので注意が必要です。今回は各バージョンによってどのような違いがでるのか調べたのでまとめていきます。

Table of Contents

ComposeバージョンによってincludeFontPaddingのデフォルト値が違う

このincludeFontPaddingですがJetpack Composeのバージョンによってデフォルト値が異なるようなので注意が必要そうです。

includeFontPaddingとは?

includeFontPaddingはフォント指標に基づいてテキストの最初の行の上と最後の行の下のパディングを追加するプロパティです。Jetpack ComposeではTextStyleのPlatformTextStyleからincludeFontPaddingを変更することができます。

@Composable
fun Demo() {
    Row 
        Text(
            text = "FALSE",
            style = TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = false)),
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f))
        )
        Text(
            text = "TRUE",
            style = TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = true)),
            modifier = Modifier.background(Color.Blue.copy(alpha = 0.5f))
        )
    }
}

v1.5.0

v1.5.0ではincludeFontPaddingのデフォルト値がtrueになっており、TextのStyleを変更せずに利用すると、最初の行の上と最後の行の下にパディングが追加されます。

@Composable
fun Demo() {
    Row {
        Text(
            text = "FALSE",
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f))
        )
        Text(
            text = "TRUE",
            modifier = Modifier.background(Color.Blue.copy(alpha = 0.5f))
        )
    }
}

v1.6.0-alpha01以降

v1.6.0-alpha01以降ではincludeFontPaddingのデフォルト値がfalseになっており、TextのStyleを変更せずに利用すると、最初の行の上と最後の行の下にパディングが削除されます。

@Composable
fun Demo() {
    Row {
        Text(
            text = "FALSE",
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f))
        )
        Text(
            text = "TRUE",
            modifier = Modifier.background(Color.Blue.copy(alpha = 0.5f))
        )
    }
}

詳しい経緯は記事で解説してくれている

このようにincludeFontPaddingのデフォルト値は変遷を遂げているらしいのですが、なぜincludeFontPaddingのデフォルト値が変わっていっているのかは、以下の記事を見ると解説してくれています。

https://medium.com/androiddevelopers/fixing-font-padding-in-compose-text-768cd232425b

targetSdkVersionによってfallbackLineSpacingの動作が異なる

Jetpack ComposeのTextですがtargetSdkVersionによっては、確保されるテキストの高さが揃わなくなることがあります。これはfallbackLineSpacingの動作による違いによるものらしいです。

※ こちら最初はincludeFontPaddingの不具合と紹介させていただいたのですが、X上でご指摘していただき間違いであることがわかりましたので、この章の部分全て書き直させていただきました。

targetSdkVersion<=32だと英語と日本語の高さが揃う

targetSdkVersion<=32で以下のように英語と日本語を表示すると、それぞれの高さが揃う動作になります。

@Preview(apiLevel = 32, showBackground = true)
@Composable
fun Demo() {
    Row {
        Text(
            text = "日本語",
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f))
        )
        Text(
            text = "English",
            modifier = Modifier.background(Color.Blue.copy(alpha = 0.5f))
        )
    }
}

targetSdkVersion>=33だと英語と日本語の高さが揃わない

targetSdkVersion>=33で以下のように英語と日本語を表示すると、それぞれの高さが揃わない動作になります。

@Preview(apiLevel = 33, showBackground = true)
@Composable
fun Demo() {
    Row {
        Text(
            text = "日本語",
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f))
        )
        Text(
            text = "English",
            modifier = Modifier.background(Color.Blue.copy(alpha = 0.5f))
        )
    }
}

targetSdkVersion>=33でfallbackLineSpacingの処理が影響している

このようにtargetSdkVersion>=33では日本語と英語で表示した際のそれぞれで高さが異なるのは、targetSdkVersion>=33でfallbackLineSpacingの処理が下記のように変更されたかららしいです。

targetSdkVersion<=32でのfallbackLineSpacingの動作

  • TextのフォントをRoboto(欧文フォント)にして、日本語を表示しようとしたとき、代替のNotoCJK(日本語フォント)で日本語を表示するが、その場合にはNotoCJKではなく無理やりRobotoの高さに合わせる

targetSdkVersion>=33でのfallbackLineSpacingの動作

  • TextのフォントをRoboto(欧文フォント)にして、日本語を表示しようとしたとき、代替のNotoCJK(日本語フォント)で日本語を表示するが、各フォントを尊重してそれぞれのフォントの高さで表示する

targetSdkVersion>=33で表示を揃える解決策

上記の通りtargetSdkVersion>=33ではTextのフォントや代替フォントの高さが尊重されるようになるため、表示されるフォントによって確保される高さの領域が異なります。ですが基本的にはalignByBaselineでベースラインを揃えるか、一つの文字列で表示するなどの対応をすれば、問題は回避できるそうです。

alignByBaselineでベースラインを揃える

@Preview(apiLevel = 33, showBackground = true)
@Composable
fun Demo() {
    Row {
        Text(
            text = "日本語",
            style = TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = true)),
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f)).alignByBaseline()
        )
        Text(
            text = "English",
            style = TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = true)),
            modifier = Modifier.background(Color.Blue.copy(alpha = 0.5f)).alignByBaseline()
        )
    }
}

1つの文字列として表示して高さを揃える

@Preview(apiLevel = 33, showBackground = true)
@Composable
fun Demo() {
    Row {
        Text(
            text = "日本語English",
            style = TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = true)),
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f)).alignByBaseline()
        )
    }
}

またこの問題は指定しているフォントではなく、代替フォントで表示することが問題っぽいので、英語と日本語の表示に対応しているフォントを利用するの対策としてはありそうです。

英語と日本語が含まれているフォントを使う

@Preview(apiLevel = 33, showBackground = true)
@Composable
fun Demo() {
    val notoSansFamily = FontFamily(
        Font(R.font.notosansjp_medium, FontWeight.Medium)
    )

    Row {
        Text(
            text = "日本語",
            style = TextStyle(fontFamily = notoSansFamily),
            modifier = Modifier.background(Color.Red.copy(alpha = 0.5f))
        )
        Text(
            text = "English",
            style = TextStyle(fontFamily = notoSansFamily),
            modifier = Modifier.background(Color.Blue.copy(alpha = 0.5f))
        )
    }
}

※ targetSdkVersion<=32までのfallbackLineSpacingの仕組みを利用して、Textの高さ領域を節約している場合にはレイアウトが崩れそうなので、その場合には別途レイアウト調整は必要かもしれません