RecyclerView: Tutarsızlık tespit edildi. Geçersiz ürün konumu


271

KG'miz bir hata tespit etti: Android cihazı (Droid Turbo) döndürürken, aşağıdaki RecyclerView ile ilgili çökme oldu:

java.lang.IndexOutOfBoundsException: Tutarsızlık algılandı. Geçersiz öğe konumu 2 (ofset: 2). Durum: 3

Bana göre, bu doğrudan bizim kod neden olduğu düşünemiyorum gibi RecyclerView içinde bir iç hata gibi görünüyor ...

Kimse bu problemle karşılaştı mı?

Çözüm ne olurdu?

Acımasız bir çözüm, bozuk bir durumdan ayrılmamak için, istisnayı gerçekleştiğinde yakalamak ve RecyclverView örneğini sıfırdan yeniden oluşturmak olabilir.

Ancak, mümkünse, sorunu maskelemek yerine daha iyi anlamak (ve belki de kaynağında düzeltmek) istiyorum.

Hatanın yeniden üretilmesi kolay değildir, ancak gerçekleştiğinde ölümcül olur.

Tam yığın izi:

W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
    E/AndroidRuntime( 7546): FATAL EXCEPTION: main
    E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
    E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
    E/AndroidRuntime( 7546):    at andro

2
Bir soru: Repro ne kadar tutarlı? Bunun burada ve burada google'ın kodunda bir hata olduğunu biliyorum . Ancak bu önlenebilir. Peki, bu her dönüşte mi oluyor?
VicVu

Selam. Sadece nadiren olur, ancak gerçekleştiğinde uygulama için ölümcüldür.
KarolDepka

Hata bağlantıları için teşekkürler. Birincisi ikincisinden daha alakalı görünüyor.
KarolDepka

1
Evet, en iyi seçeneğinizin rotasyon sırasında liste görünümünde değişiklik yapılmasına izin vermemek olduğunu düşünüyorum.
VicVu

1
Kolayca çoğaltabilseydiniz, 'bildirmek *' için yapılan tüm çağrılardan önce 'getItemCount' değerini yazdırmanızı öneririm ... öğe sayınızın varsayımlarınızla eşleşmediğini keşfedebilirsiniz.
Zengin Ehmer

Yanıtlar:


209

(Muhtemelen) ilgili bir sorun vardı - RecyclerView ile bir etkinliğin yeni bir örneğini girme, ancak daha küçük bir bağdaştırıcı ile bu çökme benim için tetikliyordu.

RecyclerView.dispatchLayout()aramadan önce öğeleri hurdadan çekmeyi deneyebilir mRecycler.clearOldPositions(). Sonuç olarak, bağdaştırıcı boyutundan daha yüksek konumlara sahip ortak havuzdan öğeler çekiyordu.

Neyse ki, bunu yalnızca PredictiveAnimationsetkinleştirilirse yapar, bu yüzden benim çözümüm alt sınıfa GridLayoutManager( LinearLayoutManageraynı sorunu ve 'düzelt' ') ve supportsPredictiveItemAnimations()yanlış döndürmek için geçersiz kılmak oldu:

/**
 * No Predictive Animations GridLayoutManager
 */
private static class NpaGridLayoutManager extends GridLayoutManager {
    /**
     * Disable predictive animations. There is a bug in RecyclerView which causes views that
     * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
     * adapter size has decreased since the ViewHolder was recycled.
     */
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public NpaGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }
}

4
Bu benim için çalıştı ve öngörülü animasyonları devre dışı bırakmak, animasyonları hep birlikte kaybetmenize neden olmaz. Bravo.
Robert Liberatore

8
Çok teşekkür ederim efendim! Muhtemelen bana gün kazandırır LinearLayoutManager ile anında çalıştı.
levavare

8
Çok teşekkürler. Bu çözüm LinearLayoutManager ile birlikte çalışmaktadır.
Pruthviraj

8
Sanırım bu adam onun değerli yardımının onuruna bir heykel inşa etmeyi hak ediyor ... Web'deki en kötü belgelenmiş sorunlardan biri, ama bu konuyla karşılaşan bir sürü şey gibi görünüyor ... Sadece bulabildiğini nasıl buldun acaba TahminAnimasyonları yanlışsa, @KasHunt? Çünkü, yığın izleme çok belirsiz ...
PAD

4
Kimse biliyor, bu kesmek olmadan bunu nasıl düzeltebilirim? Çünkü notifyDatasetChanged DiffUtil lehine düştü
Anton Shkurenko

83

Benim durumumda (veri yapımdaki verileri sil / ekle) Geri dönüşüm havuzunu temizlemem ve ardından veri kümesinin değiştiğini bildirmem gerekiyordu!

mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();


6
Normalde bunu söylemiyorum, ANCAK ÇOK TEŞEKKÜR EDERİZ. Listede bir grup öğeyi hızlı sırayla hareket ettirdiğimde oluşan bu çökmeyi düzeltmek için HER ŞEY'i denedim. Bu sorunu çözmeye çalışırken tam anlamıyla bir hafta geçirdim. Beynime farklı yaklaşma şansı vermek için birkaç ay uzaklaştım, sonra ilk Google girişimimde buldum. Seni korusun!
Chantell Osejo

Sana bir ton kurabiye getireyim çünkü her birini hak ediyorsun. Teşekkür ederim.
antonis_st

neden bunu yapmak zorundasın?
17'de dabluck

9
Bu, geri dönüşüm görünümlerinin amacını yenen oldukça ağır bir işlemdir.
gjsalot

@gjsalot Bunu kullanırsam bazı sorunlara neden olabilir mi?
Sreekanth Karumanaghat

38

notifyDataSetChanged()Bunun yerine notifyItem...bu durumda kullanın .


5
Bazı durumlarda gidilecek yol budur. Tüm eşyalarımı değiştirdiğim bir durum vardı, ancak adaptörle dürüst değildim, sadece bazı öğeleri (notifyItemRangeInserted) eklediğimi söylüyorum, ilk önce ben de öğeleri kaldırmıştı. Adaptör daha sonra aslında daha fazla ürün olmasını bekliyordu. Bağdaştırıcının bildirim yöntemlerinden herhangi birini kullanmak, notifyItemRangeRemoved / Inserted / Updated gibi notifyDataSetChanged'i beklerse, arayan, bağdaştırıcının tam olarak neyin değiştiğini söylemenin tüm sorumluluğunu alır veya bu "tutarsız durum" ile sonuçlanabilir.
JHH

19
Bu hiç de bir çözüm değil.
Miha_x64

Bu yol değil. Bu işe yararsa, notifyItem...tüm öğelerin yeniden oluşturulması yerine çalışmaya başlayacak olan aralığın düzeltilmesi ve düzeltilmesi anlamına gelir .
Ranjan

12

Adaptöre mRecycler.setAdapter(itemsAdapter)tüm öğeleri ekledikten sonra kadar geciktirerek mRecycler.addAll(items)bunu çözdüm ve çalıştı. Neden başlamak için hiçbir fikrim yok, ben baktı ve "yanlış sırada" bu satırları gördüm bir kütüphane kodundan oldu, eminim bu olsa, eminim eğer birisi neden açıklamak onaylamak eğer yani? Bunun geçerli bir cevap olup olmadığından emin değilim


Ben bu çözüm olduğunu düşünüyorum, bir kez i adaptör gecikti inanıyorum iyiydi ... şimdi UI iş parçacığı içinde adaptör ayarlandığında ve ona ürün ekleyerek açılır.
EngineSense

18
Bunun swapAdapter(adapter, true)yerine kullandım setAdapter(adapter)ve yardımcı oldu.
frangulyan

11

Benzer bir sorunum vardı ama tam olarak aynı değil. Benim durumumda 1 noktada recyclerview geçirilen dizi temizliyordum

mObjects.clear();

ve recyclerview görünümlerini hemen temizlemek istemiyordu gibi notifyDataSetChanged çağırmıyor. AsyncTask mObjects dizi yeniden dolduruyordu.


9

RecyclerView ile aynı sorunu vardı Bu yüzden sadece liste temizlendikten hemen sonra veri kümesi değişikliği hakkında adaptör haberdar.

mList.clear();
mAdapter.notifyDataSetChanged();

mList.addAll(newData);
mAdapter.notifyDataSetChanged();

1
Bu basit hata beni çok fazla zaman kaybettirdi, çok teşekkür ederim!
leb1755

7

Ben aynı sorunu var. Ben hızlı kaydırma ve API çağırma ve veri güncelleme sırasında meydana geldi. Çökmesini önlemek için her şeyi denedikten sonra, çözüm bulundu.

