Unsplash@alexandermils

Improving Android DataBinding with Bindables library

Jaewoong Eum
ProAndroidDev
5 min readApr 3, 2021

--

DataBinding is one of the most important factors for MVVM architecture. The basic concept of DataBinding is to link the view and view model via observer patterns, properties, event callbacks, etc. Linking and automating communication between the view via the bound properties or something in the view model has a lot of benefits in the MVVM architecture concept.

Microsoft-MVVM

The main purpose of the DataBinding is that helps data interaction between the view and view model, and notifies data changes to view from view model via bound properties. The view then does not need to post-process the data it receives from the domain layer, only draws UI items on the screen. It can take some advantages from this kind of structure because we can focus only on UI/UX things or domain-related logic separately when we need to add new features. Also, we can get advantages of the maintenance perspective. Here is a simple example from the Pokedex.

The MainActivity has a grid list to show the Pokemons and we need to fetch data from the network or integrate them into the database. And we should show a detailed screen when users touch a list item. As we can see from the above codes, it’s quite simple because it uses DataBinding. If we want to change the data model or the design for fetching data or integrating with the database or whatever, we don’t have to change any codes in the MainActivity.

Bindables

So we can take some advantages from DataBinding. For improving the DataBinding usages and reducing the many kinds of boilerplates that we must be required to construct DataBinding designs in the Android project, I’ve been published a library called Bindables that helps lots of DataBinding related constructions in initial projects.

BindingActivity

Through various project experiences, I felt we wrote a lot of repeated codes for inflating DataBinding layouts in our Activity, Fragment, DialogFragment or etc. Generally, we should initialize DataBinding related codes like the below.

The most repeated codes that I’ve thought of are declaring the global property, setting contentView via DataBindingUtil, and we have to use a scope expression for initializing DataBinding variables like apply, with, or etc in every activity and fragment. So Bindables introduces BindingActivity, BindingFragment, BindingDialogFragment, BindingBottomSheetDialogFragment for reducing initialization of the DataBinding boilerplates. We can see the basic usages of these classes.

BindingActivity is a base class for Activities that wish to bind content layout with DataBindingUtil. It provides a binding property that extends ViewDataBinding from abstract information. The binding property will be initialized lazily and ensures to be initialized before being called super.onCreate in Activities. So we don't need to inflate layouts, setContentView, and initialize a binding property manually. Also, we can use binding scope expression to execute a block of code within the context of the binding instance.

Extending Binding classes

Sometimes we need to implement our custom base classes. In this case, we can extend binding classes like the below for designing your own base class.

LiveData in ViewModel

Generally, we use LiveData for notifying ViewModel to UI layers and holding it. But for the reason the value of the MutableLivedata can be modified and exposed from the outside, we should create backing properties for preventing value changes from outsides.

But this kind of implementation does not looking good, also we should create two properties for delivering one data model. It’s quite a wasteful implementation, and some developers design interfaces or abstract ViewModels for overriding and reducing this kind of backing properties. But that also requires writing more codes than we need.

BindingViewModel

BindingViewModel provides a way in which UI can be notified of changes by the ViewModel without the LiveData , ObservableField , or etc. Using bindingProperty, we can notify specific data changes and it can be observed in UI layers. We can make all types of properties being observable from the UI layers using bindingProperty and Bindable annotation. Also, we can prevent modifying the value of the property from outsides.

In our XML files, the changes of properties value will be notified to the DataBinding layer automatically whenever the value changes.

notifyPropertyChanged

we can customize setters of general properties for notifying data changes to UI layers using @get:Bindable annotation and notifyPropertyChanged() in the BindingViewModel. Also, we can notify data changes whenever we want using the notifyPropertyChanged() .

Two-way binding

We can implement two-way binding properties using the bindingProperty. Here is a representative example of the two-way binding using TextView and EditText.

Here is an XML layout. The text will be changed whenever the viewModel.editText is changed.

In your Activity or Fragment, we can set the viewModel.editText value whenever the EditText's input is changed. We can implement this another way using inversebindingadapter.

Binding functions

We can also implement bindable functions using @Bindable annotation and notifyPropertyChanged() in the BindingViewModel. And the @Bindable annotated method's name must start with get.

Whenever we call notifyPropertyChanged(::getFetchedData), getFetchedString() will be called and the UI layer will get the updated data.

android:text="@{viewModel.fetchedData}"

Binding Flow

We can create a binding property from Flow using @get:Bindable and asBindingProperty. UI layers will get newly collected data from the Flow or StateFlow on the viewModelScopeor we can set a coroutine scope. And the property by the Flow must be read-only (val), because its value will be changed only by collecting data of the Flow.

As we can see from the above codes, the pokemonInfo property collects data from the pokemonInfoFlow that initialized by the repository. The read-only property pokemonInfoFlow will collect data and it can be observed in the UI layers. We can see more details in the Pokedex.

Binding SavedStateHandle

We can create a binding property from SavedStateHandle in the BindingViewModel using @get:Bindable and asBindingProperty(key: String). UI layers will get newly saved data from the SavedStateHandle and we can set the value into the SavedStateHandle when we just set a value to the property.

BindingRecyclerViewAdapter

We can create binding properties in the RecyclerView.Adapter using the BindingRecyclerViewAdapter. In the below example, the isEmpty property is observable in the XML layout. And we can notify value changes to DataBinding using notifyPropertyChanged.

In the below example, we can make the placeholder being gone when the adapter's item list is empty or loading data.

BindingModel

We can use binding properties in our own classes by extending the BindingModel and those properties are observable in UI layers.

Conclusion

In this posting, we looked around how to improve usages of DataBinding using the Bindables library. DataBinding reduces many kinds of codes in activities and fragments for post-processing and applying the data model to the UI components. Also, it allows us to correspond flexibly from the domain logic changes, structures, or etc. So If you are considering using DataBinding in your new project, or if you are already using DataBinding but want to changes to better usages, hope you try to use Bindables. 😄

--

--

Senior Android Developer Advocate @ Stream 🥑 • GDE for Android • OSS engineer. Love psychology, magic tricks, and writing poems. https://github.com/skydoves