วิธีรับ enum ที่สร้างในรหัส attrs.xml


110

ฉันสร้างมุมมองที่กำหนดเอง (ค้นหาได้ที่นี่ ) โดยมีแอตทริบิวต์ที่สามารถกำหนดรูปแบบได้ประเภท 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); แต่ฉันไม่พบ enum หรือฉันไม่รู้ว่าจะหา enum หรือชื่อ enum ได้อย่างไร

คำตอบ:


100

ดูเหมือนจะไม่มีวิธีอัตโนมัติในการรับ Java enum จากแอตทริบิวต์ 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();
}

หากคุณต้องการให้ค่าเป็น enum คุณจะต้องแมปค่ากับ Java enum ด้วยตัวคุณเองเช่น:

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

(แม้ว่าการทิ้งข้อยกเว้นในตอนนี้อาจไม่ใช่ความคิดที่ดี แต่ก็ควรเลือกค่าเริ่มต้นที่สมเหตุสมผล)


นี่เป็นวิธีที่สะอาดที่สุดในการทำ แต่จะดีกว่ามากถ้าเป็นไปได้ที่จะมี enum to id mapping ในที่เดียว ด้วยวิธีนี้เราต้องกำหนดการแม็ปทั้งในแอตทริบิวต์ xml และในคลาส enum
Tushar Kathuria

39

มันง่ายมากที่จะแสดงตัวอย่างให้ทุกคนเห็นว่ามันง่ายแค่ไหน:

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ไฟล์. Steve Moretz มีมัน ฉันรู้สึกอึ้งที่ไม่ได้เห็นมันแต่เป็นเวลา 04.30 น . เวลาเข้านอน ...
n00dles

ใช่ง่ายๆเพียงแค่นั้นคำตอบเป็นเพียงตัวอย่างสำหรับการอธิบายให้ดีขึ้น
steve moretz

3
แต่นี่ทำให้นิยามของสัญลักษณ์คู่ขนานกันสองตัว สิ่งนี้ใช้ได้ตราบเท่าที่คำจำกัดความเหมือนกันนั่นคือมันเปราะบาง OP คาดหวังว่าจะมีการเข้าถึงรหัสไปยัง enum ที่สร้างจาก XML ดูเหมือนว่าน่าจะเป็นไปได้
Steve White

@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
ฉันคิดว่าการอาศัยลำดับ enum ที่นี่ถูกกำหนดให้สร้างรหัสที่ไม่น่าเชื่อถือ สิ่งหนึ่งจะได้รับการอัปเดตไม่ใช่อย่างอื่นแล้วคุณจะมีปัญหา
tir38

1
แล้วจะมีวิธีไหนดีกว่ากัน?
Jonathan

7

ขอฉันเพิ่มคำตอบที่เขียนด้วย 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 
}

ตอนนี้การรับ enum นั้นง่ายมาก:

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

4

ฉันรู้ว่ามีการโพสต์คำถามมาระยะหนึ่งแล้ว แต่เมื่อเร็ว ๆ นี้ฉันมีปัญหาเดียวกัน ฉันแฮ็คบางอย่างเข้าด้วยกันซึ่งใช้ JavaPoet ของ Square และบางสิ่งใน build.gradle ที่สร้างคลาส Java enum โดยอัตโนมัติจาก attrs.xml ในการสร้างโครงการ

มีการสาธิตเล็กน้อยและ readme พร้อมคำอธิบายที่https://github.com/afterecho/create_enum_from_xml

หวังว่าจะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.