mRecyclerView.stopScroll();

Çalışacak.


Bu bir çözüm değil bir çözümdür. Kaydırma işlemini durdurmaya zorluyorsunuz. Kötü UX
Dr. aNdRO

1
@ Dr.aNdRO: Bağdaştırıcının konumu ayarlaması gerekiyor ve recylerview kaydırmaya devam ederseniz bağdaştırıcı çökmenin nedeni olan verileri ayarlayamıyor. Fena değil UX
Anand Savjani

1
mantıklı olmak. Veri yenileme gerçekleştiği için kaydırmayı durdurmak kötü bir ux değildir.
Sush

6

RecyclerViewArka planda için veri değiştiriyorum Thread. ExceptionOP ile aynı şeyi aldım . Verileri değiştirdikten sonra bunu ekledim:

myRecyclerView.post(new Runnable() {
    @Override
    public void run() {
        myRecyclerAdapter.notifyDataSetChanged();
    }
});

Umarım yardımcı olur


teşekkürler dostum! Android geliştirme açısından mantıklı olan tek cevap budur.
user347187

Ben de yardımıyla çözdüm view.recycler_view.post, kullandım notifyItemInserted. Benim durumumda zaten UI iş parçacığı oldu.
CoolMind

6

Bu hata, kullanıcı kaydı sırasında öğe tutucunun konumunu değiştiren, liste ve kullanıcı arabirimindeki öğe arasında ref kaybına neden olan bağdaştırıcıdaki liste temizlendiğinde, bir sonraki "notifyDataSetChanged" öğesinde hata oluştuğunda oluşur. isteğinde .

Düzeltme:

Güncelleme listesi yönteminizi gözden geçirin. Eğer böyle bir şey yaparsan

mainList.clear();
...
mainList.add() or mainList.addAll()
...
notifyDataSetChanged();

===> Error occur

Nasıl düzeltilir. Tampon işleme için yeni liste nesnesi oluşturun ve bundan sonra ana listeye yeniden atayın

List res = new ArrayList();
…..
res.add();  //add item or modify list
….
mainList = res;
notifyDataSetChanged();

Bu büyük yardım için Nhan Cao'ya teşekkürler :)


4

AdapterBir başvuru yerine items dizisinin bir kopyasını kullanmak için uygulamamı değiştirdikten sonra sorunum gitti . Bu setItems()yöntem, her yeni öğe gösterildiğindeRecyclerView .

Onun yerine:

private class MyAdapter extends RecyclerView.Adapter<ItemHolder> {
     private List<MyItem> mItems;  

    (....)

    void setItems(List<MyItem> items) {
        mItems = items;
    }
}

Yaptım:

void setItems(List<MyItem> items) {
    mItems = new ArrayList<>(items);
}

Bu sorunu çözecektir, ancak orijinal hafızanın iki katını almayacak mı?
Sreekanth Karumanaghat

@ MiguelA.Gabriel bu performansı etkileyecek mi? Örneğin benim durumumda ben şu anda bunu yapıyorum recylerview'ın dizi çok sık güncelleme suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true); ve bu benim yapıcı public CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Mateen Chaudhry

@ mateen-chaudhry Muhtemelen olacak. Durumunuzda test etmeniz ve karar vermeniz veya önerilen çözümlerden birini kullanmayı denemeniz gerekir. Dediğim gibi, bu sadece bir çözüm ve benim durumumda benim için çalışıyor.
Miguel A. Gabriel

3

Aynı durumla karşılaştım. Ve koleksiyonunuzu temizlemeden önce kodlar ekleyerek çözüldü.

mRecyclerView.getRecycledViewPool().clear();


3

Benim durumumda, öğeleri güncelliyorum ve notifyDataSetChangedbir UI olmayan iş parçacığında çağırıyordum . Çoğu zaman işe yaradı, ancak çok sayıda değişiklik hızlı bir şekilde gerçekleştiğinde çöküyordu. Bunun yerine, temelde

activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        changeData();
        notifyDataSetChanged();
    }
});

sonra çökmeyi durdurdu.


3

OnPostExecute()Yaparken değil, yalnızca listenizi temizlemeniz gerekirPull to Refresh

// Setup refresh listener which triggers new data loading
        swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {

                AsyncTask<String,Void,String> task = new get_listings();
                task.execute(); // clear listing inside onPostExecute

            }
        });

Yenilemek için bir çekme sırasında kaydırdığınızda bunun olduğunu keşfettim , çünkü listeyi daha önce temizliyorum async task. java.lang.IndexOutOfBoundsException: Inconsistency detected.

        swipeContainer.setRefreshing(false);
        //TODO : This is very crucial , You need to clear before populating new items 
        listings.clear();

Bu şekilde tutarsızlıkla bitmeyeceksiniz


2

Adaptörün aynı anda birden çok kez ayarlanmasıyla da ilgili olabilir. Aynı anda 5-6 kez tetiklenen bir geri arama yöntemi vardı ve RecycledViewPool tüm bu verilerle eşzamanlı olarak ele olamaz böylece bu geri arama bağdaştırıcı ayarlamak oldu. Şişman bir şans ama yine de kontrol etsen iyi olur.


1
evet aynı sorun .. Ama çözüm? Sadece sebebini veriyorsun .. Nasıl düzeltilir?
Ranjith Kumar

@RanjithKumar, Lütfen yukarıdaki sorunu çözmek için yolunuzu paylaşın. MRecyclerView.getRecycledViewPool (). Clear (); DataSetChanged ve adaptörün güncelleme fonksiyonu etrafında senkronize blok kullanma
Attiq ur Rehman

Lütfen benim koduma bir göz atabilir miyim benim sorunum senin gibi sanki bana yardım edebilir misin stackoverflow.com/questions/50213362/…
Mateen Chaudhry

2

kullanım

notifyDataSetChanged()

yerine

notifyItemRangeInserted(0, YourArrayList.size())

bu durumda.


1
ama bu performans için iyi değil mi? notifyItemRangeInserted daha iyi, sorun burada yatıyor
Derekyy

2

Bu sorunu gidermek için geri dönüşüm görünümünü güncellemeden önce boş liste ile notifyDataSetChanged () öğesini çağırmanız yeterlidir.

Örneğin

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear (); // Güncelleme geri dönüşüm görünümü listesi

adapter.notifyDataSetChanged();

2
Bir çözüm değil.
Miha_x64

@Milha Kilitlenme sorununu çözmek için başka bir çözüm bulamadım. Ancak yukarıdaki çözüm benim için çalıştı. Bir çözüm değilse bana doğru düzeltmeyi söyle.
EKN

Değişir. RecyclerView içeriğini güncellemek için genel amaçlı bir araç olan DiffUtil'i kullanmayı deneyebilirsiniz.
Miha_x64

2

Benim durumumda setHasStableIds(true);


Ancak HasStableIds (true) Rv'nin performansını artırır, alternatif bir çözüm var mı?
Sreekanth Karumanaghat

Aslında bunun farklı nedenlerden kaynaklanabileceğini düşünüyorum, bu yüzden bu sorunun temel nedenine bağlı olarak farklı çözümler olabilir.
Sreekanth Karumanaghat

2

Benim durumumda bir arka plan iş parçacığı benim bağdaştırıcı içeriğini değiştirmek için çalışıyordu ama ana / ui iş parçacığında notify * çağırdı.

Bu imkansız! Bildirimin ana iş parçacığına zorlanmasının nedeni, geri dönüşüm görünümünün aynı bağdaştırıcıda bile ana iş parçacığında destek bağdaştırıcınızı düzenlemenizi istemesidir.

Sorunu çözmek için, adaptörünüzün her işleminin yanı sıra her bir bildirim ... aramasının UI / ana iş parçacığında yapıldığından emin olun !


2
bağdaştırıcınızdaki listeye öğe eklemek bir arka plan iş parçacığı üzerinde yapılmalıdır ve postexecute çağrı bildirimi yapılmalıdır. UI iş parçacığına veri eklemek, birçok veri
eklerseniz

@dionellorera ile anlaştıysanız, "adaptör içeriğindeki değişiklik" in, ilkel değerler, nesnelerin özellikleri veya nesnelerin kendileri gibi doğrudan herhangi bir veriyi değiştirmek anlamına geldiği açıkça belirtilmelidir
OzzyTheGiant

2

Son zamanlarda yeni Android Mimari Bileşenleri ile bu kötü yığın izine rastladım. Temelde, ViewModel'de LiveData kullanarak Parçam tarafından gözlemlenen öğelerin bir listesi var. ViewModel veriler için yeni bir değer gönderdiğinde, Parça bu bağdaştırıcıyı günceller, bu yeni veri öğelerini iletir ve bağdaştırıcıda değişiklik olduğunu bildirir.

