Le texte est un élément central de n'importe quelle interface utilisateur. Jetpack Compose facilite l'affichage ou l'écriture de texte. Il exploite la composition de ses principaux composants. De la sorte, vous n'avez pas besoin de remplacer les propriétés et les méthodes, ni d'étendre les classes de grande taille pour disposer d'une logique et d'une conception de composable spécifiques qui fonctionnent comme vous le souhaitez.
À la base, Compose fournit BasicText
et BasicTextField
, qui sont les éléments clés permettant d'afficher le texte et de gérer les entrées utilisateur. À un niveau supérieur, Compose fournit Text
et TextField
, qui sont des composables conformes aux consignes Material Design. Nous vous recommandons de les utiliser, car ils s'adaptent à l'apparence attendue par vos utilisateurs sur Android. De plus, ils incluent d'autres options qui simplifient leur personnalisation tout en limitant les efforts de codage.
Affichage du texte
La manière la plus simple d'afficher du texte consiste à utiliser le composable Text
avec l'argument String
:
@Composable
fun SimpleText() {
Text("Hello World")
}
Afficher le texte de la ressource
Nous vous recommandons d'utiliser des ressources de chaîne au lieu de coder en dur les valeurs Text
. De cette manière, vous pouvez partager les mêmes chaînes avec vos vues Android et préparer votre application pour l'internationalisation :
@Composable
fun StringResourceText() {
Text(stringResource(R.string.hello_world))
}
Appliquer un style à du texte
Le composable Text
comporte plusieurs paramètres facultatifs pour styliser son contenu.
Vous trouverez ci-dessous la liste des paramètres qui couvrent la plupart des cas d'utilisation courants liés au texte. Pour afficher tous les paramètres de Text
, nous vous recommandons de consulter le code source de Compose Text.
Lorsque vous définissez l'un de ces paramètres, vous appliquez le style à l'ensemble de la valeur de texte. Si vous devez appliquer plusieurs styles dans la même ligne ou les mêmes paragraphes, découvrez comment intégrer plusieurs styles.
Modifier la couleur du texte
@Composable
fun BlueText() {
Text("Hello World", color = Color.Blue)
}
Modifier la taille du texte
@Composable
fun BigText() {
Text("Hello World", fontSize = 30.sp)
}
Mettre le texte en italique
Utilisez le paramètre fontStyle
pour mettre du texte en italique (ou définissez un autre style de police avec FontStyle
).
@Composable
fun ItalicText() {
Text("Hello World", fontStyle = FontStyle.Italic)
}
Mettre le texte en gras
Utilisez le paramètre fontWeight
pour mettre du texte en gras (ou définissez une autre épaisseur de police avec FontWeight
).
@Composable
fun BoldText() {
Text("Hello World", fontWeight = FontWeight.Bold)
}
Alignements de texte
Le paramètre textAlign
permet de définir l'alignement du texte dans une zone modulable Text
.
Par défaut, Text
sélectionne l'alignement de texte naturel en fonction de la valeur de son contenu :
- Bord gauche du conteneur
Text
pour les alphabets de gauche à droite tels que le latin, le cyrillique ou le hangûl - Bord droit du conteneur
Text
pour les alphabets de droite à gauche tels que l'arabe ou l'hébreu
@Preview(showBackground = true)
@Composable
fun CenterText() {
Text("Hello World", textAlign = TextAlign.Center,
modifier = Modifier.width(150.dp))
}
Si vous souhaitez définir manuellement l'alignement du texte d'un composable Text
, préférez TextAlign.Start
et TextAlign.End
au lieu de TextAlign.Left
et TextAlign.Right
respectivement, car ils définissent le côté approprié du composable Text
en fonction de l'orientation du texte de la langue préférée. Par exemple, TextAlign.End
aligne le texte français vers la droite et le texte arabe vers la gauche, mais TextAlign.Right
aligne le texte vers la droite, quel que soit l'alphabet utilisé.
Ombre
Le paramètre style
permet de définir un objet de type TextStyle
et de configurer plusieurs paramètres tels que l'ombre.
Shadow
reçoit une couleur pour l'ombre, le décalage (ou l'emplacement où elle se trouve par rapport à l'élément Text
) et le rayon de floutage, qui détermine le niveau de floutage de l'ombre.
@Preview(showBackground = true)
@Composable
fun TextShadow() {
val offset = Offset(5.0f, 10.0f)
Text(
text = "Hello world!",
style = TextStyle(
fontSize = 24.sp,
shadow = Shadow(
color = Color.Blue,
offset = offset,
blurRadius = 3f
)
)
)
}
Gestion des polices
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)
}
}
L'attribut fontFamily
permet d'utiliser les polices personnalisées définies dans le dossier res/font
:
font dans l'environnement de développement " class="l10n-absolute-url-src screenshot" l10n-attrs-original-order="src,alt,width,class" src="https://developer.android.com/static/images/jetpack/compose/text-font-folder.png" width="400" />
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)
)
Enfin, 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(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Light)
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
Text(
..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal,
fontStyle = FontStyle.Italic
)
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Medium)
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}
Pour découvrir comment définir la typographie dans l'ensemble de votre application, consultez la documentation sur les thèmes.
Plusieurs styles dans un texte
Pour définir différents styles dans le même composable Text
, vous devez utiliser AnnotatedString
, chaîne pouvant être annotée avec des styles d'annotations arbitraires.
AnnotatedString
est une classe de données contenant les éléments suivants :
- Une valeur
Text
- Une
List
deSpanStyleRange
, qui équivaut au style intégré avec une plage de positions dans la valeur de texte - Une
List
deParagraphStyleRange
, spécifiant l'alignement, la direction, la hauteur et le style du retrait du texte
TextStyle
est destiné au composable Text
, tandis que SpanStyle
et ParagraphStyle
sont destinés à être utilisés dans AnnotatedString
.
La différence entre SpanStyle
et ParagraphStyle
est que ParagraphStyle
peut être appliqué à un paragraphe entier, tandis que SpanStyle
peut être appliqué au niveau des caractères. Une fois qu'une partie du texte est marquée d'une annotation ParagraphStyle
, elle est séparée du reste du texte, comme des sauts de ligne avaient été ajoutés au début et à la fin.
AnnotatedString
dispose d'un compilateur de sûreté du typage pour faciliter la création : buildAnnotatedString
.
@Composable
fun MultipleStylesInText() {
Text(
buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Blue)) {
append("H")
}
append("ello ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Red)) {
append("W")
}
append("orld")
}
)
}
Nous pouvons définir ParagraphStyle
de la même manière :
@Composable
fun ParagraphStyle() {
Text(
buildAnnotatedString {
withStyle(style = ParagraphStyle(lineHeight = 30.sp)) {
withStyle(style = SpanStyle(color = Color.Blue)) {
append("Hello\n")
}
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold,
color = Color.Red
)
) {
append("World\n")
}
append("Compose")
}
}
)
}
Nombre maximal de lignes
Pour limiter le nombre de lignes visibles dans un composable Text
, définissez le paramètre maxLines
:
@Composable
fun LongText() {
Text("hello ".repeat(50), maxLines = 2)
}
Débordement de texte
Lorsque vous limitez un texte long, vous pouvez indiquer un TextOverflow
, qui n'intervient que si le texte affiché est tronqué. Pour ce faire, définissez le paramètre textOverflow
:
@Composable
fun OverflowedText() {
Text("Hello Compose ".repeat(50), maxLines = 2, overflow = TextOverflow.Ellipsis)
}
API includeFontPadding et lineHeight
includeFontPadding
est une ancienne propriété qui ajoute une marge intérieure basée sur les métriques de police en haut de la première ligne et en bas de la dernière ligne d'un texte. Dans Compose 1.2.0, includeFontPadding
est défini sur true
par défaut.
Nous vous recommandons de définir includeFontPadding
sur false
(ce qui supprimera la marge intérieure supplémentaire) à l'aide de l'API expérimentale/obsolète PlatformTextStyle
dans Compose 1.2.0 et d'ajuster le texte si nécessaire.
@Composable
fun AlignedText() {
Text(
text = myText,
style = LocalTextStyle.current.merge(
TextStyle(
lineHeight = 2.5.em,
platformStyle = PlatformTextStyle(
includeFontPadding = false
),
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None
)
)
)
)
}
Dans les prochaines versions de Compose, includeFontPadding
sera défini par défaut sur false
, et l'API PlatformTextStyle
sera supprimée.
Pour en savoir plus sur le contexte de cette modification, sur le fonctionnement de includeFontPadding
dans le système de vues et sur les modifications que nous avons apportées pour Compose et les nouvelles API LineHeightStyle
, consultez cet article de blog.
Personnalisation des thèmes
Si vous souhaitez utiliser le thème de l'application pour le style du texte, consultez cette documentation.
Interactions utilisateur
Jetpack Compose permet une interactivité ultraprécise dans Text
. La sélection de texte est désormais plus flexible et peut être effectuée sur différentes mises en page modulables. Les interactions utilisateur dans le texte sont différentes des autres mises en page modulables, car vous ne pouvez pas ajouter de modificateur à une partie d'un composable Text
. Cette section présente les différentes API permettant les interactions utilisateur.
Sélectionner du texte
Par défaut, les composables ne sont pas sélectionnables. Par conséquent, les utilisateurs ne peuvent pas sélectionner ni copier du texte depuis votre application. Pour activer la sélection de texte, vous devez encapsuler vos éléments de texte avec un composable SelectionContainer
:
@Composable
fun SelectableText() {
SelectionContainer {
Text("This text is selectable")
}
}
Vous pouvez désactiver la sélection à des endroits spécifiques d'une zone sélectionnable. Pour ce faire, vous devez encapsuler l'emplacement non sélectionnable avec un composable DisableSelection
:
@Composable
fun PartiallySelectableText() {
SelectionContainer {
Column {
Text("This text is selectable")
Text("This one too")
Text("This one as well")
DisableSelection {
Text("But not this one")
Text("Neither this one")
}
Text("But again, you can select this one")
Text("And this one too")
}
}
}
Déterminer la position d'un clic sur du texte
Pour écouter les clics sur Text
, vous pouvez ajouter le modificateur clickable
. Toutefois, si vous souhaitez déterminer la position d'un clic dans un composable Text
dans le cas où vous effectuez différentes actions en fonction des différentes parties du texte, vous devez utiliser ClickableText
à la place :
@Composable
fun SimpleClickableText() {
ClickableText(
text = AnnotatedString("Click Me"),
onClick = { offset ->
Log.d("ClickableText", "$offset -th character is clicked.")
}
)
}
Clic avec annotation
Lorsqu'un utilisateur clique sur un composable Text
, vous pouvez joindre des informations supplémentaires à une partie de la valeur Text
, comme une URL associée à un mot spécifique à ouvrir dans un navigateur. Pour ce faire, vous devez joindre une annotation, qui utilise une balise (String
), un élément (String
) et une plage de texte en tant que paramètres. Avec AnnotatedString
, ces annotations peuvent être filtrées par leurs balises ou plages de texte. Voici un exemple :
@Composable
fun AnnotatedClickableText() {
val annotatedText = buildAnnotatedString {
append("Click ")
// We attach this *URL* annotation to the following content
// until `pop()` is called
pushStringAnnotation(tag = "URL",
annotation = "https://developer.android.com")
withStyle(style = SpanStyle(color = Color.Blue,
fontWeight = FontWeight.Bold)) {
append("here")
}
pop()
}
ClickableText(
text = annotatedText,
onClick = { offset ->
// We check if there is an *URL* annotation attached to the text
// at the clicked position
annotatedText.getStringAnnotations(tag = "URL", start = offset,
end = offset)
.firstOrNull()?.let { annotation ->
// If yes, we log its value
Log.d("Clicked URL", annotation.item)
}
}
)
}
Saisir et modifier du texte
TextField
permet aux utilisateurs de saisir et de modifier du texte. Il existe deux niveaux d'implémentation de TextField
:
TextField
est l'implémentation Material Design. Il s'agit de celle que nous recommandons, car elle respecte les consignes Material Design :BasicTextField
permet aux utilisateurs de modifier du texte à l'aide du clavier physique ou virtuel, mais ne fournit aucune décoration comme un indice ou un espace réservé.
@Composable
fun SimpleFilledTextFieldSample() {
var text by remember { mutableStateOf("Hello") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Label") }
)
}
@Composable
fun SimpleOutlinedTextFieldSample() {
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("Label") }
)
}
Appliquer un style à un champ de texte
TextField
et BasicTextField
partagent de nombreux paramètres communs pour leur personnalisation. La liste complète des éléments TextField
est disponible dans le code source TextField
.
Voici une liste de quelques paramètres utiles :
singleLine
maxLines
textStyle
@Composable
fun StyledTextField() {
var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }
TextField(
value = value,
onValueChange = { value = it },
label = { Text("Enter text") },
maxLines = 2,
textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(20.dp)
)
}
Nous vous recommandons d'utiliser TextField
plutôt que BasicTextField
lorsque votre conception nécessite un élément Material TextField ou OutlineTextField. Toutefois, vous devez utiliser BasicTextField
lorsque vous créez des conceptions qui n'ont pas besoin des décorations de la spécification Material.
Options du clavier
TextField
vous permet de définir des options de configuration du clavier, telles que la disposition des touches, ou d'activer la correction automatique si le clavier le permet. Certaines options ne sont pas garanties si le clavier logiciel ne respecte pas les options fournies ici. Voici la liste des options de clavier compatibles :
capitalization
autoCorrect
keyboardType
imeAction
Mise en forme
TextField
vous permet de définir un élément VisualTransformation
au niveau de la valeur d'entrée. Par exemple, vous pouvez remplacer les caractères par *
pour les mots de passe ou insérer des traits d'union tous les 4 chiffres dans le cas d'un numéro de carte de paiement :
@Composable
fun PasswordTextField() {
var password by rememberSaveable { mutableStateOf("") }
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Enter password") },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
}
D'autres exemples sont disponibles dans le code source de VisualTransformSamples.
Nettoyage de la chaîne d'entrée
Une tâche courante lors de la modification de texte consiste à supprimer les caractères de début ou à transformer la chaîne d'entrée chaque fois qu'elle change.
Partez du principe que le clavier peut apporter des modifications arbitraires et importantes à chaque onValueChange
. Cela peut se produire, par exemple, si l'utilisateur a recours à la correction automatique, remplace un mot par un emoji ou exploite d'autres fonctionnalités de retouche intelligentes. Pour gérer ce comportement correctement, écrivez une logique de transformation selon laquelle le texte actuel transmis à onValueChange
n'a aucun rapport avec les valeurs précédentes ou suivantes transmises à onValueChange
.
Pour implémenter un champ de texte qui empêche les zéros au début, vous pouvez implémenter la suppression de tous les zéros au début d'une chaîne à chaque changement de valeur.
@Composable
fun NoLeadingZeroes() {
var input by rememberSaveable { mutableStateOf("") }
TextField(
value = input,
onValueChange = { newText ->
input = newText.trimStart { it == '0' }
}
)
}
Pour contrôler la position du curseur lors du nettoyage du texte, utilisez la surcharge TextFieldValue
de TextField
dans le cadre de l'état.
Polices téléchargeables
À partir de Compose 1.2-alpha07, 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 :
- Ajoutez la dépendance :
Groovy
dependencies { ... implementation "androidx.compose.ui:ui-text-google-fonts:1.2.1" }
Kotlin
dependencies { ... implementation("androidx.compose.ui:ui-text-google-fonts:1.2.1") }
- Initialisez
GoogleFont.Provider
avec les identifiants pour Google Fonts.@OptIn(ExperimentalTextApi::class) 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.
ExperimentalTextApi
pour pouvoir utiliser les API de polices téléchargeables dans votre application. - Définissez une famille de polices,
FontFamily
, comme suit :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
etFontStyle
: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) )
- Configurez la famille de polices (
FontFamily
) à utiliser dans la fonction modulable Text. Et le tour est joué !Text( fontFamily = fontFamily, text = "Hello World!" )
FontFamily
.val MyTypography = Typography( body1 = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = ... ), body2 = TextStyle( fontFamily = fontFamily, fontWeight = FontWeight.Bold, letterSpacing = ... ), h4 = 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, veillez à consulter l'application exemple JetChat.
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 ensuite à 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.body1
)
}
}
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.