En esta página, se describe cómo configurar fuentes en tu app de Compose.
Cómo configurar la fuente
Text tiene un parámetro fontFamily para permitir la configuración de la fuente usada en el elemento componible. Según la configuración predeterminada, se incluyen las familias de fuentes serif, Sans Serif, monoespaciales y cursivas
: incluidas
@Composable fun DifferentFonts() { Column { Text("Hello World", fontFamily = FontFamily.Serif) Text("Hello World", fontFamily = FontFamily.SansSerif) } }
Puedes usar el atributo fontFamily para trabajar con fuentes personalizadas y tipos de letra definidos en la carpeta res/font:
En este ejemplo, se muestra el modo en que definirás un fontFamily según esos archivos de fuente
con el uso de la función 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) )
Puedes pasar esta fontFamily a tu objeto componible Text. Debido a que una fontFamily puede incluir diferentes pesos, puedes configurar manualmente fontWeight a fin de seleccionar el peso correcto para tu texto:
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) }
Para descubrir cómo establecer la tipografía en toda tu app, consulta Sistemas de diseño personalizados en Compose.
Fuentes descargables
A partir de Compose 1.2.0, puedes usar la API de fuentes descargables en tu app de Compose para descargar fuentes de Google de forma asíncrona y usarlas en tu app.
Por el momento, no se admiten las fuentes descargables que proporcionan los proveedores personalizados.
Cómo usar las fuentes descargables de manera programática
Para descargar una fuente de manera programática desde tu app, sigue estos pasos:
- Agrega la dependencia:
Groovy
dependencies { ... implementation "androidx.compose.ui:ui-text-google-fonts:1.10.5" }
Kotlin
dependencies { ... implementation("androidx.compose.ui:ui-text-google-fonts:1.10.5") }
- Inicializa el
GoogleFont.Providercon las credenciales de Google Fonts: Los parámetros que recibe el proveedor son los siguientes:val provider = GoogleFont.Provider( providerAuthority = "com.google.android.gms.fonts", providerPackage = "com.google.android.gms", certificates = R.array.com_google_android_gms_fonts_certs )
- La autoridad del proveedor de fuentes de Google Fonts
- El paquete del proveedor de fuentes para verificar su identidad
- Una lista de conjuntos de hashes para los certificados a fin de verificar la identidad del
proveedor (puedes encontrar los valores hash necesarios para el proveedor de Google Fonts
en el archivo
font_certs.xmlen la app de ejemplo de JetChat)
- Define a
FontFamily: Puedes consultar otros parámetros para tu fuente, como el grosor y el estilo, con// ... 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) )
FontWeightyFontStylerespectivamente:// ... 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 ) )
- Configura el
FontFamilyque se usará en la función de componibilidad de texto:
Text( fontFamily = fontFamily, text = "Hello World!" )
También puedes definir
tipografía para usar
tu 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/*...*/ ), /*...*/ )
Luego, establece la tipografía en el tema de tu app:
MyAppTheme( typography = MyTypography )/*...*/
Para ver un ejemplo de una app que implementa fuentes descargables en Compose junto con Material3, consulta la app de ejemplo de Jetchat.
Cómo agregar fuentes de resguardo
Puedes determinar una cadena de resguardo para tu fuente, en caso de que esta no se descargue correctamente. Por ejemplo, si tienes una fuente descargable definida de la siguiente manera:
// ... 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) )
Puedes definir los valores predeterminados de la fuente para ambos grosores, de la siguiente manera:
// ... 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) )
Asegúrate de agregar las importaciones correctas.
Cuando defines FontFamily de esta manera, se crea una FontFamily que contiene dos cadenas, una por cada grosor. El mecanismo de carga intentará resolver la fuente en línea primero y, luego, la fuente ubicada en la carpeta de recursos locales R.font.
Cómo depurar tu implementación
Para ayudarte a verificar si la fuente se descarga correctamente, puedes definir un controlador de corrutinas de depuración. Tu controlador proporciona el comportamiento de lo que se debe hacer en caso de que la fuente no se cargue de forma asíncrona.
Para comenzar, crea un
CoroutineExceptionHandler:
val handler = CoroutineExceptionHandler { _, throwable -> // process the Throwable Log.e(TAG, "There has been an issue: ", throwable) }
Pásalo al
createFontFamilyResolver
método para que el agente de resolución use el nuevo controlador:
CompositionLocalProvider( LocalFontFamilyResolver provides createFontFamilyResolver(LocalContext.current, handler) ) { Column { Text( text = "Hello World!", style = MaterialTheme.typography.bodyMedium ) } }
También puedes usar la
isAvailableOnDevice
API del proveedor para probar si está disponible y si los certificados están
configurados de forma correcta. Para ello, puedes llamar al método isAvailableOnDevice que muestra un valor falso si el proveedor está configurado de forma incorrecta.
val context = LocalContext.current LaunchedEffect(Unit) { if (provider.isAvailableOnDevice(context)) { Log.d(TAG, "Success!") } }
Advertencias
Google Fonts tarda varios meses en habilitar las nuevas fuentes en Android.
Existe un intervalo de tiempo entre el momento en que se agrega una fuente en
fonts.google.com y el momento en que está disponible desde
la API de fuentes descargables (ya sea en el sistema de vistas o en Compose). Es posible que las fuentes agregadas recientemente no se carguen en tu app con un
IllegalStateException.
Para ayudar a los desarrolladores a identificar este error entre otros tipos de errores de carga de fuentes,
agregamos mensajes descriptivos de la excepción en Compose con los cambios
aquí.
Si encuentras algún problema, infórmalo a través de la herramienta de seguimiento de errores.
Cómo usar fuentes variables
Una fuente variable es un formato de fuente que permite que un archivo de fuente contenga diferentes estilos. Con las fuentes variables, puedes modificar los ejes (o parámetros) para generar el estilo que prefieras. Estos ejes pueden ser estándar, como el peso, el ancho, la inclinación y la cursiva, o personalizados, que difieren entre las fuentes variables.
El uso de fuentes variables en lugar de archivos de fuentes normales te permite tener solo un archivo de fuente en lugar de varios.
Para obtener más información sobre las fuentes variables, consulta Google Fonts Knowledge, el catálogo completo de fuentes variables disponibles y una tabla de los ejes compatibles para cada fuente.
En este documento, se muestra cómo implementar una fuente variable en tu app de Compose.
Cómo cargar una fuente variable
Descarga la fuente variable que deseas usar (por ejemplo, Roboto Flex) y colócala en la carpeta
app/res/fontde tu app. Asegúrate de que el archivo .ttfque agregues sea la versión de fuente variable de la fuente y que el nombre del archivo de fuente esté en minúscula y no contenga caracteres especiales.Para cargar una fuente variable, define una
FontFamilycon la fuente colocada en el directoriores/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), ) ) )
La API de
FontVariationte permite configurar ejes de fuente estándar, como el peso, el ancho y la inclinación. Estos son ejes estándar que están disponibles con cualquier fuente variable. Puedes crear diferentes configuraciones de la fuente según dónde se usará.Las fuentes variables solo están disponibles para las versiones O y posteriores de Android, por lo que debes agregar una baranda de protección y configurar un resguardo adecuado:
// 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 }
Extrae la configuración en un conjunto de constantes para facilitar la reutilización y reemplazar la configuración de la fuente con estas 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 }
Configura la tipografía de Material Design 3 typography para usar la
FontFamily:// Type.kt val Typography = Typography( displayLarge = TextStyle( fontFamily = displayLargeFontFamily, fontSize = 50.sp, lineHeight = 64.sp, letterSpacing = 0.sp, /***/ ) )
En este ejemplo, se usa
displayLargela tipografía de Material 3, que tiene diferentes parámetros de configuración de fuente predeterminados y usos recomendados. Por ejemplo, debes usardisplayLargepara texto corto y fundamental, ya que es el texto más grande de la pantalla.Con Material 3, puedes cambiar los valores predeterminados de
TextStyleyfontFamilypara personalizar tu tipografía. En el fragmento anterior, configuras instancias deTextStylepara personalizar la configuración de la fuente para cada familia de fuentes.Ahora que definiste tu tipografía, pásala al
MaterialThemede M3:MaterialTheme( colorScheme = MaterialTheme.colorScheme, typography = Typography, content = content )
Por último, usa un objeto componible
Texty especifica el estilo en uno de los estilos de tipografía definidos,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 ) } } } }
Cada objeto componible
Textse configura a través del estilo de su tema de Material y contiene una configuración de fuente variable diferente. Puedes usarMaterialTheme.typographypara recuperar la tipografía proporcionada al objeto componibleMaterialThemede M3.
Cómo usar ejes personalizados
Las fuentes también pueden tener ejes personalizados. Estos se definen dentro del archivo de fuente.
Por ejemplo, la fuente Roboto Flex tiene el eje de altura del ascendente ("YTAS"), que
ajusta la altura de los ascendentes en minúscula, mientras que el ancho del contador ("XTRA")
ajusta el ancho de cada letra.
Puedes cambiar el valor de estos ejes con la configuración de FontVariation.
Para obtener más información sobre los ejes personalizados que puedes configurar para una fuente, consulta la tabla de los ejes compatibles para cada fuente.
Para usar ejes personalizados, define funciones para los ejes personalizados
ascenderHeightycounterWidth: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()) }
Estas funciones hacen lo siguiente:
- Definen barandas de protección para los valores que pueden aceptar. Como puedes ver en el
catálogo de fuentes variables,
ascenderHeight (YTAS)tiene un valor mínimo de649fy un máximo de854f. - Muestran la configuración de la fuente, de modo que la configuración esté lista para agregar a la fuente. En el método
FontVariation.Setting(), el nombre del eje (YTAS, XTRA) está codificado y toma el valor como parámetro.
- Definen barandas de protección para los valores que pueden aceptar. Como puedes ver en el
catálogo de fuentes variables,
Con los ejes de la configuración de la fuente, pasa parámetros adicionales a cada
Fontque se carga:@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 }
Observa que la altura de los ascendentes en minúscula ahora aumentó y el otro texto es más ancho:
Recursos adicionales
Para obtener más información, consulta la siguiente entrada de blog sobre fuentes variables:
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Recursos en Compose
- Cómo aplicar estilo al texto
- Material Design 2 en Compose