Utiliser des polices

Cette page explique comment définir des polices dans votre application Compose.

Définir la police

Text utilise un paramètre fontFamily pour définir la police utilisée dans le composable. Par défaut, les familles de polices serif, sans-serif, monospace et cursive sont incluses :

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

Les mots

L'attribut fontFamily permet d'utiliser les polices personnalisées définies dans le dossier res/font:

Représentation graphique du dossier res > font dans l'environnement de développement

Cet exemple montre comment définir une propriété fontFamily en fonction de ces fichiers de polices et en utilisant la fonction 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)
)

Vous pouvez transmettre cet attribut fontFamily au composable Text. Étant donné qu'un élément fontFamily peut inclure différentes épaisseurs de police, vous pouvez spécifier fontWeight manuellement afin de sélectionner celle qui convient au texte:

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

Les mots

Pour découvrir comment définir la typographie dans l'ensemble de votre application, consultez Systèmes de conception personnalisés dans Compose.

Polices téléchargeables

À partir de Compose 1.2.0, vous pouvez utiliser l'API de polices téléchargeables de l'application Compose pour télécharger des polices Google de manière asynchrone et les utiliser dans votre application.

Les polices téléchargeables basées sur des fournisseurs personnalisés ne sont pas disponibles pour le moment.

Utiliser des polices téléchargeables par programmation

Pour télécharger une police par programmation à partir de votre application, procédez comme suit:

  1. Ajoutez la dépendance :

    Groovy

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

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.7.5")
    }
  2. Initialisez GoogleFont.Provider avec les identifiants pour Google Fonts:
    val provider = GoogleFont.Provider(
        providerAuthority = "com.google.android.gms.fonts",
        providerPackage = "com.google.android.gms",
        certificates = R.array.com_google_android_gms_fonts_certs
    )
    Voici les paramètres reçus par le fournisseur :
    • Autorité du fournisseur de polices pour Google Fonts.
    • Package du fournisseur de polices permettant de vérifier l'identité du fournisseur.
    • Liste d'ensembles de hachages des certificats permettant de vérifier l'identité du fournisseur. Vous trouverez les hachages requis pour le fournisseur Google Fonts dans le fichier font_certs.xml de l'application exemple Jetchat.
  3. Définissez un 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)
    )
    Vous pouvez interroger d'autres paramètres de police tels que l'épaisseur et le style avec, respectivement, FontWeight et FontStyle:
    // ...
     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. Configurez la famille de polices (FontFamily) à utiliser dans la fonction modulable Text:

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

Vous pouvez également définir une typographie pour utiliser votre FontFamily:

val MyTypography = Typography(
    bodyMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 12.sp/*...*/
    ),
    bodyLarge = TextStyle(
        fontFamily = fontFamily,
        fontWeight = FontWeight.Bold,
        letterSpacing = 2.sp,
        /*...*/
    ),
    headlineMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.SemiBold/*...*/
    ),
    /*...*/
)

Définissez ensuite cette typographie sur le thème de votre application:

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

Pour obtenir un exemple d'application mettant en œuvre des polices téléchargeables dans Compose avec Material3, consultez l'application exemple Jetchat.

Ajouter des polices de remplacement

Vous pouvez déterminer une série de polices de remplacement au cas où celle que vous avez spécifiée ne se téléchargerait pas correctement. Par exemple, si votre police téléchargeable est définie comme suit:

// ...
 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)
)

Vous pouvez définir les valeurs par défaut de votre police pour les deux épaisseurs, comme suit :

// ...
 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)
)

Veillez à ajouter les importations appropriées.

En définissant FontFamily de la sorte, vous créez une famille de polices (FontFamily) contenant deux chaînes, une pour chaque épaisseur de police. Le mécanisme de chargement tente d'abord de récupérer la police en ligne, puis la police située dans le dossier de ressources R.font local.

Déboguer votre implémentation

Pour vous aider à vérifier si la police est téléchargée correctement, vous pouvez définir un gestionnaire de coroutine de débogage. Le handle identifiera le comportement à suivre en cas d'échec du chargement asynchrone de la police.

Commencez par créer un CoroutineExceptionHandler:

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

Transmettez-le à la méthode createFontFamilyResolver pour que le résolveur utilise le nouveau gestionnaire:

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

Vous pouvez également utiliser l'API isAvailableOnDevice du fournisseur pour vérifier s'il est disponible et si les certificats sont configurés correctement. Pour ce faire, vous pouvez appeler la méthode isAvailableOnDevice qui renvoie la valeur "false" si le fournisseur n'est pas configuré correctement.

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

Mises en garde

Plusieurs mois sont nécessaires pour que les nouvelles polices Google Fonts soient disponibles sur Android. Il existe un intervalle de temps entre le moment où une police est ajoutée sur fonts.google.com et sa disponibilité via l'API de polices téléchargeables (dans le système de vue ou dans Compose). Les polices qui viennent d'être ajoutées risquent de ne pas se charger dans votre application et de générer une erreur IllegalStateException. Pour aider les développeurs à identifier cette erreur par rapport aux autres types d'erreurs de chargement de police, nous avons ajouté un message descriptif pour cette exception dans Compose en précisant les modifications ici. Si vous rencontrez des problèmes, signalez-les à l'aide de l'outil de suivi des problèmes.

