安卓:如何通过名称从资源中获取字符串?

674
我希望我的UI能支持两种语言,并且在资源文件res\values\strings.xml中分别存储它们的字符串值。
<string name="tab_Books_en">Books</string>
<string name="tab_Quotes_en">Quotes</string>
<string name="tab_Questions_en">Questions</string>
<string name="tab_Notes_en">Notes</string>
<string name="tab_Bookmarks_en">Bookmarks</string>

<string name="tab_Books_ru">Книги</string>
<string name="tab_Quotes_ru">Цитаты</string>
<string name="tab_Questions_ru">Вопросы</string>
<string name="tab_Notes_ru">Заметки</string>
<string name="tab_Bookmarks_ru">Закладки</string>
现在我需要在我的应用程序中动态地检索这些值:
spec.setContent(R.id.tabPage1);
String pack = getPackageName();
String id = "tab_Books_" + Central.lang;
int i = Central.Res.getIdentifier(id, "string", pack);
String str = Central.Res.getString(i);

我的问题是i = 0

为什么在我的情况下它不起作用?


1
你确定 Central.lang 是 enru 吗? - K-ballo
1
需要更多信息来检查您的代码。在获取i之前,每个变量的状态是什么?无论如何,您应该使用Android中的默认本地化支持来保持简单。 - Alin
17个回答

1245

你提到的链接似乎是与在运行时生成的字符串一起工作。strings.xml 中的字符串不是在运行时创建的。 您可以通过

获取它们。
String mystring = getResources().getString(R.string.mystring);

getResources()Context 类的一个方法。如果你在 Activity 或者 Service 中(它们都是 Context 的子类),你可以像这个代码片段一样使用它。

同时请注意,整个语言依赖关系可以由 Android 框架来处理。只需为每种语言创建不同的文件夹即可。如果英语是您的默认语言,请将英语字符串放入 res/values/strings.xml 中。然后创建一个新的文件夹 values-ru,并将具有相同名称的俄语字符串放入 res/values-ru/strings.xml 中。从此时起,Android 会根据设备区域设置自动选择正确的字符串,无论是在调用 getString() 还是在 XML 中引用字符串时使用 @string/mystring。 如果没有覆盖用户区域设置的文件夹,则来自 res/values/strings.xml 的字符串将作为默认值使用。

请参阅本地化资源提供以获取更多信息。


29
如果您正在使用android.support.v4.app.Fragment,则可以直接调用getString(R.string.mystring)来获取字符串。 - JaKXz
4
我在想,为什么谷歌要用这种复杂的方式来做呢?先从 R 对象获取 mystring 的数字 ID,然后再通过该 ID 获取字符串?"mystring 的ID => 实际值" 这对键值实际上存在哪里?据我所知,mystring=>mystring的ID存储在R.java中,但只有这些信息。 - Lukáš Řádek
2
请确保“R”是您的项目中的R,而不是默认的Android R。 - ralphgabb
7
可能这是需要的答案,但它不是所要求的答案!如果想要根据名称动态访问字符串资源,请查看@leonvian的答案......我认为那个答案应该被接受。 - Chris Hatton

396

验证您的packageName是否正确。您必须引用Android应用程序的根包。

private String getStringResourceByName(String aString) {
      String packageName = getPackageName();
      int resId = getResources().getIdentifier(aString, "string", packageName);
      return getString(resId);
    }

13
如果有人有充分的理由这样做(例如,数据文件包含一个自定义字符串来决定应该显示哪个资源字符串,或者如果此字符串从互联网获取)--那么我想指出这个解决方案是正确的。特别是要更正“资源类型”是“string”而不是“strings”的部分,因为这是合乎逻辑的(就像Android的其他部分一样)。所以,感谢@leonvian的贡献(以及更多)。 :-) - Ivan Vučica
2
要通过字符串获取数组,只需使用以下代码:res.getStringArray(R.array.planets_array)。请查看官方链接: http://developer.android.com/guide/topics/resources/string-resource.html#StringArray - leonvian
6
好的。为了避免单独的方法声明,可以通过以下方式“简化”:String someStringFromXML = getString(getResources().getIdentifier("some_string_name", "string", getPackageName()));。这行代码会从XML资源获取一个字符串,并将其存储在someStringFromXML变量中。 - Neurotransmitter
谢谢!这是一个很好的解决方案,特别是当从互联网获取字符串时。我正在实现一个聊天功能,可以在设备之间传递系统消息。设备可能有不同的语言,因此接收设备上必须显示相应的语言。 - Mike Keskinov
不要忘记处理“Resources.NotFoundException”,否则以后可能会遇到问题。 - reden

