参考:
https://developer.android.google.cn/topic/libraries/data-binding/start.html
https://www.jianshu.com/p/bd9016418af2
DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。MVVM 相对于 MVP,其实就是将 Presenter 层替换成了 ViewModel 层。DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常。
要将应用配置为使用数据绑定,请在应用模块的 build.gradle 文件中添加 dataBinding 元素,如以下示例所示:
android {
...
dataBinding {
enabled = true
}
}
打开布局文件,选中根布局的 ViewGroup,按住 Alt + 回车键,点击 “Convert to data binding layout”,就可以生成 DataBinding 需要的布局规则。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
androidx.constraintlayout.widget.ConstraintLayout>
layout>
和原始布局的区别在于多出了一个 layout 标签将原布局包裹了起来,data 标签用于声明要用到的变量以及变量类型,要实现 MVVM 的 ViewModel 就需要把数据(Model)与 UI(View)进行绑定,data 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道。
package com.hongx.databinding.model;
/**
* @author: fuchenming
* @create: 2019-12-26 13:47
*/
public class User {
private String name;
private String password;
}
在 data 标签里声明要使用到的变量名、类的全路径
<data>
<variable
name="userInfo"
type="com.hongx.databinding.model.User" />
data>
如果 User 类型要多处用到,也可以直接将之 import 进来,这样就不用每次都指明整个包名路径了,而 java.lang.* 包中的类会被自动导入,所以可以直接使用
<data>
<import type="com.hongx.databinding.model.User" />
<variable
name="userInfo"
type="User" />
</data>
如果存在 import 的类名相同的情况,可以使用 alias 指定别名
<data>
<import
alias="TempUser"
type="com.hongx.databinding.model.User" />
<variable
name="tempUserInfo"
type="TempUser" />
data>
通过设置 userInfo 的变量值 使 TextView 显示相应的文本
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`姓名:` + userInfo.name,default=zhangsan}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`密码:` + userInfo.password,default=123456}" />
LinearLayout>
可以在 Activity 中通过 DataBindingUtil 设置布局文件
public class MainActivity extends AppCompatActivity {
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
user = new User("fuhongxue", "123456");
activityMainBinding.setUserInfo(user);
}
}
如果在TextView中设置id, 可以ActivityMainBinding 直接获取到指定 ID 的控件
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
activityMainBinding.tvName.setText("zhangsan");
每个数据绑定布局文件都会生成一个绑定类,ViewDataBinding 的实例名是根据布局文件名来生成,将之改为首字母大写的驼峰命名法来命名,并省略布局文件名包含的下划线。控件的获取方式类似,但首字母小写
也可以通过如下方式自定义 ViewDataBinding 的实例名
<data class="MyActivityMainBinding">
实现数据变化自动驱动 UI 刷新的方式有三种:BaseObservable、ObservableField、ObservableCollection
一个纯净的 ViewModel 类被更新后,并不会让 UI 自动更新。而数据绑定后,我们自然会希望数据变更后 UI 会即时刷新,Observable 就是为此而生的概念
BaseObservable 提供了 notifyChange() 和 notifyPropertyChanged() 两个方法,前者会刷新所有的值域,后者则只更新对应 BR 的 flag,该 BR 的生成通过注释 @Bindable 生成,可以通过 BR notify 特定属性关联的视图
public class Goods extends BaseObservable {
//如果是 public 修饰符,则可以直接在成员变量上方加上 @Bindable 注解
@Bindable
public String name;
//如果是 private 修饰符,则在成员变量的 get 方法上添加 @Bindable 注解
private String details;
private float price;
public Goods(String name, String details, float price) {
this.name = name;
this.details = details;
this.price = price;
}
public void setName(String name) {
this.name = name;
//只更新本字段
notifyPropertyChanged(com.hongx.databinding.BR.name);
}
@Bindable
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
//更新所有字段
notifyChange();
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
在 setName() 方法中更新的只是本字段,而 setDetails() 方法中更新的是所有字段
dataBinding 提供了 BindingAdapter 这个注解用于支持自定义属性,或者是修改原有属性。注解值可以是已有的 xml 属性,例如 android:src、android:text等,也可以自定义属性然后在 xml 中使用
例如,对于一个 ImageView ,我们希望在某个变量值发生变化时,可以动态改变显示的图片,此时就可以通过 BindingAdapter 来实现
需要先定义一个静态方法,为之添加 BindingAdapter 注解,注解值是为 ImageView 控件自定义的属性名,而该静态方法的两个参数可以这样来理解:当 ImageView 控件的 url 属性值发生变化时,dataBinding 就会将 ImageView 实例以及新的 url 值传递给 loadImage() 方法,从而可以在此动态改变 ImageView 的相关属性
使用Picasso显示网络图片
public class Image {
private String url;
public Image(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
//自定义属性:提供一个静态方法来加载image
@BindingAdapter({"url"})
public static void loadImage(ImageView view, String url) {
Picasso.with(view.getContext()).load(url).into(view);
}
}
<data>
<import type="com.hongx.databinding.model.Image" />
<variable
name="image"
type="Image" />
data>
<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_launcher_background"
app:url="@{image.url}" />
image = new Image("https://www.baidu.com/img/bd_logo1.png?where=super");
activityMainBinding.setImage(image);
记得添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>