Cara mendapatkan enum yang dibuat di attrs.xml dalam kode

108

Saya membuat Tampilan kustom (temukan di sini ) dengan atribut tipe enum yang dapat dideklarasikan. Dalam xml sekarang saya dapat memilih salah satu entri enum untuk atribut khusus saya. Sekarang saya ingin membuat metode untuk menyetel nilai ini secara terprogram, tetapi saya tidak dapat mengakses 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"/>

Yang saya butuhkan adalah sesuatu seperti: mCustomView.setIcon(R.id.enum_name_x); Tapi saya tidak dapat menemukan enum atau saya bahkan tidak tahu bagaimana saya bisa mendapatkan enum atau nama enum tersebut.

Informatic0re
sumber

Jawaban:

100

Tampaknya tidak ada cara otomatis untuk mendapatkan enum Java dari enum atribut - di Java Anda bisa mendapatkan nilai numerik yang Anda tentukan - string tersebut untuk digunakan dalam file XML (seperti yang Anda tunjukkan).

Anda dapat melakukan ini di konstruktor tampilan Anda:

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

Jika Anda ingin nilai menjadi enum, Anda perlu memetakan sendiri nilai ke enum Java, misalnya:

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

Kemudian di blok kode pertama Anda bisa menggunakan:

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

(meskipun memberikan pengecualian pada saat ini mungkin bukan ide bagus, mungkin lebih baik memilih nilai default yang masuk akal)

Andy Mell
sumber
38

Sederhana saja, mari kita tunjukkan contoh kepada semua orang hanya untuk menunjukkan betapa mudahnya:

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>

Tata letak kustom:

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

sekarang gunakan arah seperti variabel enumerasi lainnya.

steve moretz
sumber
Sebagai kesimpulan, cukup gunakan ini untuk mendapatkan Enum attr: TypedArray.getInt (R.styleable.name_your_define, defaultValue)
CalvinChe
@CalvinChe ,, yang mengembalikan file int. Steve Moretz memilikinya. Saya merasa bodoh karena tidak melihatnya, tetapi sekarang jam 4:30 . Waktunya tidur ...
n00dles
Ya sesederhana itu. Jawabannya hanyalah contoh untuk menggambarkannya dengan lebih baik.
steve moretz
2
Tapi ini hanya membuat dua definisi simbol yang paralel. Ini berfungsi hanya selama definisinya identik - artinya, rapuh. OP mengharapkan akses kode ke enum yang dihasilkan dari XML. Sepertinya itu mungkin.
Steve White
@SteveWhite karena Anda tidak memiliki akses ke xml maka tidak ada cara untuk mengotomatiskannya dan membiarkannya bergantung pada satu definisi. Ini adalah cara terbersih (mungkin) untuk mencapainya.Jika Anda dapat mengurai xml dan mengaksesnya dengan cara yang bisa menjadi hebat tetapi Anda tidak bisa. (Kecuali Anda bisa tetapi tidak di kodenya, Anda dapat menulis plugin untuk membaca xml dan mengekstrak nilainya ke java Anda sehingga akan disinkronkan jadi ya cara itu tidak akan rapuh karena otomatis.)
steve moretz
13

Demi kewarasan. Pastikan ordinal Anda sama dalam gaya yang dideklarasikan seperti dalam deklarasi Enum Anda dan akses sebagai array.

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
sumber
3
Saya pikir mengandalkan enum ordinals di sini dimaksudkan untuk membuat kode yang tidak dapat diandalkan. Satu hal akan diperbarui dan bukan yang lain, lalu Anda akan mengalami masalah.
tir38
1
jadi apa cara yang lebih baik untuk melakukannya?
Jonathan
6

Izinkan saya menambahkan solusi yang ditulis dalam kotlin. Tambahkan fungsi ekstensi sebaris:

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

Sekarang mendapatkan enum itu sederhana:

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

Saya tahu sudah lama sejak pertanyaan itu diposting, tetapi saya mengalami masalah yang sama baru-baru ini. Saya meretas sedikit sesuatu bersama-sama yang menggunakan JavaPoet Square dan beberapa hal di build.gradle yang secara otomatis membuat kelas enum Java dari attrs.xml pada pembangunan proyek.

Ada sedikit demo dan readme dengan penjelasannya di https://github.com/afterecho/create_enum_from_xml

Semoga membantu.

Darren
sumber