Как получить 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

Надеюсь, поможет.

Что я сделал в Котлине, так это получил их с использованием фабрики сопутствующих объектов, например. Вот пример простого вида сортировки.

  1. Определите свое перечисление в xml

             <declare-styleable name="SortingView">
     <attr name="sortOrder" format="enum">
         <enum name="ASC" value="0"/>
         <enum name="DESC" value="1" />
     </attr>
    
  2. Определите перечисление в коде, добавьте сопутствующий метод, чтобы возвращать правильное значение перечисления в зависимости от его идентификатора.

             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")
             }
         }
     }
    

    }

  3. Получите значения перечисления из типизированного массива с помощью метода getInt() и передайте его на фабрику для создания вашего перечисления.

             sortOrder = SortOrder.fromParams(getInt(R.styleable.SortingView_sortOrder, SortOrder.ASC.id))
    

Теперь вы можете использовать свое перечисление в xml, например, так, а также использовать свой класс перечисления в коде, как на шаге 3.

Единственным недостатком является то, что они должны были быть определены в коде и в XML, но работают как шарм.

Другие вопросы по тегам