Recursos de string (Views)

Conceitos e implementação do Jetpack Compose

Um recurso de string fornece strings de texto para seu aplicativo com estilo e formatação de texto opcionais. Existem três tipos de recursos que podem fornecer strings ao seu aplicativo:

String
Recurso XML que fornece uma só string.
Matriz de strings
Recurso XML que fornece uma matriz de strings.
Strings de quantidade (plurais)
Recurso XML que contém diferentes strings para pluralização.

Todas as strings podem aplicar algumas marcações de estilo e argumentos de formatação. Para saber mais sobre estilos e formatação de strings, consulte a seção sobre Formatação e estilo.

String

É uma string única que pode ser referenciada do aplicativo ou de outros arquivos de recursos (como um layout XML).

localização do arquivo:
res/values/filename.xml
O nome do arquivo é arbitrário. O name do elemento <string> é usado como o ID do recurso.
tipo de dados do recurso compilado:
Ponteiro do recurso para um String.
referência de recurso:
Em Java: R.string.string_name
Em XML: @string/string_name
sintaxe:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
elementos:
<resources>
Obrigatório. Precisa ser o nó raiz.

Nenhum atributo.

<string>
É uma string que pode conter tags de estilo. Você precisa executar o escape de apóstrofos e aspas. Para saber mais sobre como ajustar corretamente o estilo e a formatação das strings, consulte Formatação e estilo abaixo.

atributos:

name
String. É o nome da string. Esse nome é usado como o ID do recurso.
exemplo:
Arquivo XML salvo em res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Esse XML de layout aplica uma string em uma visualização:

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

Esse código do aplicativo extrai uma string:

Kotlin

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

Java

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

Você pode usar getString(int) ou getText(int) para extrair uma string. O getText(int) mantém qualquer estilo rich text aplicado à string.

Matriz de strings

É a matriz de strings que pode ser referenciada pelo aplicativo.

localização do arquivo:
res/values/filename.xml
O nome do arquivo é arbitrário. O name do elemento <string-array> é usado como o ID do recurso.
tipo de dados do recurso compilado:
Ponteiro de recurso para uma matriz de Strings.
referência de recurso:
Em Java: R.array.string_array_name
Em XML: @[package:]array/string_array_name
sintaxe:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
elementos:
<resources>
Obrigatório. Precisa ser o nó raiz.

Nenhum atributo.

<string-array>
Define uma matriz de strings. Contém um ou mais elementos <item>.

atributos:

name
String. Um nome para a matriz. O nome do arquivo é usado como o ID do recurso para referenciar a matriz.
<item>
É uma string que pode conter tags de estilo. O valor pode ser uma referência a outro recurso de string. Precisa ser um filho de um elemento <string-array>. Você precisa executar o escape de apóstrofos e aspas. Para saber mais sobre o ajuste correto do estilo e da formatação de strings, consulte Formatação e estilo abaixo.

Nenhum atributo.

exemplo:
Arquivo XML salvo em 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>
Este código do aplicativo extrai uma matriz de strings:

Kotlin

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

Java

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

Strings de quantidade (plurais)

Diferentes idiomas têm diferentes regras de concordância gramatical de quantidade. Em inglês, por exemplo, a quantidade 1 é um caso especial. Escrevemos "1 book" (1 livro), mas, para qualquer outra quantidade, "n books" (n livros). Essa distinção entre singular e plural é muito comum, mas outros idiomas fazem distinções mais precisas. O conjunto completo permitido pelo Android é zero, one, two, few, many e other.

As regras para decidir qual caso usar para determinado idioma e quantidade podem ser muito complexas. Por isso, o Android fornece métodos como getQuantityString() para selecionar o recurso apropriado para você.

Na API 24 ou versões mais recentes, é possível usar a classe ICU MessageFormat, que é muito mais eficiente.

localização do arquivo:
res/values/filename.xml
O nome do arquivo é arbitrário. O name do elemento <plurals> é usado como o ID do recurso.
referência de recurso:
Em Java: R.plurals.plural_name
sintaxe:
<?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>
elementos:
<resources>
Obrigatório. Precisa ser o nó raiz.

Nenhum atributo.

<plurals>
Uma coleção que fornece uma string de acordo com a quantidade de um item. Contém um ou mais elementos <item>.

atributos:

name
String. É o nome do par de strings. Esse nome é usado como o ID do recurso.
</dd>

<item>
Indica se uma string é plural ou singular. O valor pode ser uma referência a outro recurso de string. Precisa ser um filho de um elemento <plurals>. Você precisa executar o escape de apóstrofos e aspas. Para saber mais sobre o ajuste correto do estilo e da formatação das suas strings, consulte Formatação e estilo abaixo.

atributos:

quantity
Palavra-chave. É o valor que indica quando é preciso usar a string. Valores válidos, com exemplos entre parênteses:
ValorDescrição
zeroIndica se o idioma exige tratamento especial para o número 0 (como em árabe).
oneIndica se o idioma exige tratamento especial para números como 1 (como em inglês e na maioria dos idiomas). Em russo, qualquer número terminado em 1, mas não em 11, se encontra nessa classe.
twoIndica se o idioma exige tratamento especial para números como 2 (como 2 em galês ou 102 em esloveno).
fewIndica se o idioma exige tratamento especial para números "pequenos" (como 2, 3 e 4 em tcheco ou números terminados em 2, 3 ou 4, mas não 12, 13 ou 14, em polonês).
manyQuando o idioma exige tratamento especial para números "grandes", como números que terminam de 11 a 99 em maltês.
otherIndica se o idioma não exige tratamento especial na quantidade em questão (como com todos os números em chinês ou 42 em inglês).

