21 January 2021
Android view binding tutorial

An activity or fragment in an Android application will usually need to change the layout in some way, whether that’s disabling a button or capturing text from an input field.

Obsolete Approach

Traditionally, developers would use the findViewById method at some point after the layout had been inflated.

val usernameTextView = findViewById<TextView>(R.id.username_text_view)

While this is fairly straightforward, it does have the following drawbacks:

  • Boiler Plate Code
    Involves an amount of boilerplate code (the findViewById method would need to be called for each View that the activity or fragment needs access to).

  • Type Safety
    findViewById only returns objects with the type View. If for instance, the view in the layout was an ImageView, and you tried casting it to a TextView, the application would crash (as it should- but you want to know there’s a problem before the application runs).

  • Null Safety
    It’s possible to use a view ID that does not exist in the current XML layout.

Other obsolete approaches include the Butterknife library and the now-deprecated Kotlin synthetics.

View Binding Approach

Android Studio 3.6 introduced a feature named View Binding, aimed at addressing the above issues.

When enabled, a single class will automatically be generated for each of your XML layouts. These classes will contain a property for each of the views in your layout that has an id. Each property will be type-safe and null safe.

These classes can then be used in your activity or fragment when inflating the view.

Configuration

View Binding does not need any additional plugins you can simply switch it on in your module-level build.gradle file ( not the one in the root directory- the one that’s in the app directory).

android {
    buildFeatures {
        viewBinding true
    }
}

Usage in an Activity

In the onCreate method, instead of using the id of the XML layout, inflate the layout using the generated class.

private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {

  super.onCreate(savedInstanceState)
  
  binding = ActivityMainBinding.inflate(layoutInflater)
  setContentView(binding.root)
  
}

The generated file is named after the XML file. In the example above, the XML is named activity_main.xml and the generated file is named ActivityMainBinding.

The properties that represent the views in your XML can then be accessed on the binding object.

binding.usernameTextView.text = "username@example.com"

Usage in a Fragment

If used in a fragment, the generated class should be used in the onCreateView method instead of inflating the views using the XML id.

private var binding: FragmentFirstBinding? = null

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
 ): View {

    binding = FragmentFirstBinding.inflate(inflater)
    return binding.root

}

Note that the binding variable is not using the lateinit modifier. This is because the view is not inflated in the Fragments onCreate method, so the binding may not always be available. It should be used safely as demonstrated below:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

  super.onViewCreated(view, savedInstanceState)

  binding?.textviewFirst?.text = "username@example.com"
        
}