Risorse stringa (visualizzazioni)

Concetti e implementazione di Jetpack Compose

Una risorsa stringa fornisce stringhe di testo per la tua applicazione con stile e formattazione del testo facoltativi. Esistono tre tipi di risorse che possono fornire stringhe alla tua applicazione:

String
Risorsa XML che fornisce una singola stringa.
String Array
Risorsa XML che fornisce un array di stringhe.
Stringhe di quantità (plurali)
Risorsa XML che contiene stringhe diverse per la pluralizzazione.

Tutte le stringhe sono in grado di applicare alcuni argomenti di formattazione e markup di stile. Per informazioni sulla formattazione e sullo stile delle stringhe, consulta la sezione Formattazione e stile.

Stringa

Una singola stringa a cui è possibile fare riferimento dall'applicazione o da altri file di risorse (ad esempio un layout XML).

posizione del file:
res/values/filename.xml
Il nome file è arbitrario. L'<string> dell'elemento name viene utilizzato come ID risorsa.
tipo di dati della risorsa compilata:
Puntatore alla risorsa String.
resource reference:
In Java: R.string.string_name
In XML: @string/string_name
sintassi:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string
        name="string_name"
        >text_string</string>
</resources>
elementi:
<resources>
Obbligatorio. Deve essere il nodo principale.

Nessun attributo.

<string>
Una stringa che può includere tag di stile. Tieni presente che devi usare il carattere di escape per gli apostrofi e le virgolette. Per ulteriori informazioni su come applicare correttamente lo stile e la formattazione alle stringhe, consulta la sezione Formattazione e stile di seguito.

attributi:

name
Stringa. Un nome per la stringa. Questo nome viene utilizzato come ID risorsa.
esempio:
File XML salvato in res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
</resources>

Questo XML di layout applica una stringa a una visualizzazione:

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

Questo codice dell'applicazione recupera una stringa:

Kotlin

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

Java

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

Puoi utilizzare getString(int) o getText(int) per recuperare una stringa. getText(int) mantiene qualsiasi stile RTF applicato alla stringa.

Array di stringhe

Un array di stringhe a cui è possibile fare riferimento dall'applicazione.

posizione del file:
res/values/filename.xml
Il nome file è arbitrario. L'<string-array> dell'elemento name viene utilizzato come ID risorsa.
tipo di dati della risorsa compilata:
Puntatore di risorse a un array di String.
resource reference:
In Java: R.array.string_array_name
In XML: @[package:]array/string_array_name
sintassi:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array
        name="string_array_name">
        <item
            >text_string</item>
    </string-array>
</resources>
elementi:
<resources>
Obbligatorio. Deve essere il nodo principale.

Nessun attributo.

<string-array>
Definisce un array di stringhe. Contiene uno o più elementi <item>.

attributi:

name
Stringa. Un nome per l'array. Questo nome viene utilizzato come ID risorsa per fare riferimento all'array.
<item>
Una stringa che può includere tag di stile. Il valore può essere un riferimento a un'altra risorsa stringa. Deve essere un elemento secondario di un elemento <string-array>. Tieni presente che devi usare il carattere di escape per gli apostrofi e le virgolette. Per informazioni su come formattare e applicare correttamente lo stile alle stringhe, consulta la sezione Formattazione e stile di seguito.

Nessun attributo.

esempio:
File XML salvato in 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>
Questo codice dell'applicazione recupera un array di stringhe:

Kotlin

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

Java

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

Stringhe di quantità (plurali)

Lingue diverse hanno regole diverse per la concordanza grammaticale con la quantità. In inglese, ad esempio, la quantità 1 è un caso speciale. Scriviamo "1 libro", ma per qualsiasi altra quantità scriveremmo "n libri". Questa distinzione tra singolare e plurale è molto comune, ma altre lingue fanno distinzioni più sottili. Il set completo supportato da Android è zero, one, two, few, many e other.

Le regole per decidere quale caso utilizzare per una determinata lingua e quantità possono essere molto complesse, quindi Android ti fornisce metodi come getQuantityString() per selezionare la risorsa appropriata.

