从XML资源创建hashmap / map

时间:2010-06-10 10:47:48

标签: xml android

我正在创建一个应用程序,其中Web服务从Web服务(I.e BEL,FRA,SWE)中获取(以及其他)一堆代码。在运行时,我想将这些代码翻译成他们的apporiate名称,以显示给用户(即比利时,法国,瑞典)。可能有很多这些代码,所以我想知道是否有任何合适的方式将(代码,名称)条目存储为Android中的XML资源中的某种地图,因此我可以快速获取名称给定的代码?

这是关于速度的全部,因为地图可以有几百个条目。

6 个答案:

答案 0 :(得分:40)

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

所以我发现了两种方式:

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

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

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

答案 1 :(得分:39)

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

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

使用它是直截了当的。在您的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>

并在您的实施中:

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

答案 2 :(得分:37)

您还可以使用XML定义地图,将其放在res / xml中并解析为HashMap(suggested in this post)。如果要保持键盘顺序解析为LinkedHashMap。简单的实现如下:

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>

资源解析器:

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

答案 3 :(得分:8)

我有类似的要求,我以比现有解决方案更强大的方式解决了这个问题。

首先创建一个国家/地区数组数组:

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

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

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

一些注意事项:

  1. 每个国家/地区数组中的name值(es,it)必须与countries_array_of_arrays中的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(); 值匹配(如果不是,则会收到错误)。

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

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

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

    List

    我将资源加载到2 Map,但您可以根据需要使用@drawable

    为什么这会更强大?

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

    2. 它允许您将任何类型的资源关联到每个国家/地区,而不仅仅是字符串。例如,您可以将<string-array>与国家/地区或包含该国家/地区/州的<array name="it"> <item>IT</item> <item>ITALIA</item> <item>@drawable/flag_italy</item> <item>@array/provinces_italy</item> </array> 相关联。

    3. 高级示例:

      countryTypedArray.getDrawable(2)

      请注意,我还没有尝试过,但我已经在other questions看过了。在这种情况下,您可以使用<string-array来获取drawable,例如。

      <强>简化:

      如果您只使用国家/地区数组的字符串,则可以使用<array>(而不是<string-array name="it"> <item>IT</item> <item>ITALIA</item> </string-array> ),如下所示:

      String[]

      您可以直接获取TypedArray代替String[] countryArray = getResources().getStringArray(id); countryCodes.add(countryArray[0]); countryNames.add(countryArray[1]); 来简化for循环代码:

      <string-array>

      关于有2 <string-array>

      的说明

      最初我想在另一个答案中提到2个<string-array>(一个用于国家代码,另一个用于国家/地区名称)。但是,使用2 // extension.js // for now, hardcode limit of 5 for testing purposes function getAllSessions(callback) { chrome.sessions.getRecentlyClosed({maxResults: 5}, function(sessions) { callback(sessions); }); } document.addEventListener('DOMContentLoaded', function() { getAllSessions(function(sessions) { console.debug(sessions) }); }); 时,您必须确保商品的数量顺序匹配。有了这个解决方案,你就不用了。在这方面它更安全。请注意,正如我所说,每个国家/地区阵列上的项目顺序都很重要:始终将国家/地区代码和国家/地区名称放在同一顺序中!

答案 4 :(得分:3)

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

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

这样可以使用正确的语言提供完整的国家/地区名称。

答案 5 :(得分:3)

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

如何在引用其显示值的字符串资源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;
   }
} 

然后在strings.xml中:

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

当您收到来自网络服务的项目时:

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

构建时间检查将确保每个国家/地区都有每个区域设置的资源,如果将列表维护为阵列资源,则不会拥有该资源。