Cách lấy enum được tạo trong mã attrs.xml


108

Tôi đã tạo một Chế độ xem tùy chỉnh (tìm nó ở đây ) với thuộc tính có thể khai báo kiểu kiểu enum. Trong xml, bây giờ tôi có thể chọn một trong các mục nhập enum cho thuộc tính tùy chỉnh của mình. Bây giờ tôi muốn tạo một phương thức để đặt giá trị này theo chương trình, nhưng tôi không thể truy cập 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"/>

Những gì tôi cần là: mCustomView.setIcon(R.id.enum_name_x); Nhưng tôi không thể tìm thấy enum hoặc tôi thậm chí không biết làm cách nào để có thể lấy enum hoặc tên của enum.

Câu trả lời:


100

Dường như không có cách tự động nào để lấy một enum Java từ một enum thuộc tính - trong Java, bạn có thể lấy giá trị số mà bạn đã chỉ định - chuỗi được sử dụng trong các tệp XML (như bạn hiển thị).

Bạn có thể làm điều này trong hàm tạo chế độ xem của mình:

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

Nếu bạn muốn giá trị thành một enum, bạn sẽ cần tự ánh xạ giá trị vào một enum Java, ví dụ:

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

Sau đó, trong khối mã đầu tiên bạn có thể sử dụng:

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

(mặc dù ném một ngoại lệ vào thời điểm này có thể không phải là một ý tưởng tuyệt vời, có lẽ tốt hơn nên chọn một giá trị mặc định hợp lý)


38

Thật đơn giản, hãy cho mọi người xem một ví dụ để cho thấy nó dễ dàng như thế nào:

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>

Bố cục tùy chỉnh:

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

bây giờ sử dụng hướng giống như bất kỳ biến liệt kê nào khác.


Tóm lại, chỉ cần sử dụng điều này để nhận Enum attr: TypedArray.getInt (R.styleable.name_your_define, defaultValue)
CalvinChe

@CalvinChe ,, trả về một int. Steve Moretz có nó. Tôi cảm thấy chết lặng vì không nhìn thấy nó, nhưng bây giờ là 4:30 sáng . Thời gian đi ngủ ...
n00dles

Vâng chỉ đơn giản như vậy. Câu trả lời chỉ là một ví dụ để minh họa nó tốt hơn.
steve moretz

2
Nhưng điều này chỉ làm cho hai định nghĩa song song về biểu tượng. Điều này chỉ hoạt động miễn là các định nghĩa giống hệt nhau - nghĩa là, nó rất mong manh. OP đã mong đợi quyền truy cập mã vào một enum được tạo từ XML. Có vẻ như điều đó là có thể.
Steve White

@SteveWhite vì bạn không có quyền truy cập vào xml nên không có cách nào để tự động hóa nó và để nó phụ thuộc vào một định nghĩa. Đây là cách rõ ràng nhất (có thể) để đạt được nó. Nếu bạn có thể phân tích cú pháp xml và truy cập nó theo cách có thể tuyệt vời nhưng bạn không thể. (Trừ khi bạn có thể nhưng không có trong mã, bạn có thể viết một plugin để đọc xml và trích xuất các giá trị vào java của bạn để nó sẽ được đồng bộ hóa vì vậy theo cách đó, nó sẽ không dễ vỡ vì nó tự động.)
steve moretz

13

Tốt cho sự tỉnh táo. Đảm bảo rằng thứ tự của bạn giống với thứ tự đã khai báo của bạn có thể định kiểu như trong khai báo Enum của bạn và truy cập nó dưới dạng một mảng.

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
Tôi nghĩ rằng dựa vào thứ tự enum ở đây được định để tạo ra mã không đáng tin cậy. Một thứ sẽ được cập nhật chứ không phải thứ khác và sau đó bạn sẽ gặp khó khăn.
tir38

1
vậy cách tốt hơn để làm điều đó là gì?
Jonathan

6

Hãy để tôi thêm một giải pháp được viết bằng kotlin. Thêm chức năng mở rộng nội tuyến:

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

Bây giờ việc nhận enum rất đơn giản:

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

4

Tôi biết đã được một thời gian kể từ khi câu hỏi được đăng, nhưng tôi đã gặp vấn đề tương tự gần đây. Tôi đã hack một chút gì đó cùng nhau sử dụng JavaPoet của Square và một số thứ trong build.gradle tự động tạo một lớp Java enum từ attrs.xml trên bản dựng dự án.

Có một bản demo nhỏ và một readme kèm theo giải thích tại https://github.com/afterecho/create_enum_from_xml

Hy vọng nó giúp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.