【问题标题】:Creating hashmap/map from XML resources从 XML 资源创建 hashmap/map
【发布时间】:2010-06-10 10:47:48
【问题描述】:

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

这里只关心速度,因为地图可以有几百个条目。

【问题讨论】:

    标签: xml android


    【解决方案1】:

    您也可以在 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;
        }
    }
    

    【讨论】:

    • @vokilam 你能帮我解决这个问题吗:stackoverflow.com/questions/22553473/…
    • 不适用于长字符串。对我来说,即使我的字符串有 208 个字符,该值也会停止在 141 个字符处。结束行 ('\n') 被取消。
    【解决方案2】:

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

    所以我找到了两种方法:

    1. 有一个字符串数组,如“BEL|Belgium”,在程序早期解析这些字符串并存储在地图中

    2. 有 2 个字符串数组:第一个是“BEL”、“FRA”、“SWE”的值,第二个是“Belgium”、“France”、“Sweden”。

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

    【讨论】:

    • 作为替代方案,您可以将 json 对象存储在 assets 中并将其解析为 Map
    • 使用第二种方式并通过测试强制同步。
    • 我其实更喜欢Albert's solution(至少原则上,因为答案本身相当冗长)。
    【解决方案3】:

    我发现 Vladimir 的 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);
    

    【讨论】:

    • 你应该像这样转义管道(“|”)字符:“\\|”让您的代码正常工作。
    • @limlim 你测试过吗?上面的解决方案对我有用,但我很乐意编辑我的帖子。
    • 是的,我测试过了。添加缺少的转义后,它就像一个魅力!
    • 您可以在代码中进行转义,从而使 XML 更清晰。即 XML 中的 &lt;item&gt;0|Item1&lt;/item&gt; 和代码中的 .split("\\|", 2)
    • @BadCash 感谢您的提醒,我编辑了我的帖子。我使用更干净的 XML。
    【解决方案4】:

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

    首先创建一个国家数组的资源:

    <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 值(例如,它)必须与 countries_array_of_arrays 中的值匹配(如果不匹配,则会出现错误)。

    2. 每个数组中项目的顺序对于所有数组必须相同。所以总是把国家代码放在第一位,然后国家名称放在第二位,例如。

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

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

    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 List,但如果需要,您可以使用 Map

    为什么它更强大?

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

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

    高级示例:

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

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

    简化:

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

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

    您可以通过直接获取 String[] 而不是 TypedArray 来简化 for 循环代码:

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

    关于拥有 2 个&lt;string-array&gt; 的说明:

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

    【讨论】:

    • TLDR:数组数组,不错!
    【解决方案5】:

    我认为最安全和最干净的方法还没有发布,所以这是我的 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);
    

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

    【讨论】:

    • 要添加一个新条目(比如美国),您必须修改两个文件,资源和枚举。对我来说,这听起来像是一种代码味道。
    • 不行,你需要修改n(语言数量)+1个文件。如果您的应用中有 15 种翻译,那么没有编译时间检查所有国家/地区的列表是否以所有语言都存在,这是因为语言选项列表没有 1 个真实来源而付出的巨大代价。
    【解决方案6】:

    对你来说可能已经晚了,但对于其他感兴趣的人来说。 如果您的 3 位国家代码是有效的 ISO 3166-1 alpha-3 代码

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

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

    【讨论】:

      猜你喜欢
      • 2018-11-30
      • 1970-01-01
      • 2011-02-15
      • 2012-12-06
      • 2021-02-27
      • 2011-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多