web-dev-qa-db-fra.com

Référencez une chaîne d'une autre chaîne dans strings.xml?

Je voudrais référencer une chaîne d'une autre chaîne dans mon fichier strings.xml, comme ci-dessous (notez spécifiquement la fin du contenu de la chaîne "message_text"):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="button_text">Add item</string>
    <string name="message_text">You don't have any items yet! Add one by pressing the \'@string/button_text\' button.</string>
</resources>

J'ai essayé la syntaxe ci-dessus, mais le texte affiche ensuite "@ string/button_text" en texte clair. Pas ce que je veux. Je voudrais que le texte du message soit imprimé "Vous n'avez encore aucun article! Ajoutez-en un en appuyant sur le bouton" Ajouter un article "."

Existe-t-il un moyen connu de réaliser ce que je veux?

JUSTIFICATION:
Mon application contient une liste d'éléments, mais lorsque cette liste est vide, je montre plutôt un TextView "@Android: id/empty". Le texte de cette vue TextView indique à l'utilisateur comment ajouter un nouvel élément. Je voudrais rendre ma mise en page infaillible aux modifications (oui, je suis le fou en question :-)

213
dbm

Une bonne façon d’insérer une chaîne fréquemment utilisée (par exemple, nom d’application) dans XML sans utiliser Java code: source

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE resources [
      <!ENTITY appname "MyAppName">
      <!ENTITY author "MrGreen">
    ]>

<resources>
    <string name="app_name">&appname;</string>
    <string name="description">The &appname; app was created by &author;</string>
</resources>

MISE À JOUR:

Vous pouvez même définir votre entité globalement, par exemple:

res/raw/entités.ent:

<!ENTITY appname "MyAppName">
<!ENTITY author "MrGreen">

res/values ​​/ string.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
    <!ENTITY % ents SYSTEM "./res/raw/entities.ent">
    %ents;   
]>

<resources>
    <string name="app_name">&appname;</string>
    <string name="description">The &appname; app was created by &author;</string>
</resources>
209
Beeing Jk

Il est possible de référencer l'un dans l'autre tant que votre chaîne entière est constituée du nom de référence. Par exemple cela fonctionnera:

<string name="app_name">My App</string>
<string name="activity_title">@string/app_name</string>
<string name="message_title">@string/app_name</string>

C'est encore plus utile pour définir des valeurs par défaut:

<string name="string1">String 1</string>
<string name="string2">String 2</string>
<string name="string3">String 3</string>
<string name="string_default">@string/string1</string>

Maintenant, vous pouvez utiliser string_default partout dans votre code et vous pouvez facilement modifier la valeur par défaut à tout moment.

171
Barry Fruitman

Je pense que tu ne peux pas. Mais vous pouvez "formater" une chaîne à votre guise:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="button_text">Add item</string>
    <string name="message_text">You don't have any items yet! Add one by pressing the %1$s button.</string>
</resources>

Dans le code:

Resources res = getResources();
String text = String.format(res.getString(R.string.message_text),
                            res.getString(R.string.button_text));
92
Francesco Laurita

Dans Android vous ne pouvez pas concaténer des chaînes dans xml

Ce qui suit n'est pas supporté

<string name="string_default">@string/string1 TEST</string>

Consultez ce lien ci-dessous pour savoir comment y parvenir

Comment concaténer plusieurs chaînes dans Android XML?

32
Mayank Mehta

J'ai créé gradle simple plugin qui vous permet de référencer une chaîne à partir d'une autre. Vous pouvez faire référence à des chaînes définies dans un autre fichier, par exemple, dans une variante de construction ou une bibliothèque différente. Inconvénients de cette approche - IDE le refactor ne trouvera pas de telles références.

Utilisation {{string_name}} _ syntaxe pour faire référence à une chaîne:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="super">Super</string>
    <string name="app_name">My {{super}} App</string>
    <string name="app_description">Name of my application is: {{app_name}}</string>
</resources>

Pour intégrer le plugin, ajoutez simplement le code suivant à votre application ou au niveau du module de bibliothèque build.gradle fichier

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "gradle.plugin.Android-text-resolver:buildSrc:1.2.0"
  }
}

apply plugin: "com.icesmith.androidtextresolver"

UPDATE: La bibliothèque ne fonctionne pas avec Android Gradle plugin version 3.0 et ultérieure, car la nouvelle version du plugin utilise aapt2, qui compresse les ressources au format binaire .flat, ce qui signifie que les ressources compressées ne sont pas disponibles pour la bibliothèque.Pour une solution temporaire, vous pouvez désactiver aapt2 en définissant Android.enableAapt2 = false dans votre fichier gradle.properties.

12
Valeriy Katkov

Vous pouvez utiliser votre propre logique, qui résout les chaînes imbriquées de manière récursive.

/**
 * Regex that matches a resource string such as <code>@string/a-b_c1</code>.
 */
private static final String REGEX_RESOURCE_STRING = "@string/([A-Za-z0-9-_]*)";

/** Name of the resource type "string" as in <code>@string/...</code> */
private static final String DEF_TYPE_STRING = "string";

/**
 * Recursively replaces resources such as <code>@string/abc</code> with
 * their localized values from the app's resource strings (e.g.
 * <code>strings.xml</code>) within a <code>source</code> string.
 * 
 * Also works recursively, that is, when a resource contains another
 * resource that contains another resource, etc.
 * 
 * @param source
 * @return <code>source</code> with replaced resources (if they exist)
 */
