Văn bản trong Compose

Văn bản là phần chính của mọi giao diện người dùng và Jetpack Compose giúp cho việc hiển thị hoặc soạn thảo văn bản dễ dàng hơn. Compose tận dụng thành phần của các khối dựng, tức là bạn không cần phải ghi đè các thuộc tính và phương thức hay mở rộng những lớp lớn để có một thiết kế và logic cụ thể cho thành phần kết hợp hoạt động theo cách bạn muốn.

Cũng giống như nền tảng của công cụ, Compose cung cấp một BasicTextBasicTextField là các khung để hiển thị văn bản và xử lý dữ liệu nhập của người dùng. Ở cấp độ cao hơn, Compose cung cấp TextTextField, là những thành phần kết hợp tuân theo các nguyên tắc của Material Design. Hệ thống khuyến khích sử dụng vì chúng có giao diện phù hợp cho người dùng trên Android và bao gồm các tuỳ chọn khác để đơn giản hoá chế độ tuỳ chỉnh mà không cần viết nhiều mã code.

Hiển thị văn bản

Cách cơ bản nhất để hiển thị văn bản là sử dụng thành phần kết hợp Text với String như một đối số:

@Composable
fun SimpleText() {
    Text("Hello World")
}

Dòng chữ "Hello World" màu đen thuần tuý

Hiển thị văn bản trong tài nguyên

Bạn nên sử dụng tài nguyên chuỗi thay vì mã hoá cứng các giá trị Text, vì bạn có thể chia sẻ cùng một chuỗi với các Khung hiển thị Android cũng như chuẩn bị đưa ứng dụng của bạn ra thị trường quốc tế:

@Composable
fun StringResourceText() {
    Text(stringResource(R.string.hello_world))
}

Tạo kiểu văn bản

Thành phần kết hợp Text có nhiều thông số tuỳ chọn để tạo kiểu cho nội dung. Dưới đây là danh sách các thông số bao gồm hầu hết các trường hợp phổ biến nhất có sử dụng văn bản. Để xem tất cả các thông số của Text, bạn nên xem phần Mã nguồn văn bản trong Compose.

Bất cứ khi nào bạn cài đặt một trong các tham số này, tức là bạn đang áp dụng một kiểu cho toàn bộ giá trị văn bản. Nếu bạn cần áp dụng nhiều kiểu trong cùng một dòng hoặc đoạn, hãy xem mục nhiều kiểu trong cùng dòng.

Thay đổi màu văn bản

@Composable
fun BlueText() {
    Text("Hello World", color = Color.Blue)
}

Dòng chữ "Hello World" màu xanh lam

Thay đổi kích cỡ văn bản

@Composable
fun BigText() {
  Text("Hello World", fontSize = 30.sp)
}

Dòng chữ "Hello World" có kích thước lớn hơn

Tạo chữ in nghiêng

Sử dụng tham số fontStyle để in nghiêng văn bản (hoặc đặt FontStyle khác).

@Composable
fun ItalicText() {
  Text("Hello World", fontStyle = FontStyle.Italic)
}

Dòng chữ "Hello World" kiểu in nghiêng

Làm cho văn bản in đậm

Dùng thông số fontWeight để in đậm văn bản (hoặc đặt một giá trị khác FontWeight).

@Composable
fun BoldText() {
    Text("Hello World", fontWeight = FontWeight.Bold)
}

Dòng chữ "Hello World" kiểu in đậm

Căn chỉnh văn bản

Thông số textAlign cho phép cài đặt độ căn chỉnh của văn bản trong một phạm vi nền tảng thành phần kết hợp Text.

Theo mặc định, Text sẽ chọn cách căn chỉnh văn bản tự nhiên tuỳ thuộc vào giá trị nội dung nó:

  • Cạnh trái của vùng chứa Text dành cho các bảng chữ cái từ trái sang phải, chẳng hạn như chữ La tinh, Cyrillic hoặc Hangul
  • Cạnh phải của vùng chứa Text dành cho các bảng chữ cái từ phải sang trái, chẳng hạn như chữ Ả Rập hoặc chữ Do Thái
@Preview(showBackground = true)
@Composable
fun CenterText() {
    Text("Hello World", textAlign = TextAlign.Center,
                modifier = Modifier.width(150.dp))
}

