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

Trang này mô tả cách bạn có thể tạo kiểu cho văn bản cho đoạn. Để thiết lập kiểu ở cấp độ đoạn, bạn có thể định cấu hình các tham số như textAlignlineHeight hoặc xác định ParagraphStyle của riêng mình.

Đặt căn chỉnh văn bản

Tham số textAlign cho phép bạn đặt căn chỉnh theo chiều ngang của văn bản trong một khu vực nền tảng của 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

@Composable
fun CenterText() {
    Text(
        "Hello World", textAlign = TextAlign.Center, modifier = Modifier.width(150.dp)
    )
}

Các từ

Nếu bạn muốn thiết lập cách căn chỉnh văn bản của một thành phần kết hợp Text theo cách thủ công, hãy ưu tiên sử dụng TextAlign.StartTextAlign.End thay vì TextAlign.LeftTextAlign.Right tương ứng, vì chúng phân giải đến cạnh bên phải của thành phần kết hợp Text tuỳ thuộc vào hướng văn bản của ngôn ngữ ư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.

Thêm nhiều kiểu trong một đoạn

Để thêm nhiều kiểu trong một đoạn, bạn có thể sử dụng ParagraphStyle trong AnnotatedString. Bạn có thể chú thích các kiểu này bằng các kiểu chú thích tuỳ ý. Sau khi một phần văn bản được đánh dấu bằng ParagraphStyle, phần đó sẽ được tách khỏi văn bản còn lại như thể có nguồn cấp dữ liệu dòng ở đầu và cuối.

Để biết thêm thông tin về cách thêm nhiều kiểu vào một văn bản, hãy xem phần Thêm nhiều kiểu vào văn bản.

AnnotatedStringtrình tạo an toàn về kiểu để dễ dàng tạo: buildAnnotatedString. Đoạn mã sau sử dụng buildAnnotatedString để đặt ParagraphStyle:

@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ý

Điều chỉnh chiều cao dòng và khoảng đệm

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. Bắt đầu từ BOM Compose phiên bản 2024.01.01, includeFontPadding được thiết lập thành false theo mặc định, giúp bố cục văn bản mặc định phù hợp hơn với các công cụ thiết kế phổ biến.

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. Để biết thêm thông tin về cách chọn đơn vị văn bản thích hợp, hãy xem TextUnit.

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.
Hình 1. Sử dụng tính năng Căn chỉnh và Cắt để điều chỉnh văn bản trong tập hợp lineHeight và cắt bớt khoảng trống nếu cần.

Text(
    text = text,
    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ể căn giữa và định kiểu văn bản thêm nữa bằng cách sử dụng các cấu hình với API thử nghiệm LineHeightStyle: 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 xem bài đăng trên blog Sửa lỗi khoảng đệm phông chữ trong văn bản Compose để tìm hiểu thêm về ngữ cảnh của thay đổi này, cách includeFontPadding hoạt động trong hệ thống Khung hiển thị, cũng như những thay đổi đối với Compose và các API LineHeightStyle mới.

Chèn dấu ngắt dòng

API LineBreak xác định các tiêu chí mà văn bản được chia thành nhiều dòng. Bạn có thể chỉ định kiểu ngắt dòng mà bạn muốn trong khối TextStyle của thành phần kết hợp Text. Sau đây là các loại ngắt dòng đặt trước:

  • Simple — Ngắt dòng cơ bản, nhanh. Nên dùng cho các trường nhập dữ liệu văn bản.
  • Heading — Ngắt dòng với các quy tắc rộng hơn. Nên dùng cho văn bản ngắn, chẳng hạn như tiêu đề.
  • Paragraph — Ngắt dòng chậm hơn, chất lượng cao hơn để dễ đọc hơn. Nên dùng cho số lượng văn bản lớn, chẳng hạn như đoạn văn.

Đoạn mã sau sử dụng cả SimpleParagraph để chỉ định hành vi ngắt dòng trên một khối văn bản dài:

TextSample(
    samples = mapOf(
        "Simple" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(130.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = LineBreak.Simple
                )
            )
        },
        "Paragraph" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(130.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = LineBreak.Paragraph
                )
            )
        }
    )
)

Một khối văn bản thể hiện chiến lược ngắt dòng đơn giản so với khối văn bản có chiến lược ngắt dòng được tối ưu hoá cho đoạn. Khối văn bản sử dụng chiến lược ngắt dòng đơn giản có nhiều độ biến thiên hơn về độ dài dòng.
Hình 1. Một khối văn bản có chiến lược ngắt dòng đơn giản (ở trên cùng) so với một khối văn bản có chiến lược ngắt dòng được tối ưu hoá cho đoạn (dưới cùng).

Trong kết quả trên, hãy lưu ý rằng hành vi ngắt dòng Paragraph tạo ra kết quả cân bằng hơn về mặt hình ảnh so với ngắt dòng Simple.

Tuỳ chỉnh dấu ngắt dòng

Bạn cũng có thể tạo cấu hình LineBreak của riêng mình bằng tham số Strategy. Strategy có thể là bất kỳ trạng thái nào sau đây:

  • Balanced – Cố gắng cân bằng độ dài dòng của văn bản, đồng thời áp dụng tính năng gạch nối tự động nếu chế độ này được bật. Nên dùng cho màn hình nhỏ, chẳng hạn như đồng hồ, để tối đa hoá lượng văn bản hiển thị.
  • HighQuality – Tối ưu hoá một đoạn văn bản để văn bản dễ đọc hơn, bao gồm cả giả thuyết nếu được bật. (Hãy đặt làm giá trị mặc định cho mọi giá trị không phải là Balanced hoặc Simple.)
  • Simple — chiến lược cơ bản và nhanh chóng. Nếu được bật, tính năng gạch nối chỉ được thực hiện đối với những từ không vừa trên toàn bộ dòng. Hữu ích khi chỉnh sửa văn bản nhằm tránh thay đổi vị trí trong khi nhập.

