Comment obtenir une énumération créée dans attrs.xml dans le code

108

J'ai créé une vue personnalisée (trouvez-la ici ) avec un attribut déclarable de type enum. En xml, je peux maintenant choisir l'une des entrées d'énumération pour mon attribut personnalisé. Maintenant, je veux créer une méthode pour définir cette valeur par programme, mais je ne peux pas accéder à l'énumération.

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

Ce dont j'ai besoin, c'est quelque chose comme: mCustomView.setIcon(R.id.enum_name_x); Mais je ne trouve pas l'énumération ou je n'ai même aucune idée de comment je peux obtenir l'énumération ou les noms de l'énumération.

Informatic0re
la source

Réponses:

100

Il ne semble pas y avoir de moyen automatisé d'obtenir une énumération Java à partir d'une énumération d'attribut - en Java, vous pouvez obtenir la valeur numérique que vous avez spécifiée - la chaîne est à utiliser dans les fichiers XML (comme vous le montrez).

Vous pouvez le faire dans votre constructeur de vue:

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

Si vous voulez la valeur dans une énumération, vous devez mapper vous-même la valeur dans une énumération Java, par exemple:

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

Ensuite, dans le premier bloc de code, vous pouvez utiliser:

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

(bien que lancer une exception à ce stade ne soit peut-être pas une bonne idée, il vaut probablement mieux choisir une valeur par défaut raisonnable)

Andy Mell
la source
38

C'est simple, montrons à tout le monde un exemple juste pour montrer à quel point c'est 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>

Disposition personnalisée:

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

utilisez maintenant la direction comme n'importe quelle autre variable d'énumération.

Steve Moretz
la source
En conclusion, utilisez simplement ceci pour obtenir Enum attr: TypedArray.getInt (R.styleable.name_your_define, defaultValue)
CalvinChe
@CalvinChe ,, qui renvoie un int. Steve Moretz l'a. Je me sens stupide de ne pas le voir, mais il est 4h30 du matin . Il est temps d'
aller
Ouais aussi simple que ça, la réponse était juste un exemple pour mieux illustrer ça.
steve moretz
2
Mais cela ne fait que deux définitions parallèles des symboles. Cela ne fonctionne que tant que les définitions sont identiques - c'est-à-dire qu'elle est fragile. L'OP attendait un accès de code à une énumération générée à partir du XML. Il semble que cela devrait être possible.
Steve White le
@SteveWhite puisque vous n'avez pas accès à xml, il n'y a aucun moyen de l'automatiser et de le laisser dépendre d'une définition.C'est le moyen le plus propre (possible) d'y parvenir.Si vous pouvez analyser le xml et y accéder d'une manière qui pourrait être génial mais vous ne pouvez pas. (À moins que vous ne puissiez mais pas dans le code, vous pouvez écrire un plugin pour lire le xml et extraire les valeurs dans votre java afin qu'il soit synchronisé, donc oui, ce ne sera pas fragile parce que c'est automatisé.)
steve moretz
13

Eh bien pour la raison. Assurez-vous que vos ordinaux sont les mêmes dans votre style déclaré que dans votre déclaration Enum et accédez-y sous forme de tableau.

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
la source
3
Je pense que s'appuyer ici sur des enum ordinals est destiné à créer du code non fiable. Une chose sera mise à jour et pas l'autre et vous aurez des problèmes.
tir38 du
1
alors quelle est la meilleure façon de le faire?
Jonathan
6

Permettez-moi d'ajouter une solution écrite en kotlin. Ajouter une fonction d'extension en ligne:

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

Maintenant, obtenir enum est simple:

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

Je sais que cela fait un moment que la question a été publiée, mais j'ai eu le même problème récemment. J'ai piraté ensemble un petit quelque chose qui utilise JavaPoet de Square et des éléments du build.gradle qui créent automatiquement une classe Java enum à partir de attrs.xml lors de la construction du projet.

Il y a une petite démo et un readme avec une explication sur https://github.com/afterecho/create_enum_from_xml

J'espère que ça aide.

Darren
la source