Dòng chữ "Hello World" được căn giữa phần tử chứa chúng

Nếu bạn muốn đặt chế độ căn chỉnh văn bản theo cách thủ công cho thành phần kết hợp Text, hãy ưu tiên sử dụng TextAlign.StartTextAlign.End thay vì TextAlign.LeftTextAlign.Right, do chúng sẽ phân giải ở cạnh bên phải của thành phần kết hợp Text tuỳ vào hướng văn bản bằng ngôn ngữ được ưu tiên. Ví dụ: TextAlign.End căn chỉnh ở bên phải cho văn bản tiếng Pháp và bên trái cho văn bản tiếng Ả Rập, nhưng TextAlign.Right sẽ căn chỉnh bên phải cho dù bạn sử dụng bảng chữ cái nào.

Bóng

Thông số style cho phép đặt một đối tượng thuộc loại TextStyle và định cấu hình nhiều thông số, chẳng hạn như bóng. Shadow nhận được màu cho bóng đổ, độ lệch hoặc vị trí màu nằm dọc trên Text và bán kính làm mờ trông như thế nào.

@Preview(showBackground = true)
@Composable
fun TextShadow() {
    val offset = Offset(5.0f, 10.0f)
    Text(
        text = "Hello world!",
        style = TextStyle(
            fontSize = 24.sp,
            shadow = Shadow(
                color = Color.Blue,
                offset = offset,
                blurRadius = 3f
            )
        )
    )
}

Dòng chữ "Hello World" có bóng đổ màu xanh dương

Thiết lập phông chữ

Text có một thông số fontFamily để cho phép cài đặt phông chữ được dùng trong thành phần kết hợp. Theo mặc định, bộ phông chữ serif, sans-serif, phông chữ đơn cách và chữ viết tay được bao gồm:

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

Dòng chữ "Hello World" trong hai phông chữ khác nhau, sử dụng và không sử dụng phông chữ có serif

Bạn có thể sử dụng thuộc tính fontFamily để làm việc với các phông chữ và kiểu chữ tuỳ chỉnh đã xác định trong thư mục res/font:

trong môi trường phát triển " class="l10n-absolute-url-src screenshot" l10n-attrs-original-order="src,alt,width,class" src="https://developer.android.com/static/images/jetpack/compose/text-font-folder.png" width="400" />

Ví dụ này cho thấy cách bạn xác định fontFamily dựa trên các tệp phông chữ đó và sử dụng hàm 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)
)

Cuối cùng, bạn có thể chuyển fontFamily sang thành phần kết hợp Text. Bởi vì fontFamily có thể bao gồm các trọng số khác nhau, nên bạn có thể đặt fontWeight theo cách thủ công để chọn trọng số phù hợp cho văn bản của mình:

Column {
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Light)
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
    Text(
        ..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal,
        fontStyle = FontStyle.Italic
    )
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Medium)
    Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}

Dòng chữ "Hello World" theo một vài kiểu và loại trọng số khác nhau

Để tìm hiểu cách đặt kiểu chữ trong toàn bộ ứng dụng, hãy xem tài liệu về giao diện.

Nhiều kiểu trong một văn bản

Để đặt các kiểu khác nhau trong cùng một thành phần kết hợpText bạn phải sử dụng một chuỗi AnnotatedString, có thể được chú thích theo kiểu tuỳ ý

AnnotatedString là một lớp dữ liệu chứa:

  • Một giá trị Text
  • List của SpanStyleRange, tương đương với định kiểu trong cùng dòng với phạm vi vị trí trong giá trị văn bản
  • List của ParagraphStyleRange, xác định cách căn chỉnh văn bản, câu lệnh văn bản, chiều cao dòng và kiểu thụt lề văn bản

TextStyle được sử dụng trong thành phần kết hợp Text , trong khi SpanStyleParagraphStyle là để sử dụng trong AnnotatedString.

Sự khác biệt giữa SpanStyleParagraphStyleParagraphStyle có thể áp dụng cho cả một đoạn văn bản, còn SpanStyle có thể áp dụng ở cấp ký tự. Khi một phần văn bản được đánh dấu bằng ParagraphStyle, phần đó sẽ được tách ra khỏi phần còn lại như thể nó có nguồn cấp dữ liệu dòng ở đầu và cuối.

AnnotatedStringtrình tạo loại an toàn để dễ dàng tạo: buildAnnotatedString.

@Composable
fun MultipleStylesInText() {
    Text(
        buildAnnotatedString {
            withStyle(style = SpanStyle(color = Color.Blue)) {
                append("H")
            }
            append("ello ")

            withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Red)) {
                append("W")
            }
            append("orld")
        }
    )
}

Dòng chữ "Hello World" với một số thay đổi về kiểu trong cùng dòng; H có màu xanh lam và W có màu đỏ và chữ đậm

Chúng ta có thể đặt ParagraphStyle theo cách tương tự:

@Composable
fun ParagraphStyle() {
    Text(
        buildAnnotatedString {
            withStyle(style = ParagraphStyle(lineHeight = 30.sp)) {
                withStyle(style = SpanStyle(color = Color.Blue)) {
                    append("Hello\n")
                }
                withStyle(
                    style = SpanStyle(
                        fontWeight = FontWeight.Bold,
                        color = Color.Red
                    )
                ) {
                    append("World\n")
                }
                append("Compose")
            }
        }
    )
}

Ba đoạn văn theo ba kiểu khác nhau: Xanh dương, đỏ và chữ đậm, và đen thuần tuý

Số dòng tối đa

Để giới hạn số dòng hiển thị trong một thành phần kết hợp Text, hãy đặt thông số maxLines:

@Composable
fun LongText() {
    Text("hello ".repeat(50), maxLines = 2)
}

Một đoạn văn bản dài bị cắt ngắn sau hai dòng

Dàn chữ văn bản

Khi giới hạn một đoạn văn bản dài, bạn có thể muốn chỉ định TextOverflow. điều này chỉ xuất hiện nếu văn bản hiển thị bị cắt ngắn. Để thực hiện việc này, hãy đặt thông số textOverflow:

@Composable
fun OverflowedText() {
    Text("Hello Compose ".repeat(50), maxLines = 2, overflow = TextOverflow.Ellipsis)
}

Đoạn văn bản dài bị cắt ngắn sau ba dòng, với dấu ba chấm ở cuối

includeFontPadding API và lineHeight API

includeFontPadding là một thuộc tính cũ bổ sung thêm khoảng đệm dựa trên chỉ số phông chữ ở đầu dòng đầu tiên và cuối dòng cuối cùng của văn bản. Trong Compose 1.2.0, includeFontPadding được đặt thành true theo mặc định.

Hiện tại, bạn nên thiết lập includeFontPadding thành false (xoá khoảng đệm bổ sung) bằng API PlatformTextStyle không dùng nữa trong Compose 1.2.0 và điều chỉnh văn bản thêm.

Tính năng định cấu hình lineHeight không còn mới – đã có kể từ Android Q. Bạn có thể định cấu hình lineHeight cho Text bằng cách sử dụng tham số lineHeight. Tham số này sẽ phân bổ chiều cao dòng trong mỗi dòng văn bản. Sau đó, bạn có thể dùng LineHeightStyle API mới để định cấu hình thêm cách căn chỉnh văn bản này trong khoảng trống và xoá khoảng trắng.

Bạn có thể cần điều chỉnh lineHeight bằng cách sử dụng đơn vị văn bản "em" (kích thước phông chữ tương đối) thay vì "sp" (pixel theo tỷ lệ) để tăng độ chính xác. Bạn có thể xem tài liệu tại đây để hiểu rõ hơn về cách chọn đơn vị văn bản thích hợp.

Hình ảnh hiển thị lineHeight dưới dạng số đo dựa trên các dòng ngay phía trên và phía dưới.
Sử dụng tính năng Căn chỉnh và Cắt bỏ để điều chỉnh văn bản trong "lineHeight" đã đặt và cắt bớt khoảng trống thừa nếu cần
@Composable
fun AlignedText() {
    Text(
        text = myText,
        style = LocalTextStyle.current.merge(
            TextStyle(
                lineHeight = 2.5.em,
                platformStyle = PlatformTextStyle(
                    includeFontPadding = false
                ),
                lineHeightStyle = LineHeightStyle(
                    alignment = LineHeightStyle.Alignment.Center,
                    trim = LineHeightStyle.Trim.None
                )
            )
        )
    )
}

