I\'m having some trouble understanding setHasFixedSize()
. I know that it is used for optimization when the size of RecyclerView
doesn\'t change, fr
If we have a RecyclerView
with match_parent
as height/width, we should add setHasFixedSize(true)
since the size of the RecyclerView
itself does not change inserting or deleting items into it.
setHasFixedSize should be false if we have a RecyclerView with wrap_content
as height/width since each element inserted by the adapter could change the size of the Recycler
depending on the items inserted/deleted, so, the size of the Recycler
will be different each time we add/delete items.
To be more clear, if we use
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
We can use my_recycler_view.setHasFixedSize(true)
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
We should use my_recycler_view.setHasFixedSize(false)
, this applies if we also use wrap_content
as width
Wen 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.