如何在代码中获取在attrs.xml中创建的枚举类型

133

我创建了一个自定义视图(在此处找到它 https://bitbucket.org/informatic0re/awesome-font-iconview),其中包含一个类型为枚举的 declare-styleable 属性。在 XML 中,我现在可以选择其中一个枚举条目作为我的自定义属性。现在我想创建一个方法以编程方式设置这个值,但是我无法访问该枚举。

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); 但我找不到枚举或者我甚至不知道如何获取枚举或枚举名称。

6个回答

110

目前似乎没有自动化的方式从属性枚举获取Java枚举,但在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();
}

如果您想将值转换为枚举类型,则需要自己将值映射到Java枚举,例如:
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))); 

尽管在此时抛出异常可能不是一个好主意,最好选择一个合理的默认值。

1
这是最干净的做法。但如果能够在一个地方拥有枚举到ID映射,那么它可能会更好。通过这种方式,我们必须在XML属性和枚举类中都定义映射。 - Tushar Kathuria

59

很简单,让我们展示一个例子来说明它有多容易:

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

现在可以像任何其他枚举变量一样使用方向。


总之,只需使用以下代码获取枚举属性:TypedArray.getInt(R.styleable.name_your_define, defaultValue) - CalvinChe
@CalvinChe ,返回一个 int。Steve Moretz 已拥有它。我觉得自己很笨,没看见,但现在已经是凌晨4:30了,该睡觉了... - n00dles
是的,就这么简单。答案只是为了更好地说明它而举的一个例子。 - Steve Moretz
9
但这只是创造了两个平行的符号定义。只有当这些定义完全相同时才有效,也就是说,它是脆弱的。原帖作者期望能够访问从XML生成的枚举类型。看起来这应该是可能的。 - Steve White
@SteveWhite 由于您无法访问xml,因此没有办法自动化并使其依赖于一个定义。这是实现它的最干净(可能)的方法。如果您可以解析xml并以某种方式访问它,那将非常好,但您不能(除非您可以,但不在代码中,您可以编写插件来读取xml并将值提取到您的java中,这样它就会同步,所以它不会很脆弱,因为它是自动化的)。 - Steve Moretz

23

让我添加一个用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 
}

现在获取枚举值变得简单了:
val a: TypedArray = obtainStyledAttributes(...)
val yourEnum: YourEnum = a.getEnum(R.styleable.YourView_someAttr, YourEnum.DEFAULT)
a.recycle()

13

为了代码的健康,请确保您在声明可样式化的对象和枚举声明中使用相同的序数,并将其作为数组进行访问。

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
我认为在这里依赖枚举序号注定会产生不可靠的代码。一件事情被更新了而另一件却没有更新,那么你就会遇到麻烦。 - tir38
2
那么有更好的方法吗? - Jono

5
我知道这个问题发布已经有一段时间了,但我最近遇到了同样的问题。我通过使用Square的JavaPoet和build.gradle中的一些东西,编写了一个小程序,可以在项目构建时从attrs.xml自动创建一个Java枚举类。
https://github.com/afterecho/create_enum_from_xml上有一个小演示和说明文档。
希望对你有所帮助。

0

我在 Kotlin 中所做的是使用伴生对象工厂获取它们,就像这样。这里是一个简单排序视图的示例。

  1. 在xml中定义您的枚举

     <declare-styleable name="SortingView">
     <attr name="sortOrder" format="enum">
         <enum name="ASC" value="0"/>
         <enum name="DESC" value="1" />
     </attr>
    
  2. 在代码中定义枚举,添加伴随方法以根据其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. 使用getInt()方法从类型化数组中获取枚举值,并将其传递给工厂以创建您的枚举。

     sortOrder = SortOrder.fromParams(getInt(R.styleable.SortingView_sortOrder, SortOrder.ASC.id))
    
现在你可以像这样在xml中使用枚举: enter image description here 并且也可以像第3步一样在代码中使用你的枚举类。
唯一的缺点是,它们必须在代码和xml中定义,但是效果非常好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接