我在理解上有些困难setHasFixedSize()
。我知道RecyclerView
文档中的大小不变时,它用于优化。
那是什么意思呢?在大多数情况下,ListView
尺寸几乎总是固定的。在什么情况下不是固定大小?这是否意味着它在屏幕上占据的实际空间会随着内容的增长而增加?
我在理解上有些困难setHasFixedSize()
。我知道RecyclerView
文档中的大小不变时,它用于优化。
那是什么意思呢?在大多数情况下,ListView
尺寸几乎总是固定的。在什么情况下不是固定大小?这是否意味着它在屏幕上占据的实际空间会随着内容的增长而增加?
Answers:
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
在更新dataSet之后是否调用。
可以确认setHasFixedSize
与RecyclerView本身有关,而不是与之适应的每个项目的大小。
现在,您可以android:layout_height="wrap_content"
在RecyclerView上使用,它可以使CollapsingToolbarLayout知道在RecyclerView为空时不应折叠。这仅在您setHasFixedSize(false)
在RecylcerView上使用时有效。
如果您setHasFixedSize(true)
在RecyclerView上使用,即使RecyclerView确实为空,这种防止CollapsingToolbarLayout崩溃的行为也不起作用。
如果setHasFixedSize
与项目的大小有关,则当RecyclerView没有项目时,该项目不起作用。
setHasFixedSize(true)
以使其在添加新项目时扩展。
hasFixedSize: set to true if adapter changes cannot affect the size of the RecyclerView.
因此,即使从文档中更改项目的大小,也可以将其设置为true。
ListView有一个类似的命名函数,我认为确实反映了有关单个列表项高度的大小的信息。RecyclerView的文档非常清楚地指出,它是指RecyclerView本身的大小,而不是其项目的大小。
在setHasFixedSize()方法上方的RecyclerView源注释中:
* 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.
温家宝我们设置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
,那么您会有一些表现,因为没有调用recycler onLayout
方法。
如果我们使用RecyclerView
with match_parent
作为height / width,则应添加,setHasFixedSize(true)
因为其RecyclerView
本身的大小不会更改在其中插入或删除项目的大小。
如果我们有一个带有高度/宽度的RecyclerView,则setHasFixedSize应当为false,wrap_content
因为适配器插入的每个元素都可以根据插入/删除的项目更改对象的大小,因此,每次添加/删除时,对象的大小都会不同。项目。Recycler
Recycler
更明确地说,如果我们使用
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:scrollbars="vertical"
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:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
我们应该使用my_recycler_view.setHasFixedSize(false)
,如果我们也将其wrap_content
用作宽度
它会影响recyclerview的动画,如果它是false
..则不会显示插入和删除动画。因此,请确保true
为recyclerview添加了动画。