google官网:https://developer.android.google.cn/topic/libraries/view-binding
有了View Binding,不需要findViewById(),也不需要ButterKnife。使用更简单。
一. 使用步骤:
在相应的模块的build.gradle文件里添加:
android {
...
viewBinding {
enabled = true
}
}
经过编译后layout的xml布局文件会生成一一对应的java文件。
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
然后,在activity代码要生成binding对象实例。有两种方式。根据业务需要任选其一。
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//方式一
binding = ActivityMainBinding.inflate(LayoutInflater.from(this));
setContentView(binding.getRoot());
//方式二
binding = DataBindingUtil.setContentView(this,
R.layout.activity_main);
binding.txtName.setText("hello world");
binding.txtName.setOnClickListener(new View.OnClickListener() {
});
}
}
下面就是最终效果:
android:id="@+id/txtName" -----》binding.txtName
此时就不需要findviewbyid(), 直接用binding.txtName就可以setText().
在Fragment使用方式也一样,但是Fragment 的存在时间比其View长。所以请务必在 Fragment 的 onDestroyView()
方法中清除对绑定类实例的所有引用。
private ResultProfileBinding binding;
@Override
public View onCreateView (LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
binding = ResultProfileBinding.inflate(inflater, container, false);
View view = binding.getRoot();
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
二. View Binding原理
Android Studio会将Resourse layout 下面的XML 文件。一一对应的编译成一个java文件。生成在目录:app\build\generated\data_binding_base_class_source_out\debug\out\com\XXX\XXX\databinding\
命名方式:将XML 文件的名称转换为驼峰式大小写,并在末尾添加“Binding”一词。
比如:activity_main.xml ---->ActivityMainBinding.java,
如果想忽略某个布局文件,那么将 tools:viewBindingIgnore="true"
属性添加到相应布局文件的根view中, 就像下面那样,在<LinearLayout>中加了tools:viewBindingIgnore="true"。这样就不会生成绑定类了。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:viewBindingIgnore="true">
<TextView
android:id="@+id/txtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
那么生成的绑定类有什么用呢?为什么不需要findViewById()这个操作?
我们看看ActivityMainBinding.java这个文件里到底做了哪些事情。简单的说,绑定类就是在做findViewById()的工作,然后把各个view的实例保存在绑定类, 方便开发者可以直接使用。
public final class ActivityMainBinding implements ViewBinding {
@NonNull
private final LinearLayout rootView;
@NonNull
public final TextView txtName;
@NonNull
public final TextView txtName1;
@NonNull
public final TextView txtName2;
private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull TextView txtName,
@NonNull TextView txtName1, @NonNull TextView txtName2) {
this.rootView = rootView;
this.txtName = txtName;
this.txtName1 = txtName1;
this.txtName2 = txtName2;
}
@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) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.txtName;
TextView txtName = rootView.findViewById(id);
if (txtName == null) {
break missingId;
}
id = R.id.txtName1;
TextView txtName1 = rootView.findViewById(id);
if (txtName1 == null) {
break missingId;
}
id = R.id.txtName2;
TextView txtName2 = rootView.findViewById(id);
if (txtName2 == null) {
break missingId;
}
return new ActivityMainBinding((LinearLayout) rootView, txtName, txtName1, txtName2);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}
比较好的一点是有个getRoot()的接口。这样就可以使用
方式一:
binding = ActivityMainBinding.inflate(LayoutInflater.from(this));
setContentView(binding.getRoot())。
由此增加了灵活度。比如,app大多数的activity都有一样风格的toolbar。那么有一个基类ToolbarActivity,其他继承ToolbarActivity的activity只要把getRoot()的view放到ToolbarActivity的body里面就可以了。
另外:
1. 不支持<include/>
2. Android UI Framework的发展会越来越靠向WPF(Windows Presentation Foundation)。Android的 View Binding & Data Binding,到现在依旧远没有赶上WPF。