フォントを操作する

このページでは、Compose アプリでフォントを設定する方法について説明します。

フォントを設定

Text には、コンポーザブルで使用されるフォントを設定するための fontFamily パラメータを指定できます。デフォルトでは、Serif、Sans Serif、Monospace、Cursive のフォント ファミリーが含まれています

@Composable
fun DifferentFonts() {
    Column {
        Text("Hello World", fontFamily = FontFamily.Serif)
        Text("Hello World", fontFamily = FontFamily.SansSerif)
    }
}

キーワード

fontFamily 属性を使用すると、res/font フォルダで定義されているカスタム フォントと書体を操作できます。

開発環境の res > font フォルダの図

次の例は、これらのフォント ファイルに基づいて fontFamily を定義し、Font 関数を使用する方法を示しています。

val firaSansFamily = FontFamily(
    Font(R.font.firasans_light, FontWeight.Light),
    Font(R.font.firasans_regular, FontWeight.Normal),
    Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
    Font(R.font.firasans_medium, FontWeight.Medium),
    Font(R.font.firasans_bold, FontWeight.Bold)
)

この fontFamilyText コンポーザブルに渡すことができます。fontFamily にはさまざまな太さを設定できるため、fontWeight を手動で設定してテキストの適切な太さを選択できます。

Column {
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Light)
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
    Text(
        text = "text",
        fontFamily = firaSansFamily,
        fontWeight = FontWeight.Normal,
        fontStyle = FontStyle.Italic
    )
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Medium)
    Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}

キーワード

アプリ全体でタイポグラフィを設定する方法については、Compose のカスタム デザイン システムをご覧ください。

ダウンロード可能なフォント

Compose 1.2.0 以降では、Compose アプリでダウンロード可能なフォントの API を使用して、Google フォントを非同期でダウンロードして、アプリで使用できます。

現在、カスタム プロバイダが提供するダウンロード可能フォントはサポートされていません。

プログラムでダウンロード可能なフォントを使用する

アプリ内からプログラムでフォントをダウンロードする手順は次のとおりです。

  1. 依存関係を追加します。

    Groovy

    dependencies {
        ...
        implementation "androidx.compose.ui:ui-text-google-fonts:1.6.8"
    }
    

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.6.8")
    }
  2. Google Fonts の認証情報で GoogleFont.Provider を初期化します。
    val provider = GoogleFont.Provider(
        providerAuthority = "com.google.android.gms.fonts",
        providerPackage = "com.google.android.gms",
        certificates = R.array.com_google_android_gms_fonts_certs
    )
    プロバイダが受け取るパラメータは次のとおりです。
    • Google Fonts のフォント プロバイダの権限
    • プロバイダの ID を確認するためのフォント プロバイダ パッケージ
    • プロバイダの ID を確認するための、証明書のハッシュセットのリストGoogle Fonts プロバイダに必要なハッシュは、Jetchat サンプルアプリの font_certs.xml ファイルで確認できます。
  3. FontFamily を定義します。
    // ...
     import androidx.compose.ui.text.googlefonts.GoogleFont
     import androidx.compose.ui.text.font.FontFamily
     import androidx.compose.ui.text.googlefonts.Font
     // ...
    
    val fontName = GoogleFont("Lobster Two")
    
    val fontFamily = FontFamily(
        Font(googleFont = fontName, fontProvider = provider)
    )
    太さやスタイルなど、フォントの他のパラメータは、FontWeightFontStyle でそれぞれクエリできます。
    // ...
     import androidx.compose.ui.text.googlefonts.GoogleFont
     import androidx.compose.ui.text.font.FontFamily
     import androidx.compose.ui.text.googlefonts.Font
     // ...
    
    val fontName = GoogleFont("Lobster Two")
    
    val fontFamily = FontFamily(
        Font(
            googleFont = fontName,
            fontProvider = provider,
            weight = FontWeight.Bold,
            style = FontStyle.Italic
        )
    )
  4. コンポーズ可能な Text 関数で使用する FontFamily を設定します。

