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
MyAdaptervacío - Cuando mis elementos se extraen de mis API, instancia un
ObservableArrayListenvolviéndolos y pasándolos albinding - El enlace de datos invoca mi
BindingAdapterpasando los elementos aMyAdapter - Cuando
MyAdapterrecibe nuevos elementos, borra los antiguos y agrega unOnListChangedCallbacka laObservableListrecibida para manejarOnListChangedCallback - Si algo cambia en la
MyAdapterObservableList,MyAdaptercambiará 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 elMyAdaptery los elementos deMyAdaptercambiará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
MyAdaptervacío - Cuando mis elementos se extraen de mis API, instancia un
ObservableArrayListenvolviéndolos y pasándolos albinding - El enlace de datos invoca mi
BindingAdapterpasando los elementos aMyAdapter - Cuando
MyAdapterrecibe nuevos elementos, borra los antiguos y agrega unOnListChangedCallbacka laObservableListrecibida para manejarOnListChangedCallback - Si algo cambia en el
ObservableList, elBindingAdapterse invoca de nuevo, por lo tanto,MyAdapterrecibe 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 .