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: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"
    />
//之前设置视图的方法;
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---------------------