Ne yazık ki, yeni veri öğelerini bağdaştırıcıya iletirken, hem ViewModel hem de Bağdaştırıcının aynı nesne başvurusunu işaret edeceğini açıklayamadım! Verileri güncellersem ve aradığımdapostValue() ViewModel içinden ararsam, verilerin güncellenebileceği ve adaptörün henüz bildirilmediği çok küçük bir pencere vardır!

Benim düzeltme, bağdaştırıcıya geçirildiğinde öğelerin yeni bir kopyasını örneklemekti:

mList = new ArrayList<>(passedList);

Bu süper kolay düzeltmeyle, adaptör verilerinizin adaptörünüz bildirilmeden hemen öncesine kadar değişmeyeceğinden emin olabilirsiniz.


2

Bu sadece benim için işe yarayan bir çözümdür.

1.) Giriş

CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);

2.) Bu yöntemi adaptöre yazın

public void updateList(List<StockListModel> list) {
stockListModels.clear();
stockListModels.addAll(list);
notifyDataSetChanged();
}

stockListModels -> bu liste adaptörde kullandığınız listedir.


2

Benim için bu kod satırını ekledikten sonra çalıştı:

mRecyclerView.setItemAnimator(null);

2
Bu çoğu durumda bir düzeltme değildir, animasyon istiyorsanız adaptör kodunuzu yeniden yazmanız ve değişiklikleri
bildirirken

Gerçek adaptör kullanıyorum, bu yüzden akışı kontrol edemiyorum ve windowActivityTransitions'ı bu soruna neden olan bir şeyle etkinleştirdim.
Arul Mani

1

listenizi temizlemeye çalıştığınızda bu sorun oluşabilir, özellikle de yenilemek için pull komutunu kullandığınızda veri listenizi temizleyecekseniz, bir boole bayrağı kullanmayı deneyin, false olarak başlatın ve OnRefresh yöntemi içinde doğru yapın, verilerinizi temizleyin bayrak yeni veriyi eklemeden hemen önce doğruysa ve bundan sonra yanlış yapar.

