Note that this note is based on the view binding section of the Android developer website. Click here to enter the official website
role
View binding makes it easier to write functions that interact with views. When view binding is enabled in a module, a binding class is generated for each XML layout file in that module, and instances of the binding class contain direct references to all views that have ids in the corresponding layout.
In most cases, view binding replaces findViewById()
ViewBinding can be enabled by module. To enable viewBinding in a module, add a viewBinding element to its build.gradle file as follows:
Android {··· viewBinding {enabled =true}}Copy the code
To create aTestViewBindingActivity
We created a TestViewBindingActivity, and the system automatically created the layout file activity_test_view_binding for this Activity. Here’s what the layout file looks like:
<? 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:context=".ui.TestViewBindingActivity">
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="View binding page Demo"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:paddingVertical="20dp"
/>
<Button
android:id="@+id/btn_change_title"
app:layout_constraintTop_toBottomOf="@id/tv_title"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Change the title"
/>
<Button
style="@style/matchWidth"
app:layout_constraintTop_toBottomOf="@id/btn_change_title"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="It doesn't work."
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
In the layout file above, we create a TextView and two buttons, where the TextView and the first Button have ids and the second Button have no ID. Since viewBinding is enabled, the system will automatically create a viewBinding file corresponding to the current layout file, Namely ActivityTestViewBindingBinding. Java file, the file located at/build/generated/data_binding_base_class_source_out directory, the following is generated all the source code for this class:
// Generated by view binder compiler. Do not edit!
package com.project.myproject.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.viewbinding.ViewBinding;
import com.project.myproject.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityTestViewBindingBinding implements ViewBinding {
@NonNull
private final ConstraintLayout rootView;
@NonNull
public final Button btnChangeTitle;
@NonNull
public final TextView tvTitle;
private ActivityTestViewBindingBinding(@NonNull ConstraintLayout rootView,
@NonNull Button btnChangeTitle, @NonNull TextView tvTitle) {
this.rootView = rootView;
this.btnChangeTitle = btnChangeTitle;
this.tvTitle = tvTitle;
}
@Override
@NonNull
public ConstraintLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityTestViewBindingBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityTestViewBindingBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_test_view_binding, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityTestViewBindingBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
String missingId;
missingId: {
Button btnChangeTitle = rootView.findViewById(R.id.btn_change_title);
if (btnChangeTitle == null) {
missingId = "btnChangeTitle";
break missingId;
}
TextView tvTitle = rootView.findViewById(R.id.tv_title);
if (tvTitle == null) {
missingId = "tvTitle";
break missingId;
}
return new ActivityTestViewBindingBinding((ConstraintLayout) rootView, btnChangeTitle,
tvTitle);
}
throw new NullPointerException("Missing required view with ID: ".concat(missingId)); }}Copy the code
ConstraintLayout is the rootView field, ConstraintLayout is the tvTitle field of TextView, and btnChangeTitle is the Button field. The field names are based on the id we set.
Then there is the private constructor. Since the fields are final, we need to assign values to the properties in the constructor, and we also know that we cannot use the constructor directly.
The getRoot() method then returns the rootView property, where we can get the root layout of our XML.
Next up is the inflate() method, which needs to pass in a LayoutInflater object, Method only invokes its overloaded method Inflate (LayoutInflater inflater,parent NULL, attachToParent False).
The inflate() method takes three arguments:
@NonNull
public static ActivityTestViewBindingBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_test_view_binding, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
Copy the code
This method first loads the specified XML file just as we normally load a View, and then calls bind(View).
A look at the bind(View) method shows that the code is written differently from the way we normally write code. A look at the comments shows that the body of the method is generated in a way that it would not have been written otherwise, to optimize the size and performance of the compiled bytecode.
@NonNull
public static ActivityTestViewBindingBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
String missingId;
missingId: {
Button btnChangeTitle = rootView.findViewById(R.id.btn_change_title);
if (btnChangeTitle == null) {
missingId = "btnChangeTitle";
break missingId;
}
TextView tvTitle = rootView.findViewById(R.id.tv_title);
if (tvTitle == null) {
missingId = "tvTitle";
break missingId;
}
return new ActivityTestViewBindingBinding((ConstraintLayout) rootView, btnChangeTitle,
tvTitle);
}
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
Copy the code
The main purpose of this method is to determine whether the variables we defined earlier can be found in the specified layout file. If one is not found, we throw an exception. If one is found, we call the constructor here to generate the corresponding view-bound object.
We briefly analyzed the contents of a view-binding file above, but there are still some aspects that are not demonstrated.
include
The label
Here’s a demonstration of what happens if you use the include tag in an XML file:
First we create a layout file, layout_view_binding_include.xml, that looks like this:
<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
style="@style/matchWidth"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text=The Text "in the" include
android:gravity="center"
android:padding="10dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
Then reference it in the previous layout file activity_test_view_binding.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:context=".ui.TestViewBindingActivity">
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="View binding page Demo"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:paddingVertical="20dp"
/>
<Button
android:id="@+id/btn_change_title"
app:layout_constraintTop_toBottomOf="@id/tv_title"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Change the title"
/>
<Button
style="@style/matchWidth"
app:layout_constraintTop_toBottomOf="@id/btn_change_title"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text="It doesn't work."
/>
<include
layout="@layout/layout_view_binding_include"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
style="@style/matchWidth"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
View/build/generated/data_binding_base_class_source_out directory, will find that many a LayoutViewBindingIncludeBinding. Java file, This is the view-binding file generated by layout_view_binding_include.xml we created earlier.
Also, although we added the above file to activity_test_view_binding.xml via include, we did not set an ID for the include tag, So we are different in the Activity through ActivityTestViewBindingBinding object access to correspond to the layout of the include tag.
Then we give the include tag set id, android: id = “@ + id/layout_include”, then examine the code in the ActivityTestViewBindingBinding (rebuild) :
package com.project.myproject.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.viewbinding.ViewBinding;
import com.project.myproject.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityTestViewBindingBinding implements ViewBinding {
@NonNull
private final ConstraintLayout rootView;
@NonNull
public final Button btnChangeTitle;
@NonNull
public final LayoutViewBindingIncludeBinding layoutInclude;
@NonNull
public final TextView tvTitle;
private ActivityTestViewBindingBinding(@NonNull ConstraintLayout rootView,
@NonNull Button btnChangeTitle, @NonNull LayoutViewBindingIncludeBinding layoutInclude,
@NonNull TextView tvTitle) {
this.rootView = rootView;
this.btnChangeTitle = btnChangeTitle;
this.layoutInclude = layoutInclude;
this.tvTitle = tvTitle;
}
@Override
@NonNull
public ConstraintLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityTestViewBindingBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityTestViewBindingBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_test_view_binding, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityTestViewBindingBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
String missingId;
missingId: {
Button btnChangeTitle = rootView.findViewById(R.id.btn_change_title);
if (btnChangeTitle == null) {
missingId = "btnChangeTitle";
break missingId;
}
View layoutInclude = rootView.findViewById(R.id.layout_include);
if (layoutInclude == null) {
missingId = "layoutInclude";
break missingId;
}
LayoutViewBindingIncludeBinding layoutIncludeBinding = LayoutViewBindingIncludeBinding.bind(layoutInclude);
TextView tvTitle = rootView.findViewById(R.id.tv_title);
if (tvTitle == null) {
missingId = "tvTitle";
break missingId;
}
return new ActivityTestViewBindingBinding((ConstraintLayout) rootView, btnChangeTitle,
layoutIncludeBinding, tvTitle);
}
throw new NullPointerException("Missing required view with ID: ".concat(missingId)); }}Copy the code
Can be found in every place and we just set the include tag, the layout of the corresponding content attribute called layoutInclude (id) this is what we set the corresponding class for LayoutViewBindingIncludeBinding, This is the Java file that was automatically generated after we created layout_view_binding_include.xml.
We can then reference the contents of the layout in the include via ViewBinding in the Activity, such as setting new text to the TextView in the include layout above:
binding.layoutInclude.tvInclude.text = "I've been modified, too."
Copy the code
ViewStub
The label
The layout file specified by the include tag is always there, but the ViewStub tag is always there. Here is a demo. For convenience, we still use the layout used in the previous include tag:
<? 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:context=".ui.TestViewBindingActivity">... < ViewStub android: id ="@+id/layout_view_stub"
app:layout_constraintTop_toBottomOf="@id/btn_change_title"
app:layout_constraintBottom_toTopOf="@id/layout_include"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
style="@style/matchWidth"
android:layout="@layout/layout_view_binding_include"/ >... < / androidx. Constraintlayout. Widget. Constraintlayout >Copy the code
After the rebuild project, can be found in the ActivityTestViewBindingBinding. Java file one more attribute, its type is ViewStub, attribute called layoutViewStub:
public final class ActivityTestViewBindingBinding implements ViewBinding { @NonNull private final ConstraintLayout rootView; @NonNull public final Button btnChangeTitle; @NonNull public final LayoutViewBindingIncludeBinding layoutInclude; @NonNull public final ViewStub layoutViewStub; @NonNull public final TextView tvTitle; private ActivityTestViewBindingBinding(@NonNull ConstraintLayout rootView, @NonNull Button btnChangeTitle, @NonNull LayoutViewBindingIncludeBinding layoutInclude, @NonNull ViewStub layoutViewStub, @nonNULL TextView tvTitle) {··· this.LayOutViewStub = layoutViewStub; this.tvTitle = tvTitle; }... @ NonNull public static ActivityTestViewBindingBindingbind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode forsize and performance. String missingId; MissingId: {··· ViewStub layoutViewStub = rootView.findViewById(R.i.layout_view_stub); missingId: {··· ViewStub layoutViewStub = rootView.findViewById(R.I.Layout_view_stub);if (layoutViewStub == null) {
missingId = "layoutViewStub";
breakmissingId; }...return new ActivityTestViewBindingBinding((ConstraintLayout) rootView, btnChangeTitle,
layoutIncludeBinding, layoutViewStub, tvTitle);
}
throw new NullPointerException("Missing required view with ID: ".concat(missingId)); }}Copy the code
Thus, we can use the layout specified by the ViewStub in our Activity:
binding.layoutViewStub.setOnInflateListener(ViewStub.OnInflateListener { stub, inflated ->
run {
viewStubBinding = LayoutViewBindingIncludeBinding.bind(inflated)
viewStubBinding.tvInclude.text = "I've been modified."
}
})
binding.layoutViewStub.inflate()
Copy the code
Note that include and ViewStub are not the same. Although we refer to the same layout, the type of the include is the view-binding class generated by the specified layout, and the type of the ViewStub is ViewStub. Because of the nature of the ViewStub, the layout referenced by the ViewStub cannot be modified directly in the Activity.
If you don’t want to use
If you don’t want to use a ViewBinding for a layout, you can add tools:viewBindingIgnore=”true” to the root view of that layout, as in:
<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
tools:viewBindingIgnore="true"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tv_include"
style="@style/matchWidth"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:text=The Text "in the" include
android:gravity="center"
android:padding="10dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code
After adding this attribute, rebuild projects, will find previously generated LayoutViewBindingIncludeBinding. Java files are deleted, and other documents with reference to the document will also be an error.