Ciągi tekstowe

Zasób tekstowy udostępnia ciągi tekstowe w aplikacji z opcjonalnymi stylami i formatowaniem. Istnieją 3 typy zasobów, które mogą dostarczać aplikacji ciąg znaków:

String
Zasób XML zawierający pojedynczy ciąg znaków.
Tablica ciągów znaków
Zasób XML, który udostępnia tablicę ciągów znaków.
Ciągi znaków ilości (liczba mnoga)
Zasób XML zawierający różne ciągi znaków liczebników liczby mnogiej.

Wszystkie ciągi mogą stosować określone znaczniki stylu i argumenty formatowania. Informacje o stylach i ciągach formatowania znajdziesz w sekcji Formatowanie i stosowanie stylów.

Ciąg znaków

Pojedynczy ciąg znaków, do którego można się odwołać z aplikacji lub innych plików zasobów (np. układu XML).

Uwaga: ciąg znaków to prosty zasób, do którego odwołuje się wartość podana w atrybucie name (nie nazwa pliku XML). Możesz więc połączyć zasoby w postaci ciągu z innymi prostymi zasobami w jednym pliku XML, w ramach jednego elementu <resources>.

lokalizacja pliku:
res/values/filename.xml
Nazwa pliku jest dowolna. Jako identyfikator zasobu używany jest element name elementu <string>.
typ danych skompilowanych zasobów:
Wskaźnik zasobu do String.
odniesienie do zasobów:
W Javie: R.string.string_name
W pliku XML:@string/string_name
składnia:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
elementy:
<resources>
Wymagane. Musi to być węzeł główny.

Brak atrybutów.

<string>
Ciąg znaków, który może zawierać tagi stylu. Pamiętajcie, że należy unikać apostrofów i cudzysłowów. Więcej informacji o odpowiednim tworzeniu stylu i formatowaniu ciągów znaków znajdziesz w sekcji Formatowanie i stylowanie poniżej.

atrybuty:

name
Ciąg znaków. Nazwa ciągu znaków. Ta nazwa jest używana jako identyfikator zasobu.
przykład:
Plik XML zapisany o res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Ten kod XML układu powoduje zastosowanie ciągu znaków do widoku:

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

Ten kod aplikacji pobiera ciąg znaków:

Kotlin

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

Java

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

Aby pobrać ciąg znaków, możesz użyć polecenia getString(int) lub getText(int). getText(int) zachowuje wszystkie style tekstu sformatowanego zastosowane do ciągu.

Tablica ciągów znaków

Tablica ciągów znaków, do których aplikacja może się odwołać.

Uwaga: tablica ciągów to prosty zasób, do którego odwołuje się wartość podana w atrybucie name (a nie nazwa pliku XML). Dzięki temu możesz połączyć zasoby tablicy tekstowej z innymi prostymi zasobami w jednym pliku XML, w ramach 1 elementu <resources>.

lokalizacja pliku:
res/values/filename.xml
Nazwa pliku jest dowolna. Jako identyfikator zasobu używany jest element name elementu <string-array>.
typ danych skompilowanych zasobów:
Wskaźnik zasobu do tablicy String.
odniesienie do zasobów:
W Javie: R.array.string_array_name
W pliku XML: @[package:]array/string_array_name
składnia:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
elementy:
<resources>
Wymagane. Musi to być węzeł główny.

Brak atrybutów.

<string-array>
Definiuje tablicę ciągów znaków. Zawiera co najmniej 1 element <item>.

atrybuty:

name
Ciąg znaków. Nazwa tablicy. Ta nazwa służy jako identyfikator zasobu do odwoływania się do tablicy.
<item>
Ciąg znaków, który może zawierać tagi stylu. Wartość może być odwołaniem do innego zasobu w postaci ciągu znaków. Musi być elementem podrzędnym elementu <string-array>. Pamiętajcie, że należy unikać apostrofów i cudzysłowów. W sekcji Formatowanie i style poniżej znajdziesz informacje o prawidłowym formatowaniu ciągów tekstowych i odpowiednim stylizowaniu ich.

