RecyclerView виходить із ладу, коли "списаний або прикріплений вид не може бути перероблений"


114

Я використовую просту реалізацію, RecyclerViewвзяті з веб-сайту Android за допомогою, StaggeredGridLayoutManagerі я постійно отримую цю помилку, яка вибиває мою програму:

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)  

Простіше, я буквально маю на увазі, що це та сама реалізація, взята з цієї сторінки на їхньому веб-сайті , різниця полягає лише в тому, що макет елемента моєї сітки - це ImageViewпара і декілька TextViews, тому я не буду турбуватися репостувати свій код.

Хтось ще отримує цю помилку і знає, як з нею боротися?


У вас є рішення?
Пратік Бутані

Відповіді:


191

Ця помилка викликається, якщо у вашому XML android:animateLayoutChangesвстановлено значення true та ви notifyDataSetChanged()запускаєте адаптер RecyclerView у коді Java.

Отже, просто уникайте використання android:animateLayoutChangesз RecyclerViews.


22
Тоді як можна використовувати функцію animateLayoutChanges в recilerlerview?
dhuma1981

Чого ви намагаєтесь досягти? Пункт анімації? Якщо так, API RecyclerView підтримує це - подивіться документацію: developer.android.com/reference/android/support/v7/widget/…
Кеннет

4
@ dhuma1981, якщо аніматор елементів встановлений через mRecyclerView.setItemAnimator (новий DefaultItemAnimator ()); тоді animateLayoutChanges не потрібно бути правдивим
Rich Ehmer

RecyclerViewвикористовує DefaultItemAnimatorза замовчуванням.
Бенджамін

-, - у мене є ця проблема саме так, як ви її описали
Кодування Ninja

52

Мені доводилося стикатися і з цим збоєм, і в моєму випадку це не мало нічого спільного android:animateLayoutChanges.

RecyclerViewБудували було більше , ніж один тип уявлень в ньому і деякі з них , які мають EditTextз в них. Через деякий час ми вирішили проблему, що стосується фокусу. Ця помилка відбувається під час переробки EditTexts, і одна з них зосереджена.

Ми, звичайно, намагалися очистити фокус, коли нові дані прив'язуються до переробленого подання, але це не спрацьовувало, поки не android:focusableInTouchMode="true"буде встановлено RecycleView. Насправді це єдина зміна, яка була потрібна врешті-решт, щоб це питання пішло.


2
Фантастичні, вирішені проблеми, пов’язані з кількома фокусами, у мене виникли під час використання EditTexts у RecyclerView. Дякую!
Rabie Jradi

1
У мене був ACET в перегляді рециркуляторів, і він руйнує. Ця публікація врятувала мене.
Кай Ван

І у мене немає елементів редагування в елементах, хоча у мене є прапорці. чи варто спробувати, android:focusableInTouchMode="true"тому що це трапляється лише іноді на деяких пристроях (рідко), і я здогадуюсь, що це не стосується моєї проблеми, але стек-стежка для аварії майже однакова.
Шиванш

Це був мій випадок, але постановка android:focusableInTouchMode="true"зовсім не допомогла мені. Тому я очистив фокус у onViewDetachedFromWindowзворотному дзвінку. public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { if (holder instanceof ItemViewHolder) { ItemViewHolder item = (ItemViewHolder) holder; if (item.editText.isFocused()) item.editText.clearFocus(); } }
художник

24

Я видалив android:animateLayoutChangesвластивість макета, і проблема була вирішена.


Я почав отримувати цю аварію, коли я також розмістив android:animateLayoutChangesсвій RV.
Mauker

2
Якщо цей прапор встановлено на батьківському контейнері (Відносна макет). Виправлена ​​проблема.
1911z

@ 1911z ти кажеш, що ти мав прапор на батьківському контейнері і видалив його звідти виправлено проблему?
RamPrasadBismil

14

Серед причин, з якими кожен може зіткнутися з цією проблемою, перевірте, чи встановлено ви атрибут android:animateLayoutChanges="true"RecyclerView. Це призведе до виходу з ладу рециркуляції та повторного приєднання елементів RecyclerView. Видаліть його та призначте атрибут батьківського контейнера RecyclerView, наприклад LinearLayout / RelativeLayout, і вам слід побачити проблему.


Я бачив цей збій навіть тоді, коли я встановив атрибут на батьківському контейнері RV.
RamPrasadBismil

@RamPrasadBismil Будь ласка, опублікуйте свій код і, можливо, ми можемо його подивитися?
Рам Ієр

12

На це пішло два дні, але я не міг цього обійти, врешті-решт, мені довелося вимкнути попередній вибір елемента.

Під час налаштування диспетчера макетів ви можете просто зателефонувати

mGridLayoutManager.setItemPrefetchEnabled(false);

Це зробило помилку для мене. Сподіваюся, це комусь стане в нагоді.


Працювали для мене. Дякую.
Вікі

1
Я дуже переживаю за те, щоб люди прийняли це рішення. Ви багато втрачаєте, відключаючи цей прапор, і помилка все ще знаходиться в іншому місці -
Felipe Castilhos

8

Під час використання тонких заголовків slimfit я зіткнувся з цією помилкою. Це було викликано неправильним встановленням першої позиції. Я отримав відповідь тут

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


Ласкаво просимо до StackOverflow. Чи можете ви надати повну відповідь, а не лише посилання?
slfan


Це пункт списку, який повинен бути показаний у поданні переробника. Отже, я в основному зберігаю перше місце у розділі проти кожного елемента списку.
Джаспіндер Каур

8

Я зустрічався з цим питанням сьогодні вранці, але я не стикаюся з тією ж причиною, як зазначено вище.

Через налагодження я виявив, що перегляд елементів у моєму ViewHolder є mParent і він не є нульовим, що в звичайному випадку воно повинно бути жодним (саме так у журналі говориться: "доданий вигляд не може бути перероблений", я думаю, це означає, що якщо дитина перегляне вже приєднано до батьківського, це може призвести до несправності при переробці.)

Але я не вкладав дочірній вигляд кожен раз вручну. І я виявив, що це робиться, коли я намагаюся надути дитячий погляд у своєму ViewHolder, щось на зразок:

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

І останній параметр attachToRootповинен бути помилковим.

Після того як я змінив його false, я усунув свою проблему.

До речі, я бачу, що цей збій стався лише тоді, коли я оновлював свою бібліотеку підтримки до останньої версії 25.0.0. До того, як я використовував версію 23.4.0, і я не бачу цієї проблеми. Я думаю, що має бути щось змінено в останній бібліотеці підтримки.

Сподіваюся, що це допоможе.


8

Я також зіткнувся з такою ж помилкою під час прокрутки на RecyclerView: тоді я видалив animateLayoutChanges="true"файл макета, щоб RecyclerViewпотім все працювало.


6

У моєму випадку це сталося тому, що я мав Transition намагався змінити розмір RecyclerView, тому що клавіатура програмного забезпечення збиралася показувати.

Я виправив це, виключивши RecyclerView з Transition, використовуючи Transition.excludeTarget(R.id.recyclerview, true);


6

Існує низка причин, чому цей виняток називається. У моєму випадку це було пов’язано із запущеними анімаціями, тому перегляди все ще додаються і їх неможливо видалити. Тільки після завершення анімації таким чином подання можна буде видалити та повторно використати.

Існує два типи анімації, які можуть вплинути на переробку перегляду рециркулятора.

1) Це RecyclerView.ItemAnimator- це не повинно бути проблемою. Це має бути досить безпечним у використанні, оскільки він перевіряє наявність вкладених та скреслених зображень та належним чином обробляє утилізацію.

2) android:animateLayoutChanges="true"або TransitionManager.beginDelayedTransition()або TransitionManager.go () тощо. - Ці анімації запускаються самостійно і захоплюють елементи для анімації. Це призводить до того, що перегляди змушуються додаватись до завершення анімації. Рециркулятор не має жодних знань про ці анімації, оскільки це поза її сферою. Тому recyclerviewможна спробувати переробити елемент, думаючи, що він може бути перероблений належним чином, але проблема полягає в тому, що ці API все ще тримаються на переглядах до тих пір, поки анімація не буде закінчена.

Якщо ви використовуєте android:animateLayoutChanges="true"або TransitionManager.beginDelayedTransition()TransitionManager.go () тощо, просто вийміть RecyclerViewз анімації та її дітей.

Ви можете просто зробити це, взявшись за Transitionта подзвонивши

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

Примітка:

Зверніть увагу, що важливо використовувати, Transition.excludeChildren()щоб виключити всіх Recyclerviewдітей з анімації, а не лише саму Recyclerviewсебе.


Дякую! TransitionManager.beginDelayedTransition () став причиною проблеми в моєму випадку. Ви можете оновити приклад коду, щоб отримати докладніші відомості про використання Transition.excludeChildren. Ви створюєте об'єкт переходу типу:, val transition = AutoTransition()викликаєте excludeChildren(recyclerView, true)цей об'єкт і передаєте його beginDelayedTransaction() as the second parameter.
Данило Прадо

5

Я теж отримував цю помилку, коли у мене був файл animateLayoutChanges = "true" у макеті для RecyclerView. Видаліть цей атрибут і помилка зникне!


Зверніть увагу, що це питання починається з 2014 року, і властивості, можливо, вже змінилися.
Корашен

2
Ніякого ще не змінилося
Sanjay Kushwah

4

Хоча в моєму випадку це видалення animateOnLayoutChangeз recilerView виправило збій, мені все ще потрібна можливість анімувати зміни макета в viewHolder. Для того, щоб це спрацювало, LinearLayout' in the view holder needs theanimateOnLayoutChange 'to to true, але мені потрібно до notifyItemChangedадаптера. Потім це дозволило розпочати обох анімацій layoutTransition (для розширення та згортання viewHolder), а також уникає викраденого виключення. Отже, так, уникайте розміщення animateOnLayoutChange на recylcerView і використовуйте різні методи сповіщення, щоб активувати анімації за замовчуванням щодо зміни розміру перегляду.


