Tài nguyên chuỗi

Tài nguyên chuỗi cung cấp chuỗi văn bản cho ứng dụng bằng định dạng và định kiểu văn bản tuỳ chọn. Có 3 loại tài nguyên có thể cung cấp chuỗi cho ứng dụng của bạn:

Chuỗi
Tài nguyên XML cung cấp một chuỗi đơn.
Mảng chuỗi
Tài nguyên XML cung cấp một mảng chuỗi.
Chuỗi số lượng (số nhiều)
Tài nguyên XML chứa các chuỗi khác nhau để số hoá.

Tất cả chuỗi đều có khả năng áp dụng một số đối số định dạng và đánh dấu kiểu. Để biết thông tin về định kiểu và định dạng các chuỗi, vui lòng xem mục Định dạng và định kiểu.

Chuỗi

Một chuỗi đơn có thể được tham chiếu từ ứng dụng hoặc từ các tệp tài nguyên khác (chẳng hạn như bố cục XML).

Lưu ý: Chuỗi là một tài nguyên đơn giản được tham chiếu bằng cách sử dụng giá trị đã cung cấp trong thuộc tính name (không phải tên của tệp XML). Vì vậy, bạn có thể kết hợp tài nguyên chuỗi với các tài nguyên đơn giản khác trong một tệp XML, bên dưới một phần tử <resources>.

vị trí tệp:
res/values/filename.xml
Bạn có thể tuỳ ý đặt tên tệp. name của phần tử <string> được dùng làm mã nhận dạng tài nguyên.
loại dữ liệu tài nguyên được biên dịch:
Con trỏ tài nguyên đến String.
mã tham chiếu tài nguyên:
Trong Java: R.string.string_name
Trong XML: @string/string_name
cú pháp:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
phần tử:
<resources>
Bắt buộc. Đây phải là nút gốc.

Không có thuộc tính nào.

<string>
Một chuỗi có thể chứa các thẻ định kiểu. Lưu ý là bạn phải thoát khỏi dấu nháy đơn và dấu ngoặc kép. Để biết thêm thông tin về cách định kiểu và định dạng đúng chuỗi của bạn, vui lòng xem phần Định dạng và định kiểu bên dưới.

thuộc tính:

name
Chuỗi. Tên cho chuỗi. Tên này sẽ được dùng làm mã nhận dạng tài nguyên.
ví dụ:
Tệp XML được lưu vào res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Tệp XML bố cục này áp dụng một chuỗi cho một Thành phần hiển thị:

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello" />

Mã xử lý ứng dụng này truy xuất một chuỗi:

Kotlin

val string: String = getString(R.string.hello)

Java

String string = getString(R.string.hello);

Bạn có thể sử dụng getString(int) hoặc getText(int) để truy xuất chuỗi. getText(int) giữ lại mọi kiểu văn bản đa dạng thức được áp dụng cho chuỗi.

Mảng chuỗi

Một mảng chuỗi có thể được tham chiếu từ ứng dụng.

Lưu ý: Mảng chuỗi là một tài nguyên đơn giản được tham chiếu bằng cách sử dụng giá trị đã cung cấp trong thuộc tính name (không phải tên của tệp XML). Do đó, bạn có thể kết hợp các tài nguyên mảng chuỗi với các tài nguyên đơn giản khác trong một tệp XML, bên dưới một phần tử <resources>.

vị trí tệp:
res/values/filename.xml
Bạn có thể tuỳ ý đặt tên tệp. name của phần tử <string-array> được dùng làm mã nhận dạng tài nguyên.
loại dữ liệu tài nguyên được biên dịch:
Con trỏ tài nguyên đến một mảng String.
mã tham chiếu tài nguyên:
Trong Java: R.array.string_array_name
Trong XML: @[package:]array/string_array_name
cú pháp:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
phần tử:
<resources>
Bắt buộc. Đây phải là nút gốc.

Không có thuộc tính nào.

<string-array>
Xác định một mảng chuỗi. Chứa một hoặc nhiều phần tử <item>.

thuộc tính:

name
Chuỗi. Tên cho mảng. Tên này được dùng làm mã nhận dạng tài nguyên tham chiếu đến mảng.
<item>
Một chuỗi có thể chứa các thẻ định kiểu. Giá trị này có thể là một tham chiếu đến tài nguyên chuỗi khác. Phải là phần tử con của phần tử <string-array>. Lưu ý là bạn phải thoát khỏi dấu nháy đơn và dấu ngoặc kép. Vui lòng xem phần Định dạng và định kiểu bên dưới để biết thông tin về cách tạo kiểu và định dạng đúng cách các chuỗi.

Không có thuộc tính nào.

ví dụ:
Tệp XML được lưu vào res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="planets_array">
        <item>Mercury</item>
        <item>Venus</item>
        <item>Earth</item>
        <item>Mars</item>
    </string-array>
</resources>

Mã xử lý ứng dụng này truy xuất một mảng chuỗi:

Kotlin

val array: Array<String> = resources.getStringArray(R.array.planets_array)

Java

Resources res = getResources();
String[] planets = res.getStringArray(R.array.planets_array);

Chuỗi số lượng (số nhiều)

Mỗi ngôn ngữ có những quy tắc riêng đối với thoả thuận ngữ pháp về số lượng. Chẳng hạn như trong tiếng Anh, số lượng 1 là một trường hợp đặc biệt. Chúng tôi viết "1 cuốn sách", nhưng đối với bất kỳ số lượng nào khác, chúng tôi sẽ viết "n cuốn sách". Sự khác biệt giữa số ít và số nhiều này rất phổ biến, nhưng các ngôn ngữ khác cũng giúp phân biệt rõ hơn. Tập hợp đầy đủ do Android hỗ trợ là zero, one, two, few, manyother.

Quy tắc quyết định trường hợp sử dụng cho một ngôn ngữ và số lượng nhất định có thể rất phức tạp, vì vậy, Android cung cấp cho bạn các phương thức như getQuantityString() để chọn tài nguyên phù hợp cho bạn.

Mặc dù trước đây được gọi là "chuỗi số lượng" (và vẫn được gọi như vậy trong API), nhưng bạn chỉ nên sử dụng chuỗi số lượng cho số nhiều. Chẳng hạn sẽ là một sai lầm nếu bạn sử dụng chuỗi số lượng để triển khai một số mục như "Hộp thư đến" của Gmail so với "Hộp thư đến (12)" khi có thư chưa đọc. Việc sử dụng chuỗi số lượng thay vì một câu lệnh if có vẻ thuận tiện, nhưng quan trọng là bạn cần lưu ý một số ngôn ngữ (chẳng hạn như tiếng Trung) không tạo ra khác biệt nào về ngữ pháp. Vì vậy, bạn sẽ luôn nhận chuỗi other.

Việc lựa chọn chuỗi cần sử dụng chỉ dựa trên sự cần thiết về mặt ngữ pháp. Trong tiếng Anh, một chuỗi cho zero sẽ bị bỏ qua ngay cả khi số lượng là 0, vì số 0 không khác về mặt ngữ pháp với số 2 hoặc bất kỳ số nào khác trừ số 1 ("0 cuốn sách", "một cuốn sách", "2 cuốn sách", v.v. ). Ngược lại, trong tiếng Hàn, chỉ chuỗi other được là sử dụng.

Cũng đừng nhầm lẫn bởi thực tế two có vẻ như chỉ có thể áp dụng cho số lượng 2: một ngôn ngữ có thể yêu cầu xử lý số 2, 12, 102 (v.v.) như nhau, nhưng khác với các số lượng khác. Hãy bám sát vào người biên dịch để biết ngôn ngữ của họ thực sự nhấn mạnh đến những điểm khác biệt nào.

Nếu thông báo của bạn không chứa số lượng, thì đó có thể không phải là lựa chọn phù hợp cho số nhiều. Chẳng hạn như trong tiếng Lithuania, dạng số ít được sử dụng cho cả 1 và 101, vì vậy "1 cuốn sách" được dịch thành "1 knyga" và "101 cuốn sách" được dịch thành "101 knyga". Trong khi đó, "một cuốn sách" là "knyga" và "nhiều cuốn sách" là "daug knygų". Nếu thông báo ở dạng số nhiều trong tiếng Anh chứa "một cuốn sách" (số ít) và "nhiều cuốn sách" (số nhiều) mà không có số thực, thì thông báo đó có thể được dịch là "knyga" (một cuốn sách)/"daug knygų" (nhiều cuốn sách), nhưng trong quy tắc của Lithuania, nó sẽ hiển thị "knyga" (một cuốn sách duy nhất) khi số đó là 101.

Bạn cũng có thể tránh các chuỗi số lượng bằng cách sử dụng các công thức trung lập về số lượng, chẳng hạn như "Sách: 1". Việc này giúp cuộc sống của bạn và những người biên dịch trở nên dễ chịu hơn nếu đó là phong cách chấp nhận được cho ứng dụng của bạn.

Thay vào đó, trên API cấp 24 trở lên, bạn có thể sử dụng lớp ICU MessageFormat mạnh mẽ hơn nhiều.

Lưu ý: Bộ sưu tập số nhiều là một tài nguyên đơn giản được tham chiếu bằng cách sử dụng giá trị đã cung cấp trong thuộc tính name (không phải tên của tệp XML). Do đó, bạn có thể kết hợp tài nguyên số nhiều với các tài nguyên đơn giản khác trong một tệp XML, bên dưới một phần tử <resources>.

vị trí tệp:
res/values/filename.xml
Bạn có thể tuỳ ý đặt tên tệp. name của phần tử <plurals> được dùng làm mã nhận dạng tài nguyên.
mã tham chiếu tài nguyên:
Trong Java: R.plurals.plural_name
cú pháp:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals
        name="plural_name">
        <item
            quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
            >text_string</item>
    </plurals>
</resources>
phần tử:
<resources>
Bắt buộc. Đây phải là nút gốc.

Không có thuộc tính nào.

<plurals>
Một bộ sưu tập các chuỗi, trong đó một chuỗi được cung cấp tuỳ thuộc vào số lượng của một nội dung nào đó. Chứa một hoặc nhiều phần tử <item>.

thuộc tính:

name
Chuỗi. Tên của cặp chuỗi. Tên này sẽ được dùng làm mã nhận dạng tài nguyên.
<item>
Một chuỗi số nhiều hoặc số ít. Giá trị này có thể là một tham chiếu đến tài nguyên chuỗi khác. Phải là phần tử con của phần tử <plurals>. Lưu ý là bạn phải thoát khỏi dấu nháy đơn và dấu ngoặc kép. Vui lòng xem phần Định dạng và định kiểu bên dưới để biết thông tin về cách tạo kiểu và định dạng đúng cách các chuỗi.

thuộc tính:

quantity
Từ khoá. Một giá trị cho biết thời điểm nên sử dụng chuỗi này. Các giá trị hợp lệ, với những ví dụ không đầy đủ trong ngoặc đơn:
Giá trịMô tả
zeroKhi ngôn ngữ yêu cầu cách xử lý đặc biệt đối với số 0 (như trong tiếng Ả Rập).
oneKhi ngôn ngữ yêu cầu xử lý đặc biệt các số chẳng hạn như số một (như với số 1 trong tiếng Anh và hầu hết ngôn ngữ khác; trong tiếng Nga, bất kỳ số nào kết thúc bằng số 1 nhưng không kết thúc bằng số 11 đều ở trong lớp này).
twoKhi ngôn ngữ yêu cầu xử lý đặc biệt các số chẳng hạn như số hai (như với số 2 trong tiếng Wales hoặc 102 trong tiếng Slovenia).
fewKhi ngôn ngữ yêu cầu cách xử lý đặc biệt các số "nhỏ" (như với số 2, 3 và 4 trong tiếng Séc; hoặc các số kết thúc bằng số 2, 3 hoặc 4 nhưng không phải là số 12, 13 hoặc 14 trong tiếng Ba Lan).
manyKhi ngôn ngữ yêu cầu xử lý đặc biệt các số "lớn" (như các số kết thúc từ 11-99 trong tiếng Malta).
otherKhi ngôn ngữ không yêu cầu xử lý đặc biệt về số lượng đã cho (như với tất cả các số trong tiếng Trung, hoặc số 42 trong tiếng Anh).
ví dụ:
Tệp XML được lưu vào res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals name="numberOfSongsAvailable">
        <!--
             As a developer, you should always supply "one" and "other"
             strings. Your translators will know which strings are actually
             needed for their language. Always include %d in "one" because
             translators will need to use %d for languages where "one"
             doesn't mean 1 (as explained above).
          -->
        <item quantity="one">%d song found.</item>
        <item quantity="other">%d songs found.</item>
    </plurals>