Sulle API 24 e successive puoi utilizzare la classe ICU MessageFormat, molto più potente.

posizione del file:
res/values/filename.xml
Il nome file è arbitrario. L'<plurals> dell'elemento name viene utilizzato come ID risorsa.
resource reference:
In Java: R.plurals.plural_name
sintassi:
<?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>
elementi:
<resources>
Obbligatorio. Deve essere il nodo principale.

Nessun attributo.

<plurals>
Una raccolta di stringhe, di cui una viene fornita in base alla quantità di qualcosa. Contiene uno o più elementi <item>.

attributi:

name
Stringa. Un nome per la coppia di stringhe. Questo nome viene utilizzato come ID risorsa.
</dd>

<item>
Una stringa al plurale o al singolare. Il valore può essere un riferimento a un'altra risorsa stringa. Deve essere un elemento secondario di un elemento <plurals>. Tieni presente che devi inserire il carattere di escape per apostrofi e virgolette. Per informazioni su come formattare e applicare correttamente lo stile alle stringhe, consulta la sezione Formattazione e Stile di seguito.

attributi:

quantity
Parola chiave. Un valore che indica quando deve essere utilizzata questa stringa. Valori validi, con esempi non esaustivi tra parentesi:
ValoreDescrizione
zeroQuando la lingua richiede un trattamento speciale del numero 0 (come in arabo).
oneQuando la lingua richiede un trattamento speciale dei numeri come uno (come il numero 1 in inglese e nella maggior parte delle altre lingue; in russo, qualsiasi numero che termina con 1 ma non con 11 rientra in questa classe).
twoQuando la lingua richiede un trattamento speciale dei numeri come due (come 2 in gallese o 102 in sloveno).
fewQuando la lingua richiede un trattamento speciale dei numeri "piccoli" (come 2, 3 e 4 in ceco o i numeri che terminano con 2, 3 o 4, ma non 12, 13 o 14 in polacco).
manyQuando la lingua richiede un trattamento speciale dei numeri "grandi" (come per i numeri che terminano con 11-99 in maltese).
otherQuando la lingua non richiede un trattamento speciale della quantità indicata (come per tutti i numeri in cinese o 42 in inglese).

esempio:
File XML salvato in 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>

File XML salvato in 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);

Quando utilizzi il metodo getQuantityString(), devi trasmettere count due volte se la stringa include la formattazione della stringa. Ad esempio, per la stringa %d songs found, il primo parametro count seleziona la stringa plurale appropriata e il secondo parametro count viene inserito nel segnaposto %d. Se le stringhe plurali non includono la formattazione delle stringhe, non devi passare il terzo parametro a getQuantityString.

Formato e stile

Ecco alcune informazioni importanti sulla formattazione e sullo stile corretti delle risorse stringa.

Stringhe di formattazione

Se devi formattare le stringhe, puoi farlo inserendo gli argomenti di formattazione nella risorsa stringa, come mostrato nell'esempio di risorsa seguente.

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

In questo esempio, la stringa di formato ha due argomenti: %1$s è una stringa e %2$d è un numero decimale. Quindi, formatta la stringa chiamando getString(int, Object...). Ad esempio:

Kotlin

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

Java

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

Applicare stili con il markup HTML

Puoi aggiungere uno stile alle stringhe con il markup HTML. Ad esempio:

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

Se non applichi la formattazione, puoi impostare il testo TextView direttamente chiamando setText(java.lang.CharSequence). In alcuni casi, tuttavia, potresti voler creare una risorsa di testo con stile che venga utilizzata anche come stringa di formato. Normalmente, questo non funziona perché i metodi format(String, Object...) e getString(int, Object...) rimuovono tutte le informazioni di stile dalla stringa. La soluzione alternativa consiste nello scrivere i tag HTML con entità di escape, che vengono poi recuperate con fromHtml(String), dopo la formattazione. Ad esempio:

  1. Memorizza la risorsa di testo con stile come stringa con escape HTML:
    <resources>
      <string name="welcome_messages">Hello, %1$s! You have &lt;b>%2$d new messages&lt;/b>.</string>
    </resources>

    In questa stringa formattata viene aggiunto un elemento <b>. Nota che la parentesi aperta è codificata in HTML utilizzando la notazione &lt;.

  2. Quindi, formatta la stringa come di consueto, ma chiama anche fromHtml(String) per convertire il testo HTML in testo con stile:

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