97

不仅限于活动:

public static String getStringByIdName(Context context, String idName) {
    Resources res = context.getResources();
    return res.getString(res.getIdentifier(idName, "string", context.getPackageName()));
}

41
getResources().getString(getResources().getIdentifier("propertyName", "string", getPackageName()))

29

我会在leonvian的解决方案上增加一些内容,所以如果字符串在资源中未被找到(返回值为0,即不是有效的资源代码),则函数可能会返回一些内容:

private String getStringResourceByName(String aString) {
    String packageName = getPackageName();
    int resId = getResources()
            .getIdentifier(aString, "string", packageName);
    if (resId == 0) {
        return aString;
    } else {
        return getString(resId);
    }
}

22

最佳方法

App.getRes().getString(R.string.some_id)

在所有地方都有效(包括Utils, Models等)。

我已经阅读了所有的答案,所有的答案都可以完成你的工作。

  • 您可以在ActivityFragment中使用getString(R.string.some_string_id)
  • 您可以在没有直接访问getString()方法的情况下使用Context.getString(R.string.some_string_id),例如Dialog

问题

您没有Context访问权限,例如在您的Util类中的方法。

假设以下方法没有Context。

public void someMethod(){
    ...
    // can't use getResource() or getString() without Context.
}

现在你需要将Context作为参数传递到该方法中,并使用getString()
public void someMethod(Context context){
    ...
    context.getString(R.string.some_id);
}

我的工作职责是:

public void someMethod(){
    ...
    App.getAppResources().getString(R.string.some_id)
}

什么?它非常简单,可以在您的应用程序中任何地方使用!

因此,这里有一个解决方案,通过它你可以从任何地方访问资源,如Util类

import android.app.Application;
import android.content.res.Resources;

public class App extends Application {
    private static Resources resources;

    @Override
    public void onCreate() {
        super.onCreate();

        resources = getResources();
    }

    public static Resources getAppResources() {
        return resources;
    }

}

在您的manifest.xml文件中的<application标签中添加名称字段。
<application
        android:name=".App"
        ...
        >
        ...
    </application>

现在您已经可以开始使用了。在应用程序中的任何地方使用App.getAppResources().getString(R.string.some_id)

+1 简单干净的解决方案。注意到了拼写错误的 getResourses,而 getResources 会导致覆盖。此外,单例模式似乎是不必要的,可能可以省略。这个解决方案有任何内存泄漏问题吗? - Xarvalus
@Xarvalus 这会导致内存泄露吗? - Khemraj Sharma
我的应用程序一切正常 :) 只是想知道是否有人遇到了任何问题。如果我遇到任何困难,我会让大家知道的。 - Xarvalus
1
@Xarvalus 内存泄漏通常发生在变量被使用时,即使父类已被销毁的情况下。比如在活动和片段中。我不认为这种情况会发生内存泄漏。 - Khemraj Sharma

15

13

getResources() 只能在 ActivityFragment 类中使用。

  • 要在任何地方访问字符串资源,

请使用:

Resources.getSystem().getString(android.R.string.somecommonstuff)

它也可以在应用程序上下文中工作。不需要活动上下文。 - Markus Ressel

8

如果您正在使用Kotlin,可以按照以下方式定义扩展函数:

fun Context.getStringResourceByName(stringName: String): String? {
    val resId = resources.getIdentifier(stringName, "string", packageName)
    return getString(resId)
}

然后简单地使用它。例如,在谜题应用中,我根据图像文件名设置活动标题:

val stringName = "${folderName}_${assetName.substringBefore(".")}"
title = getStringResourceByName(stringName)

在此示例中,我基于动态名称读取字符串资源。

7

如果您没有Activity参考,可以使用以下方法中的上下文:

getContext().getString(R.string.your_string_name);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接