Cuestión de comportamiento de ObservableList de enlace de datos de Android
android-recyclerview android-databinding (1)
Me resulta difícil detectar la razón de ser real de android.databinding.ObservableList
como una función de enlace de datos.
Al principio, parecía una herramienta genial para mostrar listas, a través del enlace de datos , y agregarlas a través de xml
a un RecyclerView
. Para hacerlo, hice un BindingAdapter como este:
@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);
}
}
De esta forma, puedo usar la app:items
atributos app:items
en un RecyclerView
con un conjunto de parámetros MyAdapter
, para actualizar sus elementos.
Ahora, la mejor característica de ObservableList
es que puede agregarle un OnListChangedCallback
, que maneja los mismos eventos disponibles en RecyclerView
para agregar / mover / eliminar / cambiar elementos sin tener que volver a cargar toda la lista.
Entonces la lógica que pensé implementar fue el siguiente:
- Comienzo con un
MyAdapter
vacío - Cuando mis elementos se extraen de mis API, instancia un
ObservableArrayList
envolviéndolos y pasándolos albinding
- El enlace de datos invoca mi
BindingAdapter
pasando los elementos aMyAdapter
- Cuando
MyAdapter
recibe nuevos elementos, borra los antiguos y agrega unOnListChangedCallback
a laObservableList
recibida para manejarOnListChangedCallback
- Si algo cambia en la
MyAdapter
ObservableList
,MyAdapter
cambiará en consecuencia sin actualizar completamente - Si quiero mostrar un conjunto completamente diferente del mismo tipo de elementos, puedo simplemente volver a establecer la variable de
binding
, por lo que se invocará de nuevo elMyAdapter
y los elementos deMyAdapter
cambiarán por completo.
Por ejemplo, si quiero mostrar elementos de tipo Game
que tengo dos listas diferentes para "juegos propiedad" y "juegos de lista de deseos", podría simplemente llamar a binding.setItems(whateverItems)
para actualizar completamente los elementos mostrados, pero por ejemplo , si muevo los "juegos de lista de deseos" por la lista para organizarlos por relevancia, solo se realizarán microcambios dentro de cada lista sin actualizar todo.
Resulta que esta idea era inviable porque el enlace de datos vuelve a ejecutar el BindingAdapter
cada vez que se realiza un cambio en una ObservableList
, por lo que, por ejemplo, observo el siguiente comportamiento:
- Comienzo con un
MyAdapter
vacío - Cuando mis elementos se extraen de mis API, instancia un
ObservableArrayList
envolviéndolos y pasándolos albinding
- El enlace de datos invoca mi
BindingAdapter
pasando los elementos aMyAdapter
- Cuando
MyAdapter
recibe nuevos elementos, borra los antiguos y agrega unOnListChangedCallback
a laObservableList
recibida para manejarOnListChangedCallback
- Si algo cambia en el
ObservableList
, elBindingAdapter
se invoca de nuevo, por lo tanto,MyAdapter
recibe toda la lista de nuevo y se actualiza completamente.
Este comportamiento me parece bastante malo porque impide que la ObservableList
sea utilizable dentro de un xml
datos enlazados. No puedo entender seriamente un caso de fiar en el que este comportamiento sea deseable.
Busqué algunos ejemplos: aquí y esta otra pregunta SO
En el primer enlace, todos los ejemplos usaron la ObservableList
directamente en el Adapter
sin pasar el formulario xml
y el enlace de datos real, mientras que en el código vinculado en la respuesta SO, el desarrollador hizo básicamente lo mismo que yo intenté, agregando:
if (this.items == items){
return;
}
al comienzo de su Adapter.setItems(ObservableList<T> items)
para descartar todos los casos en que se invoca el método debido a cambios simples en la ObservableList
.
¿Cuál es la necesidad de este comportamiento? ¿Cuáles podrían ser algunos casos en los que este comportamiento es deseable? Siento que ObservableList
es una función que se agrega al enlace de datos y es muy útil, excepto cuando se utiliza con el enlace de datos real, en cuyo caso te obliga a defenderte de su comportamiento. Si lo declaro como una simple List
en ambas etiquetas de datos xml
y en la firma BindingAdapter
, entonces puedo volver a MyAdapter
a ObservableList
dentro de MyAdapter
y funciona bien, pero este es un hack bastante malo. Si fuera solo una característica separada del enlace de datos, sin activar el enlace en cada cambio, hubiera sido mucho mejor en mi opinión.
De acuerdo con el ejemplo provisto en los documentos https://developer.android.com/topic/libraries/data-binding/index.html#observable_collections, la ObservableList se utiliza para acceder a sus elementos utilizando un entero clave, es decir:
<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"/>
Por lo tanto, cuando se cambia algo dentro de la Lista ObservableList
se activa BindingAdapter para actualizar la IU. Creo que ese es el objetivo principal de usar ObservableList
por ahora mientras DataBinding está en estado de desarrollo. Quizás en el futuro DataBinding se actualice con una nueva SomeObservableList
que se utilizará en RecyclerView. Mientras tanto, puede usar if (this.items == items){return;}
si le resulta if (this.items == items){return;}
o reconsiderar su lógica de uso de ObservableList
.