Строковые ресурсы (представления)

Концепции и реализация Jetpack Compose

Строковый ресурс предоставляет текстовые строки для вашего приложения с возможностью стилизации и форматирования текста. Существует три типа ресурсов, которые могут предоставлять вашему приложению строки:

Нить
XML-ресурс, содержащий одну строку.
Массив строк
XML-ресурс, предоставляющий массив строк.
Последовательности чисел (во множественном числе)
XML-ресурс, содержащий различные строки для образования множественного числа.

Все строки могут быть стилизованы и иметь аргументы форматирования. Информацию о стилизации и форматировании строк см. в разделе « Форматирование и стилизация» .

Нить

Отдельная строка, на которую можно ссылаться из приложения или из других файлов ресурсов (например, XML-макета).

Местоположение файла:
res/values/ filename .xml
Имя файла произвольное. В качестве идентификатора ресурса используется name элемента <string> .
скомпилированный тип данных ресурса:
Указатель ресурса на String .
Справочный ресурс:
В Java: R.string. string_name
В XML: @string/ string_name
синтаксис:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
элементы:
<resources>
Обязательно. Это должен быть корневой узел.

Нет атрибутов.

<string>
Строка, которая может содержать теги стилей. Обратите внимание, что необходимо экранировать апострофы и кавычки. Дополнительную информацию о правильном оформлении и форматировании строк см. в разделе «Форматирование и стили» ниже.

атрибуты:

name
Строка . Имя для строки. Это имя используется в качестве идентификатора ресурса.
пример:
XML-файл сохранен по адресу res/values/strings.xml :
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Этот XML-код разметки применяет строку к представлению:

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

Данный код приложения извлекает строку:

Котлин

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

Java

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

Для получения строки можно использовать либо getString(int) , либо getText(int) . getText(int) сохраняет все примененные к строке форматированные текстовые стили.

Массив строк

Массив строк, к которым можно обращаться из приложения.

Местоположение файла:
res/values/ filename .xml
Имя файла произвольное. В качестве идентификатора ресурса используется name элемента <string-array> .
скомпилированный тип данных ресурса:
Указатель ресурса на массив String .
Справочный ресурс:
В Java: R.array. string_array_name
В XML: @[ package :]array/ string_array_name
синтаксис:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
элементы:
<resources>
Обязательно. Это должен быть корневой узел.

Нет атрибутов.

<string-array>
Определяет массив строк. Содержит один или несколько элементов <item> .

атрибуты:

name
Строка . Имя для массива. Это имя используется в качестве идентификатора ресурса для ссылки на массив.
<item>
Строка, которая может содержать теги стилей. Значение может быть ссылкой на другой строковый ресурс. Должна быть дочерним элементом элемента <string-array> . Обратите внимание, что необходимо экранировать апострофы и кавычки. См. раздел «Форматирование и стилизация » ниже для получения информации о том, как правильно стилизовать и форматировать строки.

Нет атрибутов.

пример:
XML-файл сохранен по адресу 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>
Данный код приложения извлекает массив строк:

Котлин

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

Java

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

Последовательность чисел (во множественном числе)

В разных языках существуют разные правила грамматического согласования с количеством. Например, в английском языке количество 1 является особым случаем. Мы пишем "1 книга", но для любого другого количества мы бы написали " n книг". Это различие между единственным и множественным числом очень распространено, но в других языках существуют более тонкие различия. Полный набор слов, поддерживаемых Android, включает zero , one , two , few , many и other .

Правила определения того, какой регистр использовать для данного языка и количества, могут быть очень сложными, поэтому Android предоставляет вам такие методы, как getQuantityString() чтобы выбрать подходящий ресурс.

В API 24 и выше можно использовать гораздо более мощный класс ICU MessageFormat .

Местоположение файла:
res/values/ filename .xml
Имя файла произвольное. В качестве идентификатора ресурса используется name элемента <plurals> .
Справочный ресурс:
В Java: R.plurals. plural_name
синтаксис:
<?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>
элементы:
<resources>
Обязательно. Это должен быть корневой узел.

Нет атрибутов.

<plurals>
Набор строк, из которых одна строка предоставляется в зависимости от количества чего-либо. Содержит один или несколько элементов <item> .

атрибуты:

name
Строка . Имя для пары строк. Это имя используется в качестве идентификатора ресурса.
</dd>

<item>
Строка во множественном или единственном числе. Значение может быть ссылкой на другой строковый ресурс. Должна быть дочерним элементом элемента <plurals> . Обратите внимание, что необходимо экранировать апострофы и кавычки. См. раздел «Форматирование и стилизация » ниже для получения информации о том, как правильно стилизовать и форматировать строки.

атрибуты:

quantity
Ключевое слово . Значение, указывающее, когда следует использовать эту строку. Допустимые значения, неполный список примеров в скобках:
Ценить Описание
zero Когда в языке требуется особое обращение с числом 0 (как в арабском языке).
one Когда в языке требуется особое отношение к числам, таким как единица (как, например, число 1 в английском и большинстве других языков; в русском языке к этой категории относятся любые числа, оканчивающиеся на 1, но не оканчивающиеся на 11).
two Когда в языке требуется особое обращение с числами, такими как два (например, 2 в валлийском или 102 в словенском).
few Когда в языке требуется особое обращение с «малыми» числами (например, с 2, 3 и 4 в чешском языке; или с числами, оканчивающимися на 2, 3 или 4, но не на 12, 13 или 14 в польском языке).
many Когда язык требует особого подхода к «большим» числам (например, к числам, оканчивающимся на 11-99 в мальтийском языке).
other Когда язык не требует особого подхода к данной величине (как, например, со всеми числами в китайском языке или с числом 42 в английском).

