引言
这篇文章主要讲两个知识点:
- 双向绑定:主要是把上一篇文章中的一些注解通过具体实例再详细说一下,通过这些实例,尽量把双向绑定说的清楚一些。
- BaseObservable:包括与BaseObservable相关的东西。比如:@Bindable注解,响应式包装类等;
什么叫双向绑定
举个例子简单说一下:在TextView中,我们通过dataBinding把实体中的数据放到TextView中展示,这是从实体到view方向上的绑定;当TextView的数据发生改变时,比如我们手动输入了一些数据,我们通过dataBinding把view中的数据设置到对应的实体类的字段中,这是从view到实体类方向上的绑定,整合起来就是双向绑定。但是双向绑定说起来容易做起来难,为此dataBinding提供了一系列以Inverse开头的注解,来帮助DataBinding自身的整个体系以及开发者可以更好的控制和使用双向绑定。
上图是我简单画的一个双向绑定示意图:
- 当左侧数据源(实体类)发生改变时,自动通知右侧view刷新数据,把新的数据绑定到view中展示。
- 当右侧view中的内容改变时,自动通知左侧数据源(实体类)刷新数据,把view中新的内容绑定到数据源(实体类)中。
双向绑定的问题
根据上面对双向绑定的解释,我们发现两个问题:
- 死循环绑定:因为数据源改变会通知view刷新,而view改变又会通知数据源刷新,这样一直循环往复,就形成了死循环绑定。
- 数据源中的数据有时需要经过转换才能在view中展示,而view中展示的内容也需要经过转换才能绑定到对应的数据源上。
双向绑定问题的解决方式
- 死循环绑定的解决方式:
解决方式很简单,举个dataBinding源码中的例子,路径:android.databinding.adapters.TextViewBindingAdapter
:
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
final CharSequence oldText = view.getText();
if (text == oldText || (text == null && oldText.length() == 0)) {
return;
}
if (text instanceof Spanned) {
if (text.equals(oldText)) {
return; // No change in the spans, so don't set anything.
}
} else if (!haveContentsChanged(text, oldText)) {
return; // No content changes, so don't set anything.
}
view.setText(text);
}
从上面的源码可以看到,在处理双向绑定的业务逻辑时,要对新旧数据进行比较,只处理新旧数据不一样的数据,对于新旧数据一样的数据作return处理,通过这种方式来避免死循环绑定。
- 数据源中的数据有时需要经过转换才能在view中展示,而view中展示的内容也需要经过转换才能绑定到对应的数据源上的解决方式:
有些开发者可能不太了解什么叫数据源中的数据需要经过转换才能在view中展示,以及view中展示的内容也需要经过转换才能绑定到对应的数据源上,我举个实际开发中的场景:
在一些约车或者外卖等类型的APP中,都有订单类型这个字段,以约车APP为例,订单有立即单,预约单,接机单等其他订单类型,用户在提交订单后,在用户的订单列表或详情中是可以看到订单类型的,比如“立即单”,但是在服务端,存储立即单这个字段的时候,并不是直接存储“立即单”这几个字的,而是以字典表的形式来存储的,比如“OT00001”代表立即单,在开发中,我们肯定不能把“OT00001”展示到界面上给用户看吧,但是服务端给我们返回的json中就是“OT00001”,所以我们在接收到“OT00001”时要把“OT00001”转换成“立即单”展示到界面上给用户看,这就是数据源中的数据需要经过转换才能在view中展示;
而如果用户修改了订单类型,然后提交到服务端去修改,我们肯定是以“OT00001”的形式提交到服务端的,但是用户在输入时却是以“立即单”的形式输入的,所以在提交服务端时,我们需要把“立即单”转换为“OT00001”再去提交到服务端,这就是view中展示的内容也需要经过转换才能绑定到对应的数据源上。
如果不使用dataBinding,这些转换时机以及逻辑都要我们自己掌握,但是使用了dataBinding之后,这些操作都变得自动化,在你设置“OT00001”时,会自动转换为“立即单”在界面上展示,而当你输入“立即单”时,对应的实体类字段会自动变为“OT00001”,这会大大节省我们的开发成本。
我们可以通过@InverseMethod来做到这种自动化。@InverseMethod的基本用法已经在上一篇文章中有说明,这里就直接贴出代码:
1.使用@InverseMethod定义转换方法
public class InverseMethodDemo {
@InverseMethod("orderTypeToString")
public static String stringToOrderType(String value) {
if (value == null) {
return null;
}
switch (value) {
case "立即单":
return AppConstants.ORDER_TYPE_1;
case "预约单":
return AppConstants.ORDER_TYPE_2;
case "接机单":
return AppConstants.ORDER_TYPE_3;
case "送机单":
return AppConstants.ORDER_TYPE_4;
case "半日租单":
return AppConstants.ORDER_TYPE_5;
case "全日租单":
return AppConstants.ORDER_TYPE_6;
default:
return null;
}
}
public static String orderTypeToString(String code) {
if (code == null) {
return null;
}
switch (code) {
case AppConstants.ORDER_TYPE_1:
return "立即单";
case AppConstants.ORDER_TYPE_2:
return "预约单";
case AppConstants.ORDER_TYPE_3:
return "接机单";
case AppConstants.ORDER_TYPE_4:
return "送机单";
case AppConstants.ORDER_TYPE_5:
return "半日租单";
case AppConstants.ORDER_TYPE_6:
return "全日租单";
default:
return null;
}
}
}
2.在布局文件中使用
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="com.qiangxi.databindingdemo.databinding.method.InverseMethodDemo"/>
<variable
name