RecyclerView, "hurdaya ayrılan veya eklenen görünümler geri dönüştürülemeyebilir" olduğunda çöküyor


115

A kullanarak RecyclerViewAndroid web sitesinden alınan basit bir uygulama kullanıyorum StaggeredGridLayoutManagerve uygulamamın çökmesine neden olan şu hatayı almaya devam ediyorum:

java.lang.IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:false isAttached:true
            at android.support.v7.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:3501)
            at android.support.v7.widget.RecyclerView$LayoutManager.scrapOrRecycleView(RecyclerView.java:5355)
            at android.support.v7.widget.RecyclerView$LayoutManager.detachAndScrapAttachedViews(RecyclerView.java:5340)
            at android.support.v7.widget.StaggeredGridLayoutManager.onLayoutChildren(StaggeredGridLayoutManager.java:572)
            at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1918)
            at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2155)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1021)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.support.v7.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:502)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1663)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1521)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:14008)
            at android.view.ViewGroup.layout(ViewGroup.java:4373)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1892)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1711)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:989)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4351)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
            at android.view.Choreographer.doCallbacks(Choreographer.java:562)
            at android.view.Choreographer.doFrame(Choreographer.java:532)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
            at android.os.Handler.handleCallback(Handler.java:725)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5041)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
            at dalvik.system.NativeStart.main(Native Method)  

Basit olarak, kelimenin tam anlamıyla , web sitelerinde bu sayfadan alınan aynı uygulama olduğunu kastediyorum , tek fark, ızgara öğemin düzeninin bir ImageViewve birkaç TextViews olmasıdır, bu yüzden kodumu yeniden yayınlamakla uğraşmayacağım.

Bu hatayı alan ve bununla nasıl başa çıkılacağını bilen başka biri var mı?


Herhangi bir çözümün var mı?
Pratik Butani

Yanıtlar:


191

Bu hata, XML'inizde android:animateLayoutChangestrue olarak ayarladıysanız notifyDataSetChanged()ve Java kodunda RecyclerView adaptörünü çağırırsanız oluşur .

Bu nedenle, android:animateLayoutChangesRecyclerViews ile kullanmaktan kaçının .


22
o halde animateLayoutChanges özelliği geri dönüşüm görünümünde nasıl kullanılabilir?
dhuma1981

Ne elde etmeye çalışıyorsun Öğe animasyonu? Öyleyse, RecyclerView API bunu destekler - belgelere bir göz atın: developer.android.com/reference/android/support/v7/widget/…
Kenneth

4
@ dhuma1981 öğe animatörü mRecyclerView.setItemAnimator (yeni DefaultItemAnimator ()) aracılığıyla ayarlanmışsa; sonra animateLayoutChanges doğru olmak zorunda değil
Rich Ehmer

RecyclerViewDefaultItemAnimatorvarsayılan olarak kullanır .
Benjamin

-, - tam olarak nasıl tarif ettiğiniz gibi bir problemim var
Ninja Coding

52

Bu kazayla da uğraşmak zorunda kaldım ve benim durumumda bununla hiçbir ilgisi yoktu android:animateLayoutChanges.

İnşa RecyclerViewettiğimizde birden fazla türde görüş vardı ve bazılarının içinde görüşler vardı EditText. Bir süre sonra konuyu odakla bağlantılı hale getirdik. Bu hata, geri dönüşüm sırasında meydana gelir EditTextve bunlardan biri odaklanır.

Doğal olarak, yeni veriler geri dönüştürülmüş bir görünüme bağlanırken odağı temizlemeye çalıştık, ancak bu android:focusableInTouchMode="true"ayarlanana kadar işe yaramadı RecycleView. Aslında bu sorunun ortadan kalkması için sonunda ihtiyaç duyulan tek değişiklik buydu.


2
Bir RecyclerView'da EditTexts'i kullanırken karşılaştığım harika, birden çok odakla ilgili sorunları çözdüm. Teşekkürler!
Rabie Jradi

1
ACET'i geri dönüşüm görünümünde tuttum ve eziliyor. Bu gönderi beni kurtardı.
Kai Wang

Ve öğelerde edittexlerim yok, yine de onay kutularım var. denemeliyim android:focusableInTouchMode="true"çünkü sadece bazen bazı cihazlarda oluyor (nadiren) ve bunun benim sorunumla ilgili olmadığını tahmin ediyorum, ancak çökme için yığın izleme neredeyse aynı.
Shivansh

Bu benim durumumdu, ancak ayar android:focusableInTouchMode="true"bana hiç yardımcı olmadı. Ben de onViewDetachedFromWindowgeri aramaya odaklandım . public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { if (holder instanceof ItemViewHolder) { ItemViewHolder item = (ItemViewHolder) holder; if (item.editText.isFocused()) item.editText.clearFocus(); } }
artman

24

android:animateLayoutChangesDüzen özelliğini kaldırdım ve sorun çözüldü.


Karavanımı da yerleştirdiğimde bu kazayı yaşamaya başladım android:animateLayoutChanges.
Mauker

2
Bu bayrak üst kapsayıcıda (Göreli Düzen) ayarlanmıştı. Sorun düzeltildi.
1911z

@ 1911z, ana kapsayıcıda bayrağın olduğunu ve buradan kaldırmanın sorunu çözdüğünü mü söylüyorsunuz?
RamPrasadBismil

14

Herkesin bu sorunla karşılaşmasının nedenleri arasında, özniteliği android:animateLayoutChanges="true"RecyclerView olarak ayarlayıp ayarlamadığınızı kontrol edin . Bu, RecyclerView öğelerinin geri dönüşümünün ve yeniden takılmasının başarısız olmasına neden olacaktır. Bunu kaldırın ve özniteliği bir LinearLayout / RelativeLayout gibi RecyclerView'ın üst konteynerine atayın ve sorunun ortadan kalktığını görmelisiniz.


Bu çökmeyi, RV'nin ana kabındaki özniteliği ayarladığım zaman bile gördüm.
RamPrasadBismil

@RamPrasadBismil Lütfen kodunuzu gönderin ve belki bakabiliriz?
Ram Iyer

12

İki günümü aldı ama bunun üstesinden gelemedim, sonunda öğe ön yüklemesini devre dışı bırakmak zorunda kaldım.

Yerleşim yöneticisini ayarlarken,

mGridLayoutManager.setItemPrefetchEnabled(false);

Hatayı benim için ortadan kaldırdı. Umarım birisi için faydalı olur.


Benim için çalıştı. Teşekkürler.
Vicky

1
İnsanların bu çözümü benimsemesi konusunda gerçekten endişeleniyorum. Bu bayrağı devre dışı bırakarak çok şey kaybettiniz ve hata hala başka bir yerde -
Felipe Castilhos

8

Slimfit yapışkan başlıkları kullanırken bu hatayla karşılaştım. İlk konumun yanlış ayarlanmasından kaynaklanıyordu. Cevabı burada aldım

public void onBindViewHolder(MainViewHolder holder, int position) {

  final View itemView = holder.itemView;
  final LayoutManager.LayoutParams params = LayoutManager.LayoutParams.from(itemView.getLayoutParams());

  params.setSlm(LinearSLM.ID);
  params.width = ViewGroup.LayoutParams.MATCH_PARENT;
  params.setFirstPosition(item.mSectionFirstPosition);
  itemView.setLayoutParams(params);

}

mSectionFirstPosition için doğru değeri ilettiğinizden emin olun


StackOverflow'a hoş geldiniz. Lütfen sadece bir bağlantı yerine tam bir cevap verebilir misiniz?
slfan

itemburada ne var
Hatalar

Geri dönüşümcü görünümünde gösterilecek olan liste öğesidir. Yani temelde bölümün ilk konumunu her liste öğesine karşı saklıyorum.
Jaspinder Kaur

8

Bu konuyla bu sabah tanıştım, ancak yukarıda belirtilenle aynı sebeple karşılaşmıyorum.

Hata ayıklama yoluyla ViewHolder'ımdaki öğe görünümünün sahip olduğunu mParentve boş olmadığını buldum , ki bu normal durumda hiç olmamalıdır (günlük "ekli görünüm geri dönüştürülemeyebilir" diyor, bence bu, çocuk Zaten bir ebeveyne bağlıysa, geri dönüşüm sırasında bir miktar başarısızlığa neden olur.)

Ancak çocuk görüntüsünü her seferinde manuel olarak eklemedim. ViewHolder'ımdaki çocuk görünümünü şişirmeye çalıştığımda bunun bittiğini buldum, örneğin:

layoutInflater.inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

Ve son parametre attachToRootyanlış olmalıdır.

Olarak değiştirdikten sonra falsesorunumu çözdüm.

Bu arada, bu çökmenin yalnızca destek kitaplığımı en son sürüm 25.0.0'a yükselttiğimde olduğunu görüyorum. 23.4.0 sürümünü kullanmadan önce ve bu sorunun olduğunu görmüyorum. Sanırım en son destek kitaplığında bir şeyler değişmeli.

Umarım bu yardımcı olur.


8

Ayrıca kaydırırken de aynı hatayla karşılaştım RecyclerView: sonra animateLayoutChanges="true"düzen dosyasından kaldırdım , RecyclerViewsonra her şey çalıştı.


6

Benim durumumda oldu çünkü bir Transition , RecyclerView'ı yeniden boyutlandırmaya çalışırken koşu yazılım klavyesi görünmek üzereydi.

TransitionKullanarak RecyclerView'ı hariç tutmamı düzelttimTransition.excludeTarget(R.id.recyclerview, true);


6

Bu istisnanın çağrılmasının birkaç nedeni vardır. Benim durumumda, çalışan animasyonlardan kaynaklanıyordu, bu yüzden görünümler hala ekli ve görünüme kaldırılamadı. Yalnızca animasyon bittiğinde görünüm kaldırılabilir ve geri dönüştürülebilir.

Geri dönüşüm görünümü geri dönüşümünü etkileyebilecek iki tür animasyon vardır.

1) RecyclerView.ItemAnimator - sorun bu olmamalı. Bu, eklenti ve hurdaya çıkarılan görünümleri kontrol ettiği ve geri dönüşümü doğru bir şekilde gerçekleştirdiği için kullanımı oldukça güvenli olmalıdır.

2) android:animateLayoutChanges="true"veya TransitionManager.beginDelayedTransition()veya TransitionManager.go (), vb. - Bu animasyonlar kendi başına çalışır ve canlandırmak için öğeleri tutar. Bu, görünümlerin animasyon bitene kadar eklenmeye zorlanmasına neden olur. Geri dönüşümcü görünümü, kapsamı dışında olduğu için bu animasyonlarla ilgili herhangi bir bilgiye sahip değildir. Bu nedenle, recyclerviewdoğru bir şekilde geri dönüştürülebileceğini düşünerek bir öğeyi geri dönüştürmeye çalışabilir, ancak sorun, bu API'lerin animasyon bitene kadar görünümleri tutmaya devam etmeleridir.

android:animateLayoutChanges="true"Veya TransitionManager.beginDelayedTransition()veya TransitionManager.go (), vb. Kullanıyorsanız , basitçe RecyclerViewve alt öğelerini animasyondan kaldırın .

Bunu elinize alarak Transitionve arayarak yapabilirsiniz.

Transition.excludeChildren(yourRecyclerView, true)
Transition.excludeTarget(yourRecyclerView, true)

Not:

Yalnızca kendisini değil, Transition.excludeChildren()tüm Recyclerviewçocukları animasyondan hariç tutmanın önemli olduğunu unutmayın Recyclerview.


Teşekkürler! Benim durumumdaki sorunun nedeni TransitionManager.beginDelayedTransition () idi. Kod örneğinizi nasıl kullanılacağına dair daha fazla ayrıntıyla güncelleyebilirsiniz Transition.excludeChildren. Şunun gibi bir geçiş nesnesi başlatırsınız:, bu nesneyi val transition = AutoTransition()çağırın excludeChildren(recyclerView, true)ve onu iletin beginDelayedTransaction() as the second parameter.
Danilo Prado

5

RecyclerView için düzen dosyasında animateLayoutChanges = "true" olduğunda ben de bu hatayı alıyordum. Bu özniteliği silin ve hata kaybolur!


Lütfen bu sorunun 2014 yılına ait olduğunu ve mülklerin şimdiye kadar değişmiş olabileceğini unutmayın.
Korashen

2
Henüz değişmedi
Sanjay Kushwah

4

Benim durumumda animateOnLayoutChange, çökmeyi düzelten recyclerView'dan kaldırılırken , yine de viewHolder içindeki düzen değişikliklerini canlandırma yeteneğine ihtiyacım vardı. Bunun çalışması için LinearLayout' in the view holder needs theanimateOnLayoutChange 'true olarak ayarlandı, ancak notifyItemChangedadaptöre ihtiyacım vardı . Bu, daha sonra, layoutTransition animasyonlarının her ikisinin de başlamasına izin verdi (viewHolder'ı genişletmek ve daraltmak için) ve ayrıca hurda istisnasını da önler. Bu nedenle evet, animateOnLayoutChange'i recylcerView'e koymaktan kaçının ve görünüm boyutu değişikliklerinde varsayılan animasyonları etkinleştirmek için çeşitli bildirim yöntemlerini kullanın.


Öğenin genişlemesini canlandırmak için aynı şeyi yapmaya çalışıyorum, ancak adaptörde notifyItemChanged çağrısı, değişiklikten sonra öğenin yanıp sönmesine neden oluyor (animasyon çalışıyor)! Bunu nasıl önlüyorsunuz?
Flyview

Kullanım örneğimiz için, genişletilebilir bir düzen kullanmak için animateOnLayoutChange kullanarak özel kodumuzu değiştirdik. Başarmaya çalıştığımızla aynı şeyi başarıyor, ancak çok daha esnek. github.com/chuross/expandable-layout
kingargyle

3

Ben kaldırarak bu sorunu çözmek parent.addView()içindeonCreateViewHolder

Bu benim kodum

public MyViewHolder onCreateViewwHolder(ViewGroup parent, int viewType)  {
    Button addButton = new Button(context);
    //parent.addView(addButton);
    return new MyViewHolder(addButton);
}

Düğmemin android.support.v7.widget.RecyclerViewRecycler.recyclerViewHolderinternal()zaten bir ebeveyni olup olmadığını kontrol etme işlevi . Eğer üst öğeye düğme eklersek RecyclerView, mParentdeğişkenine de atanır .


Bunun yeni bir gereklilik olduğunu düşünüyorum, kütüphane desteğinin 24 versiyonunda ebeveyne ekledim, 25'e güncellendikten sonra kazayı aldım.
Kirill Kulakov

1

İçin özel bir nesne kullandığımda bunun benim için olduğunu gördüm ViewHolder.RecyclerViewAdaptör .

Sorunu çözmek için, benim durumumda onViewRecycled(ViewHolder holder)aşağıdaki gibi adaptör için bir zamanlayıcı olan özel nesneyi temizledim :

    public void onViewRecycled(ViewHolder holder) {
        if(holder instanceof  EntityViewHolder) {
            if(((EntityViewHolder)holder).timer != null) {
                ((EntityViewHolder) holder).timer.cancel();
            }
        }
        super.onViewRecycled(holder);
    }

Bu hatayı düzeltti.


1
    /**
     * Informs the recycler whether this item can be recycled. Views which are not
     * recyclable will not be reused for other items until setIsRecyclable() is
     * later set to true. Calls to setIsRecyclable() should always be paired (one
     * call to setIsRecyclabe(false) should always be matched with a later call to
     * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
     * reference-counted.
     *
     * @param recyclable Whether this item is available to be recycled. Default value
     * is true.
     *
     * @see #isRecyclable()
     */
    public final void setIsRecyclable(boolean recyclable) {
        mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
        if (mIsRecyclableCount < 0) {
            mIsRecyclableCount = 0;
            if (DEBUG) {
                throw new RuntimeException("isRecyclable decremented below 0: " +
                        "unmatched pair of setIsRecyable() calls for " + this);
            }
            Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
                    "unmatched pair of setIsRecyable() calls for " + this);
        } else if (!recyclable && mIsRecyclableCount == 1) {
            mFlags |= FLAG_NOT_RECYCLABLE;
        } else if (recyclable && mIsRecyclableCount == 0) {
            mFl`enter code here`ags &= ~FLAG_NOT_RECYCLABLE;
        }
        if (DEBUG) {
            Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
        }
    }

1

1,remove: verileri listeden kaldır.

2,notifyDataSetChanged : notifyDataSetChanged ();

3,notifyItemRemoved: animasyon göster.

4 、notifyItemRangeChanged: menzil görüntüleme boyutu veviewHolders(onBindViewHolder methods)


notifyItemRemovedKaldırırken footerve uygulama çökerken yaptım , olarak değiştirin notifyDataSetChangedve şimdi iyi çalışıyor. teşekkür ederim
Siarhei

1

Benim durumumda, TransitionManager.beginDelayedTransition()recyclerView'e bir görünüm eklemeden önce kullandım . Kaldırdım TransitionManager.beginDelayedTransition()ve çarpışma yok.


