Android Studio使用viewbinding
在AS 3.6 Canary 11之上的版本,就可以使用viewbinding了。它和ButterKnife一样都是为了省去findViewById()这样的重复代码,layout中更新控件ID后立刻可以在Activity中引用到,这绝对比ButterKnife需要编译、需要区分R和R2要舒服的多。
开启ViewBinding功能
ViewBinding支持按模块启用,在模块的build.gradle文件中,添加以下代码对viewbinding的支持;
android {
defaultConfig {
viewBinding {
enabled = true
}
}
}
ViewBinding的初始化有三种方式:
inflate(@NonNull LayoutInflater inflater);
inflate(@NonNull LayoutInflater inflater,@NonNull ViewGroup parent,boolean attachToParent);
bind();
在Activity中使用ViewBinding
布局中直接的控件
和普通的layout一样,设置要使用的控件的id。
<?xml version="1.0" encoding="utf-8"?>
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".viewbinding.ViewBindingActivity">
<TextView
android:id="@+id/viewBinding_tv"
android:layout_width="match_parent"
android:layout_height="40dp"
/>
//之前设置视图的方法;
setContentView(R.layout.activity_view_binding);
//使用ViewBinding后的方法;
ActivityViewBindingBinding mActivityViewBindingBinding = mActivityViewBindingBinding.inflate(getLayoutInfalter());
setContentView(mActivityViewBindingBinding.getRoot());
public class ViewBindingActivity extends AppCompatActivity {
private ActivityViewBindingBinding mActivityViewBindingBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivityViewBindingBinding = ActivityViewBindingBinding.inflate(LayoutInflater.from(this));
setContentView(ActivityViewBindingBinding.getRoot());
//直接就可以使用mActivityViewBindingBinding.viewBindingTv拿到我们设置的TextView这个控件;
mActivityViewBindingBinding.viewBindingTv.setText("hello world");
}
}
ViewBinding不用再手动进行类型转换,也避免了空指针错误。如果不想生成ViewBinding,可以在布局的根视图上使用tools:viewBindingIgnore="true"。
当使用了ViewBinding后,针对我们的activity_view_binding.xml文件,会自动生成一个ActivityViewBindingBinding.java文件(该文件在build/generated/data_binding_base_class_source_out/debug/out/<packageName>/databinding/ActivityViewBindingBinding.java),也就是布局文件的驼峰命名法加上一个Binding后缀,然后在Activity中直接使用就可以了。
布局中使用include和merge
例如我们有一个layout_comment.xml的布局,布局中有id为include_tv的TextView,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/include_tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="这是用来测试的"
/>
</LinearLayout>
然后在activity_view_binding.xml文件中include该布局:
<?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"
android:orientation="vertical"
tools:context=".viewbinding.ViewBindingActivity">
<TextView
android:id="@+id/viewBinding_tv"
android:layout_width="match_parent"
android:layout_height="40dp" />
<include
android:id="layout_include"
layout="@layout/layout_comment" />
</LinearLayout>
我们如何使用layout_comment.xml布局中的TextView控件呢,首先include标签需要声明id,例如我们生命的layout_include,然后在Activity中代码如下:
mBinding.layoutInclude.includeTv.setText("这是新的测试文本");
注意:当你给layout_comment.xml的根布局再添加id的时候,就会报错:
java.lang.NullPointerException:Missing required view with ID:layout_xxx
布局中使用include和merge
我们将上问的layout_comment.xml稍作修改,根布局使用merge标签,其他不作修改:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/include_tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="这是用来测试的"
/>
</merge>
在activity_view_binding.xml文件中使用include添加该布局后,如果还是用mBinding.layoutInclude.includeTv.setText("hello");就会报错。首先要声明的是在merge标签中,不可以给include标签设置id。
每个layout文件都会对应一个Binding文件,那么layout_comment.xml肯定也有一个LayoutCommentBinding.java文件。对于含有merge标签的布局,我们可以使用bind()方法来绑定到根布局上,在这里,根布局就是mBinding.getRoot()。所以代码如下:
//不可以这么写;
//mBinding.layoutInclude.includeTv.setText("这是错误的写法");
//而是需要这么写;
LayoutCommentBinding commentBinding = LayoutCommentBinding.bind(mBinding.getRoot());
commentBinding.includeTv.setText("这是正确的写法");
Fragment中使用ViewBinding
在Fragment的onCreateView()方法中:
//原来的写法;
return inflater.inflate(R.layout.fragment_blank,container,false);
//使用ViewBinding的写法;
mBinding = FragmentBlankBinding.inflate(inflater);
return mBinding.getRoot();
拿到FragmentBlankBinding的对象后,更新数据就都和先前一样了。
自定义Dialog中使用ViewBinding
dialog中使用和Activity和Fragment一样,直接使用单参数的inflate()方法即可,伪代码如下:
public class CustomDialog extend Dialog{
protected View mView;
protected DialogBottomBinding mBinding;
public CustomDialog(@NonNull Context context,@StyleRes int themeResId){
super(context,themeResId);
//原来的写法;
//mView = View.inflate(getContext(),getLayoutId(),null);
//使用ViewBinding的写法;
mBinding = DialogBottomBinding.inflate(getLayoutInflater());
mView = mBinding.getRoot();
setContentView(mView);
}
}
自定义View中使用ViewBinding
常见的两种方法如下:
使用layout文件不包含merge
使用的layout文件根标签为merge
Adapter中使用ViewBinding
在RecyclerView结合Adapter的例子,我们使用ViewBinding来尝试下,Adapter的代码如下:
public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder> {
private List<String> mList;
public MainAdapter(List<String> list) {
mList = list;
}
@NonNull
@Override
public MainAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//之前的写法
//View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_comment, parent, false);
//ViewHolder holder = new ViewHolder(view);
//使用ViewBinding的写法
LayoutCommentBinding commentBinding = LayoutCommentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
ViewHolder holder = new ViewHolder(commentBinding);
return holder;
}
@Override
public void onBindViewHolder(@NonNull MainAdapter.ViewHolder holder, int position) {
holder.mTextView.setText(mList.get(position));
}
@Override
public int getItemCount() {
return mList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView mTextView;
//之前的写法
//public ViewHolder(@NonNull View itemView) {
// super(itemView);
// mTextView = itemView.findViewById(R.id.tv_include);
//}
//使用ViewBinding的写法
ViewHolder(@NonNull LayoutCommentBinding commentBinding) {
super(commentBinding.getRoot());
mTextView = commentBinding.tvInclude;
}
只需要注意两方面:
- ViewHolder的构造器参数改为使用的Binding对象;
- 实例化ViewHolder的时候传入相应的Binding对象;
总结
使用ViewBinding的话,其实很简单,新建xxx.xml布局后就会产生一个对应的xxxBinding.java的文件,实例化xxxBinding只需要调用它自身的inflate()方法即可。
注意不同情况下使用不同的inflate()方法,以及使用了merge标签情况下的bind()方法,以及使用merge标签布局和其他正常xxxLayout布局所产生的不同的inflate()方法。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
------------------last line for now---------------------