前言
最近铺天盖地的文章,推荐使用ViewBinding。刚好最近在做android15的适配,也接入了ViewBinding 。要不是ButterKnife被禁用, 我都还不会使用ViewBinding. 可是在使用ViewBinding的过程中,也踩过了一些坑,在这里记录下来。
仅供自己学习, 如果能给你带来帮助,那实在是太好了。
目录
1. 视图绑定的发展史
2. ViewBinding的使用
3. ViewBinding踩坑
4. ViewBinding的原理
5. 参考文章
视图绑定的发展史
通过这张图能明显看出ViewBinding的优势了,这就是为什么谷歌主推ViewBinding了
大家一起来讨论一个问题:
虽然google推出了ViewBinding, 但是现在又推出了一个compose库, 未来会不会在某一天,compose直接取代了原先的命令式UI, 毕竟现在harmony都是采用声明式去写界面了, 以后就不存在xml写布局页面了
ViewBinding的使用
ViewBinding的接入流程,推荐大家还是看官网,官网毕竟写得详细。
官网ViewBinding的使用说明
- ViewBinding的使用很简单,只需要在build.gradle中启用即可
android{
...
viewBinding{
enable = true
}
...
}
-
当启用ViewBinding后,AGP插件就会扫描res/layout里面的所有布局文件, 并生成对应的绑定类,其生成的格式会以驼峰的形式命名。比如有一个activity_main.xml文件,则生成的类名为:ActivityMainBinding
-
生成的类文件的路径如下:
ViewBinding的踩坑
- 首先给一段生成的ViewBinding的代码
public final class ActivityMainBinding implements ViewBinding {
@NonNull
private final LinearLayout rootView;
@NonNull
public final TextView tv;
@NonNull
public final ViewStub viewStub;
private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull TextView tv,
@NonNull ViewStub viewStub) {
this.rootView = rootView;
this.tv = tv;
this.viewStub = viewStub;
@Override
@NonNull
public LinearLayout getRoot() {
return rootView;
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_main, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityMainBinding bind(@NonNull View rootView) {
int id;
missingId: {
id = R.id.tv;
TextView tv = ViewBindings.findChildViewById(rootView, id);
if (tv == null) {
break missingId;
}
id = R.id.view_stub;
ViewStub viewStub = ViewBindings.findChildViewById(rootView, id);
if (viewStub == null) {
break missingId;
}
return new ActivityMainBinding((LinearLayout) rootView, tv, viewStub);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}
通过上面生成的类可以看出,创建Binding实例提供了两个api
-
bind(View rootView) 这个RootView,必须是布局的根控件,如果不是,则会抛异常的
-
inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent) 这个api有三个参数, attachToParent传值为true或false,这里面也有一个坑,先看一下我下面的这几种写法
//第一种:写法
val binding = ActivityMainBinding.inflate(LayoutInflater.from(context), container, true)
第二种写法:
val view = LayoutInflater.from(context).inflate(R.layout.activity_main, container, true)
val binding = ActivityMainBinding.bind(view)
这里明确的告诉你, 第一种写法不会报错, 但是第二种写法会报错的。 具体原因是: attachRoot值为true, 通过分析源代码,可以得知返回的view是container, 而不是布局的根view, 这个时候调用bind方法传的不是布局的根控件,当然会报错了。
- merge标签的使用也遇到了坑
若根控件为merge标签的布局文件,生成的binding类,inflate方法是只有两个参数的。
@NonNull
public static ActivityMainMergeBinding inflate(@NonNull LayoutInflater inflater,
@NonNull ViewGroup parent) {
if (parent == null) {
throw new NullPointerException("parent");
}
inflater.inflate(R.layout.activity_main_merge, parent);
return bind(parent);
}
ViewBinding的原理
学习任何一门技术,本着知其然,须知其所以然的目的。于是想探究ViewBinding这个类文件是如何生成的。
起初,我以为是通过APT的技术去生成的这个类文件的,后来仔细一想,这个注解都没有,怎么可能生成这个类的, 猜测肯定不是通过APT技术去实现的。
有哪位大佬知道AGP插件是通过什么技术,在扫描了布局文件后,怎么生成的类文件
参考文章
♥️ ♥️ ♥️看到了这里的朋友,如果能给一个关注,评论,那真是太好了。感谢感谢♥️ ♥️ ♥️