</resources>

Tệp XML được lưu vào res/values-pl/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <plurals name="numberOfSongsAvailable">
        <item quantity="one">Znaleziono %d piosenkę.</item>
        <item quantity="few">Znaleziono %d piosenki.</item>
        <item quantity="other">Znaleziono %d piosenek.</item>
    </plurals>
</resources>

Cách sử dụng:

Kotlin

val count = getNumberOfSongsAvailable()
val songsFound = resources.getQuantityString(R.plurals.numberOfSongsAvailable, count, count)

Java

int count = getNumberOfSongsAvailable();
Resources res = getResources();
String songsFound = res.getQuantityString(R.plurals.numberOfSongsAvailable, count, count);

Khi sử dụng phương thức getQuantityString(), bạn cần phải truyền count hai lần nếu chuỗi của bạn mang định dạng chuỗi có số. Chẳng hạn như đối với chuỗi %d songs found, tham số count đầu tiên sẽ chọn chuỗi số nhiều phù hợp, và tham số count thứ hai được chèn vào phần giữ chỗ %d. Nếu các chuỗi số nhiều không chứa định dạng chuỗi thì bạn không cần truyền tham số thứ ba tới getQuantityString.

Định dạng và kiểu

Dưới đây là một số điều quan trọng mà bạn nên biết về cách định dạng và định kiểu đúng cách cho tài nguyên chuỗi.

Xử lý các ký tự đặc biệt

Khi một chuỗi chứa các ký tự có cách sử dụng đặc biệt trong XML, bạn phải thoát các ký tự theo quy tắc thoát XML/HTML chuẩn. Nếu cần thoát một ký tự có ý nghĩa đặc biệt trong Android, bạn nên sử dụng dấu gạch chéo ngược phía trước.

Theo mặc định, Android sẽ thu gọn các chuỗi ký tự có khoảng trắng thành một dấu cách. Bạn có thể tránh trường hợp này bằng cách đặt phần liên quan của chuỗi trong dấu ngoặc kép. Trong trường hợp này, mọi ký tự có khoảng trắng (kể cả các dòng mới) sẽ được giữ nguyên trong vùng đã được đóng ngoặc. Dấu ngoặc kép cũng sẽ cho phép bạn sử dụng dấu nháy đơn lẻ thông thường.

