dataBinding
可以理解为dataBinding只是一种工具,它解决的是View和数据之间的双向绑定。MVVM是一种架构模式,两者是有本质区别的。双向数据绑定
数据发生改变后,自动通知UI刷新页面,不再需要人工绑定最新数据到View上。UI改变后也能同步给数据
减少模板代码
有了dataBinding,
从此不用再写findViewById
,setOnClickListener
等枯燥生硬的代码,大大提高工作效率。从此Butterknife靠边站。
释放Activity/Fragment
以前,我们在Activity
,Fragment
或Presenter
中计算数据再绑定到View组件上,导致View层很臃肿,现在,这部分工作,我们可以直接在xml布局文件中完成。Activity
,Fragment
让它更加只关注核心业务。
数据绑定空安全
在xml中绑定数据它是空安全的,因为dataBinding
在数据绑定上会自动装箱和空判断,所以大大减少了数据绑定带来的NullpointException
问题。
需要在使用dataBinding
的模块的build.gradle
文件中添加dataBinding
配置,
如下所示,
每个使用dataBinding的模块都应该在build.gradle中添加如下配置
android { ... dataBinding { enabled = true } }
在布局文件中,选中根布局的标签,按住 Alt + 回车键
,点击Convert to data binding layout
,即可转换成dataBinding
布局。
转换后的布局,最外层变成了layout
标签,里面包裹了data
标签和常规的布局元素。data
元素用来声明在此布局用使用到的变量和变量的类型,以及类引用。
是不是所有属性都能用dataBinding
来绑定呢?当然不是! 如果一个属性xxx
,在该类中有setXxx
方法,我们才能使用dataBinding
来绑定。比如android:layout_width
,android_layout_height
就不能使用dataBinding
来绑定值,而android:paddingLeft
,android:textSize
都是可以的。
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" <data> <variable name="user" type="com.mooc.ppjoke.model.User" /> <import type="com.mooc.ppjoke.user.UserManager"></import> </data> <androidx.constraintlayout.widget.ConstraintLayout> <TextView android:id="@+id/tvName" android:layout_width="200dp" //不能使用dataBinding动态绑定 android:text="@{user.name}" //单向绑定数据变更自动通知UI android:textSize="@{@dimen/16sp}"//资源引用 android:text="@{user.name+@string/suffix}" //字符串的拼接需要引用资源 android:text="@{UserManager.getUserName()}" //调用静态方法,类必须先导入 android:onClick="@{()-> UserManager.login()}" /> <EditText //双向绑定数据变更自动更新UI,UI变更了也能自动更新user中name的数据,比单向绑定多个= android:text="@={user.name}" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
android:text="@{user.name}"等价于tvName.text = user.name这样就将数据和View相关联了。
那么如何实现View和数据的双向绑定呢?我们只需要让实体类User继承BaseObservable。当user中字段发生变更,只需要调用user.notifyPropertyChanged就可以让UI刷新。
public class User extends BaseObservable { public String name; //当使用name字段发生变更后,若想UI自动刷新,我们需要给它写个get方法并且标记Bindable注解。 //最后调用notifyPropertyChanged方法即可 @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(com.mooc.ppjoke.BR.user); } }
dataBinding
也支持在布局文件中使用 数组、Lsit
、Set
和Map
,且在布局文件中都可以通过 list[index] 的形式来获取元素,因为xml的特性,在声明 Lsit< String > 之类的数据类型时,需要使用尖括号的转义字符(在下面)
<?xml version="1.0" encoding="utf-8"?> <layout > <data> <import type="java.util.List" /> <import type="java.util.Map" /> <import type="java.util.Set" /> <import type="android.util.SparseArray" /> <variable name="array" type="String[]" /> <variable name="list" type="List<String>" /> //List<String> 其中<和>俩字符需要转义 <variable name="map" type="Map<String, String>" /> //Map<String> <variable name="set" type="Set<String>" /> //Set<String> <variable name="sparse" type="SparseArray<String>" /> //SparseArray<String> <variable name="index" type="int" /> <variable name="key" type="String" /> </data> <LinearLayout> <TextView android:text="@{array[1]}" /> <TextView android:text="@{sparse[index]}" /> <TextView android:text="@{list[index]}" /> <TextView android:text="@{map[key]}" /> <TextView android:text='@{map["慕课jetpack"]}' /> <TextView android:text='@{set.contains("xxx")?"慕课jetpack":key}' /> </LinearLayout> </layout>
dataBinding
在xml中数据绑定支持的语法表达式也是非常丰富的,支持在布局文件中使用以下运算符、表达式和关键字:
算术运算符 + - / * %
字符串连接运算符 +
逻辑运算符 && ||
二元运算符 & | ^
一元运算符 + - ! ~
移位运算符 >> >>> <<
比较运算符 == > < >= <= (<需要被转义成<
>需要被转义为>
)
instanceof
分组运算符 ()
字面量运算符 - 字符、字符串、数字、null
类型转换,方法调用
字段访问
数组访问 []
三元运算符 ?
不支持以下操作 this,super,new,显示泛型调用
ImageView
增加几个属性,必须要写个自定义的ImageView
在构造函数中一顿解析。那看看使用dataBinding如何拓展View属性。public class PPImageView extends ImageView{ //需要使用BindingAdapter注解并标记在public static方法上。 //value中的字段随意添加和方法参数一一对应即可。 @BindingAdapter(value = {"image_url", "isCircle"}) public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle) { view.setImageUrl(view, imageUrl, isCircle, 0); } //requireAll = false代表是否以下三个属性在xml中同时使用才会调用到该方法 //为false的话,只要有一个属性被使用就能调用到该方法 @BindingAdapter(value = {"image_url", "isCircle", "radius"}, requireAll = false) public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle, int radius) { ...... } } //在布局文件中如下使用,便能实现图片圆角和资源Url绑定的功能 <com.mooc.ppjoke.view.PPImageView ....... app:image_url ="@{user.avatar}" app:radius="@{50}"> </com.mooc.ppjoke.view.PPImageView>
如fragment_layout_my.xml
布局,在编译时会生成FragmentLayoutMyImpl.java
,我们可以搜索这种类debug跟进解决问题。
不建议在列表中乱用,因为dataBinding
数据绑定是延迟一帧的,如果列表中的ItemView
的宽高需要计算后才能正确展示,不建议使用dataBinding
操作,否则会看到列表ItemView
明显的撑开动画,体验不好。
实体类配合BaseObservable
可以友好的解决数据双向绑定的问题