Poiché il metodo fromHtml(String) formatta tutte le entità HTML, assicurati di eseguire l'escape di tutti i possibili caratteri HTML nelle stringhe che utilizzi con il testo formattato, utilizzando htmlEncode(String). Ad esempio, se stai formattando una stringa che contiene caratteri come "<" o "&", questi devono essere sottoposti all'escape prima della formattazione, in modo che quando la stringa formattata viene passata tramite fromHtml(String), i caratteri vengano visualizzati nel modo in cui erano stati originariamente scritti. Ad esempio:

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

Stili con gli spannable

Un Spannable è un oggetto di testo a cui puoi applicare uno stile con proprietà tipografiche come colore e spessore del carattere. Utilizzi SpannableStringBuilder per creare il testo e poi applicare gli stili definiti nel pacchetto android.text.style al testo.

Puoi utilizzare i seguenti metodi helper per impostare gran parte del lavoro di creazione di testo esteso:

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

I seguenti metodi bold, italic e color racchiudono i metodi helper precedenti e mostrano esempi specifici di applicazione degli stili definiti nel pacchetto android.text.style. Puoi creare metodi simili per eseguire altri tipi di formattazione del testo.

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

Ecco un esempio di come concatenare questi metodi per applicare vari stili a singole parole all'interno di una 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)));

Il modulo Kotlin core-ktx contiene anche funzioni di estensione che semplificano ulteriormente l'utilizzo degli span. Per saperne di più, puoi consultare la documentazione del pacchetto android.text su GitHub.

Per saperne di più su come lavorare con gli span, consulta i seguenti link:

Applicare uno stile con le annotazioni

Puoi applicare stili complessi o personalizzati utilizzando la classe Annotation insieme al tag <annotation> nei file di risorse strings.xml. Il tag di annotazione ti consente di contrassegnare parti della stringa per uno stile personalizzato definendo coppie chiave-valore personalizzate nell'XML che il framework converte in intervalli Annotation. Puoi quindi recuperare queste annotazioni e utilizzare la chiave e il valore per applicare lo stile.

Quando crei annotazioni, assicurati di aggiungere il tag <annotation> a tutte le traduzioni della stringa in ogni file strings.xml.


Applicazione di un carattere personalizzato alla parola "testo" in tutte le lingue

Esempio: aggiunta di un carattere personalizzato

  1. Aggiungi il tag <annotation> e definisci la coppia chiave-valore. In questo caso, la chiave è font e il valore è il tipo di carattere che vogliamo utilizzare: 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. Carica la risorsa stringa e trova le annotazioni con la chiave font. Poi crea un intervallo personalizzato e sostituisci quello esistente.

    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 utilizzi lo stesso testo più volte, devi costruire l'oggetto SpannableString una sola volta e riutilizzarlo in base alle necessità per evitare potenziali problemi di prestazioni e memoria.

Per altri esempi di utilizzo delle annotazioni, consulta Applicare stili al testo internazionalizzato in Android.

Intervalli di annotazione e suddivisione del testo

Poiché gli intervalli Annotation sono anche ParcelableSpans, le coppie chiave-valore vengono suddivise e ricomposte. Se il destinatario del pacco sa come interpretare le annotazioni, puoi utilizzare gli span Annotation per applicare uno stile personalizzato al testo del pacco.

Per mantenere lo stile personalizzato quando passi il testo a un Intent Bundle, devi prima aggiungere gli span Annotation al testo. Puoi farlo nelle risorse XML tramite il tag <annotation>, come mostrato nell'esempio precedente, o nel codice creando un nuovo Annotation e impostandolo come span, come mostrato di seguito:

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

Recupera il testo da Bundle come SpannableString e poi analizza le annotazioni allegate, come mostrato nell'esempio precedente.

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

Per saperne di più sullo stile del testo, consulta i seguenti link: