Когда мы устанавливаем setHasFixedSize(true)
на RecyclerView
, это означает, что размер ресайклера фиксирован и не зависит от содержимого адаптера. И в этом случае onLayout
не вызывается на ресайклере, когда мы обновляем данные адаптера (но есть исключение).
Перейдем к примеру:
RecyclerView
имеет RecyclerViewDataObserver
(найти реализацию по умолчанию в этом файле) несколькими способами, основным из которых является:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
Этот метод вызывается, если мы устанавливаем setHasFixedSize(true)
и обновляем данные адаптера через: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved
. В этом случае нет вызовов onLayout
переработчика, но есть вызовы requestLayout
для обновления дочерних элементов.
Но если мы установим setHasFixedSize(true)
и обновим данные адаптера через notifyItemChanged
, тогда будет вызов onChange
стандартного RecyclerViewDataObserver
ресайклера и никаких вызовов triggerUpdateProcessor
. В этом случае переработчик onLayout
вызывается всякий раз, когда мы устанавливаем setHasFixedSize
true
или 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();
}
}
Как проверить самостоятельно:
Создайте пользовательский RecyclerView
и переопределите:
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)
}
Установите размер ресайклера на match_parent
(в xml). Попробуйте обновить данные адаптера с помощью replaceData
и replaceOne
с настройкой setHasFixedSize(true)
, а затем 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)
}
И проверьте свой журнал.
Мой журнал:
// 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
Обобщить:
Если мы установим setHasFixedSize(true)
и обновим данные адаптера, уведомив наблюдателя каким-либо другим способом, кроме вызова notifyDataSetChanged
, тогда у вас будет некоторая производительность, потому что нет вызовов метода onLayout
переработчика.
person
bitvale
schedule
12.11.2018