Text(
    fontFamily = fontFamily, text = "Hello World!"
)

タイポグラフィを定義して FontFamily を使用することもできます。

val MyTypography = Typography(
    labelMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 12.sp/*...*/
    ),
    labelLarge = TextStyle(
        fontFamily = fontFamily,
        fontWeight = FontWeight.Bold,
        letterSpacing = 2.sp,
        /*...*/
    ),
    displayMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.SemiBold/*...*/
    ),
    /*...*/
)

次に、タイポグラフィをアプリのテーマに設定します。

MyAppTheme(
    typography = MyTypography
)/*...*/

Compose にダウンロード可能なフォントを Material3 とともに実装するアプリの例については、Jetchat サンプルアプリをご覧ください。

代替フォントを追加する

フォントが適切にダウンロードできない場合に備えて、フォントのフォールバック チェーンを決定できます。たとえば、ダウンロード可能なフォントを次のように定義しているとします。

// ...
 import androidx.compose.ui.text.googlefonts.Font
 // ...

val fontName = GoogleFont("Lobster Two")

val fontFamily = FontFamily(
    Font(googleFont = fontName, fontProvider = provider),
    Font(googleFont = fontName, fontProvider = provider, weight = FontWeight.Bold)
)

両方のウェイトのデフォルト フォントを次のように定義できます。

// ...
 import androidx.compose.ui.text.font.Font
 import androidx.compose.ui.text.googlefonts.Font
 // ...

val fontName = GoogleFont("Lobster Two")

val fontFamily = FontFamily(
    Font(googleFont = fontName, fontProvider = provider),
    Font(resId = R.font.my_font_regular),
    Font(googleFont = fontName, fontProvider = provider, weight = FontWeight.Bold),
    Font(resId = R.font.my_font_regular_bold, weight = FontWeight.Bold)
)

正しいインポートを追加していることを確認してください。

このように FontFamily を定義すると、2 つのチェーン(ウェイトごとに 1 つ)を含む FontFamily が作成されます。読み込みメカニズムでは、まずオンライン フォントを解決し、次にローカルの R.font リソース フォルダにあるフォントを解決しようとします。

実装をデバッグする

フォントが正しくダウンロードされていることを確認するには、デバッグ コルーチン ハンドラを定義します。ハンドルを使用すると、フォントが非同期でロードされなかった場合に実行する動作を指定できます。

まず、CoroutineExceptionHandler を作成します。

val handler = CoroutineExceptionHandler { _, throwable ->
    // process the Throwable
    Log.e(TAG, "There has been an issue: ", throwable)
}

これを createFontFamilyResolver メソッドに渡して、リゾルバが新しいハンドラを使用するようにします。

CompositionLocalProvider(
    LocalFontFamilyResolver provides createFontFamilyResolver(LocalContext.current, handler)
) {
    Column {
        Text(
            text = "Hello World!", style = MaterialTheme.typography.bodyMedium
        )
    }
}

プロバイダの isAvailableOnDevice API を使用して、プロバイダが利用可能であり、証明書が正しく構成されているかどうかをテストすることもできます。そのためには、プロバイダが正しく構成されていない場合に false を返す isAvailableOnDevice メソッドを呼び出します。

val context = LocalContext.current
LaunchedEffect(Unit) {
    if (provider.isAvailableOnDevice(context)) {
        Log.d(TAG, "Success!")
    }
}

注意点

Google Fonts では、Android で新しいフォントを利用できるようになるまでに数か月かかります。フォントが fonts.google.com で追加されてから、ダウンロード可能なフォントの API(View システムまたは Compose)を介して利用可能になるまでには時間差があります。新しく追加したフォントが、IllegalStateException ではアプリで読み込めないことがあります。デベロッパーがこのエラーを他の種類のフォント読み込みエラーと区別できるように、Compose に例外を説明するメッセージを追加しました(変更点はこちらを参照)。問題が見つかった場合は、Issue Tracker を使用して報告してください。

