Создание хэш-карты/карты из XML-ресурсов
Я делаю приложение, в котором веб-служба извлекает (среди прочего) кучу кодов из webservice (I.e BEL, FRA, SWE). Во время выполнения я хочу перевести эти коды в свои имена для отображения пользователям (I.e Belgium, France, Sweden). Могут быть много этих кодов, поэтому мне интересно, есть ли какой-либо способ хранения записи (код, имя) в качестве какой-то карты в XML-ресурсах на Android, поэтому я могу быстро получить имя данный код?
Здесь все о скорости, так как карта может иметь несколько сотен записей.
Ответы
Ответ 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);
Сроки проверки времени гарантируют, что каждая страна имеет ресурс для каждого региона, который у вас не будет, если вы сохраните этот список в качестве ресурса массива.