Jak uzyskać wyliczenie, które jest tworzone w attrs.xml w kodzie

108

Utworzyłem niestandardowy widok (znajdź go tutaj ) z deklarowanym stylem atrybutu typu enum. W xml mogę teraz wybrać jeden z wpisów wyliczenia dla mojego atrybutu niestandardowego. Teraz chcę utworzyć metodę ustawiania tej wartości programowo, ale nie mogę uzyskać dostępu do wyliczenia.

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"/>

Potrzebuję czegoś takiego: mCustomView.setIcon(R.id.enum_name_x); ale nie mogę znaleźć wyliczenia lub nawet nie mam pojęcia, w jaki sposób mogę uzyskać wyliczenie lub nazwy wyliczenia.

Informatic0re
źródło

Odpowiedzi:

100

Wydaje się, że nie ma zautomatyzowanego sposobu uzyskania wyliczenia Java z wyliczenia atrybutu - w Javie można uzyskać określoną wartość liczbową - ciąg jest do użytku w plikach XML (jak widać).

Możesz to zrobić w swoim konstruktorze widoku:

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();
}

Jeśli chcesz, aby wartość została umieszczona w wyliczeniu, musisz samodzielnie zamapować wartość na wyliczenie Java, np .:

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();
    }
}

Następnie w pierwszym bloku kodu możesz użyć:

Format format = Format.fromId(a.getInt(R.styleable.IconView_icon, 0))); 

(chociaż rzucenie wyjątku w tym momencie może nie być świetnym pomysłem, prawdopodobnie lepiej wybrać rozsądną wartość domyślną)

Andy Mell
źródło
38

To proste, pokażmy wszystkim przykład, aby pokazać, jakie to proste:

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>

Układ niestandardowy:

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)];

teraz użyj kierunku jak każdej innej zmiennej wyliczeniowej.

steve moretz
źródło
Podsumowując, po prostu użyj tego, aby uzyskać atr Enum: TypedArray.getInt (R.styleable.name_your_define, defaultValue)
CalvinChe
@CalvinChe``, która zwraca plik int. Steve Moretz to ma. Czuję się głupio, że tego nie widzę, ale jest 4:30 . Czas do łóżka ...
n00dles
Tak, to takie proste. Odpowiedź była tylko przykładem, jak to lepiej zilustrować.
steve Moretz
2
Ale to tylko tworzy dwie równoległe definicje symboli. Działa to tylko wtedy, gdy definicje są identyczne - to znaczy jest kruche. OP oczekiwał dostępu kodu do wyliczenia wygenerowanego z XML. Wydaje się, że powinno to być możliwe.
Steve White
@SteveWhite, ponieważ nie masz dostępu do xml, nie ma sposobu, aby go zautomatyzować i pozwolić mu zależeć od jednej definicji.Jest to najczystszy (możliwy) sposób, aby to osiągnąć.Jeśli możesz przeanalizować xml i uzyskać do niego dostęp w sposób, który może być świetne, ale nie możesz. (Chyba że możesz, ale nie w kodzie, możesz napisać wtyczkę do odczytu xml i wyodrębnienia wartości do twojej javy, aby była zsynchronizowana, więc tak, w ten sposób nie będzie krucha ponieważ jest zautomatyzowany.)
steve moretz
13

Cóż, przez wzgląd na zdrowie psychiczne. Upewnij się, że liczby porządkowe są takie same w zadeklarowanym stylu jak w deklaracji Enum i uzyskaj do nich dostęp jako tablicę.

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];
}
Dave Thomas
źródło
3
Myślę, że poleganie tutaj na liczbach porządkowych wyliczenia jest przeznaczone do tworzenia niewiarygodnego kodu. Jedna rzecz zostanie zaktualizowana, a druga nie, a wtedy będziesz miał problemy.
tir38
1
więc jaki jest lepszy sposób na zrobienie tego?
Jonathan
6

Dodam rozwiązanie napisane w kotlinie. Dodaj wbudowaną funkcję rozszerzenia:

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

Teraz uzyskanie wyliczenia jest proste:

val a: TypedArray = obtainStyledAttributes(...)
val yourEnum: YourEnum = a.getEnum(R.styleable.YourView_someAttr, YourEnum.DEFAULT)
a.recycle()
Oleksandr Albul
źródło
4

Wiem, że minęło trochę czasu od opublikowania pytania, ale ostatnio miałem ten sam problem. Zhakowałem razem coś, co używa JavaPoet Square i niektórych rzeczy w build.gradle, które automatycznie tworzą klasę wyliczenia Java z attrs.xml podczas budowania projektu.

Jest małe demo i plik readme z wyjaśnieniem na https://github.com/afterecho/create_enum_from_xml

Mam nadzieję, że to pomoże.

Darren
źródło