Đoạn mã sau đây cho thấy sự khác biệt giữa một đoạn có chế độ cài đặt mặc định và một đoạn được tối ưu hoá cho màn hình nhỏ bằng chiến lược ngắt dòng Balanced:

TextSample(
    samples = mapOf(
        "Balanced" to {
            val smallScreenAdaptedParagraph =
                LineBreak.Paragraph.copy(strategy = LineBreak.Strategy.Balanced)
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(200.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = smallScreenAdaptedParagraph
                )
            )
        },
        "Default" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(200.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default
            )
        }
    )
)

Một đoạn có chiến lược ngắt dòng cân bằng và một đoạn văn bản được định dạng mà không có chiến lược. Đoạn văn bản sử dụng chiến lược ngắt dòng cân bằng có độ dài dòng nhất quán hơn so với mặc định.
Hình 2. Một đoạn được định dạng bằng chiến lược ngắt dòng Balanced (trên cùng) so với một đoạn được định dạng mà không có chiến lược ngắt dòng.

Những điểm cần lưu ý của CJK

Bạn cũng có thể tuỳ chỉnh LineBreak bằng các API StrictnessWordBreak. Các API này được thiết kế riêng cho các ngôn ngữ CJK. Không phải lúc nào bạn cũng thấy hiệu quả của các API này bằng ngôn ngữ không phải CJK. Nhìn chung, các quy tắc ngắt dòng được xác định dựa trên ngôn ngữ.

Strictness mô tả độ nghiêm ngặt của ngắt dòng bằng các thuộc tính sau:

  • Default — Các quy tắc có thể gây lỗi mặc định cho ngôn ngữ. Có thể tương ứng với Normal hoặc Strict.
  • Loose — Các quy tắc ít hạn chế nhất. Phù hợp với các dòng ngắn.
  • Normal — Các quy tắc phổ biến nhất để ngắt dòng.
  • Strict — Các quy tắc nghiêm ngặt nhất về việc ngắt dòng.

WordBreak xác định cách chèn dấu ngắt dòng vào các từ có các thuộc tính sau:

  • Default — Các quy tắc có thể gây lỗi mặc định cho ngôn ngữ.
  • Phrase — Ngắt dòng dựa trên các cụm từ.

Đoạn mã sau sử dụng độ nghiêm ngặt Strict và chế độ cài đặt ngắt từ Phrase cho văn bản tiếng Nhật:

val customTitleLineBreak = LineBreak(
    strategy = LineBreak.Strategy.HighQuality,
    strictness = LineBreak.Strictness.Strict,
    wordBreak = LineBreak.WordBreak.Phrase
)
Text(
    text = "あなたに寄り添う最先端のテクノロジー。",
    modifier = Modifier.width(250.dp),
    fontSize = 14.sp,
    style = TextStyle.Default.copy(
        lineBreak = customTitleLineBreak
    )
)

Văn bản tiếng Nhật có chế độ cài đặt Strictness và WordBreak so với văn bản mặc định.
Hình 3. Định dạng văn bản bằng chế độ cài đặt StrictnessWordBreak (trên cùng) so với văn bản chỉ được định dạng bằng LineBreak.Heading (dưới cùng).

Dấu gạch nối văn bản được chia thành các dòng

Với API Hyphens, bạn có thể thêm tính năng hỗ trợ dấu gạch nối vào ứng dụng của mình. Dấu gạch nối là việc chèn một dấu câu giống như dấu gạch ngang để cho biết rằng một từ được chia trên các dòng văn bản. Khi bạn bật tuỳ chọn này, dấu gạch nối sẽ được thêm vào giữa các âm tiết của một từ tại các điểm gạch nối thích hợp.

Tính năng gạch nối không được bật theo mặc định. Để bật tính năng gạch nối, hãy thêm Hyphens.Auto làm tham số trong khối TextStyle:

TextSample(
    samples = mapOf(
        "Hyphens - None" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(130.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = LineBreak.Paragraph,
                    hyphens = Hyphens.None
                )
            )
        },
        "Hyphens - Auto" to {
            Text(
                text = SAMPLE_LONG_TEXT,
                modifier = Modifier
                    .width(130.dp)
                    .border(BorderStroke(1.dp, Color.Gray)),
                fontSize = 14.sp,
                style = TextStyle.Default.copy(
                    lineBreak = LineBreak.Paragraph,
                    hyphens = Hyphens.Auto
                )
            )
        }
    )
)

Một đoạn chưa bật tính năng dấu gạch nối và một đoạn văn bản đã bật tính năng gạch nối.
  Khi bạn bật tính năng gạch nối, một từ sẽ được gạch nối và chia thành hai dòng.
Hình 4. Một đoạn không bật tính năng gạch nối (ở trên cùng) so với một đoạn văn bản đã bật tính năng gạch nối (ở dưới cùng).

Khi được bật, tính năng gạch nối chỉ xảy ra trong các trường hợp sau:

  • Từ không vừa với một dòng. Nếu bạn sử dụng chiến lược ngắt dòng Simple, việc gạch nối một từ chỉ xảy ra nếu một dòng ngắn hơn so với một từ.
  • Ngôn ngữ thích hợp sẽ được đặt trên thiết bị của bạn, vì cách dấu gạch nối thích hợp được xác định bằng cách sử dụng các từ điển có trên hệ thống.