Come ottenere un'enumerazione creata in attrs.xml nel codice


108

Ho creato una vista personalizzata ( trovala qui ) con un attributo declare-styleable di tipo enum. In xml ora posso scegliere una delle voci enum per il mio attributo personalizzato. Ora voglio creare un metodo per impostare questo valore a livello di codice, ma non riesco ad accedere all'enumerazione.

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

Ciò di cui ho bisogno è qualcosa del tipo: mCustomView.setIcon(R.id.enum_name_x); Ma non riesco a trovare l'enumerazione o non ho nemmeno idea di come posso ottenere l'enumerazione oi nomi dell'enumerazione.

Risposte:


100

Non sembra esserci un modo automatizzato per ottenere un'enumerazione Java da un attributo enum - in Java puoi ottenere il valore numerico specificato - la stringa è per l'uso nei file XML (come mostrato).

Puoi farlo nel tuo costruttore di viste:

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

Se vuoi il valore in un'enumerazione devi mappare il valore in un'enumerazione Java tu stesso, ad esempio:

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

Quindi nel primo blocco di codice potresti usare:

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

(anche se lanciare un'eccezione a questo punto potrebbe non essere una grande idea, probabilmente è meglio scegliere un valore predefinito ragionevole)


38

È semplice, mostriamo a tutti un esempio solo per mostrare quanto sia facile:

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>

Layout personalizzato:

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

ora usa direction come qualsiasi altra variabile di enumerazione.


In conclusione, usa questo per ottenere Enum attr: TypedArray.getInt (R.styleable.name_your_define, defaultValue)
CalvinChe

@CalvinChe ,, che restituisce un file int. Ce l'ha Steve Moretz. Mi sento stupido per non averlo visto, ma sono le 4:30 del mattino . È ora di andare a letto ...
n00dles

Sì, proprio così semplice, la risposta era solo un esempio per illustrarlo meglio.
steve moretz

2
Ma questo fa solo due definizioni parallele di simboli. Funziona solo fintanto che le definizioni sono identiche, ovvero è fragile. L'OP si aspettava l'accesso al codice a un enum generato dall'XML. Sembra che dovrebbe essere possibile.
Steve White,

@SteveWhite poiché non hai accesso a xml, non c'è modo di automatizzarlo e lasciarlo dipendere da una definizione.Questo è il modo più pulito (possibile) per ottenerlo.Se puoi analizzare l'xml e accedervi in ​​un modo che potrebbe essere fantastico ma non puoi (a meno che tu non possa ma non nel codice, puoi scrivere un plugin per leggere l'xml ed estrarre i valori nel tuo java così sarà sincronizzato quindi sì in questo modo non sarà fragile perché è automatizzato.)
steve moretz

13

Bene per amore della sanità mentale. Assicurati che i tuoi ordinali siano gli stessi nello stile dichiarato e nella dichiarazione Enum e accedilo come 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];
}

3
Penso che fare affidamento sugli ordinali enum qui sia destinato a creare codice inaffidabile. Una cosa verrà aggiornata e non l'altra e quindi avrai problemi.
tir38

1
quindi qual è un modo migliore per farlo?
Jonathan

6

Vorrei aggiungere una soluzione scritta in kotlin. Aggiungi funzione di estensione in linea:

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

Ora ottenere enum è semplice:

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

4

So che è passato un po 'di tempo da quando la domanda è stata pubblicata, ma recentemente ho avuto lo stesso problema. Ho hackerato qualcosa insieme che utilizza JavaPoet di Square e alcune cose nel build.gradle che crea automaticamente una classe enum Java da attrs.xml sulla build del progetto.

C'è una piccola demo e un file readme con una spiegazione su https://github.com/afterecho/create_enum_from_xml

Spero che sia d'aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.