exemplo:
Arquivo XML salvo em 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>

Arquivo XML salvo em 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>

Uso:

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

Ao usar o método getQuantityString(), você precisa transmitir o count duas vezes se a string tiver formatação. Por exemplo, para a string %d songs found, o primeiro parâmetro count seleciona a string plural apropriada, e o segundo parâmetro count é inserido no marcador de posição %d. Se as strings de plural não incluírem formatação de string, não será necessário transmitir o terceiro parâmetro para getQuantityString.

Formato e estilo

Apresentamos abaixo alguns itens importantes sobre como definir a formatação e o estilo dos seus recursos de string.

Como formatar strings

Se você precisar formatar suas strings, é possível colocar os argumentos de formato no recurso de string, conforme demonstrado no exemplo a seguir.

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

Neste exemplo, a string de formatação tem dois argumentos: %1$s é uma string e %2$d é um número decimal. Depois, formate a string chamando getString(int, Object...). Exemplo:

Kotlin

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

Java

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

Aplicar estilo com marcação HTML

Você pode adicionar estilo às suas strings com marcações HTML. Exemplo:

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

Se você não estiver aplicando uma formatação, vai poder definir diretamente o texto de TextView chamando setText(java.lang.CharSequence). Em alguns casos, você pode criar um recurso de texto com estilo que também é usado como uma string de formatação. Normalmente, isso não funciona porque os métodos format(String, Object...) e getString(int, Object...) tiram todas as informações de estilo da string. A solução para esse problema é criar tags HTML com entidades com escape, que são então extraídas com fromHtml(String), após a formatação. Exemplo:

  1. Armazene seu recurso de texto com estilo como uma string HTML com escape:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    Nessa string formatada, é adicionado um elemento <b>. O colchete de abertura tem escape HTML usando a notação &lt;.

  2. Em seguida, formate a string normalmente, mas também chame fromHtml(String) para converter o texto HTML em texto com estilo:

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

Como o método fromHtml(String) formata todas as entidades HTML, não esqueça de usar htmlEncode(String) para executar o escape de qualquer caractere HTML possível nas strings usadas com o texto formatado. Por exemplo, se você estiver formatando uma string que contém caracteres como "<" ou "&", eles precisam de escape antes da formatação, para que, quando a string formatada for transmitida por fromHtml(String), os caracteres apareçam como foram originalmente escritos. Exemplo:

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

Criar estilo com spannables

Um Spannable é um objeto de texto que pode receber estilo com propriedades typeface como cor e peso da fonte. O SpannableStringBuilder é usado para criar o texto e aplicar estilos definidos no pacote android.text.style ao texto.

Você pode usar os métodos auxiliares abaixo para realizar grande parte da criação do texto spannable:

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

Os métodos bold, italic e color abaixo envolvem os métodos auxiliares acima e mostram exemplos específicos de aplicação de estilos definidos no pacote android.text.style. Você pode criar métodos semelhantes para outros tipos de estilos de texto.

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

Confira um exemplo de como agrupar esses métodos para aplicar vários estilos a palavras individuais em uma frase:

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

O módulo core-ktx do Kotlin também contém funções de extensão que facilitam ainda mais o trabalho com spans. Confira a documentação do pacote android.text no GitHub para saber mais.

Para mais informações sobre como trabalhar com spans, consulte os links abaixo (em inglês):

Estilo com anotações

Você pode aplicar estilos complexos ou personalizados usando a classe Annotation com a tag <annotation> nos arquivos de recurso strings.xml. A tag de anotação permite marcar partes da string para estilo personalizado definindo pares de chave-valor personalizados no XML que a biblioteca converte em spans da Annotation. Você pode extrair essas anotações e usar a chave e o valor para aplicar o estilo.

Ao criar anotações, adicione a tag <annotation> para todas as traduções da string em todos os arquivos strings.xml.


Como aplicar um typeface personalizado à palavra "texto" em todos os idiomas

Exemplo: como adicionar um typeface personalizado

  1. Adicione uma tag <annotation> e defina o par de chave-valor. Nesse caso, a chave é font, e o valor é o tipo de fonte que queremos usar: 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. Carregue o recurso da string e encontre as anotações com a chave font. Em seguida, crie um span personalizado e substitua o existente.

    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;

Se você estiver usando o mesmo texto várias vezes, vai precisar construir o objeto SpannableString uma vez e o reutilizar conforme necessário para evitar possíveis problemas de desempenho e memória.

Para mais exemplos de uso de anotações, consulte Como estilizar texto internacionalizado no Android.

Spans de anotação e parcelamento de texto

Como os spans de Annotation também são ParcelableSpans, os pares de chave-valor podem ser parcelados ou não. Desde que o receptor da parcela saiba como interpretar as anotações, você pode usar spans de Annotation para aplicar estilos personalizados ao texto parcelado.

Para manter seu estilo personalizado ao transmitir o texto para um Intent Bundle, primeiro é necessário adicionar spans de Annotation ao texto. Você pode fazer isso nos recursos XML usando a tag <annotation>, como mostrado no exemplo acima, ou no código criando um novo Annotation e definindo-o como um intervalo, conforme mostrado abaixo:

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

Extraia o texto do Bundle com uma SpannableString e, em seguida, analise as anotações anexadas, conforme mostrado no exemplo acima.

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

Para mais informações sobre estilo de texto, consulte os links abaixo (em inglês):