如何正確使用RecyclerView的setHasFixedSize方法提高布局計(jì)算性能
setHasFixedSize
setHasFixedSize(boolean hasFixedSize) 是 Android 中 RecyclerView 類的一個(gè)方法,用于設(shè)置 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的大小不受適配器內(nèi)容的影響,可以執(zhí)行幾個(gè)優(yōu)化。RecyclerView仍然可以根據(jù)其他因素(例如其父項(xiàng)的大?。└钠浯笮?,但此大小計(jì)算不能取決于其子項(xiàng)的大小或適配器的內(nèi)容(適配器中的項(xiàng)目數(shù)除外) 如果您對RecyclerView的使用屬于此類別,請將其設(shè)置為{@code true}。它將允許RecyclerView避免在適配器內(nèi)容更改時(shí)使整個(gè)布局無效。
@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
}
}
由上面內(nèi)容可知:調(diào)用 setHasFixedSize(true) 時(shí),RecyclerView 的子項(xiàng)(items)的大小不會(huì)改變,即使添加或移除了 RecyclerView 中的項(xiàng),RecyclerView 也不會(huì)重新測量和布局它的所有子項(xiàng)。好處是可以提高性能(測量和布局是一個(gè)相對耗時(shí)的操作)。
重要的是要確保RecyclerView 實(shí)際上具有固定大小。如果 RecyclerView 的子項(xiàng)大小可能會(huì)改變(例如,由于文本長度的變化或圖像加載),應(yīng)該調(diào)用 setHasFixedSize(false)。當(dāng)子項(xiàng)大小改變時(shí),RecyclerView 會(huì)重新測量和布局它們確保能正確顯示。
如果你設(shè)置 hasFixedSize(true),但在運(yùn)行時(shí) RecyclerView 的大小實(shí)際上發(fā)生了變化(例如,因?yàn)槠鋬?nèi)容或布局參數(shù)的變化),那么 RecyclerView 的布局可能不會(huì)正確地更新,可能會(huì)導(dǎo)致顯示問題。
總結(jié)
在確定 RecyclerView 的大小在整個(gè)生命周期中都不會(huì)改變時(shí),才將 hasFixedSize() 設(shè)置為 true。如果不確定,或者 RecyclerView 的大小可能會(huì)改變,應(yīng)該將其設(shè)置為 false,確保 RecyclerView 能夠正確地重新計(jì)算其布局。
- 使用固定的寬度/高度(可以用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" />
- 不使用固定的寬度/高度:應(yīng)該使用setHasFixedSize(false),因?yàn)閷挾然蚋叨瓤梢愿淖僐ecyclerView的大小。
<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() 設(shè)置為 true,RecyclerView 仍然會(huì)監(jiān)聽滾動(dòng)事件,滾動(dòng)性能不會(huì)受到影響,主要影響的是布局計(jì)算的性能。