Как работать со шрифтами

На этой странице рассказывается, как задавать шрифты в приложении Compose.

Как задать шрифт

Text имеет параметр fontFamily, позволяющий задать шрифт, используемый в composable-функции. По умолчанию семейства шрифтов с засечками, без засечек, моноширинные и рукописные включены:

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

Вы можете передать этот fontFamily в composable-функцию Text. Поскольку в теге 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, вы можете использовать в приложении API скачиваемых шрифтов, чтобы асинхронно скачивать шрифты Google и использовать их в приложении.

В настоящее время поддержка скачиваемых шрифтов, предоставленных сторонними поставщиками, недоступна.

Как использовать скачиваемые шрифты программно

Чтобы скачать шрифт программным способом из приложения, выполните следующие действия:

  1. Добавьте зависимость:

    Яркий

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

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.12.1")
    }
  2. Инициализируйте объект GoogleFont.Provider с учетными данными для 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
    )
    Поставщик получает следующие параметры:
    • Орган, предоставляющий шрифты для Google Fonts.
    • Пакет поставщика шрифтов для проверки его личности.
    • Список наборов хешей для сертификатов, которые используются для проверки личности поставщика. Хеши, необходимые для поставщика Google Fonts, можно найти в файле font_certs.xml в примере приложения Jetchat.
  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)
    )
    Вы можете запросить другие параметры шрифта, например толщину и стиль, с помощью команд FontWeight и 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. Настройте FontFamily, который будет использоваться в composable-функции Text:

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

Вы также можете задать типографику, чтобы использовать 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/*...*/
    ),
    /*...*/
)

Затем задайте типографику в соответствии с темой приложения:

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 создается 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
        )
    }
}

Вы также можете использовать API isAvailableOnDevice поставщика, чтобы проверить, доступен ли поставщик и правильно ли настроены сертификаты. Для этого можно вызвать метод isAvailableOnDevice, который возвращает значение false, если поставщик настроен неправильно.

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

Важные примечания

Новые шрифты появляются в Android через несколько месяцев после добавления в Google Fonts. Между добавлением шрифта на сайт fonts.google.com и его появлением в API скачиваемых шрифтов (в системе View или Compose) проходит некоторое время. Недавно добавленные шрифты могут не загружаться в приложении с ошибкой IllegalStateException. Чтобы разработчикам было проще отличать эту ошибку от других, связанных с загрузкой шрифтов, мы добавили в Compose описательное сообщение об исключении. Ознакомьтесь с изменениями. Если вы обнаружите какие-либо проблемы, сообщите о них в системе отслеживания ошибок.

Как использовать вариативные шрифты

Вариативный шрифт – это формат шрифта, который позволяет хранить разные стили в одном файле. В переменных шрифтах можно изменять оси (или параметры), чтобы получить нужный стиль. Оси могут быть стандартными, например насыщенность, ширина, наклон и курсив, или пользовательскими, которые различаются в разных вариативных шрифтах.

Пять конфигураций одного и того же шрифта с разными значениями осей.
Рисунок 1. Текст, в котором используется один и тот же вариативный шрифт, но разные значения осей.

Использование переменных шрифтов вместо обычных позволяет хранить только один файл шрифта вместо нескольких.

Дополнительную информацию о переменных шрифтах можно найти в Google Fonts Knowledge, каталоге доступных переменных шрифтов и таблице поддерживаемых осей для каждого шрифта.

В этом документе рассказывается, как реализовать переменный шрифт в приложении Compose.

Как загрузить вариативный шрифт

  1. Скачайте нужный вариант шрифта (например, Roboto Flex) и поместите его в папку app/res/font в приложении. Убедитесь, что у файла расширение .Добавленный файл ttf является версией шрифта с переменной шириной символов, а название файла шрифта написано строчными буквами и не содержит специальных символов.

  2. Чтобы загрузить вариативный шрифт, определите FontFamily, используя шрифт, размещенный в каталоге 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),
                )
            )
        )

    FontVariation API позволяет настраивать стандартные оси шрифта, такие как weight, width и slant. Это стандартные оси, доступные для любого вариативного шрифта. Вы можете создавать разные конфигурации шрифта в зависимости от того, где он будет использоваться.

  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. Настройте типографику Material Design 3, чтобы использовать FontFamily:

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

    В этом примере используется displayLarge типографика Material 3, в которой заданы другие настройки шрифтов по умолчанию и рекомендации по использованию. Например, для короткого важного текста следует использовать тег displayLarge, поскольку он позволяет сделать текст самым крупным на экране.

    В Material 3 можно изменить значения по умолчанию для TextStyle и fontFamily, чтобы настроить типографику. В приведенном выше фрагменте кода вы настраиваете экземпляры TextStyle, чтобы задать параметры шрифта для каждого семейства шрифтов.

  6. Теперь, когда вы определили типографику, передайте ее в M3 MaterialTheme:

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

  7. Наконец, используйте composable-функцию 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
                    )
                }
            }
        }
    }

    Каждая composable-функция Text настраивается с помощью стиля темы Material Design и содержит собственную конфигурацию переменного шрифта. Вы можете использовать MaterialTheme.typography, чтобы получить типографику, предоставленную composable-функции M3 MaterialTheme.

Три разных текста с разными конфигурациями шрифтов.
Рисунок 2. Вариативный шрифт, примененный в трех разных конфигурациях.

Как использовать специальные оси

У шрифтов также могут быть собственные оси. Они определяются в самом файле шрифта. Например, у шрифта Roboto Flex есть ось высоты выносных элементов строчных букв ("YTAS"), которая регулирует высоту выносных элементов строчных букв, и ось ширины внутренних пространств ("XTRA"), которая регулирует ширину каждой буквы.

Вы можете изменить значения этих осей с помощью настроек FontVariation.

Подробнее о настраиваемых осях можно узнать из таблицы поддерживаемых осей для каждого шрифта.

  1. Чтобы использовать специальные оси, определите функции для осей ascenderHeight и counterWidth:

    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. Текст, в котором показаны пользовательские оси, заданные для переменных шрифтов.

Дополнительные ресурсы

Дополнительную информацию о вариативных шрифтах можно найти в статье блога: