Работа со шрифтами

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

Установить шрифт

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

@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 в ваш компонуемый элемент 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 загружаемых шрифтов в своем приложении Compose для асинхронной загрузки шрифтов Google и использования их в своем приложении.

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

Используйте загружаемые шрифты программно

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

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

    Круто

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

    Котлин

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.8.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 , который будет использоваться в функции компоновки текста:

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 и его доступностью через API загрузки шрифтов (в системе View или в Compose). Недавно добавленные шрифты могут не загружаться в ваше приложение с исключением IllegalStateException . Чтобы помочь разработчикам отличать эту ошибку от других типов ошибок загрузки шрифтов, мы добавили описание исключения в Compose , изменив его здесь . Если вы обнаружите какие-либо проблемы, сообщите о них с помощью системы отслеживания ошибок .

Используйте переменные шрифты

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

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

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

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

В этом документе показано, как реализовать переменный шрифт в приложении 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. Наконец, используйте компонуемый 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
                    )
                }
            }
        }
    }

    Каждый Text компонуемый элемент настраивается через стиль своей темы Material и содержит различную конфигурацию вариативных шрифтов. Вы можете использовать MaterialTheme.typography для получения типографики, предоставленной компонуемому элементу 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. Текст, демонстрирующий пользовательские оси, заданные на переменных шрифтах.

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

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

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %}