从strings.xml中的另一个字符串引用一个字符串?

时间:2011-01-20 10:38:24

标签: android xml string

我想在strings.xml文件中引用另一个字符串中的字符串,如下所示(特别注意“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>

我尝试了上面的语法,然后文本打印出“@ string / button_text”作为明文。不是我想要的。我希望消息文本打印“你还没有任何项目!按”添加项目“按钮添加一个。”

有没有任何已知的方法来实现我想要的?

理由:
我的应用程序有一个项目列表,但当该列表为空时,我会显示“@android:id / empty”TextView。 TextView中的文本是通知用户如何添加新项目。我想让我的布局变得傻瓜变化(是的,我是个傻瓜: - )

11 个答案:

答案 0 :(得分:192)

在不使用Java代码的情况下在xml中插入常用字符串(例如app name)的好方法: 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>

更新:

您甚至可以定义您的实体全球例如:

RES /原料/ entities.ent:

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

RES /值/ 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>

答案 1 :(得分:169)

只要整个字符串包含引用名称,就可以引用另一个字符串。例如,这将起作用:

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

设置默认值更有用:

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

现在,您可以在代码中的任何位置使用string_default,并且可以随时轻松更改默认值。

答案 2 :(得分:92)

我想你不能。但您可以根据需要“格式化”字符串:

<?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>

在代码中:

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

答案 3 :(得分:31)

在Android中,您无法在xml

中连接字符串

不支持以下

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

请查看以下链接,了解如何实现

How to concatenate multiple strings in android XML?

答案 4 :(得分:11)

我创建了简单的gradle plugin,它允许您从另一个字符串中引用一个字符串。您可以引用在另一个文件中定义的字符串,例如在不同的构建变体或库中。这种方法的缺点 - IDE重构不会找到这样的引用。

使用{{string_name}}语法引用字符串:

<?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>

要集成插件,只需将下一个代码添加到您的应用或库模块级别build.gradle文件

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"

<强>更新 该库不适用于Android gradle插件3.0及更高版本,因为新版本的插件使用aapt2将资源打包成.flat二进制格式,因此打包资源不可用于库。作为临时解决方案,您可以通过在gradle.properties文件中设置android.enableAapt2 = false来禁用aapt2。

答案 5 :(得分:7)

您可以使用自己的逻辑,以递归方式解析嵌套字符串。

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

例如,从Activity开始,将其称为

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

答案 6 :(得分:2)

我知道这是一篇较老的帖子,但我想分享我为我的项目提出的快速解决方案。它只适用于TextViews但可以 适应其他小部件。请注意,它需要将链接括在方括号中(例如[@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);
    }
}

这种方法的缺点是setText()CharSequence转换为a String,如果您传递SpannableString之类的内容,则会出现问题;为了我的 项目这不是一个问题,因为我只用它TextViews,我不需要 从我的Activity访问。

答案 7 :(得分:1)

您可以使用字符串占位符(%s)并在运行时使用java替换它们

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

和java

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

然后将其设置为使用字符串

的位置

答案 8 :(得分:1)

使用新的data binding,您可以连接并在xml中执行更多操作。

例如,如果你有message1和message2,你可以:

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

你甚至可以导入一些文本工具并调用String.format和朋友。

不幸的是,如果你想在几个地方重复使用它可能会变得混乱,你不希望这些代码片到处都是。你不能在一个地方用xml定义它们(不是我所知道的) 所以你可以创建一个封装这些组合的类:

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

然后你可以使用它(你需要导入带有数据绑定的类)

android:text="@{StringCompositions.completeMessage}"

答案 9 :(得分:1)

除了Francesco Laurita的上述答案https://stackoverflow.com/a/39870268/9400836

似乎存在编译错误&#34;&amp; entity;被引用,但未被声明&#34;这可以通过引用像这样的外部声明来解决

RES /原料/ entities.ent

<!ENTITY appname "My App Name">

RES /值/ 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

虽然它编译并运行但它有一个空值。也许有人知道如何解决这个问题。我会发表评论,但最低声望是50。

答案 10 :(得分:0)

我创建了一个小库,使您可以在构建时解析这些占位符,因此您无需添加任何Java / Kotlin代码即可实现所需的功能。

根据您的示例,您必须像这样设置字符串:

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

然后该插件将负责创建以下内容:

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

此处有更多信息:https://github.com/LikeTheSalad/android-string-reference