如何在内部控制中传递 ObservableField
How to pass ObservableField inside internal control
主视图模型如下所示:
class MainVM{
public ObservableField<String> title;
public ObservableField<Boolean> isFlexible;
}
主要布局如下:
<layout>
<date><variable name="item" type="MainVM"></data>
<LinearLayout>
<TextView text="@{item.title}"/>
<CustomCtrl1 vm="@{item.isFlexible}">
</LinearLayout>
</layout>
和CustomCtrl
的布局有点像
<layout>
<date><variable name="item" type="boolean"></data>
<LinearLayout>
...
<Switch checked="@{item}"/>
...
</LinearLayout>
</layout>
问题是 ObservableField
从 MainVM
传递给 CustomCtrl
时会转换为布尔值,之后在 CustomCtrl
中更改布尔值不会影响 MainVM
的。第一个想法是将 CustomCtrl's
viewmodel 从 Boolean
更改为 ObservableField<Boolean>
但由于某些原因不允许这样做。
所以问题是 - 在内部控制中传递 ObservableField
的正确方法是什么。
最好的方法是直接使用双向绑定。这需要 Android Studio 2.1 及更高版本。 Android Studio 2.2 修复了一个 inflation 自定义控件可能会遇到的错误。如果您使用的是 include 而不是自定义控件,那将是微不足道的:
<layout>
<date><variable name="item" type="MainVM"></data>
<LinearLayout>
<TextView android:text="@{item.title}"/>
<include layout="@layout/other" app:vm="@={item.isFlexible}">
</LinearLayout>
</layout>
其他布局:
<layout>
<date><variable name="item" type="boolean"></data>
<LinearLayout>
...
<switch android:checked="@={item}"/>
...
</LinearLayout>
</layout>
使用自定义控件,您必须自己实现侦听器。这意味着您必须有一个 属性 的侦听器。我认为这应该可行(我现在在平板电脑上,所以我无法验证):
@InverseBindingMethods({
InverseBindingMethod(type = CustomControl.class, attribute="vm")})
public class CustomCtrl extends View {
private CustomCtrlBinding binding;
private InverseBindingAdapter listener;
public CustomCtrl(...) {
binding = ...
binding.addOnPropertyChangedCallback(new OnPropertyChangedCallback() {
@Overriide
public void OnPropertyChanged(Observable sender, int propertyId) {
if (listener != null) {
listener.onChange();
}
}
});
}
@Bindable
public boolean getVm() { return binding.getItem(); }
public void setVm(boolean vm) {
binding.setItem(vm);
}
@BindingAdapter("vmAttrChanged")
public static void setListener(CustomCtrl view,
InverseBindingListener listener) {
view.listener = listener;
}
}
那么你就有了双向绑定:
<layout>
<date><variable name="item" type="MainVM"></data>
<LinearLayout>
<TextView android:text="@{item.title}"/>
<CustomCtrl app:vm="@={item.isFlexible}">
</LinearLayout>
</layout>
以及自定义控件布局:
<layout>
<date><variable name="item" type="boolean"></data>
<LinearLayout>
...
<switch android:checked="@={item}"/>
...
</LinearLayout>
</layout>
我通常会编写一个具有自己的属性侦听器的自定义控件,但 InverseBindingListener 在紧要关头工作。
主视图模型如下所示:
class MainVM{
public ObservableField<String> title;
public ObservableField<Boolean> isFlexible;
}
主要布局如下:
<layout>
<date><variable name="item" type="MainVM"></data>
<LinearLayout>
<TextView text="@{item.title}"/>
<CustomCtrl1 vm="@{item.isFlexible}">
</LinearLayout>
</layout>
和CustomCtrl
的布局有点像
<layout>
<date><variable name="item" type="boolean"></data>
<LinearLayout>
...
<Switch checked="@{item}"/>
...
</LinearLayout>
</layout>
问题是 ObservableField
从 MainVM
传递给 CustomCtrl
时会转换为布尔值,之后在 CustomCtrl
中更改布尔值不会影响 MainVM
的。第一个想法是将 CustomCtrl's
viewmodel 从 Boolean
更改为 ObservableField<Boolean>
但由于某些原因不允许这样做。
所以问题是 - 在内部控制中传递 ObservableField
的正确方法是什么。
最好的方法是直接使用双向绑定。这需要 Android Studio 2.1 及更高版本。 Android Studio 2.2 修复了一个 inflation 自定义控件可能会遇到的错误。如果您使用的是 include 而不是自定义控件,那将是微不足道的:
<layout>
<date><variable name="item" type="MainVM"></data>
<LinearLayout>
<TextView android:text="@{item.title}"/>
<include layout="@layout/other" app:vm="@={item.isFlexible}">
</LinearLayout>
</layout>
其他布局:
<layout>
<date><variable name="item" type="boolean"></data>
<LinearLayout>
...
<switch android:checked="@={item}"/>
...
</LinearLayout>
</layout>
使用自定义控件,您必须自己实现侦听器。这意味着您必须有一个 属性 的侦听器。我认为这应该可行(我现在在平板电脑上,所以我无法验证):
@InverseBindingMethods({
InverseBindingMethod(type = CustomControl.class, attribute="vm")})
public class CustomCtrl extends View {
private CustomCtrlBinding binding;
private InverseBindingAdapter listener;
public CustomCtrl(...) {
binding = ...
binding.addOnPropertyChangedCallback(new OnPropertyChangedCallback() {
@Overriide
public void OnPropertyChanged(Observable sender, int propertyId) {
if (listener != null) {
listener.onChange();
}
}
});
}
@Bindable
public boolean getVm() { return binding.getItem(); }
public void setVm(boolean vm) {
binding.setItem(vm);
}
@BindingAdapter("vmAttrChanged")
public static void setListener(CustomCtrl view,
InverseBindingListener listener) {
view.listener = listener;
}
}
那么你就有了双向绑定:
<layout>
<date><variable name="item" type="MainVM"></data>
<LinearLayout>
<TextView android:text="@{item.title}"/>
<CustomCtrl app:vm="@={item.isFlexible}">
</LinearLayout>
</layout>
以及自定义控件布局:
<layout>
<date><variable name="item" type="boolean"></data>
<LinearLayout>
...
<switch android:checked="@={item}"/>
...
</LinearLayout>
</layout>
我通常会编写一个具有自己的属性侦听器的自定义控件,但 InverseBindingListener 在紧要关头工作。