RecyclerView вылетает, когда "утилизированные или прикрепленные виды не могут быть переработаны"

Я использую простую реализацию 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 и пара TextView s, поэтому я не буду беспокоиться о переносе кода.

Кто-нибудь еще получает эту ошибку и знает, как с ней справиться?

Ответ 1

Эта ошибка возникает, если в вашем XML android:animateLayoutChanges установлено значение true, и вы вызываете notifyDataSetChanged() на адаптере RecyclerView в коде Java.

Итак, просто не используйте android:animateLayoutChanges с RecyclerViews.

Ответ 2

Мне тоже пришлось иметь дело с этим сбоем, и в моем случае это не имело никакого отношения к android:animateLayoutChanges.

В RecyclerView, который мы строили, в нем было несколько видов взглядов, а некоторые из них имели EditText. Через некоторое время мы прикрепили вопрос к тому, чтобы быть связанным с фокусом. Эта ошибка возникает при утилизации EditText, и одна из них сфокусирована.

Естественно, мы попытались очистить фокус, когда новые данные привязаны к переработанному представлению, но это не сработало, пока android:focusableInTouchMode="true" не установлен на RecycleView. На самом деле это единственное изменение, которое было необходимо для завершения этой проблемы.

Ответ 3

Я удалил свойство android:animateLayoutChanges из макета и проблема была решена.

Ответ 4

При использовании 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

Ответ 5

Среди причин, по которым любой может столкнуться с этой проблемой, проверьте, установлен ли атрибут android:animateLayoutChanges="true" в RecyclerView. Это приведет к сбою и повторному подключению элементов RecyclerView. Удалите его и назначьте атрибут родительскому контейнеру RecyclerView, например LinearLayout/RelativeLayout, и вы увидите, что проблема исчезла.

Ответ 6

Я встретил эту проблему сегодня утром, но я не сталкиваюсь с той же причиной, о которой говорилось выше.

Через debug я обнаружил, что представление элемента в моем ViewHolder имеет mParent, и оно не является нулевым, что в нормальном случае оно не должно быть ничем (то, что сообщение в журнале, "прикрепленное представление не может быть переработано", я думаю, что это означает, что если дочернее представление уже привязано к родительскому объекту, это может привести к сбою при повторном использовании.)

Но я не привязывал дочерний просмотр каждый раз вручную. И я обнаружил, что это было сделано, когда я пытаюсь раздуть вид ребенка в моем ViewHolder, что-то вроде:

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

И последний параметр attachToRoot должен быть ложным.

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

Кстати, я вижу, что этот сбой произошел, когда я обновляю свою библиотеку поддержки до последней версии 25.0.0. До того, как я использовал версию 23.4.0, и я не вижу этой проблемы. Я предполагаю, что в последней библиотеке поддержки должно быть что-то изменено.

Надеюсь на эту помощь.

Ответ 7

Я решаю эту проблему, удалив 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.

Ответ 8

Мне потребовались два дня, но я не мог обойти это, в конце концов, мне пришлось отключить предварительную выборку.

При настройке диспетчера компоновки вы можете просто вызвать

mGridLayoutManager.setItemPrefetchEnabled(false);

Это заставило меня уйти. Надеюсь, что это будет полезно для кого-то.

Ответ 9

В моем случае это произошло из-за того, что при попытке изменить размер RecyclerView был запущен Transition, потому что клавиатура программного обеспечения собиралась показать.

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

Ответ 10

Я видел, что это произошло для меня, когда я использовал пользовательский объект в 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);
    }

Это исправило ошибку.

Ответ 11

    /**
     * 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);
        }
    }

Ответ 12

1, remove: удалить данные из списка.

2, notifyDataSetChanged: notifyDataSetChanged();

3, notifyItemRemoved: показать анимацию.

4, notifyItemRangeChanged: размер диапазона и перерисовка viewHolders(onBindViewHolder methods)

Ответ 13

Я решил эту проблему, позвонив

setHasStableIds(true);

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

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

Ответ 14

это исключение не является причиной

андроид: animateLayoutChanges

или

андроида: 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 происходит сбой .bug здесь.

Я пробую все ответы этой страницы. поверьте мне.

Ответ 15

A Особый случай, который возник для меня, состоял в том, что у меня был член вида в адаптере, и я ленился, создавая представление, которое не нужно делать с просмотром перекопа.

Он также противоречит принципам перекодировки, которые в этом случае сохраняют ссылку на представление. Ниже приведен краткий пример:

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

Ответ 16

Я использую com.squareup.picasso.RequestCreator

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

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

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

Ответ 17

Позвольте мне добавить еще одно возможное решение для такого рода вопросов, пожалуйста. У меня была такая же проблема с superSlim библиотека для липких заголовков в RecyclerView. Я использовал MatrixCursor для установки данных на RecyclerViewCursorAdapter. Причиной этой проблемы были столбцы ID равными 0 для всех заголовков. Надеюсь, что это поможет кому-то сохранить пару дней отладки.

Ответ 18

Для меня такая же ошибка, вызванная LayoutTransition на более высоком уровне ViewGroup.

Ответ 19

В моем случае проблема была связана с неправильным внедрением этого метода public long getItemId(int position) (переопределяется из метода RecyclerView.Adapter).

Старый код получит два разных идентификатора для одного и того же элемента (в моем случае это элемент нижнего колонтитула), после исправления реализации проблема исчезла.

Ответ 20

В моем случае я использовал TransitionManager.beginDelayedTransition() перед добавлением представления поверх recyclerView. Я удалил TransitionManager.beginDelayedTransition() и без сбоев.

Ответ 21

Обходное решение, если причина исключения - это то, что itemView имеет родительский элемент. В коде, где у вас есть notifyItemRemoved (position), удалите itemView из RecyclerView:

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

Ответ 22

Удалите android:animateLayoutChanges="true" из recycleview или установите android:animateLayoutChanges="false"