kodun böyle olabilir

 private boolean pullToRefreshFlag = false ;
 private ArrayList<your object> dataList ;
 private Adapter adapter ;

 public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{

 private void requestUpdateList() {

     if (pullToRefresh) {
        dataList.clear
        pullToRefreshFlag = false;
     }

     dataList.addAll(your data);
     adapter.notifyDataSetChanged;


 @Override
 OnRefresh() {
 PullToRefreshFlag = true
 reqUpdateList() ; 
 }

}

1

Daha önce de aynı sorunu yaşadım. Sonunda bunun için bir geçici çözüm buldu

Yaptığım şey, bağdaştırıcının öğenin kaldırıldığını bildirmek ve ardından bağdaştırıcı veri kümesi aralığının değiştiğini bildirmek

 public void setData(List<Data> dataList) {
      if (this.dataList.size() > 0) {
          notifyItemRangeRemoved(0, dataList.size());
          this.dataList.clear();
      }
      this.dataList.addAll(dataList)
      notifyItemRangeChanged(0, dataList.size());

 }

1

Benzer bir sorunla karşılaştım ve anladım. Bir test senaryosu için birkaç örneği kodladım, ancak her birinin benzersiz bir kimlik döndürdüğünden ve benim için aşağıdaki çökmeye neden olduğundan emin değildim. Kimliklerin düzeltilmesi sorunu çözdü, umarım bu başka birine yardımcı olur!


1

bir kez de hatayı aldım:

Neden: Aynı anda eski silinmiş viewHolders almaya çalışırken bir Async görevinden bir Recycler View güncellemeye çalışıyordum;

Kod: Bir düğmeye basarak veri üretiyorum, aşağıdaki gibi mantık

  1. Geri dönüşüm görünümündeki son öğeleri temizleme
  2. Veri oluşturmak için zaman uyumsuz görevi çağır
  3. OnPostExecute Recycler görünümünü güncelleyin ve NotifyDataSetChanged

Sorun: Verilerimi oluşturmadan önce hızlı kaydırdığımda

Tutarsızlık tespit edildi. Geçersiz görünüm tutucu bağdaştırıcısı konumu ViewHolder java.lang.IndexOutOfBoundsException: Tutarsızlık algılandı. Geçersiz öğe konumu 20 (ofset: 2). Durum: 3

Çözüm: Verilerimi oluşturmadan önce RecyclerView'i temizlemek yerine, onu bırakın ve aşağıda gösterildiği gibi Yeni Verilerle Değiştirin, NotifyDatasetChanged'i değiştirin;

       @Override
        protected void onPostExecute(List<Objects> o) {
            super.onPostExecute(o);
            recyclerViewAdapter.setList(o);
            mProgressBar.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
        }

benim kod bir göz atın lütfen benim sorunum senin gibi olduğunu düşünüyorum [link] ( stackoverflow.com/questions/50213362/… )
Mateen Chaudhry

1

Bildirmeden önce düzen Yöneticinizin tüm görünümlerini kaldırmanız yeterlidir. sevmek:

myLayoutmanager.removeAllViews();

İşe yarıyor. Kaydırma yükü ve sekme değişikliği ile ilgili sorun yaşadım.
Warwicky

1

ListAdapter (androidx.recyclerview.widget.ListAdapter)Aramadan adapter.submitList(null)önce aramayı kullanma adapter.submitList(list):

adapter.submitList(null)
adapter.submitList(someDataList)

1

Bu istisna API 19, 21'de ortaya çıktı (ancak yeni değil). Kotlin coroutine'de veri yükledim (arka plan iş parçacığında) ve UI iş parçacığında ekledi ve gösterdi:

adapter.addItem(item)

Adaptör:

var list: MutableList<Item> = mutableListOf()

init {
    this.setHasStableIds(true)
}

open fun addItem(item: Item) {
    list.add(item)
    notifyItemInserted(list.lastIndex)
}

Herhangi bir nedenle Android yeterince hızlı işlemiyor veya başka bir şey, bu nedenle, bir öğeyi (öğelerin olaylarını ekleme, kaldırma, güncelleme) postyönteminde RecyclerViewgüncelliyorum:

view.recycler_view.post { adapter.addItem(item) }

Bu kural dışı durum, "Bu yöntem bir kaydırma geri çağrısında çağrılamaz. Bir ölçüm ve düzen geçişi sırasında, Geri Dönüşüm Görünüm verilerini değiştiremeyeceğiniz kaydırma geri çağrıları çalıştırılabilir. RecyclerView veya bağdaştırıcı içeriğinin yapısını değiştirebilecek herhangi bir yöntem çağrısı ertelenmelidir. sonraki kare. ": Recyclerview - kaydırma geri çağrısında bu yöntemi çağıramaz .


0

MRecycler.setLayoutFrozen (true) ayarını buldum; swipeContainer öğesinin onRefresh yönteminde.

sorunu benim için çözdü.

swipeContainer.setOnRefreshListener(new   SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            orderlistRecycler.setLayoutFrozen(true);
            loadData(false);

        }
    });

0

Bu oldukça kötü bir böcek.

Ürün tıklamamı işlemek RecyclerView.OnItemTouchListeneriçin, bu soruda bulunan çözüme benzer bir uygulama kullandım .

RecyclerView'S veri kaynağını yenilemek ve bir öğeyi tıklattıktan sonra, bu IndexOutOfBoundsExceptionbenim uygulama çökmesine neden olacaktır. Bir öğeye tıklandığında, RecyclerViewdahili olarak doğru temel görünümü arar ve öğenin konumunu geri verir. Kaynak kodunu kontrol ederek, bazılarının olduğunu gördüm TasksveThreads planlanmış . Hikayeyi kısaltmak için, temelde iki veri kaynağının birbirine karıştırıldığı ve senkronize olmadığı ve her şeyin çıldırdığı yasadışı bir durum.

Buna dayanarak, ben benim uygulanmasını kaldırıldı RecyclerView.OnItemTouchListenerve sadece üzerinde tıklama yakalandı ViewHolderarasında Adapterkendim:

public void onBindViewHolder (final BaseContentView holder, final int position) {

    holder.itemView.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick (View view) {

        // do whatever you like here
      }
    });

}

Bu en iyi çözüm olmayabilir, ancak şimdilik çökmesiz .. Umarım bu size biraz zaman kazandıracaktır :).


OnBind her çağrıldığında yeni bir nesne oluşturmak, çöp toplanacak çok sayıda nesneye neden olur ve kullanıcı donma yaşayabilir.
dephinera
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.