Ever heard of DataBinding? I myself would like to live in a world where every Android developer knows about it. A world where the concept of findViewById and handwritten boilerplate glue code does not exist. Where you can easily tie your data to the UI and forget about updating it in case the data changes.
To achieve this, my fellow developers, I decided to spread the word about a very powerful feature of DataBinding: DEPENDENT PROPERTIES.
“What are dependent properties?” you may ask.
About a year ago, I found this short article by George Mount which introduced a really neat feature of DataBinding * *drrrrrrrrrrrrrrrrrum-roll ** the Bindable properties which depend on other Bindable properties.
The cool thing about dependent properties is that any change in dependencies will automatically trigger an update to the field that declared them as dependencies. This is really powerful on screens where you have input views that rely upon the state of other views.
Let’s look at an example, so you can easily get the gist of it and see how useful this can be when implementing a reactive UI.
For the sake of simplicity I chose to have a first name and a last name as input fields, format them somehow and display the result. In the followings I’ll refer to this as the “displayName”.
This is how it looks in action:
*But how can this be implemented? *Fair question. I’ll show how I did it.
As a first step I created the layout file.
What you’ve just seen is a short version of the layout. I’ve left out the irrelevant tags to focus on what’s important right now. I declared a variable, the view model **that stores and handles the data, then bound the **displayName property from within the ViewModel to the TextView. Next, the firstName and lastName properties were bound using the two-way data binding syntax to the EditTexts (meaning that any changes made in the EditTexts, will show up in the ViewModel’s String properties too).
Following this, in the view model class, I declared the displayName bindable property to depend on the firstName and lastName bindable properties, by enumerating them in the @Bindable annotation as follows:
To use the notifyPropertyChanged method which signals whenever the @Bindable properties change, the ViewModel had to extend the BaseObservable class. With this, every time the firstName & lastName changes** the **notifyPropertyChanged **is called. This updates the **displayName, which on its turn updates the UI with the new value.
To wrap this up, as a final step, I tied the UserViewModel instance to the XML layout:
In the activity class I created a binding instance and set a UserViewModel to it.
Aaaaand that’s it.
Or is it? 🤔
Well, not exactly. I mean it’s nice and everything, but as you can see I filled the UserViewModel with boilerplate code in order to make this work.
Luckily, not so long ago I stumbled upon this interesting talk by Lisa Wray about using DataBinding together with Kotlin. She used Kotlin’s delegated properties feature to create a custom delegate for bindable fields, making the code more readable this way. Following her steps, this is what I ended up having:
The BindableDelegate **has a receiver of **R which extends BaseObservable and handles any kind a property of type **T. **In the constructor of the delegate it expects an initial value of the bindable property as well as its **binding resource **identifier. So now, when a value is set, **notifyPropertyChanged **will be called on the receiver class.
After rewriting the ViewModel to use the custom delegate I had something like this:
All right, now the code is clean and concise. It can’t get any better, I’m pretty sure!
What seems to be the problem?
Turns out, the data doesn’t survive configuration changes such as screen rotations or entering multi-window mode.
So that’s where Android Architecture Components: ViewModel came into play. ViewModel is designed to store and manage UI-related data in a lifecycle conscious way. Well then, let’s extend it, use it and problem solved, right? It’s not exactly that simple, the UserViewModel already extends BaseObservable, in order to use bindable properties.
Since extending multiple classes is not possible, my options were limited. I could have extended the Architecture Components provided ViewModel and copy-paste the whole code from the BaseObservable class. It’s doable, but let’s be honest, that’s an ugly solution, so I chose not to pursue it.
Fortunately, the DataBinding library provides Observable classes, more specifically an ObservableField(or it’s primitive variants) which behaves like a BaseObservable but also wraps a value. Instances of this class can be used as properties in the view model instead of the @Bindable properties. This way properties can be bound to the view and retain the data on configuration change events by extending the ViewModel class.
Mmmkay, but what about the dependent properties? How can one deliver this feature with ObservableField? In the Android Studio 3.1 update, Google silently updated the DataBinding library, and one of the changes was:
Crystal clear, right? It turns out, using this, the same dependent property behaviour can be achieved as with @Bindable properties. So I refactored the UserViewModel to use the Architecture Component ViewModel and ObservableFields as properties:
To make the code more readable, I created a top-level inline function which hides the ObservableField instantiation and overrides the get() method:
I also modified the view model creation logic in the activity:
Et voila! Problem solved! Now the data is retained on configuration change and the dependent property is working as expected.
Like ViewModel, LiveData is another class from the Lifecycle Architecture Component library. LiveData is a data holder which also happens to be lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, this ensures that if an activity or fragment is observing a LiveData, it will only send an update to them in an active lifecycle state.
Android Studio 3.1 brought another sweet update to the DataBinding library:
So instead of using ObservableField’s I used LiveData for binding the data to the UI. The property dependency can be achieved by using a MediatorLiveData: I created another top-level inline function only this time returning a MediatorLiveData instead of ObservableField and its dependencies were LiveData instead of Observable:
All that was left do is swapping out all ObservableField properties to LiveData in the UserViewModel:
and calling setLifecycleOwner() on the binding instance in the activity:
Hope you find this feature useful and now that you’ve seen how easy it is to integrate it into your codebase, you’ll start experimenting with it. I’d be thrilled to hear your thought on this functionality, as well as ideas about cases you see yourself using it.
You can find all the code here. Any feedback, and shares are more than welcome.
Original article posted on medium here.