Jetpack可以帮助开发者减少样板代码,而findviewbyId正是需要减少的样板代码,于是就有了ViewBinding。一开始我细看Jetpack的各个组件我是懵的,里面没有ViewBinding,这不好把它归纳到Jetpack系列中,后面通过底层源码分析,它是Gradle将DataBinding中识图绑定功能单独抽离出来的一个子集,所以也算是Jetpack中的一员。
简单分析
下面是ViewBinding的简单使用,当前使用的Android Gradle Plugin版本为4.1.0及4.2.0
0,当Android Gradle Plugin 版本4.1.0时,项目中有个androidx.databinding:viewbinding:4.1.0@aar的依赖,当AndroidGradlePluginVersion切换到4.2.0时,viewbinding依赖变为androidx.databinding:viewbinding:4.2.0@aar,果然,和databinding有关系,和AndroidGradlePlugin版本也有联系,依赖中有ViewBinding类。
ViewBinding源码如下
package androidx.viewbinding;
import android.view.View;
import androidx.annotation.NonNull;
/** A type which binds the views in a layout XML to fields. */
public interface ViewBinding {
@NonNull
View getRoot();
}
Viewbinding在Activity上的使用及分析
1,在项目中的gradle中
android{
//android4.0之前
viewBinding{
enabled=true
}
//androidstudio4.0以上
buildFeatures {
viewBinding true
}
}
2, 系统会对每一个布局文件即时默认生成一个Binding类
如果不需要对布局文件进行绑定,可在布局根节点中添加下述属性:
tools:viewBindingIgnore="true" >
3,生成文件的路径在build\generated\data_binding_base_class_source_out\debug\out\com\sun\viewbinding\databinding上,查看布局及生成的文件,并在activity中添加布局
1)xml布局 activity_main5.xml
2)生成的文件 ActivityMain5Binding.java
package com.sun.viewbinding.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewbinding.ViewBinding;
import com.sun.viewbinding.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityMain5Binding implements ViewBinding { //实现了ViewBinding中的getRoot()方法
@NonNull
private final LinearLayout rootView; //布局中的根布局,是private修饰,保证安全
@NonNull
public final Button btShowframe; //全是public修饰,保证快捷访问
@NonNull
public final FrameLayout frame; //全是public修饰,保证快捷访问
@NonNull
public final TextView tvMyName; //全是public修饰,保证快捷访问
@NonNull
public static ActivityMain5Binding inflate(@NonNull LayoutInflater inflater) {//传入解析器,进行进行布局解析
return inflate(inflater, null, false);
}
@NonNull
public static ActivityMain5Binding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_main5, parent, false); //ActivityMain5Binding.java类解析activity_main5.xml
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityMain5Binding 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.bt_showframe;
Button btShowframe = rootView.findViewById(id); //终究也还是通过findviewbyId进行id与view的关联
if (btShowframe == null) {
break missingId;
}
id = R.id.frame;
FrameLayout frame = rootView.findViewById(id);
if (frame == null) {
break missingId;
}
id = R.id.tv_myName;
TextView tvMyName = rootView.findViewById(id);
if (tvMyName == null) {
break missingId;
}
return new ActivityMain5Binding((LinearLayout) rootView, btShowframe, frame, tvMyName);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
private ActivityMain5Binding(@NonNull LinearLayout rootView, @NonNull Button btShowframe,
@NonNull FrameLayout frame, @NonNull TextView tvMyName) {
this.rootView = rootView;
this.btShowframe = btShowframe;
this.frame = frame;
this.tvMyName = tvMyName;
}
@Override
@NonNull
public LinearLayout getRoot() { //实现自ViewBinding
return rootView;
}
}
3,MainActivity.java 使用绑定类
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMain5Binding binding = ActivityMain5Binding.inflate(getLayoutInflater());//给绑定类传入解析器
setContentView(binding.getRoot());//activity添加布局
binding.tvMyName.setText("hello,My name is lilei");//更改布局
}
}
这样,viewbinding在activity上就使用好了。
ViewBinding在Fragment中的使用
import com.sun.viewbinding.databinding.FragmentHelloBinding;
public class HelloFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
FragmentHelloBinding binding = FragmentHelloBinding.inflate(inflater, container, false);
binding.tvFragmentHello.setText("hello,I am hanmeimei in Fragment");
return binding.getRoot();
}
}
ViewBinding在Dialog中的使用
public void showDialog() {
Dialog dialog = new Dialog(this);
DialogHelloBinding binding = DialogHelloBinding.inflate(dialog.getLayoutInflater());
dialog.setContentView(binding.getRoot());
binding.tvDialogHello.setText("你好,我是dialog Poly");
dialog.show();
}
ViewBinding在RecycleView+Adapter中的使用
import com.sun.viewbinding.databinding.ItemStudentBinding;
public class StudentAdapter extends RecyclerView.Adapter {
private ArrayList beans;
public StudentAdapter(ArrayList beans) {
this.beans = beans;
}
@NonNull
@Override
public StudentViewHold onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemStudentBinding binding= ItemStudentBinding.inflate(LayoutInflater.from(parent.getContext()));
return new StudentViewHold(binding);
}
@Override
public void onBindViewHolder(@NonNull StudentViewHold holder, int position) {
Student bean = beans.get(position);
holder.tv_age.setText("年龄=" + bean.age);
holder.tv_name.setText("名字=" + bean.name);
}
@Override
public int getItemCount() {
return beans.size();
}
class StudentViewHold extends RecyclerView.ViewHolder{
TextView tv_name;
TextView tv_age;
public StudentViewHold(ItemStudentBinding binding) {
super(binding.getRoot());
tv_name=binding.tvName;
tv_age=binding.tvAge;
}
}
}
小结
总得来说,viewbinding是相当简单并且实用的,相比传统的直接findviewbyid省去了类型转换,也保证了控件的空安全,传入解析器就能自动得到view,也能得到根view下的子view,可以有效避免张冠李戴,这一切都是因为Android Gradle Plugin自动对全部的布局文件进行了封装绑定,在java层只需对绑定类进行直接操作即可,Android Gradle Plugin如何生成绑定类后续再深入分析吧。
Android-Jetpack代码位置:github