RecyclerView set'ini anlamaHasFixedSize


136

Anlamakta güçlük çekiyorum setHasFixedSize(). RecyclerViewDokümanların boyutu değişmediğinde optimizasyon için kullanıldığını biliyorum .

Bu ne anlama geliyor? Çoğu durumda ListViewhemen hemen her zaman sabit bir boyuta sahiptir. Hangi durumlarda sabit bir boyut olmaz? Ekranda işgal ettiği gerçek gayrimenkulün içerikle büyüdüğü anlamına mı geliyor?



Bu cevabın yararlı ve kolay anlaşılır olduğunu buldum [StackOverflow - rv.setHasFixedSize (true); ] ( stackoverflow.com/questions/28827597/… )
Mustafa Hasan

Yanıtlar:


115

Bir çok RecyclerView basitleştirilmiş versiyonu vardır:

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

Bu bağlantı , aramanın neden requestLayoutpahalı olabileceğini açıklar . Temel olarak, öğeler eklendiğinde, taşındığında veya kaldırıldığında RecyclerView boyutu (genişlik ve yükseklik) değişebilir ve buna karşılık görünüm hiyerarşisindeki herhangi bir görünümün boyutu değişebilir. Öğeler sık ​​sık eklenir veya çıkarılırsa bu özellikle zahmetlidir.

setHasFixedSizeBağdaştırıcının içeriğini değiştirirken yüksekliği veya genişliği değiştirmediğinde true olarak ayarlayarak gereksiz düzen geçişlerinden kaçının .


Güncelleme: JavaDoc, yöntemin gerçekte ne yaptığını daha iyi tanımlamak için güncellendi.

RecyclerView'in boyutunun adaptör içeriğinden etkilenmediğini önceden bilebilirse, RecyclerView birkaç optimizasyon yapabilir. RecyclerView, yine de boyutunu diğer faktörlere (ör. Ebeveyninin boyutu) göre değiştirebilir, ancak bu boyut hesaplaması çocuklarının boyutuna veya adaptörünün içeriğine (adaptördeki öğe sayısı hariç) bağlı olamaz.

RecyclerView kullanımınız bu kategoriye giriyorsa, bunu {@code true} olarak ayarlayın. Adaptör içeriği değiştiğinde RecyclerView öğesinin tüm düzeni geçersiz kılmasını önler.

Bağdaştırıcı değişiklikleri RecyclerView boyutunu etkileyemiyorsa @param hasFixedSize true değerine sahiptir.


163
Ne olursa olsun bir şey eklediğinizde RecyclerView boyutu değişir. SetHasFixedSize'ın yaptığı şey, RecyclerView boyutunun bu boyut değişikliğinin sabit kalmasını (kullanıcı girişine göre) sağlamaktır. Öğenin yüksekliği (veya genişliği) değişmez. Eklenen veya kaldırılan her öğe aynı olacaktır. Bunu ayarlamazsanız, öğenin boyutunun değişip değişmediğini ve pahalı olup olmadığını kontrol eder. Açıklığa kavuşturmak çünkü bu cevap kafa karıştırıcı.
Arnold Balliu

9
@ ArnoldB mükemmel açıklama. Hatta bunu bağımsız bir cevap olarak bile söyleyebilirim.
young_souvlaki

4
@ArnoldB - Hala kafam karıştı. Tüm çocukların genişlikleri / yükseklikleri sabitse hasFixedSize değerini true olarak ayarlamamızı mı öneriyorsunuz? Evetse, bazı çocukların çalışma zamanında kaldırılabilme olasılığı varsa (özelliği kapatmak için hızlıca kaydırırım) - doğru ayarlamak doğru mudur?
Jaguar

1
Evet. Çünkü öğenin genişliği ve yüksekliği değişmiyor. Sadece ekleniyor veya kaldırılıyor. Öğe eklemek veya kaldırmak boyutlarını değiştirmez.
Arnold Balliu

