4、DataBinding基础:布局文件转换为DataBinding布局、数据对象与Observable、@{}语法与数据绑定、@BindingAdapter自定义属性

好,咱们今天来啃DataBinding这块硬骨头。说实话,我刚开始接触DataBinding的时候,心里是有点抵触的——总觉得XML里写表达式怪怪的。但用了一段时间后,我不得不承认:这玩意儿真香。它让UI和数据之间的绑定变得特别直观,代码量直接砍半。

这一章,我会带你从零开始,把DataBinding的四个核心知识点掰开揉碎。你跟着我走一遍,保证能上手。

4.1 布局文件如何变成DataBinding布局

先说说最基础的操作。普通的布局文件,根节点是各种ViewGroup。DataBinding布局呢,说白了就是在外层套一个<layout>标签。

嗯,就这么简单。但要注意,这个<layout>标签必须是最外层,不能有任何东西包着它。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.example.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.name}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.age}" />

    </LinearLayout>
</layout>

看到<data>标签了吗?这里面就是声明变量用的。我习惯把需要用到的数据对象都放在这里,一目了然。

改完布局文件后,Android Studio会自动生成一个Binding类。比如你的布局叫activity_main.xml,那生成的类就是ActivityMainBinding。命名规则就是:下划线转驼峰,末尾加Binding。

小提示:如果你发现Binding类没生成,先检查一下build.gradle里有没有开启DataBinding。我踩过这个坑,折腾了半天才发现是配置忘写了。

4.2 数据对象与Observable

布局写好了,数据从哪来?你总得有个对象吧。咱们先定义一个普通的User类:

data class User(
    val name: String,
    val age: Int
)

然后你在Activity里这样绑定:

val binding = ActivityMainBinding.inflate(layoutInflater)
binding.user = User("张三", 25)
setContentView(binding.root)

跑起来看看?嗯,界面确实显示了。但问题来了——如果你改了user.name,UI会跟着变吗?

不会。因为普通的数据对象不具备通知能力。这就是为什么我们需要Observable

DataBinding提供了几种现成的可观察类:

  • ObservableField<T> —— 单个字段
  • ObservableBooleanObservableInt —— 基本类型专用
  • ObservableArrayList<T> —— 列表
  • ObservableArrayMap<K, V> —— 键值对

我个人最常用的是ObservableField。改造一下User类:

class User {
    val name = ObservableField<String>()
    val age = ObservableInt()

    init {
        name.set("张三")
        age.set(25)
    }
}

现在你改数据,UI会自动刷新。为什么?因为ObservableField内部实现了观察者模式,数据一变,绑定了它的View就会收到通知。

核心理解:DataBinding的响应式原理,说白了就是「数据变了,UI跟着变」。你不用再手动调用findViewById和setText了。

4.3 @{}语法与数据绑定

这个@{} 语法,是DataBinding的灵魂。它让你在XML里直接写表达式,把数据和View连起来。

基本用法:

android:text="@{user.name}"
android:visibility="@{user.age > 18 ? View.VISIBLE : View.GONE}"
android:onClick="@{() -> presenter.onUserClick(user)}"

你可以在@{} 里写:

  • 属性引用:@{user.name}
  • 三目运算:@{age > 18 ? "成年" : "未成年"}
  • 方法调用:@{user.getName()}
  • Lambda表达式:@{() -> handler.onClick()}
  • 空合并运算符:@{user.name ?? "默认名"}

我记得有一次,产品经理要求根据用户等级显示不同的背景色。用DataBinding一行搞定:

android:background="@{user.level == 1 ? @color/gold : @color/silver}"

你想想看,如果用传统方式,你得写多少行代码?findViewById、判断、setBackgroundColor……现在全没了。

注意:不要在@{} 里写太复杂的逻辑。比如循环、创建新对象、调用耗时方法。XML不是写业务逻辑的地方。我见过有人把几十行的计算塞进去,结果维护起来想哭。

4.4 @BindingAdapter自定义属性

有时候,系统自带的属性不够用。比如你想给ImageView加载网络图片,总不能写android:src="@{url}"吧?系统不认识url啊。

这时候@BindingAdapter就派上用场了。它让你自定义属性,想怎么玩就怎么玩。

来看一个加载图片的例子:

@BindingAdapter("imageUrl")
fun loadImage(view: ImageView, url: String?) {
    if (url != null) {
        Glide.with(view.context)
            .load(url)
            .into(view)
    }
}

然后在XML里:

<ImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    app:imageUrl="@{user.avatarUrl}" />

看到app:imageUrl了吗?这就是我们自定义的属性。DataBinding会自动找到对应的@BindingAdapter方法,把值传进去。

你还可以定义多个属性组合:

@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false)
fun loadImage(view: ImageView, url: String?, placeholder: Drawable?) {
    Glide.with(view.context)
        .load(url)
        .placeholder(placeholder ?: ContextCompat.getDrawable(view.context, R.drawable.ic_default))
        .into(view)
}

XML里这样用:

<ImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    app:imageUrl="@{user.avatarUrl}"
    app:placeholder="@{@drawable/ic_default}" />

我曾经在一个电商项目里,用@BindingAdapter封装了图片加载、圆角处理、占位图、加载失败图……十几个属性。团队其他人直接用,爽得不行。

技巧:requireAll = false表示这些属性不是必须同时出现。你只传imageUrl也行,只传placeholder也行。灵活度很高。

知识体系总览

下面这张图,把本章四个知识点的关系串起来了。你看一遍,心里就有谱了。

DataBinding 基础 · 知识体系 DataBinding 核心 ① 布局文件转换 <layout> 包裹 + <data> 声明变量 自动生成 Binding 类 ② 数据对象与Observable 普通对象 → 无通知能力 ObservableField / ObservableInt ③ @{} 语法与数据绑定 属性引用 / 三目运算 / Lambda 空合并运算符 ?? ④ @BindingAdapter 自定义属性 / 扩展系统能力 多属性组合 / requireAll 四个知识点层层递进,从布局改造到数据响应,再到表达式和自定义扩展

看到没?这四个知识点是环环相扣的。先改布局,再准备数据,然后用@{} 绑定,最后用@BindingAdapter扩展。一条线走下来,DataBinding的基础你就拿下了。

说实话,刚开始学的时候,你可能会觉得@BindingAdapter有点绕。但用多了你会发现,它其实是DataBinding最强大的地方——把那些重复的UI操作封装起来,一次编写,到处复用。

嗯,这一章的内容就到这。代码不多,但每个点都是实战中高频使用的。你先把这几个例子跑一遍,感受一下DataBinding的「自动感」。下一章咱们会深入更高级的用法。


公众号:蓝海资料掘金营,微信deep3321