android - recyclerview - setitemviewcachesize
Comprender RecyclerView setHasFixedSize (8)
Tengo problemas para entender
setHasFixedSize()
.
Sé que se usa para la optimización cuando el tamaño de
RecyclerView
no cambia, desde los documentos.
Pero, ¿qué significa eso?
En los casos más comunes, un
ListView
casi siempre tiene un tamaño fijo.
¿En qué casos no sería un tamaño fijo?
¿Significa que el espacio real que ocupa en pantalla crece con el contenido?
Afecta las animaciones de la vista de reciclaje, si es
false
... las animaciones de insertar y quitar no se mostrarán.
así que asegúrese de que sea
true
en caso de que agregue animación para la vista del reciclador.
Cuando configuramos
setHasFixedSize(true)
en
RecyclerView
eso significa que el tamaño del reciclador es fijo y no se ve afectado por el contenido del adaptador.
Y en este caso,
onLayout
no se llama al reciclador cuando actualizamos los datos del adaptador (pero hay una excepción).
Vayamos al ejemplo:
RecyclerView
tiene un
RecyclerViewDataObserver
(
encuentra la implementación predeterminada en este archivo
) con varios métodos, el principal importante es:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
Se llama a este método si establecemos
setHasFixedSize(true)
y actualizamos los datos de un adaptador a través de:
notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved
.
En este caso no hay llamadas al reciclador
onLayout
, pero hay llamadas a
requestLayout
para actualizar childs.
Pero si establecemos
setHasFixedSize(true)
y actualizamos los datos de un adaptador a través de
notifyItemChanged
entonces hay una llamada a
onChange
del
RecyclerViewDataObserver
predeterminado del
RecyclerViewDataObserver
y no hay llamadas a
triggerUpdateProcessor
.
En este caso, se llama al reciclador
onLayout
cada vez que establecemos
setHasFixedSize
true
o
false
.
// no calls to triggerUpdateProcessor
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
// calls to triggerUpdateProcessor
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
triggerUpdateProcessor();
}
}
Cómo verificar por ti mismo:
Cree
RecyclerView
personalizado y anule:
override fun requestLayout() {
Log.d("CustomRecycler", "requestLayout is called")
super.requestLayout()
}
override fun invalidate() {
Log.d("CustomRecycler", "invalidate is called")
super.invalidate()
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
Log.d("CustomRecycler", "onLayout is called")
super.onLayout(changed, l, t, r, b)
}
Establezca el tamaño del reciclador en
match_parent
(en xml).
Intente actualizar los datos del adaptador utilizando
replaceData
y
replaceOne
con
setHasFixedSize(true)
y luego
false
.
// onLayout is called every time
fun replaceAll(data: List<String>) {
dataSet.clear()
dataSet.addAll(data)
this.notifyDataSetChanged()
}
// onLayout is called only for setHasFixedSize(false)
fun replaceOne(data: List<String>) {
dataSet.removeAt(0)
dataSet.addAll(0, data[0])
this.notifyItemChanged(0)
}
Y revisa tu registro.
Mi registro:
// for replaceAll
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onMeasure is called
D/CustomRecycler: onMeasure is called
D/CustomRecycler: onLayout
D/CustomRecycler: requestLayout is called
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onDraw is called
// for replaceOne
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onDraw is called
D/CustomRecycler: requestLayout is called
D/CustomRecycler: onDraw is called
Resumir:
Si establecemos
setHasFixedSize(true)
y actualizamos los datos del adaptador notificando a un observador de otra manera que no sea llamar a
notifyDataSetChanged
, entonces tiene algo de rendimiento, porque no hay llamadas al método de reciclaje
onLayout
.
ListView tenía una función con nombre similar que creo que reflejaba información sobre el tamaño de las alturas de los elementos de la lista individual. La documentación de RecyclerView establece claramente que se refiere al tamaño de RecyclerView en sí, no al tamaño de sus elementos.
Del comentario de la fuente RecyclerView sobre el método setHasFixedSize ():
* RecyclerView can perform several optimizations if it can know in advance that changes in
* adapter content cannot change the size of the RecyclerView itself.
* If your use of RecyclerView falls into this category, set this to true.
No sé si estoy en lo cierto en este caso, pero mi superior solía decir que si uso
setHasFixedSize(true)
para una vista de reciclador que está dentro de una vista de desplazamiento anidada, la interfaz de usuario se desplazará suavemente como la vista de reciclador. cargar todos los elementos de una vez (y no dinámicamente como lo hace habitualmente)
Los lectores, por favor, corríjanme con un comentario si encuentran esto incorrecto.
Puede confirmar que
setHasFixedSize
relaciona con el RecyclerView en sí, y no con el tamaño de cada elemento adaptado a él.
Ahora puede usar
android:layout_height="wrap_content"
en un RecyclerView, que, entre otras cosas, permite que CollapsingToolbarLayout sepa que no debe colapsar cuando RecyclerView está vacío.
Esto solo funciona cuando usa
setHasFixedSize(false)
en RecylcerView.
Si usa
setHasFixedSize(true)
en RecyclerView, este comportamiento para evitar que CollapsingToolbarLayout se contraiga no funciona, aunque RecyclerView esté realmente vacío.
Si
setHasFixedSize
estaba relacionado con el tamaño de los elementos, no debería tener ningún efecto cuando RecyclerView no tiene elementos.
Si el tamaño de RecyclerView (el RecyclerView en sí)
... no depende del contenido del adaptador:
mRecyclerView.setHasFixedSize(true);
... depende del contenido del adaptador:
mRecyclerView.setHasFixedSize(false);
Una versión muy simplificada de RecyclerView tiene:
void onItemsInsertedOrRemoved() {
if (hasFixedSize) layoutChildren();
else requestLayout();
}
Este enlace
describe por qué llamar a
requestLayout
puede ser costoso.
Básicamente, cada vez que se insertan, mueven o eliminan elementos, el tamaño (ancho y alto) de RecyclerView puede cambiar y, a su vez, puede cambiar el tamaño de cualquier otra vista en la jerarquía de vistas.
Esto es particularmente problemático si los elementos se agregan o eliminan con frecuencia.
Evite pases de diseño innecesarios estableciendo
setHasFixedSize
en verdadero cuando el cambio del contenido del adaptador no cambie su altura o el ancho.
Actualización: JavaDoc se ha actualizado para describir mejor lo que realmente hace el método.
RecyclerView puede realizar varias optimizaciones si puede saber de antemano que el tamaño del RecyclerView no se ve afectado por el contenido del adaptador. RecyclerView aún puede cambiar su tamaño en función de otros factores (por ejemplo, el tamaño de su padre), pero este cálculo de tamaño no puede depender del tamaño de sus elementos secundarios o del contenido de su adaptador (excepto el número de elementos en el adaptador).
Si su uso de RecyclerView cae en esta categoría, configúrelo en {@code true}. Permitirá que RecyclerView evite invalidar todo el diseño cuando cambie el contenido de su adaptador.
@param hasFixedSize true si los cambios del adaptador no pueden afectar el tamaño de RecyclerView.
setHasFixedSize (true) significa que RecyclerView tiene elementos secundarios (elementos) que tienen ancho y alto fijos. Esto permite que RecyclerView se optimice mejor al calcular la altura y el ancho exactos de toda la lista en función de su adaptador.