web-dev-qa-db-ja.com

RecyclerView setHasFixedSizeについて

setHasFixedSize()を理解するのに苦労しています。ドキュメントからRecyclerViewのサイズが変わらないときに最適化に使用されることを知っています。

それはどういう意味ですか?ほとんどの場合、ListViewのサイズはほとんど常に固定です。どのような場合に固定サイズではないでしょうか?それが画面上で占める実際の不動産がコンテンツとともに成長することを意味しますか?

108
SIr Codealot

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。

92
LukaCiko

setHasFixedSizeがRecyclerView自体に関連しており、それに適応する各アイテムのサイズではないことを確認できます。

これで、RecyclerViewでAndroid:layout_height="wrap_content"を使用できるようになりました。これにより、CollapsingToolbarLayoutは、RecyclerViewが空の場合に折りたたむべきではないことがわかります。これは、RecycercerViewでsetHasFixedSize(false)を使用する場合にのみ機能します。

RecyclerViewでsetHasFixedSize(true)を使用する場合、RecyclerViewが実際に空であっても、CollapsingToolbarLayoutの折りたたみを防ぐこの動作は機能しません。

setHasFixedSizeがアイテムのサイズに関連していた場合、RecyclerViewにアイテムがない場合は効果がありません。

18
Kevin

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.
12
dangVarmit

setHasFixedSize(true)は、RecyclerViewに固定幅と高さの子(アイテム)があることを意味します。これにより、ご使用のアダプターに基づいてリスト全体の正確な高さと幅を把握することで、RecyclerViewの最適化が改善されます。

4
Calvin Park

RecyclerViewsetHasFixedSize(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を介してアダプターのデータを更新すると、リサイクラのデフォルトのonChangeRecyclerViewDataObserverの呼び出しがあり、triggerUpdateProcessorの呼び出しはありません。この場合、onLayoutsetHasFixedSizeまたは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メソッドの呼び出しがないため、パフォーマンスが向上します。

3
bitvale

falseの場合、recyclerviewのアニメーションに影響します。挿入および削除のアニメーションは表示されません。そのため、recyclerviewにアニメーションを追加した場合は、trueであることを確認してください。

0
Alaa AbuZarifa

RecyclerViewのサイズ(RecyclerView自体)

...アダプタのコンテンツに依存しません:

mRecyclerView.setHasFixedSize(true);

...アダプタの内容に依存します:

mRecyclerView.setHasFixedSize(false);
0
Alok Singh