Nhân vật (Các) Biểu mẫu thoát
@ \@
? \?
Dòng mới \n
Thẻ \t
Ký tự Unicode U+XXXX \uXXXX
Dấu nháy đơn (')

Bất kỳ trường hợp nào sau đây:

  • \'
  • Đính kèm toàn bộ chuỗi trong dấu ngoặc kép (Chẳng hạn như "This'll work")
Dấu ngoặc kép (") \"

Lưu ý là bạn không thể bao quanh chuỗi bằng dấu nháy đơn.

Việc thu gọn khoảng trắng và ký tự thoát trên Android sẽ xảy ra sau khi tệp tài nguyên của bạn được phân tích cú pháp dưới dạng XML. Tức là <string> &#32; &#8200; &#8195;</string> (dấu cách, dấu chấm câu, dấu cách Unicode Em) đều thu gọn thành một dấu cách duy nhất (" "), vì tất cả đều là dấu cách Unicode sau khi tệp được phân tích cú pháp dưới dạng XML. Để giữ nguyên các dấu cách đó, bạn có thể trích dẫn các dấu cách đó (<string>" &#32; &#8200; &#8195;"</string>) hoặc sử dụng ký tự thoát của Android (<string> \u0032 \u8200 \u8195</string>).

Lưu ý: Từ góc nhìn của trình phân tích cú pháp XML, không có sự khác biệt giữa <string>"Test this"</string><string>&quot;Test this&quot;</string>. Cả hai biểu mẫu đều không cho thấy dấu ngoặc kép nào, nhưng sẽ kích hoạt dấu ngoặc kép giữ nguyên dấu cách của Android (điều này không có tác dụng thực tế trong trường hợp này).

Định dạng chuỗi

Nếu cần định dạng chuỗi, bạn có thể thực hiện bằng cách đặt các đối số định dạng vào tài nguyên chuỗi, như được minh hoạ bằng tài nguyên ví dụ sau.

<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>

Trong ví dụ này, chuỗi định dạng có hai đối số: %1$s là một chuỗi và %2$d là một số thập phân. Sau đó, định dạng chuỗi bằng cách gọi getString(int, Object...). Ví dụ:

Kotlin

var text = getString(R.string.welcome_messages, username, mailCount)

Java

String text = getString(R.string.welcome_messages, username, mailCount);

Tạo kiểu bằng mã đánh dấu HTML

Bạn có thể thêm kiểu cho chuỗi bằng mã đánh dấu HTML. Ví dụ:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="welcome">Welcome to <b>Android</b>!</string>
</resources>

Các phần tử HTML sau được hỗ trợ:

  • In đậm: <b>
  • In nghiêng: <i>, <cite>, <dfn>, <em>
  • Văn bản lớn hơn 25%: <big>
  • Văn bản nhỏ hơn 20%: <small>
  • Đặt thuộc tính phông chữ: <font face=”font_family“ color=”hex_color”>. Ví dụ về các bộ phông chữ có thể có: monospace, serifsans_serif.
  • Đặt một bộ phông chữ đơn cách: <tt>
  • Gạch ngang chữ: <s>, <strike>, <del>
  • Gạch dưới: <u>
  • Chỉ số trên: <sup>
  • Chỉ số dưới: <sub>
  • Dấu đầu dòng: <ul>, <li>
  • Ngắt dòng: <br>
  • Phép chia: <div>
  • Kiểu CSS: <span style=”color|background_color|text-decoration”>
  • Đoạn: <p dir=”rtl | ltr” style=”…”>

Nếu không áp dụng định dạng, bạn có thể trực tiếp thiết lập văn bản TextView bằng cách gọi setText(java.lang.CharSequence). Tuy nhiên, trong một số trường hợp, có thể bạn muốn tạo một tài nguyên văn bản được định kiểu cũng được dùng làm chuỗi định dạng. Thường thì cách này không hiệu quả vì các phương thức format(String, Object...)getString(int, Object...) sẽ tách mọi thông tin về kiểu khỏi chuỗi. Giải pháp cho trường hợp này là viết các thẻ HTML chứa các thực thể thoát, sau đó được khôi phục bằng fromHtml(String) sau khi định dạng. Ví dụ:

  1. Lưu trữ tài nguyên văn bản đã được tạo kiểu dưới dạng chuỗi thoát HTML:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>
    

    Trong chuỗi được định dạng này, phần tử <b> sẽ được thêm. Lưu ý dấu ngoặc mở là ký tự thoát HTML, sử dụng ký hiệu &lt;.

  2. Sau đó định dạng chuỗi như bình thường, nhưng cũng gọi fromHtml(String) để chuyển đổi văn bản HTML thành văn bản được tạo kiểu:

    Kotlin

    val text: String = getString(R.string.welcome_messages, username, mailCount)
    val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
    

    Java

    String text = getString(R.string.welcome_messages, username, mailCount);
    Spanned styledText = Html.fromHtml(text, FROM_HTML_MODE_LEGACY);
    

Vì phương thức fromHtml(String) định dạng mọi thực thể HTML, hãy nhớ thoát mọi ký tự HTML có thể có trong chuỗi mà bạn sử dụng bằng văn bản đã định dạng, bằng cách sử dụng htmlEncode(String). Chẳng hạn như nếu bạn đang định dạng một chuỗi có chứa các ký tự như "<" hoặc "&", thì các chuỗi này phải được thoát trước khi định dạng, để khi chuỗi đã định dạng được truyền qua fromHtml(String) thì các ký tự sẽ xuất hiện theo cách viết ban đầu. Ví dụ:

Kotlin

val escapedUsername: String = TextUtils.htmlEncode(username)

val text: String = getString(R.string.welcome_messages, escapedUsername, mailCount)
val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)

Java

String escapedUsername = TextUtils.htmlEncode(username);

String text = getString(R.string.welcome_messages, escapedUsername, mailCount);
Spanned styledText = Html.fromHtml(text);

Tạo kiểu bằng các spannable

Spannable là một đối tượng văn bản mà bạn có thể tạo kiểu bằng các thuộc tính kiểu chữ như màu sắc và độ đậm của phông chữ. Bạn sử dụng SpannableStringBuilder để tạo văn bản rồi sau đó áp dụng các kiểu đã xác định trong gói android.text.style cho văn bản đó.

Bạn có thể sử dụng các phương thức trợ giúp sau đây để thiết lập phần lớn công việc tạo văn bản có span:

Kotlin

/**
 * Returns a CharSequence that concatenates the specified array of CharSequence
 * objects and then applies a list of zero or more tags to the entire range.
 *
 * @param content an array of character sequences to apply a style to
 * @param tags the styled span objects to apply to the content
 *        such as android.text.style.StyleSpan
 */
private fun apply(content: Array<out CharSequence>, vararg tags: Any): CharSequence {
    return SpannableStringBuilder().apply {
        openTags(tags)
        content.forEach { charSequence ->
            append(charSequence)
        }
        closeTags(tags)
    }
}

/**
 * Iterates over an array of tags and applies them to the beginning of the specified
 * Spannable object so that future text appended to the text will have the styling
 * applied to it. Do not call this method directly.
 */
private fun Spannable.openTags(tags: Array<out Any>) {
    tags.forEach { tag ->
        setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK)
    }
}