Brak atrybutów.

przykład:
Plik XML zapisany 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>

Ten kod aplikacji pobiera tablicę ciągów znaków:

Kotlin

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

Java

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

Ciągi znaków ilości (liczba mnoga)

W różnych językach obowiązują różne zasady gramatycznego uzgadniania liczby znaków. Na przykład w języku angielskim „ilość 1” jest szczególnym przypadkiem. Możemy napisać „1 książkę”, ale w przypadku innych ilości książek wpisz „n książek”. To rozróżnienie między liczbą pojedynczej i mnogiej jest bardzo powszechne, ale w innych językach są one drobniejsze. Pełny zestaw obsługiwanych przez Androida to zero, one, two, few, many i other.

Reguły decydujące o tym, który przypadek zastosować w przypadku danego języka i liczby, mogą być bardzo złożone, dlatego Android udostępnia takie metody jak getQuantityString() pozwalające wybrać odpowiednie zasoby.

Ciągi znaków dotyczące ilości były wcześniej nazywane „ciągami znaków ilości” (i nadal nazywa się je w interfejsie API), jednak w liczbie mnogiej należy używać tylko. Na przykład użycie ciągów ilościowych do zaimplementowania Gmaila oznacza na przykład Gmaila „Odebrane” i „Odebrane (12)”. Użycie ciągów znaków zamiast wyrażenia if może wydawać się wygodne, ale trzeba pamiętać, że w niektórych językach (np. chiński) w ogóle nie są stosowane rozróżnienia gramatyczne, dlatego zawsze otrzymasz ciąg other.

Wybór ciągu znaków zależy wyłącznie od potrzeby gramatycznej. W języku angielskim ciąg znaków zero jest ignorowany, nawet jeśli liczba wynosi 0, ponieważ 0 nie różni się gramatycznie od liczby 2 ani żadnej innej liczby z wyjątkiem 1 („zero książek”, „jedna książka”, „dwie książki” itd.). Natomiast w języku koreańskim używany jest tylko ciąg znaków other.

Nie daj się wprowadzić w błąd przez fakt, że np. wyrażenie two może mieć zastosowanie tylko do ilości 2. Język może wymagać, aby wszystkie te liczby były traktowane jako jeden, ale inaczej w przypadku innych ilości. Zdaj się na tłumacza, który będzie wiedział, jakie rozróżnienia są potrzebne w jego języku.

Jeśli Twoja wiadomość nie zawiera liczby mnogiej, prawdopodobnie nie nadaje się ona do użycia liczby mnogiej. Na przykład w języku litewskim używa się liczby pojedynczej i liczby 1 i 101, więc „1 książka” zostaje przetłumaczona jako „1 knyga”, a „101 książek” – „101 knyga”. Z kolei „książka” to „knyga”, a „wiele książek” – „daug knygų”. Jeśli angielska wiadomość w liczbie mnogiej zawiera słowa „a książka” i „wiele książek” bez faktycznej liczby, może zostać przetłumaczony jako „knyga” (książka)/„daug knygų” (wiele książek), ale w języku litewskim wyświetli się „knyga” (pojedyncza książka), gdy liczba będzie wynosić 101.

Często można uniknąć ciągów dotyczących ilości, używając formuł neutralnych pod względem ilości, np. „Książki: 1”. Akceptowalne dla Twojej aplikacji style życia ułatwią życie zarówno Tobie, jak i Twoim tłumaczom.

W przypadku interfejsu API w wersji 24 lub nowszej możesz zamiast tego użyć znacznie wydajniejszej klasy MessageFormat ICU.

Uwaga: zbiór w liczbie mnogiej to prosty zasób, do którego odwołuje się wartość podana w atrybucie name (a nie nazwa pliku XML). Dzięki temu możesz łączyć zasoby w liczbie mnogiej z innymi prostymi zasobami w jednym pliku XML zawierającym jeden element <resources>.

