Android 从 XML 资源创建 hashmap/map

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3013655/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 08:17:46  来源:igfitidea点击:

Creating hashmap/map from XML resources

xmlandroid

提问by KimHafr

I'm making an application where a web service fetches (amongst other) a bunch of codes from a webservice (I.e BEL, FRA, SWE). During runtime I want to translate these codes to their apporiate names to display to users (I.e Belgium, France, Sweden). There can be a lot of these codes, so i'm wondering if there is any approriate way to store the (code, name) entry as a some sort of map in the XML resources in Android, so I can quickly fetch the name by the given code?

我正在制作一个应用程序,其中 Web 服务从 Web 服务(即 BEL、FRA、SWE)获取(除其他外)一堆代码。在运行时,我想将这些代码转换为它们的适当名称以显示给用户(即比利时、法国、瑞典)。可能有很多这样的代码,所以我想知道是否有任何适当的方法将(代码,名称)条目存储为 Android 中 XML 资源中的某种映射,因此我可以通过以下方式快速获取名称给定的代码?

It's all about speed here, since the map can have a few hundred entries.

这里的一切都与速度有关,因为地图可以有几百个条目。

采纳答案by Vladimir

Today I came across the same problem and studying developer.android.com for a long time didn't help since Android resources cannot be hashes (maps), only arrays.

今天我遇到了同样的问题,长时间研究 developer.android.com 没有帮助,因为 Android 资源不能是哈希(映射),只能是数组。

So I found 2 ways:

所以我找到了两种方法:

  1. Have a string array of values like "BEL|Belgium", parse those string early in the program and store in a Map<>

  2. Have 2 string arrays: first with the values of "BEL", "FRA", "SWE" and second with "Belgium", "France", "Sweden".

  1. 有一个字符串数组,比如“BEL|Belgium”,在程序的早期解析这些字符串并存储在 Map<>

  2. 有 2 个字符串数组:第一个是“BEL”、“FRA”、“SWE”的值,第二个是“比利时”、“法国”、“瑞典”。

Second is more sensitive cause you have to synchronize changes and order in both arrays simultaneously.

第二个更敏感,因为您必须同时同步两个数组中的更改和顺序。

回答by Jens Kohl

I've found Vladimir's answerquite compelling so I implemented his first suggestion:

我发现弗拉基米尔的回答非常引人注目,所以我实施了他的第一个建议:

public SparseArray<String> parseStringArray(int stringArrayResourceId) {
    String[] stringArray = getResources().getStringArray(stringArrayResourceId);
    SparseArray<String> outputArray = new SparseArray<String>(stringArray.length);
    for (String entry : stringArray) {
        String[] splitResult = entry.split("\|", 2);
        outputArray.put(Integer.valueOf(splitResult[0]), splitResult[1]);
    }
    return outputArray;
}

using it is straight forward. In your strings.xmlyou have a string-arraylike so:

使用它是直接的。在strings.xml你有string-array这样的:

<string-array name="my_string_array">
    <item>0|Item 1</item>
    <item>1|Item 4</item>
    <item>2|Item 3</item>
    <item>3|Item 42</item>
    <item>4|Item 17</item>
</string-array>

and in your implementation:

并在您的实施中:

SparseArray<String> myStringArray = parseStringArray(R.array.my_string_array);

回答by vokilam

Also you can define a map in XML, put it in res/xml and parse to HashMap (suggested in this post). If you want to keep key order parse to LinkedHashMap. Simple implementation follows:

你也可以在 XML 中定义一个映射,把它放在 res/xml 中并解析为 HashMap(在这篇文章中建议)。如果您想将键顺序解析为 LinkedHashMap。简单的实现如下:

Map resource in res/xml:

映射资源res/xml

<?xml version="1.0" encoding="utf-8"?>
<map linked="true">
    <entry key="key1">value1</entry>
    <entry key="key2">value2</entry>
    <entry key="key3">value3</entry>
