(聚焦安卓)详解ViewBinding

2,080 阅读4分钟

viewBinding

Android Studio 3.6+支持;

从 Android Studio 3.6 开始,viewBinding又叫视图绑定,能够通过生成绑定对象来替代 findViewById,从而可以帮您简化代码、移除 bug,并且从 findViewById 的模版代码中解脱出来。

官方推荐使用视图绑定替代 Kotlin 合成方法和 ButterKnife;

特性

  • viewBinding是一个轻量的findViewById的替代方案;
  • 视图绑定没有添加任何额外的逻辑,他只是把视图属性暴露给您,从而帮您在不使用 findViewById 的情况下也能调用它们。这样一来便保证了生成文件简洁性(当然也避免了拖慢构建速度)。
  • 视图绑定完美支持 Java 和 Kotlin 编程语言。
  • 视图绑定会为 Module 内所有的布局文件生成绑定对象, 例如:
# eg
activity_awesome.xml → ActivityAwesomeBinding.java
  • 布局文件中每一个带有 id 的视图都会在绑定对象中有一个对应的属性,包括include标签,并且会为根布局生成 rootView 属性并通过 getRoot 暴露给开发者,这个生成的视图属性将拥有正确的类型,并且空安全。

API介绍

创建视图绑定对象实例

每个xml布局生成的XxBinding类,都有三个静态方法用来在使用的地方创建绑定对象实例, 三个静态方法如下:

  • inflate(inflater):在例如 Activity onCreate 方法里,这类没有父视图需要被传入的场合使用。
  • inflate(inflater, parent, attachToParent):在 Fragment 或 RecyclerView Adapter (或者说 ViewHolder 中) ,这类您需要传递父级 ViewGroup 给绑定对象时使用。
  • bind(rootView):在您已经获得对应视图,并且只想通过视图绑定来避免使用 findViewById 时使用。这个方法在使用视图绑定改造和重构现有代码时非常有用。
//eg: 在activity中创建
XxBinding binding = XxBinding.inflate(getLayoutInflater());

viewBinding获取子视图对象

直接子视图

对于布局的直接子视图可以通过viewBinding直接获取视图对象;

binding.tvText.setText("");
include布局子视图

视图绑定会为 Module 下的每一个布局文件生成一个绑定对象,这个说法在布局文件被另一个布局文件使用 include 引入时依然适用;

在使用引入布局的时候,视图绑定会创建一个被引入布局绑定对象的引用。引用名跟使用普通视图一样,依然是 include 标签的id,注意 include 标签需要有一个 id 才能在绑定对象中生成对应的属性

获取通过include引用的布局的内部视图的示例, 布局信息如下:

<!-- activity_awesome.xml -->
<androidx.constraintlayout.widget.ConstraintLayout>
    <include android:id="@+id/includes" 
        layout="@layout/included_buttons"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- included_buttons.xml -->
<androidx.constraintlayout.widget.ConstraintLayout>
    <Button android:id="@+id/include_me" />
</androidx.constraintlayout.widget.ConstraintLayout>

视图绑定会在 ActivityAwesomeBinding 中生成一个 IncludedButtonsBinding 的引用,拿到这个引用就可以继续获取include子视图对象。

public final class ActivityAwesomeBinding implements ViewBinding {
    @NonNull
    public final IncludedButtonsBinding includes;
}

对比dataBinding

不同点

  • databinding可以自定义binding的类名,viewbinding不可以。
  • databinding可以将view和界面上的数据进行双向绑定,viewbinding不行,视图绑定只是 findViewById 的轻量取代方案;

关联性

  • 数据绑定和视图绑定可以生成同样的组件,它们可以同时工作。
  • 您可以在同一 Module 中同时使用数据绑定和视图绑定。在两者都被开启时,使用 “layout” 标签的布局会由数据绑定来生成绑定对象;而其余的布局则由视图绑定生成绑定对象

使用实战

开启视图绑定无须引入额外依赖,从 Android Studio 3.6 开始,视图绑定将会内建于 Android Gradle 插件中。需要打开视图绑定的话,只需要在 build.gradle 文件中配置 viewBinding 选项, 配置完成后,视图绑定就会为所有布局文件自动生成对应的绑定类。无须修改原有布局的 XML 文件,视图绑定将根据您现有的布局自动完成所有工作。

1. 开启viewBinding

  • Android Studio 3.6.0:
// 需要 Android Gradle Plugin 3.6.0
android {
    viewBinding {
        enabled = true
    }
}
  • Android Studio 4.0:
// Android Studio 4.0
android {
    buildFeatures {
        viewBinding = true
    }
}

2. 使用

视图绑定将会根据现有的 XML 文件,为 Module 内所有的布局文件生成绑定对象。您可以在任何需要填充布局的地方使用绑定对象,比如 Fragment、Activity、甚至是 RecyclerView Adapter(或者说是 ViewHolder 中)。

禁止对某布局生成viewBinding对象

只需要在布局的根view添加如下忽略即可:

<FrameLayout
    ...
    tools:viewBindingIgnore="true"/>
在Activity中使用

假如您有一个布局文件名叫 act_text.xml,其中包含了一个TextView文本视图。视图绑定会为这个布局生成一个名叫 ActTextBinding 的类,布局文件中所有拥有 id 的视图,都会在这个类中有一个对应的属性;

  1. 创建xml布局文件:act_text.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="false">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
  1. activity中引用:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActTextBinding binding = ActTextBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());
    
    //对act_text.xml中id为tv_content的TextView设置内容
    binding.tvContent.setText("");
}
  1. 查看生成的viewBinding对象代码:
//生成的文件位置:app/build/generated/data_binding_base_class_source_out/debug/out/com/x/x/databinding/ActTextBinding.java

public final class ActTextBinding implements ViewBinding {
  @NonNull
  private final ConstraintLayout rootView; //自动为根view生成一个对象

  @NonNull
  public final TextView tvContent;
}