本篇文章着重于写怎么实现DataBinding自定义 View 的双向绑定,理论和定义不再赘述。理论和定义可以参考这两篇博文:
Android DataBinding (五) 自定义 View 的双向绑定
Databinding的使用(自定义控件的全局注入、布局绑定)
当然,这两篇博文也有具体实现的代码,十分感谢这两篇博主的博文参考!
首先说明一下这个自定义view的功能:MultiSelectionSpinner(自定义单多选Spinner),点击控件时弹出供单选或多选的Dialog,见下图。当点击确定时会调用回调接口与viewmodel进行属性绑定。
实现步骤如下:
1、自定义MultiSelectionSpinner类
要支持双向绑定,首先要在类名上定义 @InverseBindingMethods
@InverseBindingMethods({
@InverseBindingMethod(
type = MultiSelectionSpinner.class,
attribute = "selectConfirm",
event = "selectConfirmAttrChanged",
method = "getSelectConfirm")
})
public class MultiSelectionSpinner extends AppCompatSpinner implements DialogInterface.OnClickListener,OnMultiChoiceClickListener {
………………………… // 自定义view具体逻辑实现代码
public interface OnSelectConfirmListener{
void onSelectConfirm();
}
private OnSelectConfirmListener mListener;
public void setOnSelectConfirmListener(OnSelectConfirmListener listener) {
mListener = listener;
}
@InverseBindingAdapter(attribute = "selectConfirm", event="selectConfirmAttrChanged")
public static String getSelectConfirm(MultiSelectionSpinner view) {
return view.getSelectedItemsAsString();
}
@BindingAdapter({"selectConfirm"})
public static void setSelectConfirm(MultiSelectionSpinner spinner, String item){
if(item!=null){
spinner.setSelection(Arrays.asList(item.split("\\,")));
}
}
@BindingAdapter(value = {"onSelectConfirm", "selectConfirmAttrChanged"}, requireAll = false)
public static void setOnValueSelectListener(MultiSelectionSpinner view,
final OnSelectConfirmListener onSelectConfirmListener,final InverseBindingListener bindingListener) {
if (bindingListener == null) {
view.setOnSelectConfirmListener(onSelectConfirmListener);
} else {
view.setOnSelectConfirmListener(new OnSelectConfirmListener() {
@Override
public void onSelectConfirm() {
if(onSelectConfirmListener!=null){
onSelectConfirmListener.onSelectConfirm();
}else{
// 通知 ViewModel
bindingListener.onChange();
}
}
});
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean performClick() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
//builder.setMultiChoiceItems(_items, mSelection, this);
if(!setSingleChoice){ // 多选
builder.setMultiChoiceItems(_items, mSelection, this);
}else{
builder.setSingleChoiceItems(_items, mChoice,this); // 单选
}
_itemsAtStart = getSelectedItemsAsString();
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
// Dialog 确定按钮点击事件,调用onSelectConfirm接口实现与viewmodel属性进行绑定
@Override
public void onClick(DialogInterface dialog, int which) {
System.arraycopy(mSelection, 0, mSelectionAtStart, 0, mSelection.length);
if(mListener!=null){
mListener.onSelectConfirm();
}
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
simple_adapter.clear();
simple_adapter.add(_itemsAtStart);
System.arraycopy(mSelectionAtStart, 0, mSelection, 0, mSelectionAtStart.length);
}
});
builder.show();
return true;
}
}
2、布局文件xml中用app:selectConfirm="@={}"与viewmodel进行双向绑定。注:下面代码只是截取了布局xml中婚否那一部分
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:layout_weight="1.5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:textSize="20sp"
android:gravity="center"
android:text="(单选) 婚否"/>
<com.mvvmdemo.MultiSelectionSpinner
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="40dp"
android:entries="@array/ifnot"
android:textSize="18sp"
android:background="@drawable/spinner_bg"
custom:custom_gravity="center"
custom:singleChoice="true"
app:selectConfirm="@={simple.marriage}"/>
</LinearLayout>
2、Activity实现效果
public class BlogActivity extends AppCompatActivity implements View.OnClickListener{
private ActivityBlogBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding=DataBindingUtil.setContentView(this,R.layout.activity_blog);
binding.setSimple(new SimpleEntity("是","篮球"));
binding.setClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_blog_save:
SimpleEntity simple=binding.getSimple();
Toast.makeText(BlogActivity.this,simple.toString(),Toast.LENGTH_SHORT).show();
break;
default:
}
}
}
运行过程分析:
1、Activity在onCreate()设置布局并且绑定viewmodel后即会调用MultiSelectionSpinner类中设置的 @BindingAdapter({"selectConfirm"}),此时界面上自定义MultiSelectionSpinner即会分别显示我们设置的"是"和"篮球",同时会调用MultiSelectionSpinner类中的监听事件 setOnValueSelectListener()设置确定按钮点击回调接口。
2、当点击自定义MultiSelectionSpinner弹出Dialog选择完毕点击确定后即会进入1设置好的回调事件中调用 bindingListener.onChange(); 通知 ViewModel要改变viewl,接着会调用之前在 MultiSelectionSpinner类中的绑定好的 @InverseBindingAdapter getSelectConfirm(),在该方法中获取到选择的值后就会把值返回给@BindingAdapter({"selectConfirm"}),由该Adapter把选择值显示在Activity的View上。
至此自定义 View 的双向绑定完成。
水平有限,欢迎批评指正!