故事开场:一场“魔法胶水”的招聘大会
想象你是一位刚入职的 Android 小镇的“UI 魔法师”。
你的任务是把**数据(Model)和界面(View)**粘在一起,让它们像情侣一样同步更新。
但小镇里有个规矩:
绝对不能在 XML 里写 Java 代码!
于是,镇长 androidx.databinding 开了一家“魔法胶水工厂”,专门生产一种叫
@BindingAdapter 的魔法贴纸。
只要把它贴在任何一个静态方法上,工厂就能把它注册成“属性→方法”的胶水。
之后,你在 XML 里写 app:xxx="",魔法胶水就会“啪”一下,自动帮你调用那个方法,把数据塞进 View。
角色表(先认个脸)
| 角色 | 真实类名 | 作用 |
|---|---|---|
| 魔法贴纸 | @BindingAdapter | 标记“这是一个胶水方法” |
| 胶水工厂 | BindingAdapterStore | 存放所有贴纸的映射表 |
| 翻译官 | ViewDataBinding | 把 XML 里的表达式翻译成 Java 调用 |
| 包工头 | DataBinderMapperImpl(自动生成) | 编译期收集所有贴纸,生成索引表 |
| 小精灵 | BindingUtil | 运行期负责“找胶水、涂胶水” |
故事正文:一次“加载图片”的魔法之旅
1. 你写了一个“胶水方法”
public class ImageViewAdapter {
// 魔法贴纸:把 “imageUrl” 这个 XML 属性 → 这个静态方法
@BindingAdapter("imageUrl")
public static void loadUrl(ImageView view, String url) {
Glide.with(view).load(url).into(view);
}
}
编译期,注解处理器 BindingProcessor 看到贴纸,立刻记录:
“imageUrl” →
ImageViewAdapter.loadUrl(ImageView,String)
2. 包工头生成“胶水目录”
编译后,在 build/generated/source/kapt/... 里会蹦出一个类:
public class DataBinderMapperImpl extends DataBinderMapper {
@Override
public List<BindingAdapterStore> collectStore() {
BindingAdapterStore store = new BindingAdapterStore();
store.put("imageUrl",
new ResolvedBindingAdapter(
ImageViewAdapter.class,
"loadUrl",
ImageView.class, String.class));
return Collections.singletonList(store);
}
}
这就是一本“胶水黄页”:谁需要什么属性,对应哪个方法,写得明明白白。
3. 你在 XML 里“下订单”
<ImageView
android:id="@+id/avatar"
android:layout_width="64dp"
android:layout_height="64dp"
app:imageUrl="@{user.avatar}" />
翻译官 ActivityMainBinding(自动生成)看到 @{user.avatar},
会在 executeBindings() 里生成类似代码:
// 伪代码
String url = user.avatar;
ImageViewAdapter.loadUrl(this.avatar, url);
但真正的调用不是直接写死,而是通过“胶水黄页”动态查找——
这样你换库、改名都不怕。
4. 运行期:小精灵涂胶水
时序图
问答时间
Q1:为什么方法必须是 static?
→ 小精灵没有“new 你”的预算,直接 Class.method() 最快。
Q2:可以一贴多属性吗?
→ 可以!@BindingAdapter({"android:src", "android:tint"})
相当于“一张贴纸两面胶”。
Q3:如果写错了属性名?
→ 编译期就报错:Cannot find a setter for app:imageUlr
镇长直接把你拦在城门口。
彩蛋:偷看胶水黄页
调试用一行代码,把整张表打印出来:
Log.d("BindingAdapters",
DataBindingUtil.getDefault().getKnownAdapters().toString());
你会看到一堆:
imageUrl -> ImageViewAdapter.loadUrl(...)
android:text -> TextViewBindingAdapter.setText(...)
...
故事小结
- 你贴
@BindingAdapter→ 编译期进入“胶水黄页”。 - XML 写
app:xxx="@{...}"→ 运行期小精灵查表、反射调用。 - 从此 数据变化 → 自动刷新 UI,无需手写
findViewById和setText。
镇长 androidx.databinding 拍拍你的肩膀:
“去吧,少年!用魔法贴纸,把界面和数据粘成一对永不分手的恋人。”