setnestedscrollingenabled setitemviewcachesize recyclerview getadapter addonitemtouchlistener android android-5.0-lollipop android-recyclerview

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.