Як отримати перерахунок, створений у коді 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.