setHasFixedSize()
を理解するのに苦労しています。ドキュメントからRecyclerView
のサイズが変わらないときに最適化に使用されることを知っています。
それはどういう意味ですか?ほとんどの場合、ListView
のサイズはほとんど常に固定です。どのような場合に固定サイズではないでしょうか?それが画面上で占める実際の不動産がコンテンツとともに成長することを意味しますか?
RecyclerViewのvery簡易バージョンには次のものがあります。
void onItemsInsertedOrRemoved() {
if (hasFixedSize) layoutChildren();
else requestLayout();
}
このリンク は、requestLayout
の呼び出しが高価になる理由を説明しています。基本的に、アイテムが挿入、移動、または削除されるたびに、RecyclerViewのサイズ(幅と高さ)が変わり、ビュー階層内の他のビューのサイズが変わる可能性があります。これは、アイテムが頻繁に追加または削除される場合、特に面倒です。
アダプターの内容を変更しても高さや幅が変更されない場合は、setHasFixedSize
をtrueに設定して、不要なレイアウトパスを避けます。
更新:JavaDocが更新され、メソッドが実際に行うことをより適切に説明するようになりました。
RecyclerViewのサイズがアダプターの内容に影響されないことを事前に知ることができる場合、RecyclerViewはいくつかの最適化を実行できます。 RecyclerViewは、他の要因(親のサイズなど)に基づいてサイズを変更できますが、このサイズの計算は、子のサイズやアダプターのコンテンツ(アダプター内のアイテムの数を除く)に依存することはできません。
ご使用のRecyclerViewがこのカテゴリに該当する場合、これを{@code true}に設定します。これにより、RecyclerViewは、アダプターの内容が変更されたときにレイアウト全体が無効になることを回避できます。
@param hasFixedSizeアダプターの変更がRecyclerViewのサイズに影響を与えない場合はtrue。
setHasFixedSize
がRecyclerView自体に関連しており、それに適応する各アイテムのサイズではないことを確認できます。
これで、RecyclerViewでAndroid:layout_height="wrap_content"
を使用できるようになりました。これにより、CollapsingToolbarLayoutは、RecyclerViewが空の場合に折りたたむべきではないことがわかります。これは、RecycercerViewでsetHasFixedSize(false)
を使用する場合にのみ機能します。
RecyclerViewでsetHasFixedSize(true)
を使用する場合、RecyclerViewが実際に空であっても、CollapsingToolbarLayoutの折りたたみを防ぐこの動作は機能しません。
setHasFixedSize
がアイテムのサイズに関連していた場合、RecyclerViewにアイテムがない場合は効果がありません。
ListViewには、個々のリストアイテムの高さのサイズに関する情報を反映していると思われる同様の名前付き関数がありました。 RecyclerViewのドキュメントには、アイテムのサイズではなく、RecyclerView自体のサイズを参照していることが明記されています。
SetHasFixedSize()メソッドの上にあるRecyclerViewソースコメントから:
* RecyclerView can perform several optimizations if it can know in advance that changes in
* adapter content cannot change the size of the RecyclerView itself.
* If your use of RecyclerView falls into this category, set this to true.
setHasFixedSize(true)は、RecyclerViewに固定幅と高さの子(アイテム)があることを意味します。これにより、ご使用のアダプターに基づいてリスト全体の正確な高さと幅を把握することで、RecyclerViewの最適化が改善されます。
RecyclerView
にsetHasFixedSize(true)
を設定すると、リサイクラーのサイズが固定され、アダプターの内容の影響を受けなくなります。この場合、アダプターのデータを更新するときに、リサイクラーでonLayout
は呼び出されません(ただし、例外があります)。
例に行きましょう:
RecyclerView
にはRecyclerViewDataObserver
( このファイルでデフォルトの実装を見つける )があり、いくつかのメソッドがあります。主に重要なのは:
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
を介してアダプターのデータを更新すると、リサイクラのデフォルトのonChange
のRecyclerViewDataObserver
の呼び出しがあり、triggerUpdateProcessor
の呼び出しはありません。この場合、onLayout
setHasFixedSize
またはtrue
を設定するたびに、リサイクラfalse
が呼び出されます。
// 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内)に設定します。 setHasFixedSize(true)
を設定してからreplaceData
を設定して、replaceOne
および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
メソッドの呼び出しがないため、パフォーマンスが向上します。
false
の場合、recyclerviewのアニメーションに影響します。挿入および削除のアニメーションは表示されません。そのため、recyclerviewにアニメーションを追加した場合は、true
であることを確認してください。
RecyclerViewのサイズ(RecyclerView自体)
...アダプタのコンテンツに依存しません:
mRecyclerView.setHasFixedSize(true);
...アダプタの内容に依存します:
mRecyclerView.setHasFixedSize(false);