Utiliser des polices variables

Une police variable est un format de police qui permet à un fichier de police de contenir différents styles. Avec les polices variables, vous pouvez modifier des axes (ou des paramètres) pour générer le style de votre choix. Ces axes peuvent être standards (épaisseur, largeur, inclinaison et italique, par exemple) ou personnalisés, ce qui varie selon les polices variables.

Cinq configurations de la même police de variables avec des valeurs d'axe différentes.
Figure 1. Texte utilisant la même police variable personnalisée avec différentes valeurs d'axe.

L'utilisation de polices variables au lieu de fichiers de polices standards vous permet de n'avoir qu'un seul fichier de police au lieu de plusieurs.

Pour en savoir plus sur les polices variables, consultez les connaissances Google Fonts, l'intégralité du catalogue des polices variables disponibles et un tableau des axes compatibles pour chaque police.

Ce document explique comment implémenter une police variable dans votre application Compose.

Charger une police variable

  1. Téléchargez la police variable que vous souhaitez utiliser (par exemple, Roboto Flex) et placez-la dans le dossier app/res/font de votre application. Assurez-vous que le .Le fichier ttf que vous ajoutez est la version de la police variable de la police, et le nom de votre fichier de police est en minuscules et ne contient aucun caractère spécial.

  2. Pour charger une police variable, définissez un FontFamily à l'aide de la police placée dans le répertoire res/font/:

    // 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),
                )
            )
        )

    L'API FontVariation vous permet de configurer des axes de police standards tels que l'épaisseur, la largeur et l'inclinaison. Il s'agit d'axes standards disponibles avec n'importe quelle police variable. Vous pouvez créer différentes configurations de la police en fonction de son utilisation.

  3. Les polices variables ne sont disponibles que pour les versions Android O et ultérieures. Ajoutez donc une barrière et configurez un remplacement approprié:

    // 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. Extrayez les paramètres dans un ensemble de constantes pour faciliter leur réutilisation et remplacez les paramètres de police par ces constantes:

    // 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. Configurez la typographie Material Design 3 pour utiliser FontFamily:

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

    Cet exemple utilise la typographie Material 3 displayLarge, qui comporte différents paramètres de police par défaut et des utilisations recommandées. Par exemple, vous devez utiliser displayLarge pour le texte court et essentiel, car il s'agit du texte le plus grand à l'écran.

    Avec Material 3, vous pouvez modifier les valeurs par défaut de TextStyle et fontFamily pour personnaliser votre typographie. Dans l'extrait de code ci-dessus, vous configurez des instances de TextStyle pour personnaliser les paramètres de police de chaque famille de polices.

  6. Maintenant que vous avez défini votre typographie, transmettez-la au MaterialTheme M3:

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

  7. Enfin, utilisez un composable Text et spécifiez le style à l'un des styles typographiques définis, 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
                    )
                }
            }
        }
    }

    Chaque composable Text est configuré via le style de son thème Material et contient une configuration de police variable différente. Vous pouvez utiliser MaterialTheme.typography pour récupérer la typographie fournie au composable MaterialTheme M3.

Trois textes différents, chacun présentant des configurations de police différentes.
Figure 2. Police variable appliquée dans trois configurations différentes.

Utiliser des axes personnalisés

Les polices peuvent également avoir des axes personnalisés. Elles sont définies dans le fichier de police lui-même. Par exemple, la police Roboto Flex comporte l'axe de hauteur des ascendants ("YTAS"), qui ajuste la hauteur des ascendants en minuscules, tandis que la largeur du compteur ("XTRA") ajuste la largeur de chaque lettre.

Vous pouvez modifier la valeur de ces axes à l'aide des paramètres FontVariation.

Pour en savoir plus sur les axes personnalisés que vous pouvez configurer pour une police, consultez le tableau des axes compatibles pour chaque police.

  1. Pour utiliser des axes personnalisés, définissez des fonctions pour les axes ascenderHeight et counterWidth personnalisés:

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

    Ces fonctions permettent d'effectuer les opérations suivantes:

    • Définissez des limites pour les valeurs qu'ils peuvent accepter. Comme vous pouvez le voir dans le catalogue des polices variables, ascenderHeight (YTAS) a une valeur minimale de 649f et une valeur maximale de 854f.
    • Renvoyez le paramètre de police afin que la configuration soit prête à être ajoutée à la police. Dans la méthode FontVariation.Setting(), le nom de l'axe (YTAS, XTRA) est codé en dur et prend la valeur comme paramètre.
  2. À l'aide des axes avec la configuration de la police, transmettez des paramètres supplémentaires à chaque Font chargé:

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

    Notez que la hauteur des jambages ascendants en minuscules est désormais augmentée, et que le reste du texte est plus large:

Trois textes différents montrant différentes configurations de polices variables, avec des axes personnalisés. Certains ont des jambages minuscules plus élevés et sont plus larges qu'auparavant.
Figure 3 Texte montrant des axes personnalisés définis sur des polices variables.

Ressources supplémentaires

Pour en savoir plus, consultez l'article de blog suivant sur les polices variables: