视图绑定   Android Jetpack 的一部分。

视图绑定 是一项功能,它使编写与视图交互的代码变得更加容易。在模块中启用视图绑定后,它会为该模块中存在的每个 XML 布局文件生成一个绑定类。绑定类实例包含对相应布局中所有具有 ID 的视图的直接引用。

在大多数情况下,视图绑定会替换 findViewById

设置

视图绑定是在每个模块的基础上启用的。要在模块中启用视图绑定,请在模块级 build.gradle 文件中将 viewBinding 构建选项设置为 true,如下例所示

Groovy

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

Kotlin

android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

如果希望在生成绑定类时忽略布局文件,请将 tools:viewBindingIgnore="true" 属性添加到该布局文件的根视图

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

用法

如果为模块启用了视图绑定,则会为模块包含的每个 XML 布局文件生成一个绑定类。每个绑定类都包含对根视图和所有具有 ID 的视图的引用。绑定类的名称是通过将 XML 文件的名称转换为 Pascal 大小写并在末尾添加“Binding”来生成的。

例如,考虑一个名为 result_profile.xml 的布局文件,其中包含以下内容

<LinearLayout ... >
    <TextView android:id="@+id/name" />
    <ImageView android:cropToPadding="true" />
    <Button android:id="@+id/button"
        android:background="@drawable/rounded_button" />
</LinearLayout>

生成的绑定类称为 ResultProfileBinding。此类有两个字段:一个名为 nameTextView 和一个名为 buttonButton。布局中的 ImageView 没有 ID,因此绑定类中没有对它的引用。

每个绑定类还包含一个 getRoot() 方法,提供对相应布局文件的根视图的直接引用。在此示例中,ResultProfileBinding 类中的 getRoot() 方法返回 LinearLayout 根视图。

以下部分演示了在活动和片段中使用生成的绑定类。

在活动中使用视图绑定

要在活动中设置绑定类实例以供使用,请在活动的 onCreate() 方法中执行以下步骤

  1. 调用生成的绑定类中包含的静态 inflate() 方法。这将为活动创建绑定类实例以供使用。
  2. 通过调用 getRoot() 方法或使用 Kotlin 属性语法 获取对根视图的引用。
  3. 将根视图传递给 setContentView() 以使其成为屏幕上的活动视图。

以下示例显示了这些步骤

Kotlin

private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}

Java

private ResultProfileBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = ResultProfileBinding.inflate(getLayoutInflater());
    View view = binding.getRoot();
    setContentView(view);
}

您现在可以使用绑定类实例来引用任何视图

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

在片段中使用视图绑定

要在片段中设置绑定类实例以供使用,请在片段的 onCreateView() 方法中执行以下步骤

  1. 调用生成的绑定类中包含的静态 inflate() 方法。这将为片段创建绑定类实例以供使用。
  2. 通过调用 getRoot() 方法或使用 Kotlin 属性语法 获取对根视图的引用。
  3. onCreateView() 方法返回根视图以使其成为屏幕上的活动视图。

Kotlin

private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = ResultProfileBinding.inflate(inflater, container, false)
    val view = binding.root
    return view
}

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

Java

private ResultProfileBinding binding;

@Override
public View onCreateView (LayoutInflater inflater,
                          ViewGroup container,
                          Bundle savedInstanceState) {
    binding = ResultProfileBinding.inflate(inflater, container, false);
    View view = binding.getRoot();
    return view;
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}

您现在可以使用绑定类实例来引用任何视图

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

为不同的配置提供提示

当您在多个配置中声明视图时,有时根据特定的布局使用不同的视图类型是有意义的。以下代码片段显示了一个示例。

# in res/layout/example.xml

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" />

在这种情况下,您可能期望生成的类公开一个类型为TextView的字段userBio,因为TextView是通用的基类。由于技术限制,视图绑定代码生成器无法确定这一点,而是生成一个View字段。这需要稍后使用binding.userBio as TextView将字段强制转换为TextView

为了解决此限制,视图绑定支持tools:viewBindingType属性,允许您告诉编译器在生成的代码中使用什么类型。在前面的示例中,您可以使用此属性使编译器将该字段生成为TextView

# in res/layout/example.xml (unchanged)

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" tools:viewBindingType="TextView" />

在另一个示例中,假设您有两个布局,一个包含BottomNavigationView,另一个包含NavigationRailView。这两个类都扩展了NavigationBarView,其中包含大部分实现细节。如果您的代码不需要知道当前布局中存在哪个子类,则可以在两个布局中都使用tools:viewBindingType将生成的类型设置为NavigationBarView

# in res/layout/navigation_example.xml

<BottomNavigationView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

# in res/layout-w720/navigation_example.xml

<NavigationRailView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

视图绑定在生成代码时无法验证此属性的值。为了避免编译时和运行时错误,该值必须满足以下条件

  • 该值必须是继承自android.view.View的类。
  • 该值必须是其放置到的标签的超类。例如,以下值不起作用

      <TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView. -->
      <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView. -->
    
  • 最终类型必须在所有配置中一致地解析。

与findViewById的区别

视图绑定与使用findViewById相比具有重要的优势

  • **空安全:**由于视图绑定创建了对视图的直接引用,因此由于无效的视图 ID 而导致空指针异常的风险不存在。此外,当视图仅存在于布局的某些配置中时,包含其引用的绑定类中的字段将用@Nullable标记。
  • **类型安全:**每个绑定类中的字段都具有与其在 XML 文件中引用的视图匹配的类型。这意味着没有类转换异常的风险。

这些差异意味着布局和代码之间的不兼容会导致您的构建在编译时而不是运行时失败。

与数据绑定的比较

视图绑定和数据绑定都生成您可以用来直接引用视图的绑定类。但是,视图绑定旨在处理更简单的用例,并且与数据绑定相比提供了以下好处

  • **更快的编译:**视图绑定不需要任何注释处理,因此编译时间更快。
  • **易用性:**视图绑定不需要特殊标记的 XML 布局文件,因此在您的应用中采用起来更快。在模块中启用视图绑定后,它会自动应用于该模块的所有布局。

另一方面,与数据绑定相比,视图绑定具有以下限制

由于这些考虑因素,在某些情况下,最好在一个项目中同时使用视图绑定和数据绑定。您可以在需要高级功能的布局中使用数据绑定,而在不需要的布局中使用视图绑定。

其他资源

要了解有关视图绑定的更多信息,请参阅以下其他资源

示例

博客

视频