3
@ArnoldB Öğenin boyutunun (genişlik / yükseklik) burada bir sorun olduğunu düşünmüyorum. Öğenin boyutunu da kontrol etmez. Sadece requestLayoutDataSet güncellendikten sonra RecyclerView'e çağrılıp çağrılmayacağını söyler .
Kimi Chiu

22

Can onayla setHasFixedSizeRecyclerView kendisi ile ilgilidir ve her öğe boyutunu değil kendisine uyarladı.

Şimdi android:layout_height="wrap_content", RecyclerView boş olduğunda daraltılmaması gerektiğini bilmeniz için bir CollapsingToolbarLayout öğesinin kullanılmasına izin veren bir RecyclerView üzerinde kullanabilirsiniz. Bu yalnızca setHasFixedSize(false)RecylcerView üzerinde kullandığınızda çalışır.

Eğer kullanırsanız setHasFixedSize(true)RecyclerView üzerine, bu davranış çökmesini CollapsingToolbarLayout önlemek için RecyclerView gerçekten boş olsa bile, iş yapmaz.

Eğer setHasFixedSizeöğelerin boyutu ile ilgili RecyclerView hiç ürün olduğunda, herhangi bir etkiye sahip olmamalıdır.


4
Aynı yöne işaret eden bir deneyimim oldu. GridLayoutManager (satır başına 3 öğe) ve layout_height = wrap_content ile bir RecyclerView kullanma. Listeye 3 yeni öğe ekleyen bir düğmeyi tıklattığımda, geri dönüşüm görünümü yeni öğelere sığacak şekilde genişlemiyor. Aksine, aynı boyutu korur ve yeni öğeleri görmenin tek yolu onu kaydırmaktır. Öğeler aynı boyuta sahip olsa da, setHasFixedSize(true)yeni öğeler eklendiğinde genişletilmesi için kaldırmam gerekiyordu.
Mateus Gondim

Bence sen haklısın. Belgeden, hasFixedSize: set to true if adapter changes cannot affect the size of the RecyclerView.Öğenin boyutu değişse bile, bunu hala true olarak ayarlayabilirsiniz.
Kimi Chiu

12

ListView tek tek liste öğesi yüksekliklerinin boyutu hakkında bilgi yansıttığını düşünüyorum benzer bir adlandırılmış işlevi vardı. RecyclerView belgeleri, öğelerinin boyutuna değil, RecyclerView'in boyutuna atıfta bulunduğunu açıkça belirtmektedir.

SetHasFixedSize () yönteminin üzerindeki RecyclerView kaynak yorumundan:

 * 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
Ancak RecyclerView'in "boyutu" nasıl tanımlanır? Yalnızca ekranda görünen boyut veya RecyclerView öğesinin tam boyutu (eşittir öğe yüksekliği + dolgu + boşluk)?
Vicky Chijwani

2
Aslında bunun daha fazla bilgiye ihtiyacı var. Öğeleri kaldırırsanız ve geri dönüşüm görünümü küçülürse, boyutunun değiştiği düşünülüyor mu?
Henrique de Sousa

4
Nasıl bir TextView kendini ortaya koymak gibi düşünürdüm. Wrap_content belirtirseniz, metni ayarladığınızda TextView bir düzen geçişi isteyebilir ve ekranda kapladığı alan miktarını değiştirebilir. Match_parent veya sabit bir boyut belirtirseniz, boyut düzeltildiğinden ve metin insde miktarı hiçbir zaman işgal edilen alan miktarını değiştirmeyeceği için TextView mizanpaj geçişi istemez. RecyclerView aynı. setHasFixedSize (), RV'ye ipucu verir, adaptör öğelerindeki değişikliklere bağlı olarak düzen geçişleri istemesi gerekmez.
dangVarmit

1
@dangVarmit güzel bir açıklama!
howerknea

6

Wen set setHasFixedSize(true)üzerinde RecyclerViewen recycler boyutu sabittir ve adaptör içerikleriyle etkilenmemesini yollarla. Ve bu durumda onLayout(ama bir istisna vardır) biz adaptrer verilerini güncelleme yaptığınızda recycler çağrılmaz.

