So erhalten Sie eine Aufzählung, die in attrs.xml im Code erstellt wird

108

Ich habe eine benutzerdefinierte Ansicht ( hier zu finden ) mit einem deklarierbaren Attribut vom Typ enum erstellt. In XML kann ich jetzt einen der Aufzählungseinträge für mein benutzerdefiniertes Attribut auswählen. Jetzt möchte ich eine Methode erstellen, um diesen Wert programmgesteuert festzulegen, kann jedoch nicht auf die Aufzählung zugreifen.

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

Was ich brauche ist so etwas wie: mCustomView.setIcon(R.id.enum_name_x); Aber ich kann die Aufzählung nicht finden oder ich habe sogar keine Ahnung, wie ich die Aufzählung oder die Namen der Aufzählung bekommen kann.

Informatic0re
quelle

Antworten:

99

Es scheint keine automatisierte Möglichkeit zu geben, eine Java-Enumeration aus einer Attribut-Enumeration abzurufen. In Java können Sie den von Ihnen angegebenen numerischen Wert abrufen. Die Zeichenfolge wird in XML-Dateien verwendet (wie Sie zeigen).

Sie können dies in Ihrem Ansichtskonstruktor tun:

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

Wenn Sie den Wert in eine Aufzählung aufnehmen möchten, müssen Sie den Wert entweder selbst einer Java-Aufzählung zuordnen, z.

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

Dann könnten Sie im ersten Codeblock Folgendes verwenden:

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

(Obwohl das Auslösen einer Ausnahme an dieser Stelle möglicherweise keine gute Idee ist, ist es wahrscheinlich besser, einen vernünftigen Standardwert zu wählen.)

Andy Mell
quelle
38

Es ist ganz einfach, zeigen wir allen ein Beispiel, um zu zeigen, wie einfach es ist:

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>

Benutzerdefiniertes Layout:

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

Verwenden Sie jetzt die Richtung wie jede andere Aufzählungsvariable.

Steve Moretz
quelle
Verwenden Sie abschließend einfach Enum attr: TypedArray.getInt (R.styleable.name_your_define, defaultValue)
CalvinChe
@CalvinChe ,, das gibt ein int. Steve Moretz hat es. Ich fühle mich dumm, es nicht zu sehen, aber es ist 4:30 Uhr . Zeit fürs Bett ...
n00dles
Ja, genauso einfach. Die Antwort war nur ein Beispiel, um es besser zu veranschaulichen.
Steve Moretz
2
Dies macht jedoch nur zwei parallele Definitionen von Symbolen. Dies funktioniert nur, solange die Definitionen identisch sind - das heißt, es ist fragil. Das OP erwartete Codezugriff auf eine aus XML generierte Aufzählung. Es scheint, dass dies möglich sein sollte.
Steve White
@SteveWhite Da Sie keinen Zugriff auf XML haben, gibt es keine Möglichkeit, diese zu automatisieren und von einer Definition abhängig zu machen. Dies ist der sauberste (mögliche) Weg, dies zu erreichen. Wenn Sie die XML analysieren und auf diese Weise darauf zugreifen könnten könnte großartig sein, aber du kannst es nicht. (Wenn du es nicht kannst, aber nicht im Code, kannst du ein Plugin schreiben, um die XML zu lesen und die Werte in dein Java zu extrahieren, damit es synchronisiert wird, also ja, auf diese Weise wird es nicht zerbrechlich weil es automatisiert ist.)
steve moretz
13

Gut um der Vernunft willen. Stellen Sie sicher, dass Ihre Ordnungszahlen in Ihrem deklarierten Stil identisch sind wie in Ihrer Enum-Deklaration, und greifen Sie als Array darauf zu.

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
quelle
3
Ich denke, dass das Verlassen auf Ordnungszahlen dazu bestimmt ist, unzuverlässigen Code zu erstellen. Eine Sache wird aktualisiert und nicht die andere, und dann werden Sie Probleme haben.
Tir38
1
Was ist ein besserer Weg, dies zu tun?
Jonathan
6

Lassen Sie mich eine in Kotlin geschriebene Lösung hinzufügen. Inline-Erweiterungsfunktion hinzufügen:

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

Jetzt ist es einfach, eine Aufzählung zu erhalten:

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

Ich weiß, dass es eine Weile her ist, seit die Frage veröffentlicht wurde, aber ich hatte kürzlich das gleiche Problem. Ich habe etwas zusammen gehackt, das JavaPoet von Square und einige Dinge in build.gradle verwendet, die beim Erstellen eines Projekts automatisch eine Java-Enum-Klasse aus attrs.xml erstellen.

Es gibt eine kleine Demo und eine Readme-Datei mit einer Erklärung unter https://github.com/afterecho/create_enum_from_xml

Ich hoffe es hilft.

Darren
quelle