public static String replaceResourceStrings(Context context, String source) {
    // Recursively resolve strings
    Pattern p = Pattern.compile(REGEX_RESOURCE_STRING);
    Matcher m = p.matcher(source);
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        String stringFromResources = getStringByName(context, m.group(1));
        if (stringFromResources == null) {
            Log.w(Constants.LOG,
                    "No String resource found for ID \"" + m.group(1)
                            + "\" while inserting resources");
            /*
             * No need to try to load from defaults, Android is trying that
             * for us. If we're here, the resource does not exist. Just
             * return its ID.
             */
            stringFromResources = m.group(1);
        }
        m.appendReplacement(sb, // Recurse
                replaceResourceStrings(context, stringFromResources));
    }
    m.appendTail(sb);
    return sb.toString();
}

/**
 * Returns the string value of a string resource (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @param name
 * @return the value of the string resource or <code>null</code> if no
 *         resource found for id
 */
public static String getStringByName(Context context, String name) {
    int resourceId = getResourceId(context, DEF_TYPE_STRING, name);
    if (resourceId != 0) {
        return context.getString(resourceId);
    } else {
        return null;
    }
}

/**
 * Finds the numeric id of a string resource (e.g. defined in
 * <code>values.xml</code>).
 * 
 * @param defType
 *            Optional default resource type to find, if "type/" is not
 *            included in the name. Can be null to require an explicit type.
 * 
 * @param name
 *            the name of the desired resource
 * @return the associated resource identifier. Returns 0 if no such resource
 *         was found. (0 is not a valid resource ID.)
 */
private static int getResourceId(Context context, String defType,
        String name) {
    return context.getResources().getIdentifier(name, defType,
            context.getPackageName());
}

D'un Activity, par exemple, appelez-le comme suit

replaceResourceStrings(this, getString(R.string.message_text));
7
schnatterer

En plus de la réponse ci-dessus de Francesco Laurita https://stackoverflow.com/a/39870268/9400836

Il semble y avoir une erreur de compilation "& entité; a été référencé, mais pas déclaré" qui peut être résolu en référençant la déclaration externe comme ceci

res/raw/entités.ent

<!ENTITY appname "My App Name">

res/values ​​/ strings.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
    <!ENTITY appname SYSTEM "/raw/entities.ent">
]>
<resources>
    <string name="app_name">&appname;</string>
</resources

Bien qu'il compile et exécute, il a une valeur vide. Peut-être que quelqu'un sait comment résoudre ce problème. J'aurais posté un commentaire mais la réputation minimale est de 50.

2
pumnao

Je suis conscient qu'il s'agit d'un ancien billet, mais je voulais partager la solution rapide et sale que j'ai proposée pour un projet à moi. Cela ne fonctionne que pour TextViews mais pourrait également être adapté à d'autres widgets. Notez que le lien doit être placé entre crochets (par exemple, [@string/foo]).

public class RefResolvingTextView extends TextView
{
    // ...

    @Override
    public void setText(CharSequence text, BufferType type)
    {
        final StringBuilder sb = new StringBuilder(text);
        final String defPackage = getContext().getApplicationContext().
                getPackageName();

        int beg;

        while((beg = sb.indexOf("[@string/")) != -1)
        {
            int end = sb.indexOf("]", beg);
            String name = sb.substring(beg + 2, end);
            int resId = getResources().getIdentifier(name, null, defPackage);
            if(resId == 0)
            {
                throw new IllegalArgumentException(
                        "Failed to resolve link to @" + name);
            }

            sb.replace(beg, end + 1, getContext().getString(resId));
        }

        super.setText(sb, type);
    }
}

L'inconvénient de cette approche est que setText() convertit le CharSequence en String, ce qui pose un problème si vous transmettez des choses comme SpannableString; pour mon projet, ce n'était pas un problème car je ne l'utilisais que pour TextViews auquel je n'avais pas besoin d'accéder depuis mon Activity.

2
jclehner

Vous pouvez utiliser des espaces réservés pour les chaînes (% s) et les remplacer par Java au moment de l'exécution

<resources>
<string name="button_text">Add item</string>
<string name="message_text">Custom text %s </string>
</resources>

et en Java

String final = String.format(getString(R.string.message_text),getString(R.string.button_text));

puis placez-le à l'endroit où il utilise la chaîne

1
Ismail Iqbal

Avec le nouveau liaison de données , vous pouvez concaténer et faire beaucoup plus dans votre code XML.

par exemple, si vous avez message1 et message2, vous pouvez:

Android:text="@{@string/message1 + ': ' + @string/message2}"

vous pouvez même importer des utilitaires texte et appeler String.format et ses amis.

malheureusement, si vous voulez le réutiliser à plusieurs endroits, cela peut devenir désordonné, vous ne voulez pas que ce code soit partout. et vous ne pouvez pas les définir en XML à un endroit (pas à ma connaissance), vous pouvez donc créer une classe qui encapsulera ces compositions:

public final class StringCompositions {
    public static final String completeMessage = getString(R.string.message1) + ": " + getString(R.string.message2);
}

alors vous pouvez l'utiliser à la place (vous devrez importer la classe avec la liaison de données)

Android:text="@{StringCompositions.completeMessage}"
1
ndori