setHasFixedSize(true) means the RecyclerView has children (items) that has fixed width and height. This allows the RecyclerView to optimize better by figuring out the exact height and width of the entire list based on the your adapter.
notifyDataSetChanged. Notify any registered observers that the data set has changed. There are two different classes of data change events, item changes and structural changes.
RecyclerView is the ViewGroup that contains the views corresponding to your data. It's a view itself, so you add RecyclerView into your layout the way you would add any other UI element. Each individual element in the list is defined by a view holder object.
The RecyclerView is a more advanced and more flexible version of the ListView. This new component is a big step because the ListView is one of the most used UI widgets. The CardView widget, on the other hand, is a new component that does not “upgrade” an existing component.
A very simplified version of RecyclerView has:
void onItemsInsertedOrRemoved() {
if (hasFixedSize) layoutChildren();
else requestLayout();
}
This link describes why calling requestLayout
might be expensive. Basically whenever items are inserted, moved or removed the size (width and height) of RecyclerView might change and in turn the size of any other view in view hierarchy might change. This is particularly troublesome if items are added or removed frequently.
Avoid unnecessary layout passes by setting setHasFixedSize
to true when changing the contents of the adapter does not change it's height or the width.
Update: The JavaDoc has been updated to better describe what the method actually does.
RecyclerView can perform several optimizations if it can know in advance that RecyclerView's size is not affected by the adapter contents. RecyclerView can still change its size based on other factors (e.g. its parent's size) but this size calculation cannot depend on the size of its children or contents of its adapter (except the number of items in the adapter).
If your use of RecyclerView falls into this category, set this to {@code true}. It will allow RecyclerView to avoid invalidating the whole layout when its adapter contents change.
@param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
Can confirm setHasFixedSize
relates to the RecyclerView itself, and not the size of each item adapted to it.
You can now use android:layout_height="wrap_content"
on a RecyclerView, which, among other things, allows a CollapsingToolbarLayout to know it should not collapse when the RecyclerView is empty. This only works when you use setHasFixedSize(false)
on the RecylcerView.
If you use setHasFixedSize(true)
on the RecyclerView, this behavior to prevent the CollapsingToolbarLayout from collapsing does not work, even though the RecyclerView is indeed empty.
If setHasFixedSize
was related to the size of items, it shouldn't have any effect when the RecyclerView has no items.
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 because each element inserted by the adapter could change the size of the RecyclerView
depending on the items inserted/deleted, so, the size of the RecyclerView
will be different each time we add/delete items.
To be more clear, if we use a fixed width/height
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
We can use my_recycler_view.setHasFixedSize(true)
Then if we do not use a fixed width/height
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
We should use my_recycler_view.setHasFixedSize(false)
since wrap_content
for width or height can change the size of our RecyclerView
.
When we talk about RecyclerView
setHasFixedSize
we are not talking about the quantity of elements inside of it but instead the the size of the View
itself.
The ListView had a similar named function that I think did reflect info about the size of the individual list item heights. The documentation for RecyclerView pretty clearly states it is referring to the size of the RecyclerView itself, not the size of its items.
From the RecyclerView source comment above the setHasFixedSize() method:
* 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.
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.
setHasFixedSize(true) means the RecyclerView has children (items) that has fixed width and height. This allows the RecyclerView to optimize better by figuring out the exact height and width of the entire list based on the your adapter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With