/**
 * "Closes" the specified tags on a Spannable by updating the spans to be
 * endpoint-exclusive so that future text appended to the end will not take
 * on the same styling. Do not call this method directly.
 */
private fun Spannable.closeTags(tags: Array<out Any>) {
    tags.forEach { tag ->
    if (length > 0) {
            setSpan(tag, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        } else {
            removeSpan(tag)
        }
    }
}

Java

/**
 * Returns a CharSequence that concatenates the specified array of CharSequence
 * objects and then applies a list of zero or more tags to the entire range.
 *
 * @param content an array of character sequences to apply a style to
 * @param tags the styled span objects to apply to the content
 *        such as android.text.style.StyleSpan
 *
 */
private static CharSequence applyStyles(CharSequence[] content, Object[] tags) {
    SpannableStringBuilder text = new SpannableStringBuilder();
    openTags(text, tags);
    for (CharSequence item : content) {
        text.append(item);
    }
    closeTags(text, tags);
    return text;
}

/**
 * Iterates over an array of tags and applies them to the beginning of the specified
 * Spannable object so that future text appended to the text will have the styling
 * applied to it. Do not call this method directly.
 */
private static void openTags(Spannable text, Object[] tags) {
    for (Object tag : tags) {
        text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK);
    }
}

/**
 * "Closes" the specified tags on a Spannable by updating the spans to be
 * endpoint-exclusive so that future text appended to the end will not take
 * on the same styling. Do not call this method directly.
 */
private static void closeTags(Spannable text, Object[] tags) {
    int len = text.length();
    for (Object tag : tags) {
        if (len > 0) {
            text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        } else {
            text.removeSpan(tag);
        }
    }
}

Các phương thức bold, italiccolor sau đây bao gồm những phương thức trợ giúp ở trên và minh hoạ các ví dụ cụ thể về cách áp dụng kiểu được xác định trong gói android.text.style. Bạn có thể tạo các phương thức tương tự để thực hiện các kiểu văn bản khác.

Kotlin

/**
 * Returns a CharSequence that applies boldface to the concatenation
 * of the specified CharSequence objects.
 */
fun bold(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.BOLD))