Örneğe geçelim:

RecyclerViewBir sahiptir RecyclerViewDataObserver( bu dosyadaki bulmak varsayılan implemntation çeşitli yöntemler ile), ana önemlidir:

void triggerUpdateProcessor() {
    if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
        ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
    } else {
        mAdapterUpdateDuringMeasure = true;
        requestLayout();
    }
}

Ayarlayacağımız Bu yöntem denir setHasFixedSize(true)üzerinden bir adaptörünün veri ve güncelleyin: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved. Bu durumda geri dönüştürücüye çağrı yapılmaz onLayout, ancak alt requestLayoutöğelerin güncellenmesi için çağrılar yapılır .

Ayarlayacağımız Ama setHasFixedSize(true)üzerinden bir adaptörünün veri ve güncellemek notifyItemChangedardından çağrı var onChangeRecycler en temerrüt RecyclerViewDataObserverve hiçbir aramalar triggerUpdateProcessor. Bu durumda, geri dönüşüm cihazı onLayouther ayarladığımızda çağrılır setHasFixedSize trueveya 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();
    }
}

Kendiniz nasıl kontrol edersiniz:

Özel oluştur RecyclerViewve geçersiz kıl:

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)
}

Geri dönüşümcü boyutunu match_parent(xml cinsinden) olarak ayarlayın. replaceDataVe replaceOne öğelerini setHasFixedSize(true)ve tuşlarını kullanarak bağdaştırıcı verilerini güncellemeye çalışın 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)
}

Ve günlüğünüzü kontrol edin.

Günlüğüm:

// 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

Özetlemek:

setHasFixedSize(true)Bağdaştırıcının verilerini bir gözlemciyi çağırmaktan başka bir şekilde bilgilendirerek ayarlar ve güncellersek notifyDataSetChanged, o zaman bir performansınız olur, çünkü geri dönüşüm onLayoutyöntemine çağrı yoktur .


Wrap_content veya match_parent kullanarak RecyclerView yükseklik ile test ettiniz mi?
Lubos Mudrak

6

Biz varsa RecyclerViewile match_parentolduğu gibi yükseklik / genişlik , biz eklemek gerekir setHasFixedSize(true)büyüklüğüne beri RecyclerViewyerleştirirken veya içine öğeleri silme değişmez kendisi.

Bağdaştırıcı tarafından eklenen her öğe , eklenen / silinen öğelere bağlı olarak boyutunu değiştirebileceğinden, yükseklik / genişlikwrap_content olarak bir RecyclerView varsa, setHasFixedSize yanlış olmalıdır , bu nedenle, her eklediğimiz / sildiğimizde, öğeler.RecyclerRecycler

Daha açık olmak gerekirse, eğer kullanırsak

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

Kullanabiliriz 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"/>

Biz kullanmalıdır my_recycler_view.setHasFixedSize(false)biz de kullanıyorsanız bu geçerlidir, wrap_contentgenişlik olarak


3

setHasFixedSize (true), RecyclerView öğesinin sabit genişlik ve yüksekliğe sahip alt öğeleri (öğeleri) olduğu anlamına gelir. Bu, RecyclerView'ın adaptörünüze bağlı olarak tüm listenin tam yüksekliğini ve genişliğini anlayarak daha iyi optimize etmesini sağlar.


5
@DangVarmit'in önerdiği şey bu değil.
2016

5
Aslında Recycler görünüm boyutu değil, içeriğin boyutu var, Yanıltıcı
Benoit

0

Geri dönüşüm görünümünün animasyonlarını etkiler, eğer false.. ekleme ve kaldırma animasyonları gösterilmez. bu nedenle truegeri dönüşüm görünümü için animasyon eklemenizden emin olun .


0

RecyclerView boyutu (RecyclerView'in kendisi)

... adaptör içeriğine bağlı değildir:

mRecyclerView.setHasFixedSize(true);

... adaptör içeriğine bağlıdır:

mRecyclerView.setHasFixedSize(false);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.