lokalizacja pliku:
res/values/filename.xml
Nazwa pliku jest dowolna. Jako identyfikator zasobu używany jest element name elementu <plurals>.
odniesienie do zasobów:
W języku Java: R.plurals.plural_name
składnia:
<?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>
elementy:
<resources>
Wymagane. Musi to być węzeł główny.

Brak atrybutów.

<plurals>
Zbiór ciągów tekstowych, z którego 1 ciąg znaków jest podawany w zależności od ich ilości. Zawiera co najmniej 1 element <item>.

atrybuty:

name
Ciąg znaków. Nazwa pary ciągów znaków. Ta nazwa jest używana jako identyfikator zasobu.
<item>
Ciąg w liczbie mnogiej lub pojedynczej. Wartość może być odwołaniem do innego zasobu w postaci ciągu znaków. Musi być elementem podrzędnym elementu <plurals>. Pamiętajcie, że należy unikać apostrofów i cudzysłowów. W sekcji Formatowanie i styl poniżej znajdziesz informacje o prawidłowym tworzeniu stylu i formatowaniu ciągów tekstowych.

atrybuty:

quantity
Słowo kluczowe. Wartość wskazująca, kiedy należy użyć tego ciągu. Prawidłowe wartości, wybrane przykłady w nawiasach:
WartośćOpis
zeroGdy język wymaga specjalnego traktowania cyfr 0 (jak w języku arabskim).
oneGdy język wymaga specjalnego traktowania liczb, takich jak jeden (jak w przypadku liczby 1 w języku angielskim i większości innych języków; w języku rosyjskim każda liczba kończąca się na 1, ale nie kończąca się na 11).
twogdy język wymaga specjalnego traktowania liczb, np. 2 (np. 2 w walijskim lub 102 w języku słoweńskim).
fewgdy język wymaga specjalnego traktowania „małych” liczb (np. 2, 3 i 4 w języku czeskim oraz cyfr kończących się 2, 3 lub 4, ale nie 12, 13 i 14 w języku polskim).
manyGdy język wymaga specjalnego traktowania „dużych” liczb (jak w przypadku numerów kończących się na 11–99 w języku maltańskim).
otherGdy język nie wymaga specjalnego traktowania danej ilości (jak w przypadku wszystkich liczb w języku chińskim lub 42 w języku angielskim).
przykład:
Plik XML zapisany 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>

Plik XML zapisany 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>

Wykorzystanie:

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

Gdy używasz metody getQuantityString(), musisz dwukrotnie przekazać count, jeśli ciąg zawiera formatowanie ciągu znaków z liczbą. Na przykład w przypadku ciągu znaków %d songs found pierwszy parametr count wybiera odpowiedni ciąg znaków, a drugi parametr count jest wstawiany do symbolu zastępczego %d. Jeśli ciągi tekstowe nie zawierają formatowania ciągów, nie musisz przekazywać trzeciego parametru do funkcji getQuantityString.

Format i styl

Oto kilka ważnych informacji o prawidłowym formatowaniu i stylizowaniu zasobów ciągu znaków.

Obsługa znaków specjalnych

Jeśli ciąg zawiera znaki specjalne w języku XML, musisz zmienić znaczenie znaków zgodnie ze standardowymi regułami zmiany znaczenia znaków XML/HTML. Jeśli chcesz zmienić znaczenie znaku, który ma specjalne znaczenie na Androidzie, użyj poprzedzającego ukośnika lewego.

Domyślnie Android zwija sekwencje znaków odstępów w jedną spację. Aby tego uniknąć, umieść odpowiednią część ciągu w cudzysłowie prostym. W takim przypadku wszystkie znaki odstępu (w tym nowe wiersze) zostaną zachowane w obszarze cytowanym. Podwójne cudzysłowy pozwalają też używać zwykłych pojedynczych cudzysłowów bez zmiany znaczenia.

