RecyclerView는 "스크래핑되거나 연결된 뷰가 재활용되지 않을 수 있습니다."


114

a를 사용 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)  

간단히 말해서 말 그대로 웹 사이트의이 페이지에서 가져온 것과 동일한 구현이라는 것을 의미합니다 . 유일한 차이점은 내 그리드 항목의 레이아웃이 s ImageViewTextViews라는 것이므로 코드를 다시 게시하지 않아도됩니다.

이 오류가 발생하고 처리 방법을 아는 사람이 있습니까?


해결책이 있습니까?
Pratik Butani

답변:


191

이 오류는 XML android:animateLayoutChanges에서 true로 설정 notifyDataSetChanged()하고 Java 코드에서 RecyclerView의 어댑터 를 호출하면 발생 합니다.

따라서 android:animateLayoutChangesRecyclerViews와 함께 사용하지 마십시오 .


22
그렇다면 recyclerview에서 animateLayoutChanges 기능을 어떻게 사용할 수 있습니까?
dhuma1981 2014

당신은 무엇을 성취하려고합니까? 아이템 애니메이션? 그렇다면 RecyclerView API가이를 지원합니다. 다음 문서를 참조하세요. developer.android.com/reference/android/support/v7/widget/…
Kenneth

4
mRecyclerView.setItemAnimator (new DefaultItemAnimator ())를 통해 항목 애니메이터가 설정된 경우 @ dhuma1981); 다음 animateLayoutChanges 사실 일 필요는 없다
리치 Ehmer

RecyclerViewDefaultItemAnimator기본적으로 사용 합니다.
Benjamin

-,-정확히 어떻게 설명
Ninja Coding

52

나는 또한이 충돌을 처리해야했으며 내 경우에는 android:animateLayoutChanges.

RecyclerView그것은 일부의 견해 우리가 가진 건물 한 종류 이상 가지고 있었다 EditText그들들. 잠시 후 우리는 초점과 관련된 문제를 고정했습니다. 이 버그는 EditTexts 를 재활용하는 동안 발생하며 그중 하나가 집중됩니다.

당연히 새 데이터가 재활용 된 뷰에 바인딩 될 때 포커스를 지우려고 시도했지만 android:focusableInTouchMode="true"이 설정 될 때까지 작동하지 않았습니다 RecycleView. 사실 이것이이 문제가 사라지기 위해 결국 필요한 유일한 변화입니다.


2
RecyclerView에서 EditTexts를 사용할 때 겪었던 여러 초점 관련 문제를 환상적으로 해결했습니다. 감사!
Rabie Jradi

1
나는 recyclerview에 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(); } }
artman

24

android:animateLayoutChanges레이아웃 속성에서 제거 하고 문제가 해결되었습니다.


나는 android:animateLayoutChangesRV에도를 배치했을 때이 충돌이 발생하기 시작했습니다 .
Mauker

2
이 플래그가 상위 컨테이너 (상대 레이아웃)에 설정되었습니다. 문제를 해결했습니다.
1911z

@ 1911z는 부모 컨테이너에 플래그가 있고 거기에서 제거하면 문제가 해결되었다고 말하는 것입니까?
RamPrasadBismil

14

누구나이 문제에 직면 할 수있는 이유 중 특성 android:animateLayoutChanges="true"을 RecyclerView로 설정했는지 확인하십시오 . 이로 인해 RecyclerView 항목을 재활용하고 다시 연결하는 데 실패합니다. 이를 제거하고 LinearLayout / RelativeLayout과 같은 RecyclerView의 부모 컨테이너에 속성을 할당하면 문제가 해결되는 것을 볼 수 있습니다.


RV의 부모 컨테이너에 속성을 설정 한 경우에도이 충돌을 보았습니다.
RamPrasadBismil

@RamPrasadBismil 코드를 게시 해 주시면 볼 수 있을까요?
Ram Iyer

12

이틀이 걸렸지 만이 문제를 해결할 수 없었습니다. 결국 항목 프리 페치를 비활성화해야했습니다.

레이아웃 관리자를 설정할 때 간단히 호출 할 수 있습니다.

mGridLayoutManager.setItemPrefetchEnabled(false);

그것은 나를 위해 오류를 제거했습니다. 누군가에게 유용하기를 바랍니다.


나를 위해 일했습니다. 감사.
Vicky

1
이 솔루션을 채택하는 사람들이 정말 걱정됩니다. 이 플래그를 사용하지 않도록 많이 잃고 버그는 다른 장소에 아직 -
펠리페 Castilhos

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

무엇 item여기?
버그 일어날

리사이클 러보기에 표시되는 목록 항목입니다. 그래서 기본적으로 각 목록 항목에 대해 섹션 첫 번째 위치를 저장합니다.
Jaspinder 카 우르

8

오늘 아침에이 문제를 만났지만 위에서 언급 한 것과 같은 이유에 직면하지 않았습니다.

디버그를 통해 내 ViewHolder의 항목보기에 mParent 있고 null이 아니라는 정상적인 경우에는 none이어야합니다 (로그에서 "첨부 된 뷰는 재활용되지 않을 수 있습니다"라고 말하는 것입니다. 이미 부모에 연결되어 있으면 재활용 할 때 실패가 발생할 수 있습니다.)

하지만 매번 수동으로 자식 뷰를 첨부하지 않았습니다. 내 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의 크기를 조정하려고 할 때 실행 중이 어서 발생했습니다 .

다음 Transition을 사용하여 RecyclerView를 제외하고 수정했습니다.Transition.excludeTarget(R.id.recyclerview, true);


6

이 예외가 호출되는 데는 여러 가지 이유가 있습니다. 제 경우에는 애니메이션이 실행 되었기 때문에 뷰가 여전히 연결되어 있고 뷰에서 제거 할 수 없었습니다. 애니메이션이 완료되었을 때만 뷰를 제거하고 재활용 할 수 있습니다.

recyclerview 재활용에 영향을 줄 수있는 애니메이션에는 두 가지 유형이 있습니다.

1) RecyclerView.ItemAnimator -이것은 문제가되지 않아야합니다. 첨부 및 스크랩 뷰를 확인하고 재활용을 적절하게 처리하므로 사용하기에 매우 안전합니다.

2) android:animateLayoutChanges="true"또는 TransitionManager.beginDelayedTransition()TransitionManager.go () 등-이러한 애니메이션은 자체적으로 실행되며 애니메이션 할 항목을 유지합니다. 이 결과 애니메이션이 완료 될 때까지 뷰가 강제로 연결됩니다. recyclerview는 범위 밖에 있기 때문에 이러한 애니메이션에 대한 지식이 없습니다. 따라서 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.
Danilo Prado

5

RecyclerView의 레이아웃 파일에 animateLayoutChanges = "true"가있을 때마다이 오류가 발생했습니다. 이 속성을 삭제하면 오류가 사라집니다!


이 질문은 2014 년의 질문이며 지금까지 속성이 변경되었을 수 있습니다.
Korashen

2
아니 그것은 아직 변경되지
산 제이 Kushwah

4

제 경우 animateOnLayoutChange에는 크래시를 수정 한 recyclerView에서 제거 했지만 viewHolder 내에서 레이아웃 변경을 애니메이션하는 기능이 여전히 필요했습니다. 이 작업을 수행하려면 LinearLayout' in the view holder needs theanimateOnLayoutChange '를 true로 설정했지만 notifyItemChanged어댑터가 필요했습니다 . 그러면 두 layoutTransition 애니메이션이 시작되고 (viewHolder를 확장하고 축소하기 위해) 스크랩 된 예외를 방지 할 수 있습니다. 예, recylcerView에 animateOnLayoutChange를 넣지 말고 다양한 알림 메서드를 사용하여 뷰 크기 변경시 기본 애니메이션을 활성화하십시오.


항목의 확장을 애니메이션화하기 위해 동일한 작업을 시도하고 있지만 어댑터에서 notifyItemChanged를 호출하면 변경 후 항목이 깜박입니다 (애니메이션이 작동 함)! 어떻게 방지합니까?
Flyview

우리의 사용 사례에서는 확장 가능한 레이아웃을 사용하기 위해 animateOnLayoutChange를 사용하여 사용자 지정 코드를 교체했습니다. 그것은 우리가 성취하려고했던 것과 같은 것을 성취하지만 훨씬 더 유연합니다. github.com/chuross/expandable-layout
kingargyle 2019-02-04

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 ();

삼,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;
}

1

android:animateLayoutChanges="true"Recycleview에서 제거 하거나 설정android:animateLayoutChanges="false"


0

나는 사용한다 com.squareup.picasso.RequestCreator

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

인터넷에서 사진을 다운로드 한 후 ImageView의 크기를 동적으로 조정하고 크기를 조정 한 너비와 높이를 저장하여보기 크기를 유지합니다. 내가 저장하기 때문에 나는이 예외를 가지고 LayoutParamsA의 Map, 내 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

이런 종류의 문제에 대한 또 다른 가능한 수정 사항을 추가하겠습니다. .NET의 고정 헤더에 대한 superSlim 라이브러리 와 동일한 문제가 발생 했습니다 RecyclerView. 나는 MatrixCursor데이터를 RecyclerViewCursorAdapter. 이 문제의 원인은 0모든 헤더 에 대해 ID 열이 같기 때문 입니다. 누군가가 며칠의 디버깅을 절약하는 데 도움이되기를 바랍니다.


0

제 경우에는이 메서드를 잘못 구현했기 때문에 문제가 발생했습니다 (메소드 public long getItemId(int position)에서 재정의 됨 RecyclerView.Adapter).

이전 코드는 동일한 항목 (제 경우에는 바닥 글 항목)에 대해 두 개의 다른 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는 괜찮습니다. nameLP2가 발생하면 충돌 .bug가 여기에 있습니다.

이 페이지의 모든 답변을 시도합니다. 날 믿어.


0

내가 덮어 쓰기 때문에 나는이 문제를 가지고 equals()hashcode()방법 ViewHolderRecyclerView데이터 평등과 해시 코드를 계산하여 .ViewHolder 후 재활용 논리가 일을하지 않았고 추락 난 그냥이 덮어 쓰기 고정 제거합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.