Programming

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

procodes 2020. 8. 8. 13:15
반응형

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


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라는 것이므로 코드를 다시 게시하지 않아도됩니다.

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


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

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


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

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

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


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


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


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

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

mGridLayoutManager.setItemPrefetchEnabled(false);

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


슬림핏 고정 헤더를 사용하는 동안이 오류가 발생했습니다. 첫 번째 위치를 잘못 설정했기 때문입니다. 나는 여기 에 답을 얻었다

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에 올바른 값을 전달하고 있는지 확인하십시오.


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

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

하지만 매번 수동으로 자식 뷰를 첨부하지 않았습니다. 그리고 내 ViewHolder에서 자식 뷰를 확장하려고 할 때 다음과 같이 완료되었음을 알았습니다.

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

마지막 매개 변수 attachToRoot는 거짓이어야합니다.

으로 변경 한 후 false문제를 해결했습니다.

그건 그렇고, 내 지원 라이브러리를 최신 버전 25.0.0으로 업그레이드 할 때만이 충돌이 발생한 것을 볼 수 있습니다. 버전 23.4.0을 사용하기 전에는이 문제가 발생하지 않습니다. 최신 지원 라이브러리에서 변경된 것이있을 것 같습니다.

이 도움을 바랍니다.


스크롤 할 때도 같은 오류가 발생했습니다 RecyclerView: animateLayoutChanges="true"레이아웃 파일에서 제거 RecyclerView한 다음 모든 것이 작동했습니다.


제 경우 Transition에는 소프트웨어 키보드가 표시 되려고했기 때문에 RecyclerView의 크기를 조정하려고 할 때 실행 중이 어서 발생했습니다 .

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


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


While in my case it was removing animateOnLayoutChange from the recyclerView that fixed the crash, I still needed the ability to animate the layout changes within the viewHolder. In order to get this to work, the LinearLayout' in the view holder needs theanimateOnLayoutChange' to to true, but I needed to notifyItemChanged to the adapter. This then allowed both of the layoutTransition animations to kick off (for expanding and collapsing the viewHolder), and also avoids the scrapped exception. So yes, avoid putting the animateOnLayoutChange on the recylcerView and use the various notify methods to enable the default animations on view size changes.


I solve this problem by removing parent.addView() in onCreateViewHolder

This is my code

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

Function at android.support.v7.widget.RecyclerViewRecycler.recyclerViewHolderinternal() check whether my button has already a parent or not. Which if we add button to parent, it will be also assign RecyclerView to its mParent variable.


There are a number of reason why this exception is called. In my case it was due to animations running thats why the views are still attached and could not be removed to the view. Only when the animation is finished thus the view could be removed and recycled.

There are two types of animation that could affect the recyclerview recycling.

1) Is the RecyclerView.ItemAnimator - this should not be the problem. This should be pretty much safe to use as it check for attach and scrapped views and handle recycling properly.

2) android:animateLayoutChanges="true" or TransitionManager.beginDelayedTransition() or TransitionManager.go(), etc. - These animations runs on its own and take hold of the items to animate. This result to the views being force to be attached until the animation is finished. The recyclerview do not have any knowledge of these animations since it is outside its scope. Therefore the recyclerview might try to recycle an item thinking it could be recycled properly but the problem is that these APIs are still holding on to the views until there animation is finished.

If you are using android:animateLayoutChanges="true" or TransitionManager.beginDelayedTransition() or TransitionManager.go(), etc. simply remove the RecyclerView and its children from the animation.

You can simply do this by taking hold of the Transition and calling

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

Note:

Take note that it is important to use Transition.excludeChildren() to exclude all of Recyclerview children from the animation and not just the Recyclerview itself.


I solved this issue by calling

setHasStableIds(true);

in the adapter's constructor and overriding getItemId in the adapter:

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

I saw this happen for me when I used a custom object in the ViewHolder for the RecyclerView adapter.

To fix the issue I cleared the custom object which in my case was a timer in the onViewRecycled(ViewHolder holder) for the adapter as below:

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

This fixed the bug.


    /**
     * 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、remove:remove data from list.

2、notifyDataSetChanged:notifyDataSetChanged();

3、notifyItemRemoved:show animation.

4、notifyItemRangeChanged:range view size and redraw the viewHolders(onBindViewHolder methods)


In my case, I used the TransitionManager.beginDelayedTransition() before adding a view on top of the recyclerView. I removed the TransitionManager.beginDelayedTransition() and no crash.


Remove android:animateLayoutChanges="true" from recycleview or set android:animateLayoutChanges="false"


this exception is not cause of

android:animateLayoutChanges

or

android:focusableInTouchMode

this final correct answer is just because you set a 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);

the nameLP is OK. the nameLP2 occur the crash .bug is here.

I try all of answers of this page. trust me.


I use com.squareup.picasso.RequestCreator

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

to dynamically resize ImageView's size after download picture from Internet, and saves the resized width and height to preserve the view size. I got this Exception because I saved LayoutParams in a Map, and in my onBindViewHolder, I retrieved it and directly set it to my ImageView. I fix this by use ImmutablePair<Integer, Integer> to only store ImageView's size rather than a lot of other states, and use following code to restore it.

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

For me the same bug with caused by a LayoutTransition on a higher level ViewGroup.


Let me add another possible fix for this kind of issue, please. I had the same problem with superSlim library for sticky headers in RecyclerView. I used MatrixCursor to set data to RecyclerViewCursorAdapter. The reason for this issue was ID columns equals to 0 for all headers. Hope that would help someone to save couple days of debugging.


In my case the problem was because of the incorrect implementing of this method public long getItemId(int position) (overrided from RecyclerView.Adapter method).

The old code will get two different ids for the same item (in my case it is the footer item), after fixing the implementation the issue has gone.


Workaround solution if reason of Exception is what itemView has parent. In code, where you have notifyItemRemoved(position), remove itemView from RecyclerView:

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

A Peculiar case which occured for me was that i had a view member in the adapter and i was lazy instantiating a view which there is no need to do with the recycle view.

It also goes against the recycle views principles which as your storing a reference to the view in this case. I give a quick example below:

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

I had this issue because I overwrite equals() and hashcode() method of ViewHolder of RecyclerView.ViewHolder by calculating data equality and hashcode, then the recycle logic did not work and crashed, I just remove the overwrite and fixed.

참고URL : https://stackoverflow.com/questions/26477660/recyclerview-crashes-when-scrapped-or-attached-views-may-not-be-recycled

반응형