我对setHasFixedSize()
的理解有些困难。从文档中了解到,它用于在RecyclerView
的大小不变时进行优化。
但这是什么意思呢?在大多数情况下,ListView
几乎总是固定大小的。那么在什么情况下它不是固定大小的呢?这是否意味着它在屏幕上占用的实际空间会随内容增长而增加?
我对setHasFixedSize()
的理解有些困难。从文档中了解到,它用于在RecyclerView
的大小不变时进行优化。
但这是什么意思呢?在大多数情况下,ListView
几乎总是固定大小的。那么在什么情况下它不是固定大小的呢?这是否意味着它在屏幕上占用的实际空间会随内容增长而增加?
RecyclerView 的一个非常简化的版本有:
void onItemsInsertedOrRemoved() {
if (hasFixedSize) layoutChildren();
else requestLayout();
}
这个链接解释了为什么调用requestLayout
可能很耗费资源。基本上,每当插入、移动或删除项目时,RecyclerView的尺寸(宽度和高度)可能会发生变化,从而影响视图层次结构中任何其他视图的大小。如果频繁添加或删除项,这可能会特别麻烦。
当更改适配器的内容不会改变其高度或宽度时,通过设置setHasFixedSize
为true来避免不必要的布局传递。
更新: JavaDoc已经更新,以更好地描述该方法实际上所做的工作。
如果RecyclerView事先知道RecyclerView的大小不受适配器内容的影响,则可以执行几个优化。RecyclerView仍然可以根据其他因素(例如其父级的大小)更改其大小,但是此大小计算不能依赖于其子元素的大小或适配器的内容(除了适配器中的项目数量)。
如果您使用的RecyclerView属于此类别,请将其设置为{@code true}。这将允许RecyclerView在其适配器内容更改时避免使整个布局无效。
@param hasFixedSize 如果适配器更改不会影响RecyclerView的大小,则为true。
requestLayout
。 - Kimi Chiu如果我们有一个高度/宽度使用match_parent
的RecyclerView
,我们应该添加setHasFixedSize(true)
,因为插入或删除项目时RecyclerView
本身的大小不会发生变化。
如果我们有一个高度/宽度使用wrap_content
的RecyclerView
,则setHasFixedSize
应该是false。因为适配器插入的每个元素都可能根据插入/删除的项目而改变RecyclerView
的大小,因此每次添加/删除项目时RecyclerView
的大小都会有所不同。
更清楚地说,如果我们使用固定的宽度/高度
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
我们可以使用my_recycler_view.setHasFixedSize(true)
如果我们不使用固定的宽度/高度
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
我们应该使用my_recycler_view.setHasFixedSize(false)
,因为对于width或height的wrap_content
可能会改变我们的RecyclerView
的大小。
当我们谈论RecyclerView
setHasFixedSize
时,我们不是在谈论其中的元素数量,而是在谈论View
本身的大小。这个大小取决于其中的项目是否也具有不同的大小,所以,如果您在recyclerview中有一个比其他项目更大的项目,则RecyclerView的大小不是固定的。
setHasFixedSize
与RecyclerView本身有关,而不是每个适配到其中的项目的大小。现在可以在RecyclerView上使用android:layout_height="wrap_content"
,这样CollapsingToolbarLayout就知道当RecyclerView为空时它不应该折叠。这仅在您在RecylcerView上使用setHasFixedSize(false)
时才有效。setHasFixedSize(true)
,即使RecyclerView确实为空,也无法防止CollapsingToolbarLayout折叠。如果setHasFixedSize
与项目的大小有关,则在RecyclerView没有任何项目时它不应产生任何影响。setHasFixedSize(true)
才能使它在添加新项目时扩展。 - Mateus GondimhasFixedSize: 如果适配器更改不会影响RecyclerView的大小,则设置为true。
因此,即使项目的大小发生变化,您仍然可以将其设置为true。 - Kimi ChiuListView有一个类似的函数,我认为它反映了各个列表项高度的大小信息。RecyclerView的文档非常明确地说明它是指RecyclerView本身的大小,而不是其项目的大小。
从RecyclerView源代码的注释中可以看到setHasFixedSize()方法:
* 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.
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
更新适配器的数据,则会调用回收器默认RecyclerViewDataObserver
的onChange
,而不会调用triggerUpdateProcessor
。在这种情况下,每当我们将setHasFixedSize
设置为true
或false
时,都会调用回收器的onLayout
。// 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();
}
}
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
方法。
mRecyclerView.setHasFixedSize(true);
...取决于适配器内容:
"mRecyclerView.setHasFixedSize(false);
false
,则插入和删除动画将不会显示。因此,请确保在为RecyclerView添加动画时将其设置为true
。setHasFixedSize() 是指 itemView 或者可以说是 view 的大小,所以它与列表或 ArrayList 没有任何关系。如果您想要在适配器中为不同的位置更改视图的高度和宽度,则应将其设置为 false。
无论添加什么,RecyclerView的大小每次都会改变。
setHasFixedSize的作用是确保(通过用户输入)RecyclerView的大小不变。项目的高度(或宽度)不会改变。添加或删除的每个项目都将相同。
如果您不设置此项,则会检查项目的大小是否已更改,这是昂贵的。(来自评论)