当前位置:   article > 正文

studio3.6新特性【视图绑定】是时候开始使用ViewBinding了_adapter viewbinding

adapter viewbinding

首先,这是官方的文档讲解,很不错官方文档 【点击即可】

目录

一、前言

二、初识ViewBinding

三、拥抱ViewBinding

3.1、环境要求

3.2、开启ViewBinding功能

3.3、Activity中ViewBinding的使用

3.3.1、布局中直接的控件

3.3.2、布局中使用include

3.3.2、布局中使用include和merge

3.4、Fragment中使用ViewBinding

3.5、自定义View中使用ViewBinding

3.6、Adapter中使用ViewBinding

四、总结


一、前言

随着Android Studio 3.6的正式发布,我义无反顾的走在了更新尝鲜的前列。AS的升级一如往常的顺利,重启后就进入了令人血脉喷张的 Gradle 升级的环节,需要从3.5.1升级到3.6.0。果不其然,出问题了!!

ButterKnife居然报错,日志如下:

  1. D:\xxx\libbase\component\dialog\BottomDialog.java:33: : Attempt to use @BindView for an already bound ID 0 on 'mTvNegative'. (com.xxx.libbase.component.dialog.BottomDialog.mLayoutContent)
  2. ViewGroup mLayoutContent;

我真是摸不着头脑啊。解决吧,升级ButterKnife、翻资料、找issue、看源码等等等等。最终老天不负有心人,我将Gradle版本回退了,一切都回归平静。【如果有解决办法的请告知我,感激不尽】

二、初识ViewBinding

它和ButterKnife一样都是为了省去findViewById()这样的重复代码。其实在2019谷歌开发者峰会上对ViewBinding就已经有所耳闻了,layout中更新控件ID后立刻可以在Activity中引用到,这绝对比ButterKnife需要编译、需要区分R和R2要舒服的多。
上面升级到3.6.0就是为了使用它,然而现实永远这么的残酷,十之八九不尽人意,ViewBinding和ButterKnife看来只能二选一了。

三、拥抱ViewBinding

关于ViewBinding的文档,官方写的很详细,请看 视图绑定 。本文一切从简,主要说下Google官方没有提到的一些问题。

3.1、环境要求

  • Android Studio版本3.6及以上
  • Gradle 插件版本3.6.0及以上

3.2、开启ViewBinding功能

ViewBinding支持按模块启用,在模块的build.gradle文件中添加如下代码:

  1. android {
  2. ...
  3. viewBinding {
  4. enabled = true
  5. }
  6. }

3.3、Activity中ViewBinding的使用

  1. //之前设置视图的方法
  2. setContentView(R.layout.activity_main);
  3. //使用ViewBinding后的方法
  4. mBinding = ActivityMainBinding.inflate(getLayoutInflater());
  5. setContentView(mBinding.getRoot());

可以看到,当你使用了ViewBinding后,针对你的activity_main.xml文件,会自动帮你生成一个ActivityMainBinding.java文件(该文件在build/generated/data_binding_base_class_source_out/xxx…目录下),也就是布局文件的驼峰命名法加上一个Binding后缀,然后在Activity中直接使用就可以。

3.3.1、布局中直接的控件

当我们在布局中添加一个id为 tv_text 的TextView后,直接在Activity中使用mBinding.tvText即可拿到该控件。如下所示,可以看到也是以控件ID的驼峰命名法来获取的:

mBinding.tvText.setText("是你得不到的ViewBinding");

3.3.2、布局中使用include

例如我们有个layout_comment.xml的布局,布局中有id为tv_include的TextView,代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent">
  5. <TextView
  6. android:id="@+id/tv_include"
  7. android:text="这就是测试啊"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent" />
  10. </androidx.constraintlayout.widget.ConstraintLayout>

然后在activity_main.xml文件中include该布局:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <TextView
  9. android:id="@+id/tv_text"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:text="Hello World!"
  13. app:layout_constraintBottom_toBottomOf="parent"
  14. app:layout_constraintLeft_toLeftOf="parent"
  15. app:layout_constraintRight_toRightOf="parent"
  16. app:layout_constraintTop_toTopOf="parent" />
  17. <include
  18. android:id="@+id/layout_include"
  19. layout="@layout/layout_comment" />
  20. </androidx.constraintlayout.widget.ConstraintLayout>

那么此时我们如何使用到layout_comment.xml布局中的TextView控件呢,首先include标签需要声明id,例如layout_include,然后Activity中代码如下:

mBinding.layoutInclude.tvInclude.setText("这就是你的不对了");

是不是很神奇,是不是很简单。

注意:
当你给layout_comment.xml的根布局再添加id(比如添加了layout_xxx的ID)的时候,此时会报错:

java.lang.NullPointerException: Missing required view with ID: layout_xxx

3.3.2、布局中使用include和merge

我们将上文的layout_comment.xml稍作修改,根布局使用merge标签,其他不做修改:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <merge xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content">
  5. <TextView
  6. android:id="@+id/tv_include"
  7. android:text="这就是测试啊"
  8. android:gravity="end"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent" />
  11. </merge>
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <include
  9. android:id="@+id/layout_include"
  10. layout="@layout/layout_comment" />
  11. </androidx.constraintlayout.widget.ConstraintLayout>

