Подтвердить что ты не робот

Создание хэш-карты/карты из XML-ресурсов

Я делаю приложение, в котором веб-служба извлекает (среди прочего) кучу кодов из webservice (I.e BEL, FRA, SWE). Во время выполнения я хочу перевести эти коды в свои имена для отображения пользователям (I.e Belgium, France, Sweden). Могут быть много этих кодов, поэтому мне интересно, есть ли какой-либо способ хранения записи (код, имя) в качестве какой-то карты в XML-ресурсах на Android, поэтому я могу быстро получить имя данный код?

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

4b9b3361

Ответ 1

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

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

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

  • Имейте 2 строковых массива: сначала со значениями "BEL", "FRA", "SWE", а затем с "Бельгия", "Франция", "Швеция".

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

Ответ 2

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

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

Ответ 3

Также вы можете определить карту в 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;
    }
}

Ответ 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>

Некоторые примечания:

  • Значения 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 List, но вы можете использовать Map, если хотите.

Почему это более мощно?

  • Он позволяет использовать строковые ресурсы и, следовательно, переводить названия стран для разных языков.

  • Он позволяет связывать любой тип ресурса с каждой страной, а не только с строками. Например, вы можете связать @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), чтобы получить, например, drawable.

Облегчение:

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

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

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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