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 仍然会监听滚动事件,滚动性能不会受到影响,主要影响的是布局计算的性能。