How to get an enum which is created in attrs.xml in code

I created a custom View (find it here) with an declare-styleable attribute of type enum. In xml I can now choose one of the enum entries for my custom attribute. Now I want to create an method to set this value programmatically, but I can not access the 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"/>

What I need is something like: mCustomView.setIcon(R.id.enum_name_x); But I can not find the enum or I even have no idea how I can get the enum or the names of the enum.

64494 次浏览

There does not seem to be an automated way to get a Java enum from an attribute enum - in Java you can get the numeric value you specified - the string is for use in XML files (as you show).

You could do this in your view constructor:

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

If you want the value into an enum you would need to either map the value into a Java enum yourself, e.g.:

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

Then in the first code block you could use:

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

(though throwing an exception at this point may not be a great idea, probably better to choose a sensible default value)

Well for sanity's sake. Make sure your ordinals are the same in your declared styleable as in your Enum declaration and access it as an 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];
}

I know it's been a while since the question was posted, but I had the same issue recently. I hacked a little something together that uses Square's JavaPoet and some stuff in the build.gradle that automatically creates a Java enum class from the attrs.xml on project build.

There's a little demo and a readme with an explanation at https://github.com/afterecho/create_enum_from_xml

Hope it helps.

It's simple let's show everybody an example just to show how easy it is:

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>

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

now use direction like any other enumeration variable.

Let me add a solution written in kotlin. Add inline extension function:

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

Now getting enum is simple:

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

What I did i kotlin, was getting them with usage of companion object factory, like so. Here an exmaple of simple sorting view.

  1. Define your enum in xml

     <declare-styleable name="SortingView">
    <attr name="sortOrder" format="enum">
    <enum name="ASC" value="0"/>
    <enum name="DESC" value="1" />
    </attr>
    
  2. Define enum in code, add companion method to return proper enum value depending on it's id.

     enum class SortOrder(val id:Int) {
    ASC(0),
    DESC(1);
    
    
    companion object {
    fun fromParams(id:Int):SortOrder {
    return when(id) {
    0 -> ASC
    1 -> DESC
    else -> throw IllegalAccessException("Unsupported SortOrder")
    }
    }
    }
    

    }

  3. Get The enum values from typed array with getInt() method, and pass it to the factory to create your enum.

     sortOrder = SortOrder.fromParams(getInt(R.styleable.SortingView_sortOrder, SortOrder.ASC.id))
    

Now you can use your enum in xml like so enter image description here and also use your enum class in the code like in step 3.

The only downside is, they had to be defined in code and in xml as well, but works like a charm.