Как получить перечисление, которое создается в attrs.xml в коде

109

Я создал настраиваемый View (его можно найти здесь ) с атрибутом declare-styleable типа enum. В xml теперь я могу выбрать одну из записей перечисления для моего настраиваемого атрибута. Теперь я хочу создать метод для установки этого значения программно, но у меня нет доступа к перечислению.

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); Но я не могу найти перечисление или даже не знаю, как мне получить перечисление или имена перечисления.

Informatic0re
источник

Ответы:

100

Кажется, не существует автоматического способа получить перечисление 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))); 

(хотя создание исключения на этом этапе может быть не очень хорошей идеей, возможно, лучше выбрать разумное значение по умолчанию)

Энди Мелл
источник
38

Это просто, давайте покажем всем пример, чтобы показать, насколько это просто:

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"/>
    </attr>
</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)];

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

Стив Морец
источник
В заключение, просто используйте это, чтобы получить Enum attr: TypedArray.getInt (R.styleable.name_your_define, defaultValue)
CalvinChe
@CalvinChe ,, который возвращает файл int. Он есть у Стива Моретца. Я чувствую себя глупо из-за того, что не вижу этого, но сейчас 4:30 утра . Пора спать ...
n00dles
Да так просто. Ответ был просто примером для лучшей иллюстрации.
Стив Морец
2
Но это просто два параллельных определения символов. Это работает только до тех пор, пока определения идентичны, то есть хрупко. OP ожидал доступа кода к перечислению, сгенерированному из XML. Кажется, это должно быть возможно.
Стив Уайт,
@SteveWhite, так как у вас нет доступа к xml, нет способа автоматизировать его и позволить ему зависеть от одного определения. Это самый чистый (возможный) способ его достижения. Если вы можете проанализировать xml и получить к нему доступ таким образом, чтобы могло бы быть здорово, но вы не можете. (Если вы не можете, но не в коде, вы можете написать плагин для чтения xml и извлечения значений в вашу java, чтобы он был синхронизирован, так что да, таким образом он не будет хрупким потому что это автоматизировано.)
Стив Морец,
13

Ну, ради здравого смысла. Убедитесь, что ваши порядковые номера в объявленном вами styleable совпадают с объявлением 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];
}
Дэйв Томас
источник
3
Я думаю, что использование порядковых номеров перечислений суждено создать ненадежный код. Одно будет обновлено, а другое нет, и тогда у вас будут проблемы.
tir38
1
так что лучше сделать это?
Джонатан
7

Разрешите добавить решение, написанное на котлине. Добавить встроенную функцию расширения:

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()
Александр Албул
источник
4

Я знаю, что прошло некоторое время с момента публикации вопроса, но недавно у меня была такая же проблема. Я вместе взломал кое-что, что использует Square JavaPoet и кое-что из build.gradle, которое автоматически создает класс перечисления Java из attrs.xml при сборке проекта.

На https://github.com/afterecho/create_enum_from_xml есть небольшая демонстрация и файл readme с объяснением.

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

Даррен
источник