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 (thefindViewById
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"
}