「スクラップされたビューまたはアタッチされたビューがリサイクルされない可能性がある」と、RecyclerViewがクラッシュする


114

私はを使用RecyclerViewしてAndroid Webサイトから取得したの単純な実装を使用し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と2、3であるTextViewため、コードを再投稿する必要はありません。

他の誰かがこのエラーを受け取り、それに対処する方法を知っていますか?


解決策はありますか?
Pratik Butani 2015

回答:


191

このエラーは、XMLでandroid:animateLayoutChangestrueに設定notifyDataSetChanged()し、JavaコードでRecyclerViewのアダプターを呼び出すと発生します。

したがって、android:animateLayoutChangesRecyclerViewsでの使用は避けてください。


22
では、recyclerviewでanimateLayoutChanges機能をどのように使用できますか?
dhuma1981 14

何を達成しようとしていますか?アイテムアニメーション?もしそうなら、RecyclerView APIがサポートは、この-ドキュメントを見てみましょう:developer.android.com/reference/android/support/v7/widget/...
ケネス・

4
@ dhuma1981、アイテムアニメーターがmRecyclerView.setItemAnimator(new DefaultItemAnimator());を介して設定されている場合。その場合、animateLayoutChangesはtrueである必要はありません
Rich Ehmer

RecyclerViewDefaultItemAnimatorデフォルトで使用します。
ベンジャミン

-、-私はあなたがそれをどのように説明したのか正確にこの問題を抱えています
Ninja Coding

52

私もこのクラッシュに対処する必要があり、私の場合はそれとは何の関係もありませんでした android:animateLayoutChanges

RecyclerView我々はそれに景色の持っていた複数のタイプを構築し、いくつかは持ったEditText彼らで秒。しばらくして、問題をフォーカス関連に固定しました。このバグはリサイクル中に発生しますEditText sの、そのうちの1つに焦点が当てられています。

当然、新しいデータがリサイクルされたビューにバインドされているときにフォーカスをクリアしようとしましたが、がandroid:focusableInTouchMode="true"オンになるまで機能しませんでしたRecycleView。実際、それがこの問題がなくなるために最後に必要だった唯一の変更です。


2
RecyclerViewでEditTextsを使用するときに発生した複数のフォーカス関連の問題を解決しました。ありがとう!
Rabie Jradi 2015

1
私はリサイクル業者のビューでACETを持っていました、そしてそれは粉砕します。この投稿は私を救った。
Kai Wang

アイテムにエディットテキストはありませんが、チェックボックスはあります。android:focusableInTouchMode="true"一部のデバイスでまれにしか発生しないため、試してみる必要があります(まれに)。問題とは関係ないと思いますが、クラッシュのスタックトレースはほとんど同じです。
Shivansh、

これは私の場合でしたが、設定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:animateLayoutChangesRVにも同様に配置したときに、このクラッシュが発生し始めました。
Mauker

2
このフラグを親コンテナに設定しました(相対レイアウト)。問題を修正しました。
-1911z

@ 1911z親コンテナにフラグがあり、そこから削除すると問題が解決したと言っていますか?
RamPrasadBismil

14

誰もがこの問題に直面する可能性がある理由の1つとして、属性android:animateLayoutChanges="true"をRecyclerViewに設定しているかどうかを確認してください。これにより、RecyclerViewのアイテムのリサイクルと再接続が失敗します。それを削除して、LinearLayout / RelativeLayoutなどのRecyclerViewの親コンテナーに属性を割り当てます。問題が解消されるはずです。


RVの親コンテナに属性を設定した場合でも、このクラッシュが発生しました。
RamPrasadBismil

@RamPrasadBismilあなたのコードを投稿してください、多分私たちはそれを見ることができますか?
Ram Iyer

12

2日かかりましたが、これを回避することができませんでした。結局、アイテムのプリフェッチを無効にする必要がありました。

レイアウトマネージャーを設定するときは、単に呼び出すことができます

mGridLayoutManager.setItemPrefetchEnabled(false);

エラーが解消されました。それが誰かのために役立つことを願っています。


私のために働いた。ありがとう。
ヴィッキー

1
私はこのソリューションを採用する人々を本当に心配しています。あなたはこのフラグを無効にすることで多くを失い、バグはまだ他の場所にあります–
フェリペカスティリョス

8

スリムフィットのスティッキーヘッダーを使用しているときに、このエラーが発生しました。間違った最初の位置を設定したために発生しました。私はここで答えを得まし

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 2015

itemここは何ですか?
バグの発生

これは、リサイクラービューに表示されるリストアイテムです。したがって、基本的には各リストアイテムに対してセクションの最初の位置を保存しています。
Jaspinder Kaur 2017

8

今朝この問題に会いましたが、上記と同じ理由に直面していません。

デバッグによって、ViewHolderのアイテムビューに mParent nullがないこと。これは通常、なしである必要があります(これは、ログに「アタッチされたビューがリサイクルされない可能性がある」とことです)、子ビューがはすでに親にアタッチされていますが、リサイクル時にエラーが発生する可能性があります。)

しかし、私は毎回手動で子ビューをアタッチしませんでした。そして、ViewHolderで子ビューを膨らませようとしたときに、次のようなことが行われたことがわかりました。

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

そして最後のパラメータattachToRootはfalseでなければなりません。

に変更した後false、問題を修正しました。

ちなみに、このクラッシュはサポートライブラリを最新バージョン25.0.0にアップグレードしたときにのみ発生します。以前はバージョン23.4.0を使用していましたが、この問題が発生することはありません。最新のサポートライブラリで何か変更があるはずだと思います。

この助けを願っています。


8

