Як отримати перерахунок, створений у коді attrs.xml


108

Я створив призначений для користувача вид (знайдіть його тут ) з атрибутом оголошення, який може бути стилізований типу enum. Тепер у xml я можу вибрати один із записів enum для мого спеціального атрибута. Тепер я хочу створити метод для програмного встановлення цього значення, але я не можу отримати доступ до 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); Але я не можу знайти перерахунок або навіть не маю уявлення, як я можу отримати перерахунок або назви переліків.

Відповіді:


100

Мабуть, не існує автоматизованого способу отримання перерахунку Java з атрибута enum - в 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

Так просто, як це. Відповідь був лише прикладом для кращого ілюстрації.
— steve moretz

2
Але це просто робить два паралельних визначення символів. Це працює лише до тих пір, поки визначення ідентичні - тобто тендітні. ОП очікував доступ коду до перерахунку, генерованого з XML. Здається, що це повинно бути можливим.
— Стів Вайт

@SteveWhite, оскільки ви не маєте доступу до xml, тоді немає можливості його автоматизувати і нехай це залежить від одного визначення. Це найчистіший (можливий) спосіб досягти цього. Якщо ви могли б проаналізувати xml і отримати доступ до нього таким чином, може бути чудово, але ви не можете. (Якщо ви не можете, але не в коді, ви можете написати плагін, щоб прочитати XML і витягнути значення на вашу Java, так що він буде синхронізований, так що так, це не буде крихким тому що це автоматизовано.)
— steve moretz

13

Ну заради розуму. Переконайтесь, що ваші розпорядження такі самі, як у оголошеному стилі, як у вашій декларації 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
так який кращий спосіб зробити це?
— Джонатан

6

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

inline fun <reified T : Enum<T>> TypedArray.getEnum(index: Int, default: T) =
    getInt(index, -1).let { if (it >= 0) enumValues<T>()[it] else default 
}

Зараз перерахувати просто:

val a: TypedArray = obtainStyledAttributes(...)
val yourEnum: YourEnum = a.getEnum(R.styleable.YourView_someAttr, YourEnum.DEFAULT)
a.recycle()

4

Я знаю, що минуло деякий час з моменту опублікування запитання, але у мене був той самий випуск нещодавно. Я трохи зламав щось разом, що використовує JavaPoet Square і деякі речі в build.gradle, що автоматично створює клас перерахунку Java з attrs.xml для збирання проекту.

На сайті https://github.com/afterecho/create_enum_from_xml є невелика демонстрація та readme з поясненням.

Сподіваюся, це допомагає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.