An Untold Story of Observable Dependencies in Data Binding
Hope you all heard about data binding. If not, head out here and check it out.
So one usage of Data Binding is replacing findViewById and another one is, binding data obviously.
The true power of data binding comes when we use it with Observables (
ObservableField etc..). Instead of binding data using regular fields/methods, if we binding it using Observable types, UI will be automatically updated whenever we change the Observable values using
But what if one Observable depends on another Observable to get its value. Let’s take an example of updating text of an
TextView using an
ObservableField<String> and updating it’s visibility based on whether it have any text or not.
Now how would we update the
textVisibility? Whenever we update
text? That would be quite a boilerplate and not exactly foolproof because if we have multiple Observables depends on multiple other Observables, even if we forgot to update one Observable in a place, there would be an issue. We can do better.
Let’s step back a little bit and think about what we actually want to solve.
textVisibility should be updated whenever
text value is updated. That’s all.
Observables can depend on other Observables and can react to changes from the dependents.
This change was done with Android Studio 3.1 release last year.
With this, we can pass other Observables as dependencies when creating Observables which can react to changes when the dependent Observable change. So, instead of this,
we can do this.
You might think the code actually got bigger due to the
ObservableInt anonymous class creation, but those can be abstracted away into an until function. The thing is, now we are not updating the
Data Binding Extension: map
To abstract the object creation part away and to make the dependency thingy more functional, I wrote an extension function for that.
Does the method name look familiar? You would have seen this name often if you used RxJava or Kotlin collection utils. Since we are transforming one Observable field to another, the name suits here perfectly.
Now the TextViewModel.kt would be like this
You might notice that the
textVisibility type is now changed from
ObservableField<Int>. This is because of the return type from
map function and we made that way because we need to
map function to work will all Observables. Of course, you can make another function that does the same and return
ObservableInt if you want.
Let’s take an another case where we do some form validation and submit button should be visible only if all fields are filled.
This is just an example, but in real project, we won’t have these update* methods and those fields would get the latest value directly from the EditText through two-way binding. In that case it gets more tricker since we have to add PropertyChangedCallback to each Observable fields to know when update happens and we should call the
checkAndUpdateSubmitButtonVisibility method there. I’m not gonna show the code for that, because, as you can image, the code would be quite a lot.
Now we can simplify and solve the boilerplate issue with our Observable dependency concept.
Let’s write an util extension function for this too.
Data Binding Extension: combineLatest
Again, this function name is inspired from RxJava combineLatest operator. Now the ViewModel becomes,
Note that, the
combineLatest method we created here only works with this example where one Observable depends on three other Observables because, we get all the Observables as individual parameters instead of
vararg since we can’t deduce the type of each Observable from
vararg and each Observable can have different type.