(一)概述

DataBinding是Jetpack中实现数据双向绑定的组件。

android {
    ...
    // 开启 dataBinding
    dataBinding {
        enabled = true
    }
}

(二)DataBinding 注解

1)@Bindable
说明:该注解用于双向绑定,需要与 notifyPropertyChanged()方法结合使用
该注解用于标记实体类的方法, 且实体类必须继承BaseObserable.
@Bindable
var hasFavorite: Boolean = false
    set(value) {
        field = value
        notifyPropertyChanged(BR._all)
    }

2) @BindingAdapter
/**
 * 加载网络图片
 *
 * 自定义属性:使用@BindingAdapter
 * 方法必须为公共静态方法,可以有一到多个参数。
 * requireAll = false:可以使用这两个属性中的任一个或同时使用.
 * 注意:方法的第一个参数必须是 自定义 XXImageView 本身
 */
@BindingAdapter(value = ["image_url", "isCircle", "radius"], requireAll = false)
fun setImageUrl(view: XXImageView, imgUrl: String, isCircle: Boolean = false, radius: Int = 0) {
    val builder = Glide.with(view).load(imgUrl)
    if (isCircle) {
        builder.transform(CircleCrop())
    } else if (radius > 0) {
        builder.transform(RoundedCornersTransformation(PixUtils.dp2px(radius), 0))
    }
    val layoutParams = view.layoutParams
    layoutParams?.let {// 设置图片尺寸
        if (it.width > 0 && it.height > 0) {
            builder.override(it.width, it.height)
        }
    }
    builder.into(view)
}
---例如---
<!-- app:自定义属性=@{data变量名.xxx} -->
<!-- 监听器绑定 android:onClick="@{()->xx.xx(param1, param2…)} -->
<com.mooc.ppjoke.view.PPImageView
    android:id="@+id/avatar"
    android:layout_width="@dimen/dp_40"
    android:layout_height="@dimen/dp_40"
    app:image_url="@{user.avatar}"
    app:isCircle="@{true}"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:onClick="@{()->xx.xx(param1, param2…)}"
    tools:src="@mipmap/ic_launcher_round" />

 布局内变量引用 layout_xxx.xml:

<data>

    <variable
        name="feed"
        type="com.xxx.xxx.model.Feed" />
     <variable
        name="tagText"
        type="String" />
        
    <!-- 默认会自动导入java.lang.*,除此之外必须手动导入 -->
    <import type="android.view.View" />
    <import type="android.text.TextUtils" />
</data>

<!-- app:${variable.name} = "@{xxx.xx}" -->
<!-- 例如:app:user 引用的是layout_feed_author布局里定义的user变量 -->
<!--头像昵称区-->
<include
    layout="@layout/layout_feed_author"
    app:user="@{feed.author}" />

val inflater: LayoutInflater = LayoutInflater.from(context)
val binding = LayoutFeedTypeVideoBinding.inflate(inflater)
说明:通过这个binding 对象就可以拿到所有layout_feed_type_video.xml组件对象和变量。

注意:当导入多个类型时,类型可能会冲突。当类名有冲突时,可以使用别名重命名
<import type="android.view.View"/>
<import type="com.gfd.demo.View” alias="Vista"/>

另外:
1) 只需导入Context:
<import type="android.content.Context" />
就可以使用访问数据绑定的上下文对象context(无需定义变量)
android:onClick="@{()->InteractionPresenter.INSTANCE.openShare(context, feed)}"

2) 可以给 databinding 布局的 xml 变量(如:user)直接赋值:
binding = FragmentMyBinding.inflate(inflater, container, false)
binding.setVariable(BR.user, UserManager.mUser)

资源引用 xxx.xml

<string name="nameFormat">%s---%s</string>  
<!--字符串拼接-->
android:text="@{@string/nameFormat(vm.firstName, vm.lastName)}"
android:padding="@{vm.large? @dimen/largePadding : @dimen/smallPadding}"

注意:编写databinding xml布局时,如果编译器不提示详细的错误信息,可以使用命令gradlew compileDebugSource --stacktrace -info或者点击Android Studio右侧Gradle-app->other->assembleDebug来查看具体的错误。

(三)ViewBinding与DataBinding区别

1)ViewBinding

ViewBinding会根据xml布局文件自动生成对应的XXXBinding类,然后通过XXXBinding.inflate(layoutInflater)生成一个对应的binding对象, 这个binding对象包含了这个xml布局文件中具有 ID 的所有视图对象,可以直接引用,省去了findViewById的操作。 binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root)


2)DataBinding

DataBinding是一个数据绑定库,它将xml布局中的界面组件绑定到代码中的数据对象, 可以通过对实体字段添@Bindable注解结合notifyPropertyChanged()实现双向绑定,也可以通过对自定义view添加带@BindingAdapter注解的方法来实现自定义属性。 将xml改成databinding 布局后,这样就可以直接绑定并注入xml了: binding = DataBindingUtil.setContentView(this, R.layout.activity_xxx)


通过导包了解,ViewBinding自动生成的XXXBinding也属于DataBinding,也就是DataBinding包含了ViewBinding。