2011-01-20 31 views
159

我想在我的strings.xml文件从另一字符串中引用一个字符串,如以下(特别注意“MESSAGE_TEXT”字符串内容的末尾):从strings.xml中的另一个字符串引用一个字符串?

<?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 /空”的TextView来代替。该TextView中的文本是通知用户如何添加新项目。我想让我的布局更加傻傻(是的,我是有问题的傻瓜:-)

+3

[这个答案](http://stackoverflow.com/a/24903097/4348328)为我工作的另一个类似的问题。没有必要的java,但它只能在同一个资源文件中工作。 – mpkuth 2016-08-10 21:51:44

回答

116

一个很好的方式插入XML中的频繁使用的字符串(如应用程序名称),而不使用Java代码: 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> 

UPDATE:

你甚至可以DEF INE你的实体globaly如:

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

我认为你不能。但是,你可以在“格式化”,只要你喜欢的字符串:

<?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)); 
+5

我其实希望有一个“非逻辑”的解决方案。然而有一个公平的足够的答案(和它的纯粹的速度给予您一个绿色的对勾:-) – dbm 2011-01-20 11:04:18

+31

`字符串文本= res.getString(R.string.message_text,res.getString(R.string.button_text));`是一点点清洁。 – 2011-01-20 11:23:44

+0

@Barry Fruitman的回答帮了我 – Kalisky 2014-02-10 10:35:49

154

是可能的引用中一个又一个,只要你的整个字符串由基准名称。例如,这将工作:

<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无处不在,你可以轻松地随时更改默认设置。

+0

这也回答了这个问题:我如何参考活动标题中的app_name字符串。 :) – 2012-11-01 05:16:20

+45

只要引用整个字符串“(你总是按照定义)”,但“只要引用字符串资源只包含引用名称”,就不应该读取它。 – sschuberth 2012-11-20 14:57:40

+40

这不是一个真正的参考,这只是一个别名,它不能解决组成字符串的问题。 – 2013-12-23 06:00:19

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()转换CharSequenceString,这是一个问题,如果你传递的东西像一个SpannableString;为我的 项目这是不是一个问题,因为我只用它TextViews,我不需要 从我的Activity访问。

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

我创建的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。

0

你可以使用字符串占位符(%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)); 

,然后将其设置到地方,它使用字符串

1

随着新的data binding你可以连接,并在你的xml做更多。

例如,如果你有MESSAGE1和MESSAGE2您可以:

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

你甚至可以导入一些文字utils的和呼叫的String.format和朋友。

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

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

,那么你可以用它代替(你会需要导入类的数据绑定)

android:text="@{StringCompositions.completeMessage}" 
相关问题