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.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

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.

Note: You might see an error in 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:

Crew tab with filter

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.