从strings.xml 中的另一个字符串引用一个字符串?
Posted
技术标签:
【中文标题】从strings.xml 中的另一个字符串引用一个字符串?【英文标题】:Reference one string from another string in strings.xml? 【发布时间】:2011-06-12 09:01:44 【问题描述】:我想从我的 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 中的文本用于通知用户如何添加新项目。我想让我的布局万无一失(是的,我就是这个傻瓜:-)
【问题讨论】:
This answer 到另一个对我有用的类似问题。不需要java,但它只能在同一个资源文件中工作。 【参考方案1】:不使用 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/raw/entities.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>
【讨论】:
这是对 OP 的正确答案。此解决方案还有其他很大的好处:(1) 您可以在外部文件中定义 DTD,并从任何资源文件中引用它,以便在资源中的任何位置应用替换。 (2) android studio 让你重命名/引用/重构定义的实体。虽然,它不会在编写时自动完成名称。 (androidstudio 2.2.2) @RJFares 您可以采用 2 种方式:(a) “包括”整个实体文件 EG:查看 this answer。或 (b):包括在外部 DTD 中定义的每个实体,如 here 所示。请注意,两者都不是完美的,因为(a)您将失去“重构”功能,并且(b)非常冗长 如果您在 Android 库项目中使用它,请小心 - 您不能在您的应用中覆盖它。 在gradle文件中定义flavors时是否可以改变实体值? Android Studio 不再支持外部实体,因为这里讨论了一个错误:***.com/a/51330953/8154765【参考方案2】:只要您的整个字符串包含引用名称,就可以在另一个中引用。例如,这将起作用:
<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
,并且您可以随时轻松更改默认值。
【讨论】:
这也回答了这个问题:如何在活动标题中引用 app_name 字符串。 :) 这不应该读作“只要你引用整个字符串”(你总是按照定义)而是“只要引用的字符串资源只包含引用名称”。 这不是真正的引用,这只是一个别名,并不能解决组合字符串的问题。 这完全没用,我想不出任何有用的情况。只需引用实际字符串而不是“别名” 这并不能回答问题,他们希望将一个字符串嵌入到另一个字符串中。【参考方案3】:我认为你不能。但是你可以随意“格式化”一个字符串:
<?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));
【讨论】:
我实际上希望有一个“非逻辑”的解决方案。尽管如此,一个足够公平的答案(而且它的绝对速度会给你一个绿色的复选标记:-)String text = res.getString(R.string.message_text, res.getString(R.string.button_text));
有点干净。
这似乎是最干净的解决方案。其他的都很乱。【参考方案4】:
在 Android 中,您不能在 xml 中连接字符串
不支持关注
<string name="string_default">@string/string1 TEST</string>
查看下面的链接了解如何实现它
How to concatenate multiple strings in android XML?
【讨论】:
好吧,有趣的故事:这是我对您引用的类似问题的回答:-) 有没有办法做到这一点? 这是@Francesco Laurita 答案的副本,如何以编程方式替换字符串。【参考方案5】:我创建了简单的 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。
【讨论】:
我喜欢这个主意.. 但它对我来说失败了这个错误:> Could not get unknown property 'referencedString'
这与 aapt2 中断
我创建了一个与 aapt2 几乎相同的插件:github.com/LikeTheSalad/android-string-reference :)【参考方案6】:
您可以使用自己的逻辑,递归地解析嵌套字符串。
/**
* 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】:我知道这是一篇较旧的帖子,但我想分享一下我为我的项目提出的快速解决方案。它仅适用于 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
访问。
【讨论】:
这是最接近这个问题的答案。我认为我们需要考虑挂钩到布局 xml 解析。【参考方案8】:使用新的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】:除了上述回答 Francesco Laurita https://***.com/a/39870268/9400836
似乎有一个编译错误“&entity; was referenced, but not declared”可以通过像这样引用外部声明来解决
res/raw/entities.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
虽然它编译并运行它有一个空值。也许有人知道如何解决这个问题。我会发表评论,但最低声誉是 50。
【讨论】:
现在你有 53 个了。【参考方案10】:您可以使用字符串占位符 (%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$s
、%2$s
等代替%s
。
@CoolMind 你能提到对副本的引用吗?【参考方案11】:
我创建了一个小型库,可让您在构建时解析这些占位符,因此您无需添加任何 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
【讨论】:
我试过你的图书馆。按照您给出的步骤进行操作后。它不断给出构建错误。你的图书馆根本不工作 我明白了,很抱歉听到这个消息。如果您愿意,请在此处创建一个问题:github.com/LikeTheSalad/android-string-reference/issues 使用日志,如果这是功能问题,或者如果是配置问题,我可以尽快解决它,我也可以在那里为您提供帮助。 ?以上是关于从strings.xml 中的另一个字符串引用一个字符串?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 strings.xml 中引用其他字符串? [复制]