如何正確使用RecyclerView的setHasFixedSize方法提高布局計算性能
setHasFixedSize
setHasFixedSize(boolean hasFixedSize) 是 Android 中 RecyclerView 類的一個方法,用于設置 RecyclerView 是否具有固定大小。
RecyclerView源碼中setHasFixedSize方法的解釋:
/**
* 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;
}
翻譯一下注釋如下:
如果RecyclerView能夠提前知道RecyclerView的大小不受適配器內容的影響,可以執行幾個優化。RecyclerView仍然可以根據其他因素(例如其父項的大小)更改其大小,但此大小計算不能取決于其子項的大小或適配器的內容(適配器中的項目數除外) 如果您對RecyclerView的使用屬于此類別,請將其設置為{@code true}。它將允許RecyclerView避免在適配器內容更改時使整個布局無效。
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
defaultOnMeasure(widthSpec, heightSpec);
return;
}
if (mLayout.isAutoMeasureEnabled()) {
//....... 省略部分代碼
} else {
if (mHasFixedSize) {
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
return;
}
// custom onMeasure
//...... 省略部分代碼
if (mAdapter != null) {
mState.mItemCount = mAdapter.getItemCount();
} else {
mState.mItemCount = 0;
}
startInterceptRequestLayout();
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
stopInterceptRequestLayout(false);
mState.mInPreLayout = false; // clear
}
}
由上面內容可知:調用 setHasFixedSize(true) 時,RecyclerView 的子項(items)的大小不會改變,即使添加或移除了 RecyclerView 中的項,RecyclerView 也不會重新測量和布局它的所有子項。好處是可以提高性能(測量和布局是一個相對耗時的操作)。
重要的是要確保RecyclerView 實際上具有固定大小。如果 RecyclerView 的子項大小可能會改變(例如,由于文本長度的變化或圖像加載),應該調用 setHasFixedSize(false)。當子項大小改變時,RecyclerView 會重新測量和布局它們確保能正確顯示。
如果你設置 hasFixedSize(true),但在運行時 RecyclerView 的大小實際上發生了變化(例如,因為其內容或布局參數的變化),那么 RecyclerView 的布局可能不會正確地更新,可能會導致顯示問題。
總結
在確定 RecyclerView 的大小在整個生命周期中都不會改變時,才將 hasFixedSize() 設置為 true。如果不確定,或者 RecyclerView 的大小可能會改變,應該將其設置為 false,確保 RecyclerView 能夠正確地重新計算其布局。
- 使用固定的寬度/高度(可以用setHasFixedSize(true)):
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2" />
- 不使用固定的寬度/高度:應該使用setHasFixedSize(false),因為寬度或高度可以改變RecyclerView的大小。
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2" />
即使將 hasFixedSize() 設置為 true,RecyclerView 仍然會監聽滾動事件,滾動性能不會受到影響,主要影響的是布局計算的性能。