/**
* 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).
* <p>
* 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.
*/
public void setHasFixedSize(boolean hasFixedSize) {
mHasFixedSize = hasFixedSize;
}
注釋翻譯:
當知道Adapter內Item的改變不會影響RecyclerView寬高的時候,可以設置為true讓RecyclerView避免重新計算大小。
注意:
當setHasFixedSize為true時,再調用notifyDataSetChanged(),發現大小還是重新計算了,看來理解出現錯誤了。
解答
首先是onMeasure里用到,這個和自定義LayoutManager相關,先不管它。然后是triggerUpdateProcessor()方法用到了,請看源碼:
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
然后看一下triggerUpdateProcessor()方法被哪些調用法:
onItemRangeChanged()
onItemRangeInserted()
onItemRangeRemoved()
onItemRangeMoved()
這樣看就很明白了,當調用Adapter的增刪改插方法,最后就會根據mHasFixedSize這個值來判斷需要不需要requestLayout();所以這4個方法不會重新繪制。
那我們再來看一下notifyDataSetChanged()執行的代碼,最后是調用了onChanged,調用了requestLayout(),會去重新測量寬高,所以這也是為什么我們設置為true時,大小還是重新計算了的原因。
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
總結
當我們確定Item的改變不會影響RecyclerView的寬高的時候可以設置setHasFixedSize(true),並通過Adapter的增刪改插方法去刷新RecyclerView,而不是通過notifyDataSetChanged()。(其實可以直接設置為true,當需要改變寬高的時候就用notifyDataSetChanged()去整體刷新一下)