如何在 Android 布局中按值而不是名称设置枚举属性?
How to set enum attribute by value instead of name in Android Layout?
我有一个自定义视图持有另一个。层次结构:
MyOuterView
->MyInnerView
MyInnerView
具有如下枚举属性:
<attr name="myAttr" format="enum">
<enum name="foo" value="0"/>
<enum name="bar" value="1"/>
</attr>
所以我可以在 MyOuterView
XML 中实例化组件,例如:
<com.example.MyInnerView
....
app:myAttr="foo"/>
这当然有效。
MyOuterView
为定制本身提供了一个论据。基于这个论点,我想设置 MyInnerView
.
的论点
希望的行为是我可以像这样使用数据绑定:
<com.example.MyInnerView
....
app:myAttr="@{data.getMyAttr()}"/>
其中 getMyAttr()
看起来像:
public int getMyAttr() {
return myAttr; // returns 0 or 1
}
结果是编译问题。
****/ data binding error ****msg:Cannot find the setter for attribute 'app:myAttr' with parameter type int on
com.example.MyInnerView
所以显然我不能按值设置枚举,而只能按名称设置。除了以编程方式创建 MyInnerView
之外还有什么想法吗?请注意,我无法更改 MyInnerView
.
不幸的是,只有当有 setter 并且此 setter 映射到属性名称或可以通过其名称解析时,您才能使用像 app:myAttr="@{...}"
这样的属性名称。
在您的情况下,如果此属性仅在 View 的构造函数中使用并且视图没有任何 public 方法来更改此 - 您仍然有一些可能的解决方案:
- 扩展视图并实现逻辑以像此属性一样自定义视图(如果可能)
- 制作 BindingAdapter 并使用反射(如果可能)。
- 通过 copy-paste 创建您自己的视图并修复)
I can't set the enum by value but only by name
这不完全正确。即使通过名称,您也无法使用 DataBinding 设置值,问题是 xml 中定义的属性值通过构造函数传递给 View
。 View
可以同时具有属性和 setter,但情况不一定如此。例如,如果您为 TextView
的 android:text
设置了一个值,则该值设置为 com.android.internal.R.styleable.TextView_text
,然后从构造函数中的 AttributeSet
检索该值。如果您使用 DataBinding,则会调用 setText()
,但这是两个完全不同的东西,功能相同,但它们在代码中不相关。
鉴于您无法修改 MyInnerView
并且没有 setter 您可以调用 myAttr
,您唯一的选择是通过构造函数传递它。 DataBinding 根本不可行,即使使用 BinderAdapter,您也无法在 AttributeSet
中设置值,因为此时 View
已经实例化。
选项 1 - 主题
定义新属性
<attr name="MyInnerViewAttrValue" format="integer" />
然后用样式解析这个属性,例如你可以
<style name="AppTheme.Foo">
<item name="MyInnerViewAttrValue">0</item>
</style>
<style name="AppTheme.Bar">
<item name="MyInnerViewAttrValue">1</item>
</style>
在布局中设置xml
<com.example.MyInnerView
...
app:myAttr="?attr/MyInnerViewAttrValue" />
然后在 Activity
视图实例化之前调用 setTheme(int)
。
选项 2 - 自定义 BindingAdapter
如果你想设置你的值 DataBinding(因为你有一个 setter,或者因为你想用反射破解 MyInnerView
)那么你需要创建一个自定义 BindingAdapter,例如
@BindingAdapter("myAttrValue")
public static void setMyAttr(MyInnerView myInnerView, int value) {
switch (value) {
case 0:
myInnerView.foo();
break;
default:
myInnerView.bar();
}
}
然后在布局中xml
<com.example.lelloman.dummy.MyInnerView
...
myAttrValue="@{model.attr}" />
并从您的 ViewModel
中给出 int
值
public class MyInnerViewModel {
public int getAttr() {
return 1234;
}
}
我有一个自定义视图持有另一个。层次结构:
MyOuterView
->MyInnerView
MyInnerView
具有如下枚举属性:
<attr name="myAttr" format="enum">
<enum name="foo" value="0"/>
<enum name="bar" value="1"/>
</attr>
所以我可以在 MyOuterView
XML 中实例化组件,例如:
<com.example.MyInnerView
....
app:myAttr="foo"/>
这当然有效。
MyOuterView
为定制本身提供了一个论据。基于这个论点,我想设置 MyInnerView
.
希望的行为是我可以像这样使用数据绑定:
<com.example.MyInnerView
....
app:myAttr="@{data.getMyAttr()}"/>
其中 getMyAttr()
看起来像:
public int getMyAttr() {
return myAttr; // returns 0 or 1
}
结果是编译问题。
****/ data binding error ****msg:Cannot find the setter for attribute 'app:myAttr' with parameter type int on com.example.MyInnerView
所以显然我不能按值设置枚举,而只能按名称设置。除了以编程方式创建 MyInnerView
之外还有什么想法吗?请注意,我无法更改 MyInnerView
.
不幸的是,只有当有 setter 并且此 setter 映射到属性名称或可以通过其名称解析时,您才能使用像 app:myAttr="@{...}"
这样的属性名称。
在您的情况下,如果此属性仅在 View 的构造函数中使用并且视图没有任何 public 方法来更改此 - 您仍然有一些可能的解决方案:
- 扩展视图并实现逻辑以像此属性一样自定义视图(如果可能)
- 制作 BindingAdapter 并使用反射(如果可能)。
- 通过 copy-paste 创建您自己的视图并修复)
I can't set the enum by value but only by name
这不完全正确。即使通过名称,您也无法使用 DataBinding 设置值,问题是 xml 中定义的属性值通过构造函数传递给 View
。 View
可以同时具有属性和 setter,但情况不一定如此。例如,如果您为 TextView
的 android:text
设置了一个值,则该值设置为 com.android.internal.R.styleable.TextView_text
,然后从构造函数中的 AttributeSet
检索该值。如果您使用 DataBinding,则会调用 setText()
,但这是两个完全不同的东西,功能相同,但它们在代码中不相关。
鉴于您无法修改 MyInnerView
并且没有 setter 您可以调用 myAttr
,您唯一的选择是通过构造函数传递它。 DataBinding 根本不可行,即使使用 BinderAdapter,您也无法在 AttributeSet
中设置值,因为此时 View
已经实例化。
选项 1 - 主题
定义新属性
<attr name="MyInnerViewAttrValue" format="integer" />
然后用样式解析这个属性,例如你可以
<style name="AppTheme.Foo">
<item name="MyInnerViewAttrValue">0</item>
</style>
<style name="AppTheme.Bar">
<item name="MyInnerViewAttrValue">1</item>
</style>
在布局中设置xml
<com.example.MyInnerView
...
app:myAttr="?attr/MyInnerViewAttrValue" />
然后在 Activity
视图实例化之前调用 setTheme(int)
。
选项 2 - 自定义 BindingAdapter
如果你想设置你的值 DataBinding(因为你有一个 setter,或者因为你想用反射破解 MyInnerView
)那么你需要创建一个自定义 BindingAdapter,例如
@BindingAdapter("myAttrValue")
public static void setMyAttr(MyInnerView myInnerView, int value) {
switch (value) {
case 0:
myInnerView.foo();
break;
default:
myInnerView.bar();
}
}
然后在布局中xml
<com.example.lelloman.dummy.MyInnerView
...
myAttrValue="@{model.attr}" />
并从您的 ViewModel
中给出int
值
public class MyInnerViewModel {
public int getAttr() {
return 1234;
}
}