Создание hashmap / map из ресурсов XML - PullRequest
44 голосов
/ 10 июня 2010

Я делаю приложение, в котором веб-сервис извлекает (среди прочего) набор кодов из веб-сервиса (т. Е. BEL, FRA, SWE).Во время выполнения я хочу перевести эти коды в соответствующие имена для отображения пользователям (т. Е. В Бельгии, Франции, Швеции).Таких кодов может быть много, поэтому мне интересно, есть ли какой-нибудь достойный способ сохранить запись (код, имя) в качестве некой карты в ресурсах XML в Android, поэтому я могу быстро получить имяданный код?

Здесь все зависит от скорости, поскольку на карте может быть несколько сотен записей.

Ответы [ 6 ]

40 голосов
/ 10 февраля 2011

Сегодня я столкнулся с той же проблемой, и изучение developer.android.com долгое время не помогало, поскольку ресурсы Android не могут быть хешами (картами), а только массивами.

Итак, я нашел 2 способа:

  1. Имеет строковый массив значений, таких как "BEL | Belgium", анализирует эти строки в начале программы и сохраняет на карте <>

  2. Имеют 2 строковых массива: первый со значениями «BEL», «FRA», «SWE» и второй со «Belgium», «France», «Sweden».

Второе более чувствительно, потому что вам нужно синхронизировать изменения и порядок в обоих массивах одновременно.

39 голосов
/ 22 февраля 2013

Я нашел ответ Владимира довольно убедительным, поэтому я реализовал его первое предложение:

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);
37 голосов
/ 22 февраля 2013

Также вы можете определить карту в XML, поместить ее в res / xml и проанализировать HashMap (, предложенный в этом посте ). Если вы хотите сохранить порядок ключей, проанализируйте 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;
    }
}
8 голосов
/ 19 октября 2016

У меня было похожее требование, и я решил его таким образом, что более мощен , чем текущие решения.

Сначала создайте ресурс, который является массивом массивов стран:

<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 с флагом страны или <string-array>, который содержит провинции / штаты для страны.

Расширенный пример:

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

Обратите внимание, что я не пробовал это, но я видел это в других вопросах .В таком случае вы можете использовать countryTypedArray.getDrawable(2), например, для получения отрисовки.

Упрощение:

Если вы используете только строки для массива страны, вы можетеиспользуйте <string-array (вместо <array>) следующим образом:

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

И вы можете немного упростить код цикла for, непосредственно получив String[] вместо TypedArray:

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

Примечание о наличии 2 <string-array>:

Изначально я думал о том, чтобы иметь 2 <string-array> (один для кода страны и другой дляназвание страны), как указано в другом ответе.Однако при 2 <string-array> вы должны убедиться, что порядковые номера элементов и совпадают.С этим решением вы не делаете.В этом отношении безопаснее.Обратите внимание, что, как я уже сказал, порядок элементов в каждом массиве стран имеет значение: всегда ставьте код страны и название страны в одном порядке!

3 голосов
/ 16 февраля 2017

Я думаю, что самый безопасный и чистый подход еще не опубликован, так что вот мои $ 0,02:

Как насчет создания типа enum в коде, который ссылается на идентификатор строкового ресурса для его отображаемого значения:

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

Проверки времени сборки гарантируют, что каждая странаесть ресурс для каждой локали, которого у вас не будет, если вы будете поддерживать список как ресурс массива.

3 голосов
/ 20 июля 2014

Это может быть поздно для вас, но для всех, кто заинтересован. Если ваши 3-значные коды стран действительны, коды ISO 3166-1 alpha-3

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

Это дает полное название страны с правильным языком.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...