Wen stawiamy setHasFixedSize(true)
na RecyclerView
tym, że środki recyklingowa na wielkość jest stała i nie zależy od zawartości karty. I w tym przypadku onLayout
nie jest wywoływany recykler, gdy aktualizujemy dane adaptera (ale jest wyjątek).
Przejdźmy do przykładu:
RecyclerView
ma RecyclerViewDataObserver
( znajdź domyślną implementację w tym pliku ) z kilkoma metodami, najważniejsze to:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
Metoda ta nazywana jest jeśli mamy ustawiony setHasFixedSize(true)
i aktualizować dane za pośrednictwem adaptera: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved
. W tym przypadku nie ma wywołań do recyklera onLayout
, ale są wezwania do requestLayout
aktualizacji child.
Ale jeśli ustawimy setHasFixedSize(true)
i zaktualizujemy dane adaptera za pośrednictwem, notifyItemChanged
zostanie onChange
wywołane domyślne RecyclerViewDataObserver
wywołanie recyklera i nie będzie wywołań triggerUpdateProcessor
. W tym przypadku recykler onLayout
jest wywoływany za każdym razem, gdy ustawimy setHasFixedSize
true
lub 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();
}
}
Jak sprawdzić samodzielnie:
Utwórz własne RecyclerView
i zastąp:
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)
}
Ustaw rozmiar recyklera na match_parent
(w xml). Spróbuj zaktualizować dane adaptera za pomocą replaceData
i replaceOne
z ustawieniem, setHasFixedSize(true)
a następnie 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)
}
I sprawdź swój dziennik.
Mój dziennik:
// 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
Podsumować:
Jeśli ustawimy setHasFixedSize(true)
i zaktualizujemy dane adaptera, powiadamiając obserwatora w inny sposób niż wywołanie notifyDataSetChanged
, masz pewną wydajność, ponieważ nie ma wywołań onLayout
metody recyklera .