</map>

Resource parser:

资源解析器:

public class ResourceUtils {
    public static Map<String,String> getHashMapResource(Context c, int hashMapResId) {
        Map<String,String> map = null;
        XmlResourceParser parser = c.getResources().getXml(hashMapResId);

        String key = null, value = null;

        try {
            int eventType = parser.getEventType();

            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_DOCUMENT) {
                    Log.d("utils","Start document");
                } else if (eventType == XmlPullParser.START_TAG) {
                    if (parser.getName().equals("map")) {
                        boolean isLinked = parser.getAttributeBooleanValue(null, "linked", false);

                        map = isLinked ? new LinkedHashMap<String, String>() : new HashMap<String, String>();
                    } else if (parser.getName().equals("entry")) {
                        key = parser.getAttributeValue(null, "key");

                        if (null == key) {
                            parser.close();
                            return null;
                        }
                    }
                } else if (eventType == XmlPullParser.END_TAG) {
                    if (parser.getName().equals("entry")) {
                        map.put(key, value);
                        key = null;
                        value = null;
                    }
                } else if (eventType == XmlPullParser.TEXT) {
                    if (null != key) {
                        value = parser.getText();
                    }
                }
                eventType = parser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        return map;
    }
}

回答by Albert Vila Calvo

I had a similar requirement, and I solved it in a way that is more powerfulthan the current solutions.

我有一个类似的需求,我以比当前解决方案更强大的方式解决了它。

First create a resource that is an array of country arrays:

首先创建一个资源,它是一个国家数组数组:

<array name="countries_array_of_arrays">
    <item>@array/es</item>
    <item>@array/it</item>
</array>

Then for each country add an array with the country info:

然后为每个国家添加一个包含国家信息的数组:

<array name="es">
    <item>ES</item>
    <item>ESPA?A</item>
</array>
<array name="it">
    <item>IT</item>
    <item>ITALIA</item>
</array>

Some notes:

一些注意事项:

  1. The namevalues (es, it) on each country array must match the ones in countries_array_of_arrays(you'll get an error if it doesn't).

  2. The order of the items on each array must be the same for all the arrays. So always put the country code first, and the country name second, for example.

  3. You can have any number of strings for each country, not just the country code and name.

  1. name每个国家/地区数组上的值 (es, it) 必须与中的值匹配countries_array_of_arrays(如果不匹配,您将收到错误消息)。

  2. 对于所有数组,每个数组上的项目顺序必须相同。因此,例如,始终将国家/地区代码放在第一位,然后将国家/地区名称放在第二位。

  3. 您可以为每个国家/地区设置任意数量的字符串,而不仅仅是国家/地区代码和名称。

To load the strings on your code do this:

要在代码中加载字符串,请执行以下操作:

TypedArray countriesArrayOfArrays = getResources().obtainTypedArray(R.array.countries_array_of_arrays);
int size = countriesArrayOfArrays.length();
List<String> countryCodes = new ArrayList<>(size);
List<String> countryNames = new ArrayList<>(size);
TypedArray countryTypedArray = null;
for (int i = 0; i < size; i++) {
    int id = countriesArrayOfArrays.getResourceId(i, -1);
    if (id == -1) {
        throw new IllegalStateException("R.array.countries_array_of_arrays is not valid");
    }
    countryTypedArray = getResources().obtainTypedArray(id);
    countryCodes.add(countryTypedArray.getString(0));
    //noinspection ResourceType
    countryNames.add(countryTypedArray.getString(1));
}
if (countryTypedArray != null) {
    countryTypedArray.recycle();
}
countriesArrayOfArrays.recycle();

Here I'm loading the resources to 2 Listbut you can use a Mapif you want.

在这里,我将资源加载到 2,List但您可以Map根据需要使用 a 。

Why is this more powerful?

