I find it hard to spot the real raison d'etre of the android.databinding.ObservableList as a data binding feature.
At first it looked like a cool tool to display lists, through data binding, adding them via xml to a RecyclerView.
To do so, I made a BindingAdapter like this:
@BindingAdapter(value = {"items"}, requireAll = false)
public static void setMyAdapterItems(RecyclerView view, ObservableList <T> items) {
if(items != null && (view.getAdapter() instanceof MyAdapter)) {
((GenericAdapter<T>) view.getAdapter()).setItems(items);
}
}
This way, I can use the attribute app:items in a RecyclerView with a MyAdapter set to it, to update its items.
Now the best feature of ObservableList is you can add an OnListChangedCallback to it, which handles the same events available in the RecyclerView to add/move/remove/change items in it without actually reloading the whole list.
So the logic I thought to implement was the fallowing:
- I start with an empty
MyAdapter - When my items are fetched from my APIs, I instantiate an
ObservableArrayListwrapping them and pass it to thebinding - Data binding invokes my
BindingAdapterpassing the items toMyAdapter - When
MyAdapterreceives new items, it clears its old ones and adds anOnListChangedCallbackto theObservableListreceived to handle micro-changes - If anything changes in the
ObservableList,MyAdapterwill change accordingly without refreshing completely - If i want to display a completely different set of the same items type, I can just re-set the
bindingvariable, so theBindingAdapterwill be invoked again andMyAdapteritems will be completely changed.
For example, if I want to display items of type Game which I have two different lists for: "owned games" and "wishlist games", I could just call binding.setItems(whateverItems) to completely refresh the displayed items, but for example, if I move the "wishlist games" around the list to organize them by relevance, only micro-changes will be executed within each list without refreshing the whole thing.
Turns out this idea was unfeasible because data binding re-executes the BindingAdapter every time a single change is made to an ObservableList, so for example I observe the fallowing behaviour:
- I start with an empty
MyAdapter - When my items are fetched from my APIs, I instantiate an
ObservableArrayListwrapping them and pass it to thebinding - Data binding invokes my
BindingAdapterpassing the items toMyAdapter - When
MyAdapterreceives new items, it clears its old ones and adds anOnListChangedCallbackto theObservableListreceived to handle micro-changes - If anything changes in the
ObservableList, theBindingAdapteris invoked again, thusMyAdapterreceives the whole list again and completely refreshes.
This behaviour seems quite broken to me because prevents the ObservableList from being usable within an data-bound xml. I cannot seriously figure out a legit case in which this behaviour is desirable.
I looked up some examples: here and this other SO question
In the first link all the examples used the ObservableList directly to the Adapter without even passing form xml and actual data binding, while in the code linked in the SO answer, the developer did basically the same thing I tried to do, adding:
if (this.items == items){
return;
}
at the beginning of his Adapter.setItems(ObservableList<T> items) to discard all the cases where the method is invoked because of simple changes in the ObservableList.
What is the need of this behaviour? What might be some cases where this behaviour is desirable? I feel like ObservableList is a feature added with data binding and is really useful except when used with actual data binding, in which case it forces you to defend from its behaviour.
If I declare it as a simple List in both xml data tags and in BindingAdapter signature, then I can cast it back to ObservableList inside MyAdapter and it works just fine, but this is quite a bad hack.
If it was just a separate feature from data binding, without triggering the binding at every change it would have been much better in my opinion.