Ngoài việc điều chỉnh lineHeight, giờ đây, bạn có thể tiếp tục căn giữa và tạo kiểu cho văn bản bằng cách sử dụng các cấu hình với API LineHeightStyle thử nghiệm: LineHeightStyle.AlignmentLineHeightStyle.Trim (includeFontPadding phải được đặt thành false để tính năng Cắt bỏ hoạt động). Tính năng Căn chỉnh và Cắt bỏ sử dụng khoảng trống đo được giữa các dòng văn bản để phân bổ phù hợp hơn cho tất cả các dòng – bao gồm cả một dòng văn bản và dòng trên cùng của khối văn bản.

LineHeightStyle.Alignment xác định cách căn chỉnh dòng trong khoảng trống được cung cấp theo chiều cao của dòng. Trong mỗi dòng, bạn có thể căn chỉnh để văn bản hiện trên cùng, dưới cùng, ở giữa hoặc theo tỷ lệ. Sau đó, LineHeightStyle.Trim sẽ cho phép bạn để lại hoặc xoá khoảng trống thừa ở đầu dòng đầu tiên và cuối dòng cuối cùng của văn bản, được tạo từ bất kỳ thao tác điều chỉnh nào dùng lineHeight và tính năng Căn chỉnh. Sau đây là các ví dụ minh hoạ văn bản nhiều dòng với các cấu hình LineHeightStyle.Trim khác nhau khi căn chỉnh ở giữa (LineHeightStyle.Alignment.Center).

Hình ảnh minh hoạ LineHeightStyle.Trim.None Hình ảnh minh hoạ LineHeightStyle.Trim.Both
LineHeightStyle.Trim.None LineHeightStyle.Trim.Both
Hình ảnh minh hoạ LineHeightStyle.Trim.FirstLineTop Hình ảnh minh hoạ LineHeightStyle.Trim.LastLineBottom
LineHeightStyle.Trim.FirstLineTop LineHeightStyle.Trim.LastLineBottom

Hãy tham khảo bài đăng trên blog về Khắc phục khoảng đệm phông chữ trong văn bản Compose để tìm hiểu thêm về ngữ cảnh của sự thay đổi này, cách includeFontPadding hoạt động trong hệ thống Khung hiển thị, những thay đổi mà chúng tôi đã thực hiện đối với Compose và các API LineHeightStyle mới.

Giao diện

Để sử dụng giao diện của ứng dụng cho kiểu văn bản, hãy xem tài liệu về giao diện.

Sự tương tác của người dùng

Jetpack Compose cho phép tương tác chi tiết trong Text. Hiện tại, việc lựa chọn văn bản linh hoạt hơn và có thể thực hiện trên các bố cục thành phần kết hợp. Các lượt tương tác của người dùng trong văn bản khác với các bố cục thành phần kết hợp khác, vì bạn không thể thêm công cụ sửa đổi vào một phần của thành phần kết hợp Text. Phần này làm nổi bật các API khác nhau để cho phép người dùng tương tác.

Chọn văn bản

Theo mặc định, các thành phần kết hợp không thể chọn được, nghĩa là theo mặc định, người dùng không thể chọn và sao chép văn bản từ ứng dụng của bạn. Để bật tính năng lựa chọn văn bản, bạn cần bao trùm các thành phần văn bản bằng một thành phần kết hợp SelectionContainer:

@Composable
fun SelectableText() {
    SelectionContainer {
        Text("This text is selectable")
    }
}

Một đoạn văn bản ngắn do người dùng chọn.

Bạn có thể muốn tắt tính năng lựa chọn trên các phần cụ thể của một phạm vi có thể chọn. Để làm như vậy, bạn cần bao bọc phần không được chọn bằng một thành phần kết hợp DisableSelection:

@Composable
fun PartiallySelectableText() {
    SelectionContainer {
        Column {
            Text("This text is selectable")
            Text("This one too")
            Text("This one as well")
            DisableSelection {
                Text("But not this one")
                Text("Neither this one")
            }
            Text("But again, you can select this one")
            Text("And this one too")
        }
    }
}