RecyclerView:をスクロールしたときにも同じエラーが発生しました。animateLayoutChanges="true"レイアウトファイルで削除したためRecyclerView、すべてが機能しました。


6

私の場合、それは私が持っていたために起こりました Transition、ソフトウェアキーボードが表示されようとしているため、RecyclerViewのサイズを変更しようとしたときに実行ししました。

私はそれTransitionを使ってRecyclerViewを除外して修正しました Transition.excludeTarget(R.id.recyclerview, true);


6

この例外が呼び出される理由はいくつかあります。私の場合、アニメーションが実行されていたため、ビューがまだアタッチされていて、ビューから削除できませんでした。アニメーションが終了したときにのみ、ビューを削除してリサイクルできます。

recyclerviewのリサイクルに影響を与える可能性のあるアニメーションには2つのタイプがあります。

1)は RecyclerView.ItemAnimator -これは問題ではないはずです。アタッチビューとスクラップビューをチェックし、適切にリサイクルを処理するため、これは非常に安全に使用できます。

2)android:animateLayoutChanges="true"またはTransitionManager.beginDelayedTransition()またはTransitionManager.go()など-これらのアニメーションは単独で実行され、アニメーション化するアイテムを取得します。これにより、アニメーションが終了するまでビューが強制的にアタッチされます。recyclerviewはスコープ外であるため、これらのアニメーションについての知識はありません。したがって、recyclerviewは適切にリサイクルできると考えてアイテムをリサイクルしようとする場合がありますが、問題は、アニメーションが終了するまでこれらのAPIがビューを保持し続けることです。

android:animateLayoutChanges="true"or TransitionManager.beginDelayedTransition()またはTransitionManager.go()などを使用している場合は、RecyclerViewとその子をアニメーションから削除するだけです。

あなたは単にをつかんでTransition呼び出すことでこれを行うことができます

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

注意:

を使用Transition.excludeChildren()してRecyclerviewRecyclerviewそれ自体だけでなくすべての子をアニメーションから除外することが重要であることに注意してください。


ありがとう!私の場合、TransitionManager.beginDelayedTransition()が問題の原因でした。使用方法の詳細でコード例を更新できますTransition.excludeChildren。次のような遷移オブジェクトをインスタンス化し、このオブジェクトをval transition = AutoTransition()呼び出してexcludeChildren(recyclerView, true)に渡しますbeginDelayedTransaction() as the second parameter
Danilo Prado

5

RecyclerViewのレイアウトファイルにanimateLayoutChanges = "true"があると、私もこのエラーを受け取りました。この属性を削除するとエラーが消えます!


この質問は2014年のものであり、プロパティは今までに変更されている可能性があります。
Korashen

2
いいえ、まだ変更されていません
Sanjay Kushwah

4

私の場合animateOnLayoutChange、クラッシュを修正したのはrecyclerViewからの削除でしたが、viewHolder内のレイアウト変更をアニメーション化する機能が必要でした。これを機能させるには、LinearLayout' in the view holder needs theanimateOnLayoutChange 'をtrueに設定しますがnotifyItemChanged、アダプターに設定する必要があります。これにより、両方のlayoutTransitionアニメーションが開始され(viewHolderを展開および縮小するため)、廃棄された例外も回避されました。したがって、はい、animateOnLayoutChangeをrecylcerViewに配置することを避け、さまざまな通知メソッドを使用して、ビューサイズの変更時にデフォルトのアニメーションを有効にします。


同じことを行ってアイテムの展開をアニメーション化しようとしていますが、アダプターでnotifyItemChangedを呼び出すと、変更後にアイテムが点滅します(アニメーションは機能します)。それをどうやって防ぐのですか?
Flyview

私たちのユースケースでは、expandable-layoutを使用するために、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に更新するとクラッシュしました。
キリルクラコフ2017年

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()recyclerViewの上にビューを追加する前にを使用しました。と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のサイズを動的にサイズ変更し、サイズ変更された幅と高さを保存してビューのサイズを維持します。この例外が発生したのは、に保存LayoutParamsMap、onBindViewHolderでそれを取得して直接に設定したためImageViewです。ImmutablePair<Integer, Integer>他の多くの状態ではなくImageViewのサイズのみを保存するために使用してこれを修正し、次のコードを使用してそれを復元します。

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

0

私にとっては、より高いレベルのViewGroupのLayoutTransitionが原因で発生したのと同じバグです。


0

この種の問題に対して考えられる別の修正を追加させてください。のスティッキーヘッダー用のsuperSlimライブラリで同じ問題が発生しましたRecyclerView。以前MatrixCursorはデータをに設定していましたRecyclerViewCursorAdapter。この問題の理由は、ID列が0すべてのヘッダーに等しいことでした。誰かがデバッグの数日間を節約するのに役立つことを願っています。


0

私の場合、問題はこのメソッドの誤った実装public long getItemId(int position)RecyclerView.Adapterメソッドからオーバーライドされた)によるものでした。

古いコードは、問題がなくなった実装を修正した後、同じ項目(私の場合はフッター項目)に対して2つの異なるIDを取得します。


0

例外の理由がitemViewが親を持つものである場合の回避策。notifyItemRemoved(position)があるコードで、RecyclerViewからitemViewを削除します。

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

この最終的な正解は、間違った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);

nameLPはOKです。nameLP2が発生すると、クラッシュ.bugが発生します。

このページのすべての回答を試してみます。私を信じて。


0

データの等価性とハッシュコードを計算して.ViewHolderのメソッドequals()hashcode()メソッドを上書きしたため、この問題ViewHolderRecyclerView発生しました。リサイクルロジックが機能せず、クラッシュしました。上書きを削除して修正しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.