可変フォントを使用する

可変フォントは、1 つのフォント ファイルにさまざまなスタイルを含めることができるフォント形式です。可変フォントでは、軸(またはパラメータ)を変更して好みのスタイルを生成できます。これらの軸には、太さ、幅、傾斜、斜体などの標準の軸や、可変フォントごとに異なるカスタム軸を使用できます。

同じ変数フォントで軸の値が異なる 5 つの構成。
図 1: 同じ変数フォントを使用し、異なる軸の値でカスタマイズしたテキスト。

通常のフォント ファイルの代わりに可変フォントを使用すると、フォント ファイルが複数ではなく 1 つだけになります。

可変フォントの背景の詳細については、Google Fonts の知識、使用可能な可変フォントのカタログ全体、各フォントでサポートされている軸のをご覧ください。

このドキュメントでは、Compose アプリに可変フォントを実装する方法について説明します。

可変フォントを読み込む

  1. 使用する変数フォント(Roboto Flex など)をダウンロードして、アプリの app/res/font フォルダに配置します。追加する ttf ファイルはフォントの可変フォント バージョンであり、フォント ファイルの名前はすべて小文字で、特殊文字は含まれていません。

  2. 可変フォントを読み込むには、res/font/ ディレクトリに配置されたフォントを使用して FontFamily を定義します。

    // In Typography.kt
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily =
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(950),
                    FontVariation.width(30f),
                    FontVariation.slant(-6f),
                )
            )
        )

    FontVariation API を使用すると、太さ傾斜などの標準のフォント軸を設定できます。これらは任意の可変フォントで使用できる標準軸です。フォントが使用される場所に応じて、さまざまなフォント構成を作成できます。

  3. 可変フォントは Android バージョン O 以降でのみ使用できるため、ガードレールを追加して適切なフォールバックを設定します。

    // In Typography.kt
    val default = FontFamily(
        /*
        * This can be any font that makes sense
        */
        Font(
            R.font.robotoflex_static_regular
        )
    )
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(950),
                    FontVariation.width(30f),
                    FontVariation.slant(-6f),
                )
            )
        )
    } else {
        default
    }

  4. 再利用しやすくするために設定を一連の定数に抽出し、フォント設定を次の定数に置き換えます。

    // VariableFontDimension.kt
    object DisplayLargeVFConfig {
        const val WEIGHT = 950
        const val WIDTH = 30f
        const val SLANT = -6f
        const val ASCENDER_HEIGHT = 800f
        const val COUNTER_WIDTH = 500
    }
    
    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(DisplayLargeVFConfig.WEIGHT),
                    FontVariation.width(DisplayLargeVFConfig.WIDTH),
                    FontVariation.slant(DisplayLargeVFConfig.SLANT),
                )
            )
        )
    } else {
        default
    }

  5. FontFamily を使用するようにマテリアル デザイン 3 のタイポグラフィを設定します。

    // Type.kt
    val Typography = Typography(
        displayLarge = TextStyle(
            fontFamily = displayLargeFontFamily,
            fontSize = 50.sp,
            lineHeight = 64.sp,
            letterSpacing = 0.sp,
            /***/
        )
    )

    このサンプルでは、デフォルトのフォント設定と推奨される使用方法が異なる displayLarge マテリアル 3 タイポグラフィを使用します。たとえば、画面上の最大のテキストであるため、短く重要なテキストには displayLarge を使用します。

    マテリアル 3 では、TextStylefontFamily のデフォルト値を変更して、タイポグラフィをカスタマイズできます。上記のスニペットでは、TextStyle のインスタンスを構成して、各フォント ファミリーのフォント設定をカスタマイズしています。

  6. タイポグラフィを定義したので、それを M3 MaterialTheme に渡します。

    MaterialTheme(
        colorScheme = MaterialTheme.colorScheme,
        typography = Typography,
        content = content
    )

  7. 最後に、Text コンポーザブルを使用して、スタイルを定義済みのタイポグラフィ スタイルのいずれか MaterialTheme.typography.displayLarge に指定します。

    @Composable
    @Preview
    fun CardDetails() {
        MyCustomTheme {
            Card(
                shape = RoundedCornerShape(8.dp),
                elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
            ) {
                Column(
                    modifier = Modifier.padding(16.dp)
                ) {
                    Text(
                        text = "Compose",
                        style = MaterialTheme.typography.displayLarge,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 1
                    )
                    Text(
                        text = "Beautiful UIs on Android",
                        style = MaterialTheme.typography.headlineMedium,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 2
                    )
                    Text(
                        text = "Jetpack Compose is Android’s recommended modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.",
                        style = MaterialTheme.typography.bodyLarge,
                        modifier = Modifier.padding(bottom = 8.dp),
                        maxLines = 3
                    )
                }
            }
        }
    }

    Text コンポーザブルはマテリアル テーマのスタイルで構成され、異なる可変フォント構成を含んでいます。MaterialTheme.typography を使用すると、M3 の MaterialTheme コンポーザブルに提供されたタイポグラフィを取得できます。