Đoạn văn bản dài hơn. Người dùng đã cố gắng chọn toàn bộ đoạn văn, nhưng vì hai dòng đã áp dụng tính năng DisableSelection nên chúng không được chọn.

Nhận vị trí của một lượt nhấp vào văn bản

Để theo dõi số lượt nhấp trên Text, bạn có thể thêm công cụ sửa đổi clickable. Tuy nhiên, nếu bạn muốn biết vị trí lượt nhấp trong thành phần kết hợp Text, trong trường hợp bạn có nhiều hành động dựa trên các phần văn bản khác nhau, thì bạn cần sử dụng một ClickableText thay thế:

@Composable
fun SimpleClickableText() {
    ClickableText(
        text = AnnotatedString("Click Me"),
        onClick = { offset ->
            Log.d("ClickableText", "$offset -th character is clicked.")
        }
    )
}

Nhấp kèm chú thích

Khi người dùng nhấp vào thành phần kết hợp Text, bạn cần đính kèm thông tin bổ sung vào một phần của giá trị Text, ví dụ như URL được đính kèm với một từ cụ thể để mở trong trình duyệt. Để thực hiện việc này, bạn cần đính kèm chú thích, nội dung này sẽ lấy thẻ (String), một mục (String) và một phạm vi văn bản làm thông số. Từ AnnotatedString, các chú thích này có thể được lọc bằng thẻ hoặc phạm vi văn bản. Sau đây là ví dụ:

@Composable
fun AnnotatedClickableText() {
    val annotatedText = buildAnnotatedString {
        append("Click ")

        // We attach this *URL* annotation to the following content
        // until `pop()` is called
        pushStringAnnotation(tag = "URL",
                             annotation = "https://developer.android.com")
        withStyle(style = SpanStyle(color = Color.Blue,
                                    fontWeight = FontWeight.Bold)) {
            append("here")
        }

        pop()
    }

    ClickableText(
        text = annotatedText,
        onClick = { offset ->
            // We check if there is an *URL* annotation attached to the text
            // at the clicked position
            annotatedText.getStringAnnotations(tag = "URL", start = offset,
                                                    end = offset)
                .firstOrNull()?.let { annotation ->
                    // If yes, we log its value
                    Log.d("Clicked URL", annotation.item)
                }
        }
    )
}

Nhập và sửa đổi văn bản

TextField cho phép người dùng nhập dữ liệu và sửa đổi văn bản. Có hai cấp độ triển khai TextField:

  1. TextField là cách triển khai Material Design. Bạn nên chọn cách triển khai này vì cách này tuân theo các nguyên tắc material design:
    • Kiểu mặc định là filled
    • OutlinedTextField là phiên bản định kiểu outline
  2. BasicTextField cho phép người dùng chỉnh sửa văn bản thông qua bàn phím phần cứng hoặc phần mềm, nhưng không cung cấp đồ trang trí như gợi ý hoặc phần giữ chỗ.
@Composable
fun SimpleFilledTextFieldSample() {
    var text by remember { mutableStateOf("Hello") }

    TextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

Một trường văn bản có thể chỉnh sửa có từ "Hello". Trường này có nhãn "label" không thể chỉnh sửa.

@Composable
fun SimpleOutlinedTextFieldSample() {
    var text by remember { mutableStateOf("") }

    OutlinedTextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("Label") }
    )
}

Một trường văn bản có thể chỉnh sửa với đường viền và nhãn màu tím.

Tạo kiểu TextField

TextFieldBasicTextField cùng có chung nhiều thông số phổ biến để tuỳ chỉnh các thông số đó. Danh sách đầy đủ cho TextField có sẵn trong mã nguồn TextField. Đây là danh sách không đầy đủ một vài thông số hữu ích:

  • singleLine
  • maxLines
  • textStyle
@Composable
fun StyledTextField() {
    var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }

    TextField(
        value = value,
        onValueChange = { value = it },
        label = { Text("Enter text") },
        maxLines = 2,
        textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
        modifier = Modifier.padding(20.dp)
    )
}

Một TextField nhiều dòng, có hai dòng có thể chỉnh sửa cùng với nhãn