/**
 * Returns a CharSequence that applies italics to the concatenation
 * of the specified CharSequence objects.
 */
fun italic(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.ITALIC))

/**
 * Returns a CharSequence that applies a foreground color to the
 * concatenation of the specified CharSequence objects.
 */
fun color(color: Int, vararg content: CharSequence): CharSequence =
        apply(content, ForegroundColorSpan(color))

Java

/**
 * Returns a CharSequence that applies boldface to the concatenation
 * of the specified CharSequence objects.
 */
public static CharSequence bold(CharSequence... content) {
    return apply(content, new StyleSpan(Typeface.BOLD));
}

/**
 * Returns a CharSequence that applies italics to the concatenation
 * of the specified CharSequence objects.
 */
public static CharSequence italic(CharSequence... content) {
    return apply(content, new StyleSpan(Typeface.ITALIC));
}

/**
 * Returns a CharSequence that applies a foreground color to the
 * concatenation of the specified CharSequence objects.
 */
public static CharSequence color(int color, CharSequence... content) {
    return apply(content, new ForegroundColorSpan(color));
}

Sau đây là ví dụ về cách liên kết các phương thức này với nhau để áp dụng nhiều kiểu cho từng từ trong một cụm từ:

Kotlin

// Create an italic "hello, " a red "world",
// and bold the entire sequence.
val text: CharSequence = bold(italic(getString(R.string.hello)),
        color(Color.RED, getString(R.string.world)))

Java

// Create an italic "hello, " a red "world",
// and bold the entire sequence.
CharSequence text = bold(italic(getString(R.string.hello)),
    color(Color.RED, getString(R.string.world)));

Mô-đun core-ktx cũng chứa các hàm mở rộng giúp việc sử dụng span thậm chí còn dễ dàng hơn. Bạn có thể xem tài liệu về gói android.text trên GitHub để tìm hiểu thêm.

Để biết thêm thông tin về cách sử dụng span, vui lòng xem các đường liên kết sau:

Tạo kiểu bằng chú thích

Bạn có thể tạo kiểu phức tạp hoặc tuỳ chỉnh bằng cách sử dụng lớp Annotation cùng với thẻ <annotation> trong các tệp tài nguyên strings.xml. Thẻ chú thích cho phép bạn đánh dấu các phần của chuỗi để định kiểu tuỳ chỉnh bằng cách xác định các cặp khoá-giá trị tuỳ chỉnh trong XML mà khung đó chuyển đổi thành các span Annotation. Sau đó, bạn có thể truy xuất các chú thích này và sử dụng khoá và giá trị để áp dụng kiểu.

Khi tạo các chú thích, hãy đảm bảo bạn thêm thẻ <annotation> vào tất cả các bản dịch của chuỗi trong mọi tệp strings.xml.


Áp dụng một kiểu chữ tuỳ chỉnh cho từ “văn bản” trong tất cả ngôn ngữ

