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

9
@ArnoldB 우수한 설명. 나는 심지어 그것을 독립형 답변으로 주장 할 것입니다.
young_souvlaki

4
@ArnoldB-여전히 혼란 스럽습니다. 모든 자식의 너비 / 높이가 일정한 경우 hasFixedSize를 true로 설정해야한다고 제안하고 있습니까? 그렇다면 일부 어린이가 런타임에 제거 될 수있는 가능성이 있다면 어떻게해야합니까?
재규어

1
예. 항목의 너비와 높이는 변경되지 않기 때문입니다. 추가 또는 제거 중입니다. 항목을 추가하거나 제거해도 크기는 변경되지 않습니다.
Arnold Balliu 2018 년

3
@ArnoldB 나는 항목의 크기 (너비 / 높이)가 문제가되지 않는다고 생각합니다. 항목의 크기도 확인하지 않습니다. requestLayoutdataSet이 업데이트 된 후 RecyclerView에 호출 여부 를 알려줍니다 .
Kimi Chiu

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)새 항목이 추가 될 때 확장 되도록 제거 해야했습니다.
Mateus Gondim

내 생각 엔 당신이 맞다. 문서 hasFixedSize: set to true if adapter changes cannot affect the size of the RecyclerView.에서 항목의 크기가 변경 되더라도이 값을 true로 설정할 수 있습니다.
Kimi Chiu

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

4
TextView가 어떻게 배치 될 수 있는지와 같이 생각합니다. wrap_content를 지정하면 텍스트를 설정할 때 TextView가 레이아웃 패스를 요청하고 화면에서 차지하는 공간의 양을 변경할 수 있습니다. match_parent 또는 고정 치수를 지정하면 TextView는 크기가 고정되어 있고 텍스트 크기가 차지하는 공간을 절대 변경하지 않기 때문에 레이아웃 패스를 요청하지 않습니다. RecyclerView는 동일합니다. setHasFixedSize ()는 RV에 대한 힌트로, 어댑터 항목에 대한 변경 사항을 기반으로 레이아웃 패스를 요청할 필요가 없습니다.
dangVarmit

1
@dangVarmit 좋은 설명!
howerknea

6

원 총리는 우리는 설정 setHasFixedSize(true)에서 RecyclerView의 재활용 크기가 고정되어 어댑터의 내용에 의해 영향을받지 않는 것을 의미한다. 그리고이 경우 onLayout어댑터의 데이터를 업데이트 할 때 리사이클 러에서 호출되지 않습니다 (그러나 예외가 있습니다).

예를 보자.

RecyclerViewRecyclerViewDataObserver( 이 파일 찾기 기본 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. 이 경우 리사이클 러 onLayoutrequestLayout대한 호출은 없지만 자식 업데이트 에 대한 호출이 있습니다.

우리가 설정하지만 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) 로 설정하십시오 . 어댑터의 데이터를 사용하여 업데이트하는 것을 시도 replaceData하고 replaceOne 최저치를 함께 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

우리가있는 경우 RecyclerViewmatch_parent같은 높이 / 폭 , 우리는 추가해야합니다 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이 제안한 것은 아닙니다.
strangetimes

5
오해의 여지가 있지만 실제로는 내용의 크기가 아니라 Recycler보기 크기입니다.
Benoit

0

이 경우 recyclerview의 애니메이션에 영향을 미칩니다 false. 삽입 및 제거 애니메이션은 표시되지 않습니다. truerecyclerview에 애니메이션을 추가 한 경우에 대비 하십시오 .


0

RecyclerView의 크기 (RecyclerView 자체)

... 어댑터 내용에 의존하지 않습니다 :

mRecyclerView.setHasFixedSize(true);

... 어댑터 내용에 따라 다릅니다.

mRecyclerView.setHasFixedSize(false);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.