When we set setHasFixedSize(true) on RecyclerView that means recycler's size is fixed and is not affected by the adapter contents. And in this case onLayout is not called on recycler when we update the adaptrer's data (but there is an exception).
Let's go to the example:
RecyclerView has a RecyclerViewDataObserver (find default implemntation in this file) with several methods, the main important is:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
This method is called if we set setHasFixedSize(true) and update an adapter's data via: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved. In this case there is no calls to the recycler's onLayout, but there is calls to requestLayout for updating childs.
But if we set setHasFixedSize(true) and update an adapter's data via notifyItemChanged then there is call to onChange of the recycler's default RecyclerViewDataObserver and no calls to triggerUpdateProcessor. In this case the recycler onLayout is called whenever we set setHasFixedSize true or 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();
}
}
How to check by yourself:
Create custom RecyclerView and override:
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)
}
Set the recycler size to match_parent (in xml). Try to update adapter's data using replaceData and replaceOne with seting setHasFixedSize(true) and then 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)
}
And check your log.
My log:
// 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
Summarize:
If we set setHasFixedSize(true) and update adapter's data with notifying an observer in some other way than calling notifyDataSetChanged, then you have some perfomance, because the is no calls to the recycler onLayout method.