пример:
XML-файл сохранен по адресу 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>

XML-файл сохранен по адресу 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>

Применение:

Котлин

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

При использовании метода getQuantityString() необходимо дважды передать count , если ваша строка содержит форматирование . Например, для строки %d songs found " первый параметр count выбирает соответствующую строку во множественном числе, а второй параметр count вставляется в заполнитель %d . Если ваши строки во множественном числе не содержат форматирования, третий параметр в метод getQuantityString передавать не нужно.

Формат и стиль

Вот несколько важных моментов, которые следует знать о правильном форматировании и оформлении строковых ресурсов.

Форматирование строк

Если вам необходимо отформатировать строки, вы можете сделать это, указав аргументы форматирования в строковом ресурсе, как показано в следующем примере ресурса.

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

В этом примере строка форматирования имеет два аргумента: %1$s — это строка, а %2$d — десятичное число. Затем отформатируйте строку, вызвав getString(int, Object...) . Например:

Котлин

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

Java

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

Оформление с использованием HTML-разметки

Вы можете добавить стили к своим строкам с помощью HTML-разметки. Например:

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

Если форматирование не применяется, вы можете установить текст TextView напрямую, вызвав setText(java.lang.CharSequence) . Однако в некоторых случаях может потребоваться создать ресурс стилизованного текста, который также используется в качестве строки форматирования. Обычно это не работает, поскольку методы format(String, Object...) и getString(int, Object...) удаляют всю информацию о стиле из строки. Решение этой проблемы заключается в том, чтобы записывать HTML-теги с экранированными сущностями, которые затем восстанавливаются с помощью fromHtml(String) после форматирования. Например:

  1. Сохраняйте стилизованный текстовый ресурс в виде строки, экранированной HTML-кодом:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    В эту отформатированную строку добавлен элемент <b> . Обратите внимание, что открывающая скобка экранирована с помощью HTML-кода &lt; .

  2. Затем отформатируйте строку как обычно, но также вызовите функцию fromHtml(String) чтобы преобразовать HTML-текст в текст с форматированием:

    Котлин

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

Поскольку метод fromHtml(String) форматирует все HTML-сущности, обязательно экранируйте все возможные HTML-символы в строках, используемых с форматируемым текстом, с помощью htmlEncode(String) . Например, если вы форматируете строку, содержащую такие символы, как "<" или "&", то их необходимо экранировать перед форматированием, чтобы при передаче отформатированной строки через fromHtml(String) символы отображались так, как они были записаны изначально. Например:

Котлин

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

Стиль с использованием шпандалей

Spannable — это текстовый объект, который можно стилизовать с помощью свойств шрифта, таких как цвет и толщина шрифта. Для создания текста используется SpannableStringBuilder , а затем к тексту применяются стили, определенные в пакете android.text.style .

Для упрощения процесса создания составного текста можно использовать следующие вспомогательные методы:

Котлин

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

Следующие методы для выделения текста bold , italic и color являются обертками над приведенными выше вспомогательными методами и демонстрируют конкретные примеры применения стилей, определенных в пакете android.text.style . Вы можете создать аналогичные методы для стилизации других типов текста.

Котлин

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

Вот пример того, как можно объединить эти методы, чтобы применить различные стили к отдельным словам во фразе:

Котлин

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

Модуль Core-ktx на Kotlin также содержит функции расширения, которые еще больше упрощают работу со span-элементами. Более подробную информацию можно найти в документации пакета android.text на GitHub.

Для получения дополнительной информации о работе с пролетами, перейдите по следующим ссылкам:

Оформление с помощью аннотаций

Сложную или пользовательскую стилизацию можно применять с помощью класса Annotation и тега <annotation> в файлах ресурсов strings.xml. Тег annotation позволяет помечать части строки для пользовательской стилизации, определяя пользовательские пары ключ-значение в XML, которые затем фреймворк преобразует в блоки Annotation . Затем вы можете получить эти аннотации и использовать ключ и значение для применения стилизации.

При создании аннотаций обязательно добавляйте тег <annotation> ко всем переводам строки в каждом файле strings.xml.


Применение пользовательского шрифта к слову "текст" на всех языках.

Пример — добавление пользовательского шрифта.

  1. Добавьте тег <annotation> и определите пару ключ-значение. В данном случае ключом является font , а значением — тип шрифта, который мы хотим использовать: 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. Загрузите строковый ресурс и найдите аннотации по ключу шрифта . Затем создайте пользовательский элемент <span> и замените существующий элемент <span>.

    Котлин

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

Если вы используете один и тот же текст несколько раз, следует создать объект SpannableString один раз и использовать его повторно по мере необходимости, чтобы избежать потенциальных проблем с производительностью и памятью.

Дополнительные примеры использования аннотаций см. в разделе «Стилизация интернационализированного текста в Android» .

Аннотации охватывают целые блоки текста и сегментирование текста.

Поскольку Annotation spans также являются ParcelableSpans , пары ключ-значение разделяются на части и не разделяются. Пока получатель части текста знает, как интерпретировать аннотации, вы можете использовать Annotation spans для применения пользовательских стилей к разделенному тексту.

Чтобы сохранить пользовательские стили при передаче текста в Intent Bundle, сначала необходимо добавить к тексту теги Annotation в виде тега <annotation>. Это можно сделать в XML-ресурсах с помощью тега <annotation> , как показано в примере выше, или в коде, создав новую Annotation и установив её в качестве тега <span>, как показано ниже:

Котлин

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

Извлеките текст из Bundle в виде SpannableString , а затем проанализируйте прикрепленные к нему аннотации, как показано в приведенном выше примере.

Котлин

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

Java

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

Для получения дополнительной информации о стилях текста перейдите по следующим ссылкам: