Praca z czcionkami

Na tej stronie opisujemy, jak ustawić czcionki w aplikacji Compose.

Ustaw czcionkę

Text ma parametr fontFamily, który umożliwia ustawienie czcionki używanej w komponowalnym elemencie. Domyślnie uwzględnione są rodziny czcionek szeryfowych, bezszeryfowych, o stałej szerokości znaków i kursywnych:

@Composable
fun DifferentFonts() {
    Column {
        Text("Hello World", fontFamily = FontFamily.Serif)
        Text("Hello World", fontFamily = FontFamily.SansSerif)
    }
}

Słowa

Możesz użyć atrybutu fontFamily do pracy z niestandardowymi czcionkami i krojami czcionki zdefiniowanymi w folderze res/font:

Graficzne przedstawienie folderu res > font w środowisku programistycznym

Ten przykład pokazuje, jak można zdefiniować element fontFamily na podstawie tych plików czcionek i użyć funkcji 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)
)

Możesz przekazać ten element fontFamily do kompozytu Text. fontFamily może obejmować różne wagi, więc możesz ręcznie ustawić fontWeight, aby wybrać odpowiednią wagę dla tekstu:

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

Słowa

Aby dowiedzieć się, jak ustawić typografię w całej aplikacji, przeczytaj artykuł Niestandardowe systemy projektowania w Compose.

Czcionki do pobrania

Od wersji Compose 1.2.0 możesz używać interfejsu API do pobierania czcionek w aplikacji Compose, aby asynchronicznie pobierać czcionki Google i korzystać z nich w aplikacji.

Obsługa czcionek do pobrania udostępnianych przez dostawców niestandardowych nie jest obecnie dostępna.

Używanie czcionek do pobrania za pomocą kodu

Aby pobrać czcionkę z poziomu aplikacji za pomocą kodu:

  1. Dodaj zależność:

    Groovy

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

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.7.5")
    }
  2. Inicjuj GoogleFont.Provider za pomocą danych logowania do 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
    )
    Dostawca otrzymuje te parametry:
    • Urząd dostawcy czcionek Google Fonts.
    • Pakiet dostawcy czcionek służący do weryfikacji tożsamości dostawcy.
    • Lista zestawów haszy certyfikatów w celu weryfikacji tożsamości dostawcy. Hasze wymagane przez dostawcę Google Fonts znajdziesz w pliku font_certs.xml w aplikacji przykładowej Jetchat.
  3. Określ 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)
    )
    Możesz wysłać zapytanie o inne parametry czcionki, takie jak grubość i styl, odpowiednio za pomocą parametrów FontWeightFontStyle:
    // ...
     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. Skonfiguruj FontFamily, aby używać go w funkcji tekstowej:

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

Możesz też zdefiniować Typografię, aby używać 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/*...*/
    ),
    /*...*/
)

Następnie ustaw typografię zgodnie z motywem aplikacji:

MyAppTheme(
    typography = MyTypography
)/*...*/

Przykład aplikacji, która w funkcji Compose oprócz Material3 implementuje czcionki do pobrania, znajdziesz w przykładowej aplikacji Jetchat.

Dodawanie czcionek zastępczych

Możesz określić łańcuch czcionek zapasowych na wypadek, gdyby czcionka nie została prawidłowo pobrana. Jeśli na przykład masz zdefiniowany czcionkę do pobrania w ten sposób:

// ...
 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)
)

Możesz w ten sposób zdefiniować wartości domyślne czcionki dla obu tych grubości:

// ...
 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)
)

Upewnij się, że dodajesz prawidłowe importy.

Zdefiniowanie elementu FontFamily w ten sposób powoduje utworzenie elementu FontFamily zawierającego 2 łańcuchy, po jednym na każdą wagę. Mechanizm wczytywania najpierw spróbuje rozpoznać czcionkę online, a potem czcionkę z lokalnego folderu zasobów R.font.

Debugowanie implementacji

Aby sprawdzić, czy czcionka jest pobierana prawidłowo, możesz zdefiniować moduł obsługi współrzędu debugowania. Nick zawiera informacje o czynnościach, które należy wykonać w przypadku, gdy czcionka nie załaduje się asynchronicznie.

Zacznij od utworzenia CoroutineExceptionHandler:

val handler = CoroutineExceptionHandler { _, throwable ->
    // process the Throwable
    Log.e(TAG, "There has been an issue: ", throwable)
}

Przekaż go do metody createFontFamilyResolver, aby resolver używał nowego modułu obsługi:

CompositionLocalProvider(
    LocalFontFamilyResolver provides createFontFamilyResolver(LocalContext.current, handler)
) {
    Column {
        Text(
            text = "Hello World!", style = MaterialTheme.typography.bodyMedium
        )
    }
}

Możesz też użyć interfejsu API isAvailableOnDevice od dostawcy, aby sprawdzić, czy dostawca jest dostępny, a certyfikaty są prawidłowo skonfigurowane. Aby to zrobić, możesz wywołać metodę isAvailableOnDevice, która zwraca wartość false, jeśli dostawca jest skonfigurowany nieprawidłowo.

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

Ograniczenia

Uzyskiwanie dostępu do nowych czcionek w Google Fonts na Androida zajmuje kilka miesięcy. Istnieje pewien czas pomiędzy dodaniem czcionki na stronie fonts.google.com a momentem, gdy staje się ona dostępna w interfejsie API do pobrania czcionek (w systemie View lub w Compose). Nowo dodane czcionki mogą nie załadować się w aplikacji z parametrem IllegalStateException. Aby ułatwić deweloperom identyfikację tego błędu w porównaniu z innymi typami błędów wczytywania czcionek, dodaliśmy opisy wyjątków w sekcji Tworzenie z zmianami. Jeśli znajdziesz jakieś problemy, zgłoś je za pomocą narzędzia do śledzenia problemów.

Używanie czcionek zmiennych

Czcionka zmienna to format czcionki, który umożliwia umieszczenie w jednym pliku czcionek różnych stylów. Dzięki zmiennym czcionkom możesz modyfikować osie (lub parametry), by generować preferowany styl. Te osie mogą być standardowe, np. grubość, szerokość, pochylenie i kursywa, lub niestandardowe, które różnią się w zależności od czcionek zmiennych.

5 konfiguracji tego samego czcionki zmiennej z różnymi wartościami osi.
Rysunek 1. Tekst korzystający z tego samego zmiennego czcionki spersonalizowanego za pomocą różnych wartości osi.

Korzystanie z czcionek zmiennych zamiast zwykłych plików czcionek pozwala mieć tylko 1 plik czcionki zamiast wielu.

Więcej informacji o czcionkach zmiennych znajdziesz w Google Fonts Knowledge, całym katalogu dostępnych czcionek zmiennych oraz tabeli z obejmującymi się osiami dla każdego czcionki.

Ten dokument pokazuje, jak wdrożyć czcionkę zmiennej w aplikacji Compose.

Ładowanie czcionki zmiennej

  1. Pobierz czcionkę zmienną, której chcesz użyć (np. Roboto Flex), i umieść ją w folderze app/res/font w aplikacji .Plik ttf, który dodajesz, jest wersją czcionki zmiennej. Nazwa pliku czcionki musi składać się wyłącznie z małych liter i nie może zawierać znaków specjalnych.

  2. Aby wczytać czcionkę zmiennej, zdefiniuj FontFamily za pomocą czcionki umieszczonej w katalogu 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),
                )
            )
        )

    Interfejs API FontVariation umożliwia konfigurowanie standardowych osi czcionek, takich jak grubość, szerokość i nachylenie. Są to standardowe osie dostępne w przypadku dowolnej czcionki zmiennej. Możesz tworzyć różne konfiguracje czcionek w zależności od tego, gdzie będą one używane.

  3. Czcionki zmienne są dostępne tylko w Androidzie w wersji O lub nowszej, dlatego dodaj zabezpieczenie i skonfiguruj odpowiednie ustawienie zastępcze:

    // 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. Wyodrębnij ustawienia w zbiór stałych wartości, aby ułatwić ich ponowne użycie, i zastąp ustawienia czcionek tymi stałymi wartościami:

    // 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. Skonfiguruj typografię interfejsu Material Design 3, aby korzystać z interfejsu FontFamily:

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

    Ten przykład używa displayLarge typografii Material 3, która ma inne domyślne ustawienia czcionki i zalecane zastosowania. Na przykład displayLarge używaj w przypadku krótkich, krytycznych tekstów, bo jest to największy tekst na ekranie.

    W Material 3 możesz zmienić domyślne wartości TextStylefontFamily, aby dostosować typografię. W tym fragmencie kodu konfigurujesz wystąpienia TextStyle, aby dostosować ustawienia czcionki dla każdej rodziny czcionek.

  6. Gdy już zdefiniujesz typografię, prześlij ją do M3:MaterialTheme

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

  7. Na koniec użyj funkcji kompozycyjnej Text i określ styl do jednego ze zdefiniowanych stylów typografii (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
                    )
                }
            }
        }
    }

    Każdy komponent Text jest skonfigurowany za pomocą stylu motywu Material Design i zawiera inną konfigurację czcionki zmiennej. Możesz użyć funkcji MaterialTheme.typography, aby pobrać typografię przekazaną do elementu kompozycyjnego M3 MaterialTheme.

3 różne teksty z różnymi konfiguracjami czcionek.
Rys. 2. Zmienna czcionka zastosowana w 3 różnych konfiguracjach.

Używanie osi niestandardowych

Czcionki mogą też mieć osi niestandardowe. Są one zdefiniowane w pliku czcionek. Na przykład czcionka Roboto Flex ma oś wysokości rosnącej ("YTAS"), która dostosowuje wysokość małych liter, a szerokość licznika ("XTRA") dostosowuje szerokość każdej litery.

Wartość tych osi możesz zmienić za pomocą ustawień FontVariation.

Więcej informacji o osiach niestandardowych, które możesz skonfigurować dla czcionki, znajdziesz w tabeli obsługiwanych osi dla danej czcionki.

  1. Aby używać osi niestandardowych, zdefiniuj funkcje dla niestandardowych osi ascenderHeight i 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())
    }

    Te funkcje:

    • Określ ograniczenia dotyczące wartości, które mogą być akceptowane. Jak widać w katalogu czcionek zmiennych, ascenderHeight (YTAS) ma minimalną wartość 649f i maksymalną 854f.
    • Przywróć ustawienia czcionki, aby konfiguracja była gotowa do dodania do czcionki. W metodzie FontVariation.Setting() nazwa osi (YTAS, XTRA) jest zaprogramowana na stałe i przyjmuje wartość jako parametr.
  2. Korzystając z osi z konfiguracją czcionki, możesz przekazywać dodatkowe parametry do każdego załadowanego elementu 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
    }

    Zwróć uwagę, że wysokość liter z wydłużeniem górnym została zwiększona, a inny tekst jest szerszy:

3 różne teksty pokazujące różne konfiguracje czcionek zmiennych z ustawionymi osiami niestandardowymi – niektóre mają wyższe małe litery z wydłużonymi ogonkami i są szersze niż wcześniej.
Rys. 3. Tekst pokazujący niestandardowe osie ustawione na czcionki zmienne.

Dodatkowe materiały

Więcej informacji o czcionkach zmiennych znajdziesz w tym poście na blogu: