Как получить enum, который создается в attrs.xml в коде
Я создал собственный вид (найдите его здесь) с объявленным стилем атрибута типа enum. В xml теперь я могу выбрать одну из записей enum для своего пользовательского атрибута. Теперь я хочу создать метод для установки этого значения программно, но я не могу получить доступ к перечислению.
attr.xml
<declare-styleable name="IconView">
<attr name="icon" format="enum">
<enum name="enum_name_one" value="0"/>
....
<enum name="enum_name_n" value="666"/>
</attr>
</declare-styleable>
layout.xml
<com.xyz.views.IconView
android:id="@+id/heart_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="enum_name_x"/>
Что мне нужно, это что-то вроде: mCustomView.setIcon(R.id.enum_name_x);
Но я не могу найти перечисление, или я даже понятия не имею, как я могу получить перечисление или имена перечисления.
6 ответов
Похоже, не существует автоматического способа получить перечисление Java из перечисления атрибута - в Java вы можете получить указанное вами числовое значение - строка предназначена для использования в файлах XML (как показано на рисунке).
Вы можете сделать это в своем конструкторе представления:
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.IconView,
0, 0);
// Gets you the 'value' number - 0 or 666 in your example
if (a.hasValue(R.styleable.IconView_icon)) {
int value = a.getInt(R.styleable.IconView_icon, 0));
}
a.recycle();
}
Если вы хотите, чтобы значение было в перечислении, вам нужно либо отобразить значение в перечисление Java самостоятельно, например:
private enum Format {
enum_name_one(0), enum_name_n(666);
int id;
Format(int id) {
this.id = id;
}
static Format fromId(int id) {
for (Format f : values()) {
if (f.id == id) return f;
}
throw new IllegalArgumentException();
}
}
Тогда в первом блоке кода вы можете использовать:
Format format = Format.fromId(a.getInt(R.styleable.IconView_icon, 0)));
(хотя выбрасывать исключение на этом этапе не может быть хорошей идеей, вероятно, лучше выбрать разумное значение по умолчанию)
Это просто, давайте покажем всем пример, чтобы показать, насколько это просто:
attr.xml:
<declare-styleable name="MyMotionLayout">
<attr name="motionOrientation" format="enum">
<enum name="RIGHT_TO_LEFT" value="0"/>
<enum name="LEFT_TO_RIGHT" value="1"/>
<enum name="TOP_TO_BOTTOM" value="2"/>
<enum name="BOTTOM_TO_TOP" value="3"/>
</declare-styleable>
Пользовательский макет:
public enum Direction {RIGHT_TO_LEFT, LEFT_TO_RIGHT, TOP_TO_BOTTOM, BOTTOM_TO_TOP}
Direction direction;
...
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.MyMotionLayout);
Direction direction = Direction.values()[ta.getInt(R.styleable.MyMotionLayout_motionOrientation,0)];
Теперь используйте направление как любую другую переменную перечисления.
Разрешите добавить решение, написанное на котлине. Добавить встроенную функцию расширения:
inline fun <reified T : Enum<T>> TypedArray.getEnum(index: Int, default: T) =
getInt(index, -1).let { if (it >= 0) enumValues<T>()[it] else default
}
Теперь получить enum просто:
val a: TypedArray = obtainStyledAttributes(...)
val yourEnum: YourEnum = a.getEnum(R.styleable.YourView_someAttr, YourEnum.DEFAULT)
a.recycle()
Ну ради здравомыслия. Убедитесь, что ваши порядковые номера в объявленной стилистике такие же, как в объявлении Enum, и обращайтесь к нему как к массиву.
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.IconView,
0, 0);
int ordinal = a.getInt(R.styleable.IconView_icon, 0);
if (ordinal >= 0 && ordinal < MyEnum.values().length) {
enumValue = MyEnum.values()[ordinal];
}
Я знаю, что прошло много времени с тех пор, как вопрос был опубликован, но у меня недавно была такая же проблема. Я вместе взломал кое-что, использующее JavaPoet от Square, и кое-что из build.gradle, которое автоматически создает класс перечисления Java из attrs.xml при сборке проекта.
Там есть небольшая демонстрация и readme с объяснением на https://github.com/afterecho/create_enum_from_xml
Надеюсь, поможет.
Что я сделал в Котлине, так это получил их с использованием фабрики сопутствующих объектов, например. Вот пример простого вида сортировки.
Определите свое перечисление в xml
<declare-styleable name="SortingView"> <attr name="sortOrder" format="enum"> <enum name="ASC" value="0"/> <enum name="DESC" value="1" /> </attr>
Определите перечисление в коде, добавьте сопутствующий метод, чтобы возвращать правильное значение перечисления в зависимости от его идентификатора.
enum class SortOrder(val id:Int) { ASC(0), DESC(1); companion object { fun fromParams(id:Int):SortOrder { return when(id) { 0 -> ASC 1 -> DESC else -> throw IllegalAccessException("Unsupported SortOrder") } } }
}
Получите значения перечисления из типизированного массива с помощью метода getInt() и передайте его на фабрику для создания вашего перечисления.
sortOrder = SortOrder.fromParams(getInt(R.styleable.SortingView_sortOrder, SortOrder.ASC.id))
Теперь вы можете использовать свое перечисление в xml, например, так, а также использовать свой класс перечисления в коде, как на шаге 3.
Единственным недостатком является то, что они должны были быть определены в коде и в XML, но работают как шарм.