首页 文章

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

提问于
浏览
185

我想在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”作为明文 . 不是我想要的 . 我想要打印消息文本“你还没有任何项目!按”添加项目“按钮添加一个项目 . ”

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

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

10 回答

  • 31

    在不使用Java代码的情况下在xml中插入常用字符串(例如应用程序名称)的好方法: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>
    
  • 91

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

    <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 ,您可以随时轻松更改默认值 .

  • 7

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

    <?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));
    
  • 1

    在Android中,你无法在xml中连接字符串

    Following is not supported

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

    请查看下面的链接,了解如何实现它

    How to concatenate multiple strings in android XML?

  • 163

    我创建了简单的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"
    

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

  • 163

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

    /**
     * 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));
    
  • 10

    我已经为我的一个项目提出了一个肮脏的解决方案 . 它仅适用于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 转换为 String ,如果你传递像 SpannableString 这样的东西就会出现问题 . 对于我的项目,这不是问题,因为我只使用它 TextViews ,我不需要从我的 Activity 访问 .

  • 0

    使用新的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}"
    
  • 2

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

    似乎有一个编译错误“&entity;被引用,但没有声明”,这可以通过引用像这样的外部声明来解决

    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 .

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

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

相关问题