View Binding Part of Android Jetpack.
View binding is a feature that allows you to more easily write code that interacts with views. Once view binding is enabled in a module, it generates a binding class for each XML layout file present in that module. An instance of a binding class contains direct references to all views that have an ID in the corresponding layout.
In most cases, view binding replaces findViewById
.
Setup instructions
View binding is enabled on a module by module basis. To enable view binding in a
module, set the viewBinding
build option to true
in the module-level
build.gradle
file, as shown in the following example:
Groovy
android { ... buildFeatures { viewBinding true } }
Kotlin
android { ... buildFeatures { viewBinding = true } }
If you want a layout file to be ignored while generating binding classes, add
the tools:viewBindingIgnore="true"
attribute to the root view of that layout
file:
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
Usage
If view binding is enabled for a module, a binding class is generated for each XML layout file that the module contains. Each binding class contains references to the root view and all views that have an ID. The name of the binding class is generated by converting the name of the XML file to Pascal case and adding the word "Binding" to the end.
For example, given a layout file called result_profile.xml
:
<LinearLayout ... >
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"
android:background="@drawable/rounded_button" />
</LinearLayout>
The generated binding class is called ResultProfileBinding
. This class has two
fields: a TextView
called name
and a Button
called button
. The
ImageView
in the layout has no ID, so there is no reference to it in the
binding class.
Every binding class also includes a getRoot()
method, providing a direct
reference for the root view of the corresponding layout file. In this example,
the getRoot()
method in the ResultProfileBinding
class returns the
LinearLayout
root view.
The following sections demonstrate the use of generated binding classes in activities and fragments.
Use view binding in activities
To set up an instance of the binding class for use with an activity, perform the
following steps in the activity's
onCreate()
method:
- Call the static
inflate()
method included in the generated binding class. This creates an instance of the binding class for the activity to use. - Get a reference to the root view by either calling the
getRoot()
method or using Kotlin property syntax. - Pass the root view to
setContentView()
to make it the active view on the screen.
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); }
You can now use the instance of the binding class to reference any of the views:
Kotlin
binding.name.text = viewModel.name binding.button.setOnClickListener { viewModel.userClicked() }
Java
binding.getName().setText(viewModel.getName()); binding.getButton().setOnClickListener(new View.OnClickListener() { viewModel.userClicked() });
Use view binding in fragments
To set up an instance of the binding class for use with a fragment, perform the
following steps in the fragment's
onCreateView()
method:
- Call the static
inflate()
method included in the generated binding class. This creates an instance of the binding class for the fragment to use. - Get a reference to the root view by either calling the
getRoot()
method or using Kotlin property syntax. - Return the root view from the
onCreateView()
method to make it the active view on the screen.
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; }
You can now use the instance of the binding class to reference any of the views:
Kotlin
binding.name.text = viewModel.name binding.button.setOnClickListener { viewModel.userClicked() }
Java
binding.getName().setText(viewModel.getName()); binding.button.setOnClickListener(new View.OnClickListener() { viewModel.userClicked() });
Provide hints for different configurations
When you declare views across multiple configurations, occasionally it makes sense to use a different view type depending on the particular layout. For example:
# in res/layout/example.xml
<TextView android:id="@+id/user_bio" />
# in res/layout-land/example.xml
<EditText android:id="@+id/user_bio" />
In this case, you might expect the generated class to expose a field userBio
of type TextView
, because TextView
is the common base class. Unfortunately,
due to technical limitations, the view binding code generator is unable to make
this determination and simply generates a View
field instead. This would
require casting the field later with binding.userBio as TextView
.
To work around this limitation, view binding supports a tools:viewBindingType
attribute, allowing you to tell the compiler what type to use in the generated
code. In the above example, you can use this attribute to make the compiler
generate the field as a 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" />
In another example, suppose you have two layouts where one contains a
BottomNavigationView
and another contains a NavigationRailView
. Both classes
extend NavigationBarView
, which contains most of the implementation details.
If your code doesn't need to know exactly which subclass is present in the
current layout, you can use tools:viewBindingType
to set the generated type
to NavigationBarView
in both layouts:
# 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" />
Note that view binding is unable to validate the value of this attribute when generating code. To avoid compile time and runtime errors, the value must meet the following conditions:
- The value must be a class that inherits from
android.view.View
. - The value must be a superclass of the tag it is placed on. For example, the
following values would not work:
xml <TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView --> <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView -->
- The final type must resolve consistently across all configurations.
Differences from findViewById
View binding has important advantages over using findViewById
:
- Null safety: Since view binding creates direct references to views,
there's no risk of a null pointer exception due to an invalid view ID.
Additionally, when a view is only present in some configurations of a layout,
the field containing its reference in the binding class is marked with
@Nullable
. - Type safety: The fields in each binding class have types matching the views they reference in the XML file. This means that there's no risk of a class cast exception.
These differences mean that incompatibilities between your layout and your code will result in your build failing at compile time rather than at runtime.
Comparison with data binding
View binding and data binding both generate binding classes that you can use to reference views directly. However, view binding is intended to handle simpler use cases and provides the following benefits over data binding:
- Faster compilation: View binding requires no annotation processing, so compile times are faster.
- Ease of use: View binding does not require specially-tagged XML layout files, so it is faster to adopt in your apps. Once you enable view binding in a module, it applies to all of that module's layouts automatically.
Conversely, view binding has the following limitations compared to data binding:
- View binding doesn't support layout variables or layout expressions, so it can't be used to declare dynamic UI content straight from XML layout files.
- View binding doesn't support two-way data binding.
Because of these considerations, it is best in some cases to use both view binding and data binding in a project. You can use data binding in layouts that require advanced features and use view binding in layouts that do not.
Additional resources
To learn more about view binding, see the following additional resources: