当我们在RecyclerView 上设置setHasFixedSize(true) 时,这意味着recycler 的大小是固定的并且不受适配器内容的影响。在这种情况下,当我们更新适配器的数据时,不会在回收器上调用onLayout(但有一个例外)。
我们来看例子:
RecyclerView 有一个RecyclerViewDataObserver (find default implemntation in this file) 有几种方法,主要的是:
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();
}
}
如何自行检查:
创建自定义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 以外的其他方式通知观察者,那么您有一些性能,因为没有调用回收器onLayout 方法。