1

Bu sorunu arayarak çözdüm

setHasStableIds(true);

bağdaştırıcının kurucusunda ve getItemIdbağdaştırıcıda geçersiz kılma :

@Override
public long getItemId(int position) {
    return position;
}

1

Geri android:animateLayoutChanges="true"dönüşümden kaldırın veya ayarlayınandroid:animateLayoutChanges="false"


0

kullanırım com.squareup.picasso.RequestCreator

public void into(android.widget.ImageView target,
             Callback callback)

İnternetten resim indirdikten sonra ImageView boyutunu dinamik olarak yeniden boyutlandırmak ve görünüm boyutunu korumak için yeniden boyutlandırılan genişlik ve yüksekliği kaydeder. Bu İstisnayı aldım çünkü LayoutParamsbir Mapve onBindViewHolder'ıma kaydettim, onu aldım ve doğrudan ImageView. Bunu ImmutablePair<Integer, Integer>, diğer birçok durum yerine yalnızca ImageView boyutunu depolamak için ve geri yüklemek için aşağıdaki kodu kullanarak düzeltirim .

ViewGroup.LayoutParams params = image.getLayoutParams();
params.width = widthAndHeight.getLeft();
params.height = widthAndHeight.getRight();
image.setLayoutParams(params);

0

Benim için aynı hata daha yüksek bir ViewGroup üzerindeki LayoutTransition'dan kaynaklanıyor.


0

Bu tür bir sorun için olası başka bir düzeltme ekleyeyim, lütfen. Yapışkan başlıklar için superSlim kitaplığında aynı sorunu yaşadımRecyclerView . Kullandığım MatrixCursoriçin veri ayarlamak RecyclerViewCursorAdapter. Bu sorunun nedeni, kimlik sütunlarının 0tüm başlıklar için eşit olmasıdır . Umarım bu, birinin birkaç günlük hata ayıklamayı kurtarmasına yardımcı olur.


0

Benim durumumda sorun, bu yöntemin yanlış uygulanmasından kaynaklanıyordu public long getItemId(int position)(geçersiz kılınmıştır.RecyclerView.Adapter ).

Eski kod aynı öğe için iki farklı kimlik alacak (benim durumumda bu altbilgi öğesidir), uygulamayı düzelttikten sonra sorun giderildi.


0

İstisna nedeni, itemView'ün ebeveyni ise geçici çözüm. NotifyItemRemoved (position) yaptığınız kodda, RecyclerView'dan itemView öğesini kaldırın:

View itemView = mRecyclerView.getLayoutManager().findViewByPosition(position);
if (itemView != null && itemView.getParent() != null) {
    ((ViewGroup) itemView.getParent()).removeView(itemView);
}
notifyItemRemoved(position);

0

Benim için ortaya çıkan tuhaf bir durum, adaptörde bir görünüm üyem olması ve geri dönüşüm görünümüyle ilgisi olmayan bir görünümü tembelce göstermemdi.

Ayrıca, bu durumda görünüme bir referansı depoladığınız için geri dönüşüm görünümleri ilkelerine aykırıdır. Aşağıda hızlı bir örnek veriyorum:

// typically we would do this in a grid view adapter:
View v;
// ...
if(v = null){
v = LayoutInflater.inflate ...;
}

// Now with recycle view there is NO need to store a reference to View
// and lazy instantiate. So get rid of your View v member

0

bu istisna sebebi değil

android: animateLayoutChanges

veya

android: focusableInTouchMode

bu son doğru cevap sadece bir YANLIŞ LayoutParams ayarlamış olmanızdır .

    nameLP = new LinearLayout.LayoutParams(context.getResources().getDisplayMetrics().widthPixels, LinearLayout.LayoutParams.WRAP_CONTENT);
    nameLP2 = new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT);

nameLP tamam. nameLP2 çökme meydana gelir .bug burada.

Bu sayfanın tüm cevaplarını deniyorum. güven Bana.


0

Ben üzerine çünkü bu sorunu vardı equals()ve hashcode()yöntemi ViewHolderait RecyclerViewveri eşitliği ve karma kodunun, hesaplayarak .ViewHolder sonra geri dönüşüm mantık işi yoktu ve çöktü ben sadece yazma ve sabit kaldırmak.

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.