为什么这个功能更强大?

  1. It allows you to use string resources and hence translate the country names for different languages.

  2. It allows you associate any type of resourceto each country, not just strings. For example, you can associate a @drawablewith a country flag or an <string-array>that contains provinces/states for the country.

  1. 它允许您使用字符串资源,从而翻译不同语言的国家/地区名称。

  2. 它允许您将任何类型的资源与每个国家/地区相关联,而不仅仅是字符串。例如,您可以将 a@drawable与国旗或<string-array>包含该国家/地区的省/州的an相关联。

Advanced example:

高级示例:

<array name="it">
    <item>IT</item>
    <item>ITALIA</item>
    <item>@drawable/flag_italy</item>
    <item>@array/provinces_italy</item>
</array>

Note that I haven't tried this but I've seen it in other questions. In such case you would use countryTypedArray.getDrawable(2)to get the drawable, for example.

请注意,我还没有尝试过这个,但我在其他问题中看到过。例如,在这种情况下,您将使用countryTypedArray.getDrawable(2)获取可绘制对象。

Simplification:

简化:

If you are only using strings for the country array, you can use an <string-array(instead of an <array>) like this:

如果您只为国家/地区数组使用字符串,则可以像这样使用<string-array(而不是<array>):

<string-array name="it">
    <item>IT</item>
    <item>ITALIA</item>
</string-array>

And you can simplify a bit the for-loop code by directly getting an String[]instead of a TypedArray:

您可以通过直接获取 aString[]而不是a 来稍微简化 for 循环代码TypedArray

String[] countryArray = getResources().getStringArray(id);
countryCodes.add(countryArray[0]);
countryNames.add(countryArray[1]);

A note on having 2 <string-array>:

关于有 2 的说明<string-array>

Initially I was thinking of having 2 <string-array>(one for the country code and another for the country name) as mentioned on another answer. However, with 2 <string-array>you have to make sure that the number andorder of the items match. With this solution you don't. It's safer in this regard. Note that, as I've said, the order of the items on each country array matters: always put the country code and country name in the same order!

最初我想<string-array>在另一个答案中提到2 个(一个用于国家代码,另一个用于国家名称)。但是,对于 2,<string-array>您必须确保项目的数量顺序匹配。使用此解决方案,您不会。在这方面它更安全。请注意,正如我所说,每个国家/地区数组中项目的顺序很重要:始终将国家/地区代码和国家/地区名称放在相同的顺序中!

回答by Johnny C

I think the safest and cleanest approach hasn't been posted yet, so here's my $0.02:

我认为最安全和最干净的方法尚未发布,所以这是我的 0.02 美元:

How about creating an enum type in the code that references the string resource id for its display value:

如何在代码中创建一个枚举类型来引用字符串资源 id 作为其显示值:

public enum Country {
   FRA(R.string.france),
   BEL(R.string.belgium),
   SWE(R.string.sweden),
   ...;

   @StringRes public int stringResId;

   Country(@StringRes id stringResId) {
       this.stringResId = stringResId;
   }
} 

Then in strings.xml:

然后在strings.xml中:

<string name="france">France</string>
...

And when you receive an item from the web service:

当您从 Web 服务收到项目时:

Country country = Country.valueOf(countryCodeFromServer);
context.getResources().getString(country.stringResId);

Build time checks will ensure that each country has a resource for each locale, which you won't have if you maintain the list as an array resource.

构建时间检查将确保每个国家/地区的每个区域设置都有一个资源,如果您将列表作为数组资源维护,您将不会拥有这些资源。

回答by user1435828

It might be late for you, but for anyone else interested. If your 3 digit country codes are valid ISO 3166-1 alpha-3 codes

对你来说可能会迟到,但对于其他感兴趣的人来说。如果您的 3 位国家/地区代码是有效的 ISO 3166-1 alpha-3 代码

Locale locale = new Locale("", "SWE");
String countryName = locale.getDisplayCountry();

This gives full country name with correct language.

这给出了具有正确语言的完整国家名称。