異なるフォント構成を示す 3 つの異なるテキスト。
図 2. 3 つの異なる構成で適用された可変フォント

カスタム軸を使用する

フォントにカスタム軸を設定することもできます。これらはフォント ファイル内で定義します。 たとえば、Roboto Flex フォントのアセンダーの高さ("YTAS")軸で小文字のアセンダーの高さが調整され、カウンタの幅("XTRA")で各文字の幅が調整されます。

これらの軸の値は FontVariation 設定で変更できます。

フォントに設定できるカスタム軸の詳細については、各フォントのサポートされている軸の表をご覧ください。

  1. カスタム軸を使用するには、カスタム軸 ascenderHeightcounterWidth 軸の関数を定義します。

    fun ascenderHeight(ascenderHeight: Float): FontVariation.Setting {
        require(ascenderHeight in 649f..854f) { "'Ascender Height' must be in 649f..854f" }
        return FontVariation.Setting("YTAS", ascenderHeight)
    }
    
    fun counterWidth(counterWidth: Int): FontVariation.Setting {
        require(counterWidth in 323..603) { "'Counter width' must be in 323..603" }
        return FontVariation.Setting("XTRA", counterWidth.toFloat())
    }

    これらの関数は次の処理を行います。

    • 受け入れ可能な値のガードレールを定義します。可変フォント カタログを見るとわかるように、ascenderHeight (YTAS) の最小値は 649f、最大値は 854f です。
    • フォントの設定を返して、フォントに追加する準備を整えます。FontVariation.Setting() メソッドでは、軸名(YTAS, XTRA)をハードコードし、値をパラメータとして受け取ります。
  2. フォント設定で軸を使用して、読み込まれる各 Font に追加のパラメータを渡します。

    @OptIn(ExperimentalTextApi::class)
    val displayLargeFontFamily = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        FontFamily(
            Font(
                R.font.robotoflex_variable,
                variationSettings = FontVariation.Settings(
                    FontVariation.weight(DisplayLargeVFConfig.WEIGHT),
                    FontVariation.width(DisplayLargeVFConfig.WIDTH),
                    FontVariation.slant(DisplayLargeVFConfig.SLANT),
                    ascenderHeight(DisplayLargeVFConfig.ASCENDER_HEIGHT),
                    counterWidth(DisplayLargeVFConfig.COUNTER_WIDTH)
                )
            )
        )
    } else {
        default
    }

    小文字のアセンダーの高さが高くなり、他のテキストの幅は広くなりました。

カスタム軸を設定して、可変フォントの構成が異なる 3 つのテキスト。一部のテキストは小文字のアセンダーが高く、以前よりも幅が広くなっています。
図 3.可変フォントで設定されたカスタム軸を示すテキスト。

参考情報

詳細については、可変フォントに関する次のブログ投稿をご覧ください。