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

На этой странице описано, как настроить шрифты в приложении 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, вы можете использовать Downloadable Fonts API в приложении Compose, чтобы асинхронно скачивать Google Fonts и использовать их в приложении.

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

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

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

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

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

Чтобы новые шрифты Google Fonts стали доступны на Android, требуется несколько месяцев. Между добавлением шрифта на сайте fonts.google.com и его появлением в Downloadable Fonts 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),
                )
            )
        )

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

  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-функции MaterialTheme в M3.

Три разных текста, каждый из которых демонстрирует разные конфигурации шрифта.
Рисунок 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. Текст, показывающий настраиваемые оси, установленные для вариативных шрифтов.

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

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