Znak Formularz(y) ze zmianą znaczenia
@ \@
? \?
Nowy wiersz \n
Tab \t
Znak Unicode U+XXXX \uXXXX
Pojedynczy cudzysłów (')

Dowolny z tych elementów:

  • \'
  • Umieść cały ciąg w cudzysłowie prostym (np. "This'll work")
Podwójny cudzysłów (") \"

Pamiętaj, że ujęcie ciągu znaków w pojedynczy cudzysłów nie zadziała.

Zwijanie odstępów i przekształcanie znaków ucieczki Androida odbywa się po przetworzeniu pliku zasobów jako pliku XML. Oznacza to, że <string> &#32; &#8200; &#8195;</string> (spacja, spacja interpunkcyjna, spacja Unicode Em) zwija się do jednej spacji (" "), ponieważ po przetworzeniu pliku jako pliku XML są to spacje Unicode. Aby zachować te pokoje w obecnej postaci, możesz je zacytować (<string>" &#32; &#8200; &#8195;"</string>) lub użyć funkcji zmiany znaczenia Androida (<string> \u0032 \u8200 \u8195</string>).

Uwaga: z punktu widzenia parsera XML nie ma różnicy między <string>"Test this"</string> a <string>&quot;Test this&quot;</string>. Oba formularze nie będą wyświetlać żadnych cudzysłowów, ale będą aktywować cytaty z zachowaniem spacji na Androidzie (co w tym przypadku nie będzie miało żadnego praktycznego zastosowania).

Ciągi formatowania

Jeśli musisz sformatować ciągi tekstowe, możesz to zrobić, umieszczając argumenty formatu w zasobie ciągu znaków, jak pokazano na przykładzie poniżej.

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

W tym przykładzie ciąg formatu ma 2 argumenty: %1$s to ciąg znaków, a %2$d to liczba dziesiętna. Następnie sformatuj ciąg znaków, wywołując funkcję getString(int, Object...). Na przykład:

Kotlin

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

Java

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

Styl ze znacznikami HTML

Możesz dodać styl do ciągów znaków za pomocą znaczników HTML. Na przykład:

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

Obsługiwane są następujące elementy HTML:

  • Pogrubienie: <b>
  • Kursywa: <i>, <cite>, <dfn>, <em>
  • O 25% większy tekst: <big>
  • 20% mniejszy tekst: <small>
  • Ustawianie właściwości czcionki: <font face=”font_family“ color=”hex_color”>. Przykłady możliwych rodzin czcionek to monospace, serif i sans_serif.
  • Ustawianie rodziny czcionek o stałej szerokości: <tt>
  • Przekreślenie: <s>, <strike>, <del>
  • Podkreślenie: <u>
  • Indeks górny: <sup>
  • Indeks dolny: <sub>
  • Punktory: <ul>, <li>
  • Podziały wierszy: <br>
  • Podział: <div>
  • Styl CSS: <span style=”color|background_color|text-decoration”>
  • Akapity: <p dir=”rtl | ltr” style=”...”>

Jeśli nie stosujesz formatowania, możesz ustawić tekst TextView bezpośrednio, wywołując metodę setText(java.lang.CharSequence). W niektórych przypadkach warto jednak utworzyć zasób tekstowy ze stylem, który będzie też używany jako ciąg formatu. Normalnie to nie działa, ponieważ metody format(String, Object...) i getString(int, Object...) usuwają z ciągu znaków wszystkie informacje o stylu. Aby obejść ten problem, należy napisać tagi HTML z elementami ze zmianą znaczenia, które po formatowaniu są przywracane za pomocą funkcji fromHtml(String). Na przykład:

  1. Zapisz zasób tekstowy ze stylem jako ciąg znaków zakodowany w kodzie HTML:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>
    

    Do tego sformatowanego ciągu znaków dodawany jest element <b>. Zwróć uwagę, że nawias otwierający zawiera kod ucieczki HTML za pomocą notacji &lt;.

  2. Następnie sformatuj ciąg znaków w zwykły sposób, ale użyj też metody fromHtml(String), aby przekonwertować tekst HTML na tekst ze stylem:

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

Metoda fromHtml(String) formatuje wszystkie encje HTML, dlatego pamiętaj o zmianie znaczenia wszystkich możliwych znaków HTML w ciągach, których używasz ze sformatowanym tekstem, za pomocą funkcji htmlEncode(String). Jeśli na przykład formatujesz ciąg, który zawiera znaki takie jak „<” czy „&”, przed sformatowaniem musisz użyć w nich zmiany znaczenia. Dzięki temu podczas przekazywania sformatowanego ciągu znaków za pomocą funkcji fromHtml(String) wyświetlą się w takiej postaci, w jakiej zostały napisane. Na przykład:

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

Styl za pomocą elementów spanowych

Spannable to obiekt tekstowy, którego styl można dostosować za pomocą właściwości czcionki, takich jak kolor i grubość czcionki. Za pomocą SpannableStringBuilder tworzysz tekst, a potem stosujesz do niego style zdefiniowane w pakiecie android.text.style.

Większość zadań związanych z tworzeniem tekstu rozciąganego możesz wykonać, korzystając z tych metod pomocniczych:

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

Poniższe metody bold, italic i color obejmują powyższe metody pomocnicze i pokazują konkretne przykłady stosowania stylów zdefiniowanych w pakiecie android.text.style. Podobne metody możesz stosować do innych typów stylów tekstu.

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

Oto przykład łączenia tych metod w celu zastosowania różnych stylów do poszczególnych słów w wyrażeniu:

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

Moduł Core-ktx Kotlin zawiera również funkcje rozszerzeń, które jeszcze bardziej ułatwiają pracę z spanami. Więcej informacji znajdziesz w dokumentacji pakietu android.text na GitHubie.

Więcej informacji o pracy ze spanami znajdziesz tutaj:

Styl z adnotacjami

Możesz zastosować styl złożony lub niestandardowy, używając klasy Annotation razem z tagiem <annotation> w plikach zasobów string.xml. Tag adnotacji umożliwia oznaczenie części ciągu znaków na potrzeby niestandardowego stylu przez zdefiniowanie w pliku XML niestandardowych par klucz-wartość, które platforma następnie przekształca w spany Annotation. Możesz potem pobrać te adnotacje i zastosować styl za pomocą klucza i wartości.

Podczas tworzenia adnotacji pamiętaj, aby dodać tag <annotation> do wszystkich tłumaczeń ciągu znaków w każdym pliku string.xml.


Stosowanie niestandardowej kroju czcionki do słowa „tekst” we wszystkich językach

Przykład – dodawanie niestandardowej kroju czcionki

  1. Dodaj tag <annotation> i zdefiniuj parę klucz-wartość. W tym przypadku kluczem jest font, a wartością jest typ czcionki, której chcemy użyć: 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. Wczytaj zasób tekstowy i znajdź adnotacje za pomocą klawisza font. Następnie utwórz span niestandardowy i zastąp obecne.

    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;
    

Jeśli używasz tego samego tekstu wiele razy, utwórz obiekt SpannableString raz i użyj go w razie potrzeby, aby uniknąć potencjalnych problemów z wydajnością i pamięcią.

Więcej przykładów użycia adnotacji znajdziesz w artykule o międzynarodowych stylach tekstu na Androidzie.

Rozpiętości adnotacji i grupowanie tekstu

Ponieważ spany Annotation mają też wartość ParcelableSpans, pary klucz-wartość są uporządkowane i rozdzielane. O ile odbiorca przesyłki wie, jak interpretować adnotacje, możesz stosować do tekstu w pakiecie style niestandardowe, za pomocą rozpiętości Annotation.

Aby zachować styl niestandardowy podczas przekazywania tekstu do pakietu intencji, musisz najpierw dodać do tekstu spany Annotation. Możesz to zrobić w zasobach XML za pomocą tagu <annotation> (jak pokazano w przykładzie powyżej) lub w kodzie, tworząc nowy obiekt Annotation i ustawiając go jako span, jak pokazano poniżej:

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

Pobierz tekst z Bundle jako SpannableString i przeanalizuj dołączone adnotacje, jak pokazano w przykładzie powyżej.

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

Więcej informacji o stylu tekstu znajdziesz tutaj: