Ich habe Probleme, setHasFixedSize()
zu verstehen. Ich weiß, dass es zur Optimierung verwendet wird, wenn sich die Größe von RecyclerView
in den Dokumenten nicht ändert.
Was heißt das aber? In den meisten Fällen hat eine ListView
fast immer eine feste Größe. In welchen Fällen wäre es keine feste Größe? Bedeutet das, dass die tatsächliche Immobilie, die sie auf dem Bildschirm belegt, mit dem Inhalt wächst?
Eine sehr vereinfachte Version von RecyclerView hat:
void onItemsInsertedOrRemoved() {
if (hasFixedSize) layoutChildren();
else requestLayout();
}
Dieser Link beschreibt, warum der Aufruf von requestLayout
teuer sein kann. Grundsätzlich ändern sich beim Einfügen, Verschieben oder Entfernen von Elementen die Größe (Breite und Höhe) von RecyclerView und die Größe einer anderen Ansicht in der Ansichtshierarchie kann sich ändern. Dies ist besonders problematisch, wenn Elemente häufig hinzugefügt oder entfernt werden.
Vermeiden Sie unnötige Layoutdurchläufe, indem Sie setHasFixedSize
auf true setzen, wenn der Inhalt des Adapters nicht seine Höhe oder Breite ändert.
Update: Das JavaDoc wurde aktualisiert, um besser zu beschreiben, was die Methode tatsächlich bewirkt.
RecyclerView kann mehrere Optimierungen durchführen, wenn dies in .__ bekannt ist. voraus, dass die Größe von RecyclerView nicht durch den Adapter beeinflusst wird Inhalt. RecyclerView kann seine Größe weiterhin basierend auf anderen .__ ändern. Faktoren (z. B. die Größe des übergeordneten Elements), diese Größenberechnung kann jedoch nicht abhängig von der Größe der untergeordneten Elemente oder vom Inhalt des Adapters (außer der Anzahl der Elemente im Adapter).
Wenn Sie RecyclerView verwenden fällt in diese Kategorie, setzen Sie diese auf {@code true}. Es wird erlaubt RecyclerView, um zu vermeiden, dass das gesamte Layout ungültig wird, wenn der Adapter Inhalte ändern sich.
@param hasFixedSize true, wenn Adapteränderungen die Größe von .__ nicht beeinflussen können. die RecyclerView.
Kann bestätigen, dass setHasFixedSize
sich auf die RecyclerView selbst bezieht und nicht auf die Größe jedes Elements, das an sie angepasst ist.
Sie können Android:layout_height="wrap_content"
jetzt für eine RecyclerView verwenden, wodurch einem CollapsingToolbarLayout unter anderem ermöglicht wird, dass es nicht kollabiert, wenn die RecyclerView leer ist. Dies funktioniert nur, wenn Sie setHasFixedSize(false)
in RecylcerView verwenden.
Wenn Sie setHasFixedSize(true)
für RecyclerView verwenden, funktioniert dieses Verhalten, um zu verhindern, dass CollapsingToolbarLayout zusammenbricht, nicht, obwohl RecyclerView tatsächlich leer ist.
Wenn sich setHasFixedSize
auf die Größe der Elemente bezieht, sollte dies keine Auswirkungen haben, wenn die RecyclerView keine Elemente enthält.
Die ListView hatte eine ähnliche benannte Funktion, die Informationen über die Größe der einzelnen Listenelementhöhen widerspiegelt. In der Dokumentation zu RecyclerView wird ziemlich eindeutig darauf hingewiesen, dass es sich auf die Größe des RecyclerView selbst bezieht, nicht auf die Größe seiner Elemente.
Aus dem RecyclerView-Quellkommentar über der setHasFixedSize () - Methode:
* 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.
setHasFixedSize (true) bedeutet, dass der RecyclerView untergeordnete Elemente (Elemente) mit fester Breite und Höhe hat. Dadurch kann RecyclerView besser optimiert werden, indem die genaue Höhe und Breite der gesamten Liste basierend auf Ihrem Adapter ermittelt wird.
Wenn wir setHasFixedSize(true)
auf RecyclerView
setzen, bedeutet dies, dass die Größe des Recyclers festgelegt ist und nicht vom Adapterinhalt beeinflusst wird. In diesem Fall wird onLayout
nicht beim Recycler aufgerufen, wenn wir die Daten des Adaptrers aktualisieren (es gibt jedoch eine Ausnahme).
Gehen wir zum Beispiel:
RecyclerView
hat eine RecyclerViewDataObserver
( find Standardimplementierung in dieser Datei ) mit mehreren Methoden. Das wichtigste ist:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
Diese Methode wird aufgerufen, wenn wir setHasFixedSize(true)
setzen und die Daten eines Adapters aktualisieren: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved
. In diesem Fall gibt es keine Aufrufe der onLayout
des Recyclers, aber requestLayout
Aufrufe zum Aktualisieren von Childs.
Wenn wir jedoch setHasFixedSize(true)
setzen und die Daten eines Adapters über notifyItemChanged
aktualisieren, gibt es einen Aufruf an onChange
der Standardeinstellung RecyclerViewDataObserver
des Verwerters und keine Aufrufe an triggerUpdateProcessor
. In diesem Fall wird der Recycler onLayout
aufgerufen, wenn wir setHasFixedSize
true
oder false
setzen.
// 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();
}
}
So prüfen Sie selbst:
Benutzerdefinierte RecyclerView
erstellen und überschreiben:
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)
}
Legen Sie die Größe des Recyclers auf match_parent
(in xml) fest. Aktualisieren Sie die Daten des Adapters mit replaceData
und replaceOne
mit der Einstellung setHasFixedSize(true)
und dann 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)
}
Und überprüfe dein Logbuch.
Mein Protokoll:
// 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
Zusammenfassen:
Wenn Sie setHasFixedSize(true)
setzen und die Daten des Adapters aktualisieren, indem Sie einen Beobachter auf eine andere Weise als den Aufruf von notifyDataSetChanged
benachrichtigen, haben Sie etwas Leistung, da dies keine Aufrufe der onLayout
-Methode des Recyclers ist.
Dies hat Auswirkungen auf die Animationen des Recyclerüberblicks, wenn dies false
ist. Die Animationen zum Einfügen und Entfernen werden nicht angezeigt. Stellen Sie daher sicher, dass es true
ist, falls Sie eine Animation für das Recycling-Fenster hinzugefügt haben.
Wenn die Größe der RecyclerView (die RecyclerView selbst)
... hängt nicht vom Adapterinhalt ab:
mRecyclerView.setHasFixedSize(true);
... hängt vom Adapterinhalt ab:
mRecyclerView.setHasFixedSize(false);