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:
MyAdapter
ObservableArrayList
wrapping them and pass it to the binding
BindingAdapter
passing the items to MyAdapter
MyAdapter
receives new items, it clears its old ones and adds an OnListChangedCallback
to the ObservableList
received to handle micro-changesObservableList
, MyAdapter
will change accordingly without refreshing completelybinding
variable, so the BindingAdapter
will be invoked again and MyAdapter
items 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:
MyAdapter
ObservableArrayList
wrapping them and pass it to the binding
BindingAdapter
passing the items to MyAdapter
MyAdapter
receives new items, it clears its old ones and adds an OnListChangedCallback
to the ObservableList
received to handle micro-changesObservableList
, the BindingAdapter
is invoked again, thus MyAdapter
receives 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.
According to the example provided in the docs https://developer.android.com/topic/libraries/data-binding/index.html#observable_collections the ObservableList is used for accessing it's items using key integer, i.e.:
<data>
<import type="android.databinding.ObservableList"/>
<import type="com.example.my.app.Fields"/>
<variable name="user" type="ObservableList<Object>"/>
</data>
…
<TextView
android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Therefore, when something is changed inside the ObservableList
it triggers BindingAdapter to update UI. I think that is the main purpose of using ObservableList
for now while DataBinding is in development state. Maybe in the future DataBinding will be updated with a new SomeObservableList
which will be intended to use in RecyclerView.
Meanwhile, you can use if (this.items == items){return;}
if it works for you, or reconsider your logic of using ObservableList
.