activity_main.xml文件中使用include添加该布局后,在java代码中依旧是可以正常使用以下代码的:

mBinding.layoutInclude.tvInclude.setText("会不会出现问题呀");

但是但是!!!运行就会报错:

java.lang.NullPointerException: Missing required view with ID: layoutInclude

要是把include标签的id去掉的话,这时mBinding中也是找不到tvInclude这个控件呀,怎么办??
之前是不是说过,每个layout文件都会对应一个Binding文件,那么layout_comment.xml,肯定也有一个LayoutCommentBinding.java文件,我们去看下这个文件的源代码,里面有个可疑的方法,bind()方法:

  1. @NonNull
  2. public static LayoutCommentBinding bind(@NonNull View rootView) {
  3. // The body of this method is generated in a way you would not otherwise write.
  4. // This is done to optimize the compiled bytecode for size and performance.
  5. String missingId;
  6. missingId: {
  7. TextView tvInclude = rootView.findViewById(R.id.tv_include);
  8. if (tvInclude == null) {
  9. missingId = "tvInclude";
  10. break missingId;
  11. }
  12. return new LayoutCommentBinding(rootView, tvInclude);
  13. }
  14. throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  15. }

所以对于含有merge标签的布局我们可以使用bind()方法来绑定到根布局上,在这里,根布局就是mBinding.getRoot()了。所以代码如下:

  1. //这么写不可以
  2. //mBinding.layoutInclude.tvInclude.setText("会不会出现问题呀");
  3. LayoutCommentBinding commentBinding = LayoutCommentBinding.bind(mBinding.getRoot());
  4. commentBinding.tvInclude.setText("这就不会出现问题了吧");

同时需要注意: include标签不可以有id

3.4、Fragment中使用ViewBinding

在Fragment的**onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)**方法中:

  1. //原来的写法
  2. return inflater.inflate(R.layout.fragment_blank, container, false);
  3. //使用ViewBinding的写法
  4. mBinding = FragmentBlankBinding.inflate(inflater);
  5. return mBinding.getRoot();

拿到FragmentBlankBinding的对象后,更新数据的都和之前一样了。

3.5、自定义View中使用ViewBinding

这里直接贴出来代码吧,就是自定义了一个Fragment然后往其中添加了一个布局,该布局是上文的layout_comment.xml文件,会生成一个对应的LayoutCommentBinding.java文件,同样可以直接使用:

  1. public class MyView extends FrameLayout {
  2. LayoutCommentBinding commentBinding;
  3. public MyView(@NonNull Context context) {
  4. this(context, null);
  5. }
  6. public MyView(@NonNull Context context, @Nullable AttributeSet attrs) {
  7. this(context, attrs, 0);
  8. }
  9. public MyView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  10. super(context, attrs, defStyleAttr);
  11. initLayout();
  12. }
  13. private void initLayout() {
  14. //之前的写法
  15. //View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_comment, this, false);
  16. //addView(view);
  17. //使用ViewBinding的写法
  18. commentBinding = LayoutCommentBinding.inflate(LayoutInflater.from(getContext()), this, false);
  19. addView(commentBinding.getRoot());
  20. }
  21. public void setText(String str) {
  22. commentBinding.tvInclude.setText(str);
  23. }
  24. }

3.6、Adapter中使用ViewBinding

在RecyclerView结合Adapter的例子中我们再使用ViewBinding来尝试下,直接贴Adapter的代码:

  1. public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder> {
  2. private List<String> mList;
  3. public MainAdapter(List<String> list) {
  4. mList = list;
  5. }
  6. @NonNull
  7. @Override
  8. public MainAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
  9. //之前的写法
  10. //View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_comment, parent, false);
  11. //ViewHolder holder = new ViewHolder(view);
  12. //使用ViewBinding的写法
  13. LayoutCommentBinding commentBinding = LayoutCommentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
  14. ViewHolder holder = new ViewHolder(commentBinding);
  15. return holder;
  16. }
  17. @Override
  18. public void onBindViewHolder(@NonNull MainAdapter.ViewHolder holder, int position) {
  19. holder.mTextView.setText(mList.get(position));
  20. }
  21. @Override
  22. public int getItemCount() {
  23. return mList.size();
  24. }
  25. static class ViewHolder extends RecyclerView.ViewHolder {
  26. TextView mTextView;
  27. //之前的写法
  28. //public ViewHolder(@NonNull View itemView) {
  29. // super(itemView);
  30. // mTextView = itemView.findViewById(R.id.tv_include);
  31. //}
  32. //使用ViewBinding的写法
  33. ViewHolder(@NonNull LayoutCommentBinding commentBinding) {
  34. super(commentBinding.getRoot());
  35. mTextView = commentBinding.tvInclude;
  36. }
  37. }
  38. }

只需要注意两方面:

  • ViewHolder的构造器参数改为使用的Binding对象
  • 实例化ViewHolder的时候传入相应的Binding对象

四、总结

使用ViewBinding的话,其实很简单,新建xxx.xml布局后就会产生一个对应的 xxxBinding.java的文件,实例化xxxBinding只需要调用它自身的inflate()方法即可。
注意不同情况下使用不同的inflate()方法,以及使用了merge标签情况下的bind()方法。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/319972
推荐阅读
相关标签
  

闽ICP备14008679号