View Binding in Android Jetpack | Android View Binding using in Activity, Fragment, Dialog, Map, RecyclerViewHolder

Android Jetpack: Replace findViewById with view binding.

  • View binding is a feature that allows you to 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.

Setup instructions

In the module-level build.gradle file

android {
    ...
    buildFeatures {
        viewBinding true
    }
}


Usage

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.


Use view binding in activities

  • 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 root method.
  • Pass the root view to setContentView() to make it the active view on the screen.

private lateinit var binding: ResultProfileBinding

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

You can now use the instance of the binding class to reference any of the views:

Eg: 

binding.name.text = "hello bolt user"

binding.button.setOnClickListener { 
   // code
}


Use view binding in fragments

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
}

Fragments outlive their views. 

Make sure you clean up any references to the binding class instance in the fragment's onDestroyView() method.


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" />

It 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" />

  • 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.


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. 

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.


View binding in different scenarios:

  • To use view binding for Google MapFragment's ID:

val mapFragment = childFragmentManager.findFragmentById(binding.map.id) as SupportMapFragment

  • To use view binding for Custom Dialog inflate xml
        val bind: CustomDialogBinding = CustomDialogBinding.inflate(layoutInflater)
        dialog!!.setContentView(bind.root)
      
        bind.btClose.setOnClickListener { 
           // dialog!!.dismiss() 
        }

Read more


  • To use view binding for Scope function : Read more
In your xml file:
<Button
            android:id="@+id/button_first"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/next"
        />
In your activity / fragment code:

 binding.buttonFirst.apply {
            setOnClickListener {
               // findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
            }
            text="Button Demo"
        }


  • To use view binding for included layouts:
<include
    android:id="@+id/include_id"
    layout="@layout/some_layout" />

For example, given a layout file called some_layout.xml:
<LinearLayout ... >
    <TextView android:id="@+id/name" />
</LinearLayout>


In Code:
binding.includeId.name.text  ="Message"

  • To use view binding for Null safety
For example, given a layout file called  layout/main_layout.xml:
<LinearLayout ... >
    <TextView android:id="@+id/name" />
</LinearLayout>

layout-land/main_layout.xml:
<LinearLayout ... >
    .. // id is missing in land layout
</LinearLayout>

In Code: 
binding.name?.text  ="Message" // Safe handling

  • To use view binding for Recycler view's Adapter
Ref ComponentsAdapter.kt file in this page : Read more


  • To ignore generating binding class
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>





Difference Between Camel Case / Pascal Case / Snake Case.

I recommended to use view binding 

..

Comments