Advanced Data Binding in Android: Binding Adapters
In this advanced data binding tutorial, you’ll learn how you can interact directly with the components in your layouts, assign a value and handle events dispatched by the views using binding adapters. By Rodrigo Guerrero.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Advanced Data Binding in Android: Binding Adapters
25 mins
- Getting Started
- Introducing Data Binding
- Understanding Data Binding
- Initializing Data Binding
- Binding Data in RecyclerViews
- Understanding Binding Adapters
- Setting Values Automatically
- Creating Custom Binding Adapters
- Handling View Visibility
- Loading Images
- Using Multiple Attributes
- Adding Other Custom Binding Adapters
- Learning About Conversions
- Using Two-Way Data Binding
- Using a Binding Adapter
- Creating an InverseBindingAdapter
- Binding Listener Methods
- Where to Go From Here?
Using a Binding Adapter
Open RadioGroupBindingAdapters.kt and add the following code:
@BindingAdapter("crewFilter")
fun RadioGroup.setCheckedButton(crewAgency: MutableLiveData<CrewAgency>?) {
val selectedId = when (crewAgency?.value) {
CrewAgency.SPACEX -> R.id.spacex
CrewAgency.NASA -> R.id.nasa
CrewAgency.JAXA -> R.id.jaxa
CrewAgency.ESA -> R.id.esa
else -> null
}
if (selectedId != null && selectedId != checkedRadioButtonId) {
check(selectedId)
}
}
You’re adding the annotation @BindingAdapter
, which will respond to values set to crewFilter
. Depending on CrewAgency
, the adapter returns the ID that will select the correct radio button. To prevent infinite cycles, you need to add an if
condition to check the radio button only if the selected ID has changed.
The next step is to return a CrewAgency
depending on the selected radio button.
Creating an InverseBindingAdapter
To achieve this, you need to create an inverse binding adapter. In RadioGroupBindingAdapter.kt, add the following code:
@InverseBindingAdapter(attribute = "crewFilter")
fun RadioGroup.getCheckedButton(): CrewAgency? {
return when (checkedRadioButtonId) {
R.id.spacex -> CrewAgency.SPACEX
R.id.nasa -> CrewAgency.NASA
R.id.jaxa -> CrewAgency.JAXA
R.id.esa -> CrewAgency.ESA
else -> null
}
}
Here, you add @InverseBindingAdapter
to a method and set the attribute name to which this inverse binding adapter will respond. It should always be the same attribute name as the one used in the normal binding adapter. In this case, the attribute name is crewFilter
.
Finally, the binding adapters need a way to know when the attributes change.
Binding Listener Methods
To give the binding adapters a way to listen for changes in their attributes, add the following code to RadioGroupBindingAdapters.kt:
@BindingAdapter("app:crewFilterAttrChanged")
fun RadioGroup.setListeners(listener: InverseBindingListener?) {
listener?.let {
setOnCheckedChangeListener { radioGroup, id ->
listener.onChange()
}
}
}
This is a binding adapter for the attribute app:crewFilterAttrChanged
. The name of this attribute is the name you give to your attribute plus AttrChanged
. This adapter receives InverseBindingListener
. This listener has one method: onChange()
. In this case, you’ll call onChange()
whenever the checked state of the radio button changes.
Finally, it’s time to use two-way data binding. Open fragment_crew.xml and modify RadioGroup
as follows:
<RadioGroup
android:id="@+id/filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:crewFilter="@={viewModel.crewAgency}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
This code adds crewFilter
to RadioGroup
. Two-way data binding uses a different notation: @=. This notation adds two-way data binding using crewAgency
.
Finally, open CrewFragment.kt and modify onViewCreated() as follows:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.viewModel = viewModel
setupList()
viewModel.result.observe(viewLifecycleOwner) { result ->
handleResult(result)
}
viewModel.crewAgency.observe(viewLifecycleOwner) {
adapter.addItems(viewModel.getFilteredCrew())
}
viewModel.getCrew()
}
Here, you assign the instance of viewModel
to the binding, so the layout has access to it.
binding?.viewModel
. Remember to build the project after adding <data>
to the layout so that Android Studio creates the necessary files for you.
Build and run. Open the Crew tab. Select any radio button to filter the crew by agency. You’ll see something similar to the image below:
Great job! You’ve implemented two-way data binding.
Where to Go From Here?
Download the final project by using the Download Materials button at the top or bottom of the tutorial.
To learn more about data binding, visit the official documentation. You can learn more about view binding, which is related to data binding, in the View Binding Tutorial for Android: Getting Started.
I hope you enjoyed this tutorial on data binding. If you have any questions or comments, please join the forum discussion below.