Я намагаюся зробити те ж саме, щоб анімувати розширення елемента, але виклик notifyItemChanged в адаптері змушує елемент спалахнути після зміни (анімація працює)! Як ти цьому запобігаєш?
Flyview

У нашому випадку ми замінили власний код за допомогою animateOnLayoutChange, щоб використовувати макет, що розгортається. Це робить те саме, що ми намагалися зробити, але більш гнучким. github.com/chuross/expandable-layout
kingargyle

3

Я вирішити цю проблему шляхом видалення parent.addView()вonCreateViewHolder

Це мій код

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

Функція android.support.v7.widget.RecyclerViewRecycler.recyclerViewHolderinternal()перевірити, чи є у моєї кнопки вже батько чи ні. Що, якщо ми додамо кнопку до батьківського, воно також буде присвоєно RecyclerViewйого mParentзмінній.


Я думаю, що це нова вимога, я приєднався до батьків у версії 24 підтримки бібліотеки, після оновлення до 25 я отримав збій.
Кирило Кулаков

1

Я бачив це сталося зі мною , коли я використовував для користувача об'єкт в ViewHolderдля RecyclerViewадаптера.

Щоб вирішити проблему, я очистив спеціальний об'єкт, який у моєму випадку був таймером у onViewRecycled(ViewHolder holder)адаптері, як показано нижче:

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

Це виправило помилку.


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: видалити дані зі списку.

2 、notifyDataSetChanged: notifyDataSetChanged ();

3 、notifyItemRemoved: показати анімацію.

4 、notifyItemRangeChanged: розмір перегляду діапазону та перемалюйтеviewHolders(onBindViewHolder methods)


я зробив, notifyItemRemovedколи вилучаєте footerпрограму та виходить з ладу, змініть її на notifyDataSetChangedі тепер це працює нормально. дякую
Siarhei

1

У моєму випадку я використовував TransitionManager.beginDelayedTransition()раніше, ніж додавати вигляд на поверх reciklilerView. Я усунув TransitionManager.beginDelayedTransition()і без збоїв.


1

Я вирішив це питання, зателефонувавши

setHasStableIds(true);

в конструкторі адаптера і переопределенні getItemIdв адаптері:

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


0

я використовую com.squareup.picasso.RequestCreator

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

динамічно змінювати розмір ImageView після завантаження зображення з Інтернету та зберігає розмір ширини та висоти, щоб зберегти розмір перегляду. Я отримав цей виняток, тому що я зберег LayoutParamsу a Map, і в своєму onBindViewHolder я отримав його та безпосередньо встановив його на свій ImageView. Я виправляю це за допомогою ImmutablePair<Integer, Integer>лише збереження розміру ImageView, а не багатьох інших станів, і використовую наступний код, щоб відновити його.

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

0

Для мене та сама помилка, яку викликає LayoutTransition на вищому рівні ViewGroup.


0

Дозвольте додати ще одне можливе виправлення подібного питання. У мене була така ж проблема із бібліотекою SuperSlim для липких заголовків у RecyclerView. Раніше я MatrixCursorвстановлював дані RecyclerViewCursorAdapter. Причиною цього випуску стали стовпці ID, рівні 0для всіх заголовків. Сподіваюся, що це допоможе комусь заощадити пару днів налагодження.


0

У моєму випадку проблема була через неправильну реалізацію цього методу public long getItemId(int position)(переосмислену від RecyclerView.Adapterметоду).

Старий код отримає два різних ідентифікатора для одного елемента (в моєму випадку це елемент нижнього колонтитулу), після виправлення реалізації проблема пішла.


0

Вирішення способу вирішення, якщо причиною винятку є те, що у елемента itemView є батьківський. У коді, де ви повідомилиItemRemoved (позиція), видаліть itemView з RecyclerView:

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

0

Своєрідний випадок, який стався для мене, полягає в тому, що в мене був переглядач в перехіднику, і я лінивий створювати подання, яке не потрібно робити з поданням переробки.

Це також суперечить принципам перегляду перегляду, які як ваше зберігання посилаються на вигляд у цьому випадку. Я наводжу короткий приклад нижче:

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

цей виняток не є причиною

android: animateLayoutChanges

або

android: focusableInTouchMode

ця остаточна правильна відповідь лише тому, що ви встановили WRONG LayoutParams .

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

ім'яLP в порядку. ім'яLP2 відбувається збій.

Я пробую всі відповіді цієї сторінки. Довірся мені.


0

У мене виникла ця проблема, тому що я перезаписую equals()і hashcode()метод .ViewHolder, обчислюючи рівність даних і хеш-код, тоді логіка переробки не спрацювала і вийшла ViewHolderз RecyclerViewладу, я просто видаляю перезапис і виправляю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.