Chẳng hạn như thêm kiểu chữ tuỳ chỉnh

  1. Thêm thẻ <annotation> và xác định cặp khoá-giá trị. Trong trường hợp này, khoá là phông chữ, và giá trị là loại phông chữ chúng ta muốn sử dụng: title_emphasis

    // values/strings.xml
    <string name="title">Best practices for <annotation font="title_emphasis">text</annotation> on Android</string>
    
    // values-es/strings.xml
    <string name="title"><annotation font="title_emphasis">Texto</annotation> en Android: mejores prácticas</string>
    
  2. Tải tài nguyên chuỗi và tìm các chú thích bằng khoá phông chữ. Sau đó, tạo một span tuỳ chỉnh và thay thế span hiện có.

    Kotlin

    // get the text as SpannedString so we can get the spans attached to the text
    val titleText = getText(R.string.title) as SpannedString
    
    // get all the annotation spans from the text
    val annotations = titleText.getSpans(0, titleText.length, Annotation::class.java)
    
    // create a copy of the title text as a SpannableString.
    // the constructor copies both the text and the spans. so we can add and remove spans
    val spannableString = SpannableString(titleText)
    
    // iterate through all the annotation spans
    for (annotation in annotations) {
       // look for the span with the key font
       if (annotation.key == "font") {
          val fontName = annotation.value
          // check the value associated to the annotation key
          if (fontName == "title_emphasis") {
             // create the typeface
             val typeface = getFontCompat(R.font.permanent_marker)
             // set the span at the same indices as the annotation
             spannableString.setSpan(CustomTypefaceSpan(typeface),
                titleText.getSpanStart(annotation),
                titleText.getSpanEnd(annotation),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
          }
       }
    }
    
    // now, the spannableString contains both the annotation spans and the CustomTypefaceSpan
    styledText.text = spannableString
    

    Java

    // get the text as SpannedString so we can get the spans attached to the text
    SpannedString titleText = (SpannedString) getText(R.string.title);
    
    // get all the annotation spans from the text
    Annotation[] annotations = titleText.getSpans(0, titleText.length(), Annotation.class);
    
    // create a copy of the title text as a SpannableString.
    // the constructor copies both the text and the spans. so we can add and remove spans
    SpannableString spannableString = new SpannableString(titleText);
    
    // iterate through all the annotation spans
    for (Annotation annotation: annotations) {
      // look for the span with the key font
      if (annotation.getKey().equals("font")) {
        String fontName = annotation.getValue();
        // check the value associated to the annotation key
        if (fontName.equals("title_emphasis")) {
        // create the typeface
        Typeface typeface = ResourcesCompat.getFont(this, R.font.roboto_mono);
        // set the span at the same indices as the annotation
        spannableString.setSpan(new CustomTypefaceSpan(typeface),
          titleText.getSpanStart(annotation),
          titleText.getSpanEnd(annotation),
          Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
      }
    }
    
    // now, the spannableString contains both the annotation spans and the CustomTypefaceSpan
    styledText.text = spannableString;
    

Nếu đang dùng nhiều lần cùng một văn bản, bạn nên tạo đối tượng SpannableString một lần và sử dụng lại nếu cần để tránh các vấn đề tiềm ẩn về hiệu suất và bộ nhớ.

Để xem thêm ví dụ về cách sử dụng chú thích, vui lòng xem phần Tạo kiểu cho văn bản quốc tế hoá trong Android

Các span chú thích và phân vùng văn bản

Vì các span Annotation cũng là ParcelableSpans, các cặp khoá-giá trị sẽ được phân loại lẫn không phân loại. Miễn là người nhận phân vùng biết cách diễn giải các chú thích, bạn có thể sử dụng các span Annotation để áp dụng kiểu tuỳ chỉnh cho văn bản đã được phân vùng.

Để duy trì kiểu tuỳ chỉnh khi chuyển văn bản đến Gói ý định, trước tiên, bạn cần thêm các span Annotation vào văn bản. Bạn có thể thực hiện việc này trong các tài nguyên XML thông qua thẻ <annotation>, như minh hoạ trong ví dụ ở trên hoặc trong mã bằng cách tạo một Annotation mới và thiết lập dưới dạng span như sau:

Kotlin

val spannableString = SpannableString("My spantastic text")
val annotation = Annotation("font", "title_emphasis")
spannableString.setSpan(annotation, 3, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

// start Activity with text with spans
val intent = Intent(this, MainActivity::class.java)
intent.putExtra(TEXT_EXTRA, spannableString)
startActivity(intent)

Java

SpannableString spannableString = new SpannableString("My spantastic text");
Annotation annotation = new Annotation("font", "title_emphasis");
spannableString.setSpan(annotation, 3, 7, 33);

// start Activity with text with spans
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(TEXT_EXTRA, spannableString);
this.startActivity(intent);

Truy xuất văn bản từ Bundle dưới dạng SpannableString rồi phân tích cú pháp các chú thích đính kèm, như trong ví dụ trên.

Kotlin

// read text with Spans
val intentCharSequence = intent.getCharSequenceExtra(TEXT_EXTRA) as SpannableString

Java

// read text with Spans
SpannableString intentCharSequence = (SpannableString)intent.getCharSequenceExtra(TEXT_EXTRA);

Để biết thêm thông tin về kiểu văn bản, vui lòng xem các đường liên kết sau: