RecyclerView setHasFixedSizeについて


136

理解できませんsetHasFixedSize()。私はそれがRecyclerViewドキュメントから、サイズが変わらないときに最適化に使用されることを知っています。

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



この回答は役に立ち、非常に理解しやすいとわかりました[StackOverflow-rv.setHasFixedSize(true); ](stackoverflow.com/questions/28827597/…
Mustafa Hasan

回答:


115

非常に RecyclerViewの簡素化バージョンがあります:

void onItemsInsertedOrRemoved() {
   if (hasFixedSize) layoutChildren();
   else requestLayout();
}

このリンクは、通話requestLayoutが高価になる理由を説明しています。基本的に、アイテムが挿入、移動、または削除されるたびに、RecyclerViewのサイズ(幅と高さ)が変化し、ビュー階層内の他のビューのサイズが変化する可能性があります。これは、アイテムが頻繁に追加または削除される場合に特に厄介です。

setHasFixedSizeアダプターのコンテンツを変更しても高さまたは幅が変更されない場合は、trueに設定して不要なレイアウトパスを回避します。


更新:メソッドが実際に何をするかをよりよく説明するために、JavaDocが更新されました。

RecyclerViewのサイズがアダプタの内容に影響されないことが事前にわかっている場合、RecyclerViewはいくつかの最適化を実行できます。RecyclerViewは、他の要素(親のサイズなど)に基づいてサイズを変更できますが、このサイズの計算は、子のサイズやアダプターのコンテンツ(アダプター内のアイテムの数を除く)に依存できません。

RecyclerViewの使用がこのカテゴリに該当する場合は、これを{@code true}に設定します。これにより、RecyclerViewは、アダプターの内容が変更されたときにレイアウト全体が無効になるのを回避できます。

@param hasFixedSizeアダプターの変更がRecyclerViewのサイズに影響を与えない場合はtrue。


162
RecyclerViewのサイズは、何を追加するたびに変化します。setHasFixedSizeが行うことは、RecyclerViewのサイズのこの変更が一定であることを(ユーザー入力によって)確認することです。アイテムの高さ(または幅)は変更されません。追加または削除されるすべてのアイテムは同じになります。これを設定しない場合、アイテムのサイズが変更されたかどうかがチェックされます。この答えは混乱を招くので、はっきりさせておきます。
Arnold Balliu 2016年

9
@ArnoldBの明確な説明。私はそれを独立した答えとしてさえ主張します。
young_souvlaki 2016年

4
@ArnoldB-私はまだ混乱しています。すべての子の幅/高さが一定である場合、hasFixedSizeをtrueに設定することを提案していますか?はいの場合、実行時に一部の子が削除される可能性がある場合(スワイプして閉じる機能があります)-trueに設定しても問題ありませんか?
ジャガー

1
はい。アイテムの幅と高さが変わらないため。追加または削除中です。アイテムを追加または削除しても、サイズは変わりません。
Arnold Balliu 2016年

3
@ArnoldBこちらの商品のサイズ(幅/高さ)は問題ないと思います。アイテムのサイズもチェックしません。requestLayoutdataSetが更新された後にRecyclerViewに呼び出すかどうかを通知するだけです。
Kimi Chiu 2017年

22

確認できるのsetHasFixedSizeはRecyclerView自体に関するものであり、それに適合した各アイテムのサイズではありません。

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

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

場合はsetHasFixedSizeアイテムの大きさに関係していたRecyclerViewは何のアイテムを持っていないとき、それはどんな効果を持つべきではありません。


4
同じ方向を向く経験をしたばかりです。GridLayoutManager(行あたり3アイテム)およびlayout_height = wrap_contentでRecyclerViewを使用する。リストに3つの新しいアイテムを追加するボタンをクリックすると、リサイクラービューが新しいアイテムに合わせて拡大されません。むしろ、同じサイズを維持し、新しいアイテムを表示する唯一の方法はスクロールすることです。アイテムは同じサイズですが、setHasFixedSize(true)新しいアイテムが追加されたときに展開するように削除する必要がありました。
マテウスゴンディム

私はあなたが正しいと思います。ドキュメントから、hasFixedSize: set to true if adapter changes cannot affect the size of the RecyclerView.アイテムのサイズが変わる場合でも、これをtrueに設定できます。
Kimi Chiu 2017年

12

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.

16
しかし、RecyclerViewの「サイズ」はどのように定義されるのでしょうか。それは画面にのみ表示されるサイズですか、それとも(アイテムの高さ+パディング+スペースの合計)に等しいRecyclerViewのフルサイズですか?
Vicky Chijwani

2
実際、これにはより多くの情報が必要です。アイテムを削除してrecyclerviewが縮小した場合、サイズが変更されたと考えられますか?
Henrique de Sousa、2015

4
TextViewがどのようにレイアウトできるかを考えます。wrap_contentを指定した場合、テキストを設定すると、TextViewはレイアウトパスを要求し、画面上で占めるスペースの量を変更できます。match_parentまたは固定サイズを指定した場合、TextViewはサイズが固定されており、テキストの量によって占有されるスペースの量が変わることはないため、レイアウトパスを要求しません。RecyclerViewも同じです。setHasFixedSize()はRVにヒントを与えるため、アダプター項目の変更に基づいてレイアウトパスを要求する必要はありません。
dangVarmit

1
@dangVarmit素敵な説明!
howerknea

6

設定setHasFixedSize(true)するRecyclerViewと、リサイクラーのサイズは固定され、アダプターの内容の影響を受けません。この場合onLayout、アダプターのデータを更新するときにリサイクラーで呼び出されません(ただし、例外があります)。

例に行きましょう:

RecyclerView持っているRecyclerViewDataObserverこのファイルにデフォルトimplemntationを見つけるいくつかの方法で)、メイン重要です:

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 truefalse

// 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で)に設定します。使用して、アダプタのデータを更新しようreplaceDatareplaceOne SETINGとsetHasFixedSize(true)、その後と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と、recycler onLayoutメソッドの呼び出しがないため、パフォーマンスが向上します。


wrap_contentまたはmatch_parentを使用して高さのRecyclerViewでテストしましたか?
Lubos Mudrak

6

RecyclerViewwith match_parentas height / widthがある場合setHasFixedSize(true)RecyclerViewそれ自体のサイズはアイテムの挿入または削除に影響しないため、追加する必要があります。

setHasFixedSizeは、我々がRecyclerViewを持っている場合はfalseでなければなりませんwrap_contentよう、高さ/幅アダプタが挿入された各要素はサイズ変更される可能性があるためRecycler、挿入項目に応じて/ので、削除のサイズRecycler、我々は/削除追加するたびに異なるだろうがアイテム。

より明確に言えば、

<android.support.v7.widget.RecyclerView
    android:id="@+id/my_recycler_view"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

使用できます my_recycler_view.setHasFixedSize(true)

<android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

を使用する必要がありますmy_recycler_view.setHasFixedSize(false)。これはwrap_content、幅としても使用する場合に適用されます


3

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


5
これは@dangVarmitが提案したものではありません。
奇妙な時間2016

5
誤解を招くのは、実際にはコンテンツのサイズではなく、リサイクラーのビューサイズです
Benoit

0

false挿入と削除のアニメーションが表示されない場合、recyclerviewのアニメーションに影響します。したがってtrue、recyclerviewのアニメーションを追加した場合に備えてください。


0

RecyclerView(RecyclerView自体)のサイズ

...アダプタの内容には依存しません:

mRecyclerView.setHasFixedSize(true);

...アダプターの内容によって異なります。

mRecyclerView.setHasFixedSize(false);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.