Концепции и реализация 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>
- элементы:
- пример:
- 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" />
Данный код приложения извлекает строку:
Для получения строки можно использовать либо
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>
- элементы:
- пример:
- 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>
- элементы:
- пример:
- 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) после форматирования. Например:
- Сохраняйте стилизованный текстовый ресурс в виде строки, экранированной HTML-кодом:
<resources> <string name="welcome_messages">Hello, %1$s! You have <b>%2$d new messages</b>.</string> </resources>
В эту отформатированную строку добавлен элемент
<b>. Обратите внимание, что открывающая скобка экранирована с помощью HTML-кода<. - Затем отформатируйте строку как обычно, но также вызовите функцию
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.

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