Praca z czcionkami

Na tej stronie opisaliśmy, jak ustawić czcionki w aplikacji do tworzenia wiadomości.

Ustaw czcionkę

Text zawiera parametr fontFamily, który umożliwia ustawienie czcionki używanej w komponencie. Domyślnie uwzględniane są rodziny czcionek szeryfowych, bezszeryfowych, o stałej szerokości i pochylenia:

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

Słowa

Atrybutu fontFamily możesz używać 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 zdefiniować atrybut fontFamily na podstawie tych plików czcionek i za pomocą 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 funkcji kompozycyjnej Text. Element fontFamily może mieć różne wagi, dlatego możesz ręcznie ustawić fontWeight, aby wybrać odpowiednią wagę 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 sekcji Utwórz.

Czcionki do pobrania

Od wersji Compose 1.2.0 możesz używać dostępnego do pobrania interfejsu Fonts API w aplikacji Compose, aby asynchronicznie pobierać czcionki Google i używać ich w swojej aplikacji.

Obsługa czcionek dostępnych do pobrania od dostawców niestandardowych nie jest obecnie dostępna.

Automatyczne używanie czcionek do pobrania

Aby pobrać czcionkę automatycznie z poziomu aplikacji, wykonaj te czynności:

  1. Dodaj zależność:

    Odlotowy

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

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.6.1")
    }
  2. Zainicjuj 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
    )
    Otrzymywane parametry to:
    • Informacje o dostawcy czcionek Google Fonts.
    • Pakiet dostawcy czcionki służący do weryfikacji tożsamości dostawcy.
    • Lista zestawów haszów certyfikatów umożliwiających weryfikację tożsamości dostawcy. Skróty wymagane dla dostawcy Google Fonts znajdziesz w pliku font_certs.xml w przykładowej aplikacji Jetchat.
  3. Zdefiniuj 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 poprosić o inne parametry czcionki, takie jak waga i styl, za pomocą FontWeight i 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. Skonfiguruj obiekt FontFamily do użycia w funkcji tekstu kompozycyjnego:

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

Możesz też zdefiniować typografia, aby użyć właściwości FontFamily:

val MyTypography = Typography(
    labelMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.Normal, fontSize = 12.sp/*...*/
    ),
    labelLarge = TextStyle(
        fontFamily = fontFamily,
        fontWeight = FontWeight.Bold,
        letterSpacing = 2.sp,
        /*...*/
    ),
    displayMedium = TextStyle(
        fontFamily = fontFamily, fontWeight = FontWeight.SemiBold/*...*/
    ),
    /*...*/
)

Następnie ustaw Typografia na motyw aplikacji:

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

Aplikacja, która razem z Material3 implementuje czcionki do pobrania w usłudze Compose, znajduje się w przykładowej aplikacji Jetchat.

Dodaj czcionki zastępcze

Możesz określić łańcuch wartości zastępczych czcionki, jeśli nie zostanie ona prawidłowo pobrana. Jeśli np. masz już zdefiniowaną do pobrania czcionkę:

// ...
 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 zdefiniować domyślne ustawienia czcionki dla obu wag w ten sposób:

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

Sprawdź, czy importujesz odpowiednie dane.

Zdefiniowanie tego parametru FontFamily w ten sposób tworzy element FontFamily zawierający 2 łańcuchy, po jednym na wagę. Mechanizm wczytywania najpierw spróbuje rozpoznać czcionkę online, a potem czcionkę znajdującą się w lokalnym folderze zasobów R.font.

Debugowanie implementacji

Aby sprawdzić, czy czcionka jest pobierana prawidłowo, możesz zdefiniować moduł obsługi Koordynatora debugowania. Nick określa, co ma się stać, gdy czcionka nie zostanie wczytana 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 nieprawidłowo skonfigurowany.

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

Zastrzeżenia

Udostępnienie nowych czcionek Google Fonts na Androidzie zajmuje kilka miesięcy. Między dodaniem czcionki na stronie fonts.google.com a udostępnieniem czcionki za pomocą interfejsu Fonts API do pobrania (w systemie wyświetlania lub w komponencie tworzenia) następuje pewien czas. Nowo dodane czcionki mogą nie wczytywać się w aplikacji z elementem IllegalStateException. Aby ułatwić deweloperom identyfikowanie tego błędu w stosunku do innych typów błędów wczytywania czcionek, dodaliśmy opisowe komunikaty dotyczące wyjątku w sekcji Utwórz ze zmianami tutaj. Jeśli zauważysz jakieś problemy, zgłoś je za pomocą narzędzia do śledzenia problemów.

Używaj zmiennych czcionek

Czcionka zmienna to format czcionki, który pozwala na umieszczanie w jednym pliku czcionek różnych stylów. Za pomocą zmiennych czcionek możesz modyfikować osie (lub parametry), aby wygenerować preferowany styl. Osie mogą być standardowe, np. grubość, szerokość, skrzydła i kursywa, albo niestandardowe, które różnią się w zależności od czcionek.

Pięć konfiguracji tej samej zmiennej czcionki z różnymi wartościami osi.
Rysunek 1. Tekst z użyciem tej samej zmiennej czcionki dostosowanej do różnych wartości osi.

Użycie zmiennych czcionek zamiast zwykłych plików z czcionkami umożliwia utworzenie tylko jednego pliku czcionek.

Więcej informacji o czcionkach zmiennych znajdziesz w Google Fonts Knowledge, całym katalogu dostępnych czcionek zmiennych oraz w tabeli z obsługiwanymi osiami poszczególnych czcionek.

Ten dokument pokazuje, jak wdrożyć zmienną czcionkę w aplikacji do tworzenia wiadomości.

Wczytaj czcionkę zmiennej

  1. Pobierz zmienną czcionki, której chcesz używać (np. Roboto Flex) i umieść ją w folderze app/res/font w aplikacji. Upewnij się, że plikttfdodawany plik to zmienna wersja czcionki, nazwa pliku z czcionkami jest pisana małymi literami i nie zawiera żadnych znaków specjalnych.

  2. Aby wczytać czcionkę ze zmienną, zdefiniuj atrybut 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 FontVariation API umożliwia skonfigurowanie standardowych osi czcionek, takich jak weight [szerokość], width (szerokość) i (skośna). Są to standardowe osie dostępne z dowolną czcionkami. Można tworzyć różne konfiguracje czcionki w zależności od tego, gdzie będzie ona używana.

  3. Czcionki zmiennych są dostępne tylko w Androidzie O i nowszych, więc dodaj barierę i skonfiguruj odpowiednią kreację zastępczą:

    // 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 do zestawu stałych, aby łatwiej je wykorzystywać ponownie, i zastąp ustawienia czcionek tymi stałymi:

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

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

    W tym przykładzie wykorzystano typografię displayLarge typografii Material 3, która ma inne domyślne ustawienia czcionki i zalecane zastosowania. Używaj na przykład displayLarge jako krótkiego, o znaczeniu krytycznym, ponieważ jest to największy tekst na ekranie.

    W Material 3 możesz zmienić domyślne wartości TextStyle i fontFamily, aby dostosować swoją typografię. We fragmencie kodu powyżej konfigurujesz instancje TextStyle, aby dostosowywać ustawienia czcionek dla każdej rodziny czcionek.

  6. Po zdefiniowaniu typografii przekaż ją do MaterialTheme M3:

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

  7. Na koniec użyj funkcji kompozycyjnej Text i określ styl według 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 element kompozycyjny Text jest konfigurowany za pomocą stylu motywu Material Design i zawiera inną zmienną konfigurację czcionki. Możesz użyć MaterialTheme.typography, aby pobrać typografię przekazaną w komponencie M3 MaterialTheme.

3 różne teksty, z których każdy zawiera odmienne konfiguracje czcionek.
Rysunek 2. Zastosowano zmienną czcionkę w 3 różnych konfiguracjach.

Użyj niestandardowych osi

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

Wartości tych osi możesz zmienić w ustawieniach FontVariation.

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

  1. Aby użyć 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())
    }

    Wykonują te czynności:

    • Określ bariery dla wartości, które mogą akceptować. Jak widać w katalogu czcionek zmiennych, ascenderHeight (YTAS) ma minimalną wartość 649f, a maksymalną 854f.
    • Zwraca ustawienie czcionki, aby konfiguracja była gotowa do dodania do czcionki. W metodzie FontVariation.Setting() nazwa osi (YTAS, XTRA) jest zakodowana na stałe i przyjmuje wartość jako parametr.
  2. Używając osi z konfiguracją czcionki, przekaż dodatkowe parametry do każdego wczytywanego 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ść małych liter rosnących wzrosła, a pozostały tekst jest szerszy:

3 różne teksty przedstawiające różne konfiguracje czcionek ze zmiennymi, z ustawionymi niestandardowymi osiami – niektóre mają większe litery lub są szersze.
Rysunek 3. Tekst przedstawiający niestandardowe osie ustawione dla zmiennych czcionek.

Dodatkowe materiały

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