Bạn nên sử dụng TextField thay vì BasicTextField khi thiết kế yêu cầu một Material TextField hoặc OutlineTextField. Tuy nhiên, nên sử dụng BasicTextField khi xây dựng các thiết kế không cần trang trí từ thông số Material.

Tuỳ chọn bàn phím

TextField cho phép bạn cài đặt các tuỳ chọn cấu hình bàn phím, chẳng hạn như bố cục bàn phím hoặc bật tính năng tự động sửa lỗi nếu được bàn phím hỗ trợ. Một số tuỳ chọn có thể không được đảm bảo nếu bàn phím phần mềm không tuân thủ các tuỳ chọn được cung cấp ở đây. Dưới đây là danh sách các tuỳ chọn bàn phím được hỗ trợ:

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

Định dạng

TextField cho phép bạn thiết lập VisualTransformation trên giá trị nhập, ví dụ như thay thế các ký tự bằng * đối với mật khẩu hoặc chèn dấu gạch ngang cứ sau 4 chữ số đối với số thẻ tín dụng:

@Composable
fun PasswordTextField() {
    var password by rememberSaveable { mutableStateOf("") }

    TextField(
        value = password,
        onValueChange = { password = it },
        label = { Text("Enter password") },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
    )
}

Một trường văn bản nhập mật khẩu, với văn bản bị che

Các ví dụ khác có sẵn trong mã nguồn VisualTransformSamples.

Xoá dữ liệu đầu vào

Một tác vụ phổ biến khi chỉnh sửa văn bản là xoá các ký tự ở đầu hoặc biến đổi chuỗi nhập vào mỗi khi có sự thay đổi.

Như một mô hình, bạn nên giả định rằng bàn phím có thể tạo ra chỉnh sửa lớn và tuỳ ý cho từng onValueChange. Điều này có thể xảy ra, ví dụ như khi người dùng sử dụng tính năng tự động sửa, thay thế một từ bằng một biểu tượng cảm xúc hoặc các tính năng chỉnh sửa thông minh khác. Để xử lý chính xác điều này, hãy viết bất kỳ logic chuyển đổi nào với giả định rằng văn bản hiện tại đã được chuyển đến onValueChange không liên quan đến các giá trị trước đó hoặc tiếp theo sẽ được chuyển tới onValueChange.

Để triển khai trường văn bản không cho phép các số 0 ở đầu, bạn có thể thực hiện bằng cách loại bỏ tất cả số 0 đứng đầu trên mọi thay đổi giá trị.

@Composable
fun NoLeadingZeroes() {
  var input by rememberSaveable { mutableStateOf("") }
  TextField(
      value = input,
      onValueChange = { newText ->
          input = newText.trimStart { it == '0' }
      }
  )
}

Để kiểm soát vị trí con trỏ khi xóa văn bản, hãy sử dụng phương thức nạp chồng TextFieldValue của TextField như là một phần của trạng thái.

Phông chữ có thể tải xuống

Kể từ phiên bản Compose 1.2-alpha07, bạn có thể dùng API phông chữ có thể tải xuống trong ứng dụng Compose để tải phông chữ của Google xuống một cách không đồng bộ và dùng cho ứng dụng của mình.

Hiện tại, chúng tôi chưa hỗ trợ phông chữ có thể tải xuống do nhà cung cấp tuỳ chỉnh cung cấp.

Sử dụng phông chữ có thể tải xuống theo lập trình

Để tải phông chữ theo phương thức lập trình trong ứng dụng, hãy thực hiện các bước sau:

  1. Thêm phần phụ thuộc:

    Groovy

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

    Kotlin

    dependencies {
        ...
        implementation("androidx.compose.ui:ui-text-google-fonts:1.3.0")
    }
  2. Khởi chạy GoogleFont.Provider bằng thông tin xác thực của Google Fonts.
    @OptIn(ExperimentalTextApi::class)
    val provider = GoogleFont.Provider(
       providerAuthority = "com.google.android.gms.fonts",
       providerPackage = "com.google.android.gms",
       certificates = R.array.com_google_android_gms_fonts_certs
    )
    
    Các tham số mà nhà cung cấp nhận được là:
    • Nhà cung cấp phông chữ cho Google Fonts.
    • Gói trình cung cấp phông chữ để xác minh danh tính của trình cung cấp
    • Danh sách tập hợp hàm băm cho các chứng chỉ để xác minh danh tính của trình cung cấp. Bạn có thể tìm các hàm băm bắt buộc cho nhà cung cấp Google Fonts ở tệp font_certs.xml trong ứng dụng mẫu JetChat.
    Lưu ý bạn cần thêm chú thích ExperimentalTextApi để có thể sử dụng các API phông chữ có thể tải xuống trong ứng dụng của mình.
  3. Xác định FontFamily như sau:
    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)
    )
    
    Bạn có thể truy vấn các tham số khác cho phông chữ như độ đậm và kiểu chữ bằng FontWeightFontStyle tương ứng:
    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. Định cấu hình FontFamily để sử dụng trong hàm kết hợp Văn bản, và thế là xong.
    Text(
        fontFamily = fontFamily,
        text = "Hello World!"
    )
    
    Bạn cũng có thể xác định Kiểu chữ để sử dụng FontFamily.
    val MyTypography = Typography(
       body1 = TextStyle(
       fontFamily = fontFamily,
       fontWeight = FontWeight.Normal,
       fontSize = ...
    ),
       body2 = TextStyle(
       fontFamily = fontFamily,
       fontWeight = FontWeight.Bold,
       letterSpacing = ...
    ),
       h4 = TextStyle(
       fontFamily = fontFamily,
       fontWeight = FontWeight.SemiBold
       ...
    ),
    ...
    
    Sau đó, hãy đặt Kiểu chữ thành chủ đề của ứng dụng:
    MyAppTheme(
       typography = MyTypography
    ) {
    ...
    

Để xem ví dụ về một ứng dụng triển khai phông chữ có thể tải xuống trong Compose cùng với Material3, hãy nhớ kiểm tra ứng dụng mẫu JetChat.

Phông chữ dự phòng

Bạn có thể xác định một chuỗi dự phòng cho phông chữ của mình trong trường hợp phông chữ không được tải xuống đúng cách. Chẳng hạn nếu bạn có phông chữ có thể tải xuống được xác định như sau:

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

Bạn có thể xác định giá trị mặc định cho phông chữ với cả hai trọng số như sau:

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

Hãy nhớ thêm chính xác dữ liệu nhập.

Việc xác định FontFamily như thế này sẽ tạo ra một FontFamily chứa hai chuỗi, mỗi trọng số một chuỗi. Cơ chế tải sẽ cố gắng phân giải phông chữ trực tuyến trước, sau đó mới đến phông chữ nằm trong thư mục tài nguyên R.font cục bộ của bạn.

Gỡ lỗi triển khai

Để xác minh liệu phông chữ có được tải xuống chính xác hay không, bạn cần xác định một trình xử lý coroutine gỡ lỗi. Trình xử lý của bạn cung cấp hành vi của việc cần làm trong trường hợp phông chữ không tải không đồng bộ được.

Hãy bắt đầu bằng cách tạo CoroutineExceptionHandler.

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

Sau đó, truyền hàm này đến phương thức createFontFamilyResolver để yêu cầu trình phân giải sử dụng trình xử lý mới:

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

Bạn cũng có thể sử dụng API isAvailableOnDevice của nhà cung cấp để kiểm tra xem họ có sẵn không và các chứng chỉ có được định cấu hình chính xác hay không. Để thực hiện việc này, bạn có thể gọi phương thức isAvailableOnDevice trả về giá trị false nếu nhà cung cấp được định cấu hình không chính xác.

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

Chú ý

Google Fonts mất vài tháng để cung cấp phông chữ mới trên Android. Có một khoảng thời gian trống giữa thời điểm phông chữ được thêm vào fonts.google.com và thời điểm phông chữ đó có sẵn thông qua API phông chữ có thể tải xuống (trong hệ thống chế độ xem hoặc trong Compose). Phông chữ mới được thêm vào có thể không tải được trên ứng dụng của bạn bằng IllegalStateException. Để giúp nhà phát triển xác định lỗi này so với các loại lỗi tải phông chữ khác, chúng tôi đã thêm thông báo mô tả cho trường hợp ngoại lệ này trong Compose với những thay đổi tại đây.