RecyclerView : 불일치가 감지되었습니다. 잘못된 상품 위치


271

QA에서 버그를 감지했습니다. Android 기기 (Droid Turbo)를 회전 할 때 다음 RecyclerView 관련 충돌 이 발생했습니다.

java.lang.IndexOutOfBoundsException : 불일치가 발견되었습니다. 잘못된 품목 위치 2 (오프셋 : 2). 상태 : 3

나에게 이것은 RecyclerView 내부의 오류처럼 보입니다. 코드에 의해 직접 발생하는 방법을 생각할 수 없기 때문에 ...

누구든지이 문제가 발생 했습니까?

해결책은 무엇입니까?

잔인한 해결 방법은 예외가 발생할 때 예외를 포착하고 손상된 상태로 남겨지지 않도록 RecyclverView 인스턴스를 처음부터 다시 작성하는 것일 수 있습니다.

그러나 가능한 경우 문제를 마스킹하는 대신 문제를 더 잘 이해하고 소스에서 수정하고 싶습니다.

버그는 재현하기 쉽지 않지만 발생하면 치명적입니다.

전체 스택 추적 :

W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
    E/AndroidRuntime( 7546): FATAL EXCEPTION: main
    E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
    E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
    E/AndroidRuntime( 7546):    at andro

2
질문 : 재생산은 얼마나 일관성이 있습니까? Google 코드의 여기여기에 오류가 있음을 알고 있습니다 . 그러나 이것은 피할 수 있습니다. 자, 이것이 매 회전마다 발생합니까?
VicVu 2016 년

안녕하세요. 거의 발생하지 않지만 발생하면 앱에 치명적입니다.
KarolDepka

버그 링크를 주셔서 감사합니다. 첫 번째는 두 번째 것보다 더 관련이있는 것 같습니다.
KarolDepka

1
네, 가장 좋은 방법은 회전 중에 목록보기를 변경하지 않는 것입니다.
VicVu 2016 년

1
쉽게 재현 할 수 있다면 'notify *'를 호출하기 전에 'getItemCount'의 값을 인쇄하는 것이 좋습니다 ... 항목 수가 가정과 일치하지 않을 수 있습니다.
Rich Ehmer 2016 년

답변:


209

RecyclerView를 사용하여 새 활동 인스턴스를 입력하는 것과 관련하여 (아마도) 관련 문제가 있었지만 더 작은 어댑터를 사용하면이 충돌이 발생했습니다.

RecyclerView.dispatchLayout()전화하기 전에 스크랩에서 품목을 꺼내려고 시도 할 수 있습니다 mRecycler.clearOldPositions(). 결과적으로 어댑터 크기보다 높은 위치를 가진 공통 풀에서 항목을 가져 오는 것입니다.

다행히도 PredictiveAnimations활성화 된 경우에만이 작업을 수행 하므로 내 솔루션은 하위 클래스 GridLayoutManager( LinearLayoutManager동일한 문제 및 '수정')이며 supportsPredictiveItemAnimations()false를 반환하도록 재정의 했습니다.

/**
 * No Predictive Animations GridLayoutManager
 */
private static class NpaGridLayoutManager extends GridLayoutManager {
    /**
     * Disable predictive animations. There is a bug in RecyclerView which causes views that
     * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
     * adapter size has decreased since the ViewHolder was recycled.
     */
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public NpaGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }
}

4
이것은 나를 위해 일했으며 예측 애니메이션을 비활성화해도 애니메이션이 모두 손실되지는 않습니다. 브라보.
Robert Liberatore

8
정말로 감사합니다! LinearLayoutManager로 즉시 작업하여 일을 절약 할 수 있습니다.
levavare

8
고마워 이 솔루션은 LinearLayoutManager와 함께 작동합니다.
Pruthviraj

8
나는이 사람이 우리가 자신의 소중한 도움의 영광에서 동상을 만들 자격이 ... 그것은 웹에서 최악의 문서화 된 문제 중 하나라고 생각하지만 dev에 만남이 문제가 많은 것 같다 ... 난 그냥 궁금해 하는 방법 당신은 할 수 찾았어요 PredictiveAnimations가 false 인 경우 건너 뛰십시오. @KasHunt? 스택 트레이스가 매우 불분명하기 때문에 ...
PAD

4
아무도이 해킹없이 그것을 고치는 방법을 알고 있습니까? notifyDatasetChanged가 DiffUtil을 선호하여 삭제되었으므로
Anton Shkurenko

83

필자의 경우 (데이터 구조에서 데이터 삭제 / 삽입) 재활용 풀을 지우고 데이터 세트가 변경되었음을 알리는 것이 필요했습니다!

mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();


6
나는 일반적으로 이것을 말하지는 않지만 너무 감사합니다. 나는 목록에서 많은 항목을 빠른 순서로 움직일 때 산발적으로 발생하는이 충돌을 해결하기 위해 모든 것을 시도했습니다. 나는이 문제를 해결하기 위해 문자 그대로 1 주일을 보냈습니다. 나는 뇌에 다르게 접근 할 수있는 기회를주기 위해 몇 달 동안 그것에서 멀어졌고, 첫 번째 Google 시도에서 이것을 발견했다. 축복합니다!
Chantell Osejo

쿠키 하나 하나를 갖도록하겠습니다. 감사합니다.
antonis_st

왜 이렇게해야합니까?
dabluck

9
그것은 뷰를 재활용하는 목적을 무너 뜨리는 꽤 무거운 작업입니다.
gjsalot

@gjsalot 그래서 이것을 사용하면 문제가 발생할 수 있습니까?
Sreekanth Karumanaghat

38

이 경우에 notifyDataSetChanged()대신 사용하십시오 notifyItem....


5
어떤 경우에는 이것이 갈 길입니다. 모든 항목을 교체 한 상황이 있었지만 어댑터에 정직하지 않았으며 항목을 제거했다는 사실을 알리지 않고 새로운 항목 (notifyItemRangeInserted)을 삽입했다는 사실 만 알았습니다. 그런 다음 어댑터는 실제로 더 많은 항목이있을 것으로 예상했습니다. notifyItemRangeRemoved / Insert / Updated와 같은 어댑터의 notify 메소드를 사용하여 notifyDataSetChanged가 예상되는 경우 호출자는 어댑터에게 변경된 내용을 정확하게 알려주는 전적인 책임이 있거나이 "일관되지 않은 상태"로 끝날 수 있습니다.
JHH

19
이것은 전혀 해결책이 아닙니다.
Miha_x64

이것은 갈 길이 아닙니다. 이것이 효과가 있다면 notifyItem...모든 항목을 다시 렌더링하는 대신 작동하기 시작하는 범위를 엉망 으로 만들었습니다.
Ranjan

12

mRecycler.setAdapter(itemsAdapter)어댑터에 모든 항목을 추가 한 후 경작 을 지연시켜 해결 mRecycler.addAll(items)했습니다. 내가 그것을 시작한 이유를 모르겠습니다. 라이브러리 코드를 살펴보고 그 줄을 "잘못된 순서"로 보았습니다. 이것이 확실합니다. 누군가가 왜 그런지를 설명 할 수 있는지 확인하십시오. 그래서? 이것이 올바른 답변인지 확실하지 않습니다.


나는 이것이 해결책이라고 생각합니다. 어댑터를 지연시킨 후에는 괜찮습니다 ... 지금 UI 스레드에서 어댑터를 설정하고 항목을 추가 할 때 팝업됩니다.
EngineSense

18
swapAdapter(adapter, true)대신에 사용 setAdapter(adapter)했고 도움이되었습니다.
frangulyan

11

비슷한 문제가 있었지만 정확히 같지는 않았습니다. 제 경우에는 1 지점에서 recyclerview에 전달 된 배열을 지우고있었습니다.

mObjects.clear();

recyclerview가 즉시보기를 지우고 싶지 않기 때문에 notifyDataSetChanged를 호출하지 마십시오. AsyncTask에서 mObjects 배열을 다시 채우고있었습니다.


9

recyclerView와 동일한 문제가 있었으므로 목록을 지우 자마자 데이터 세트 변경에 대해 어댑터에 알 렸습니다.

mList.clear();
mAdapter.notifyDataSetChanged();

mList.addAll(newData);
mAdapter.notifyDataSetChanged();

1
이 간단한 실수로 많은 시간을 허비했습니다. 정말 감사합니다!
leb1755

7

나는 같은 문제가 있습니다. 빠르게 스크롤하고 API를 호출하고 데이터를 업데이트 할 때 발생했습니다. 충돌을 막기 위해 모든 것을 시도한 후에 해결책을 찾았습니다.

mRecyclerView.stopScroll();

작동합니다.


이것은 해결 방법이 아닙니다. 스크롤을 중지하도록 강요하고 있습니다. 나쁜 UX
박사 aNdRO

1
@ Dr.aNdRO : 어댑터가 위치를 설정해야하며 recylerview를 계속 스크롤하면 어댑터가 충돌의 원인이되는 데이터를 설정할 수 없습니다. 그것은 나쁜 UX가 아닙니다
Anand Savjani

1
이해합니다. 데이터 새로 고침이 발생하므로 스크롤 중지가 좋지 않습니다.
Sush

6

RecyclerView백그라운드에서에 대한 데이터를 변경 하고 Thread있습니다. 나는 ExceptionOP 와 같은 것을 얻었다 . 데이터를 변경 한 후 이것을 추가했습니다.

myRecyclerView.post(new Runnable() {
    @Override
    public void run() {
        myRecyclerAdapter.notifyDataSetChanged();
    }
});

그것이 도움이되기를 바랍니다.


고마워요! 이것은 안드로이드 개발 관점에서 볼 수있는 유일한 대답입니다.
user347187

의 도움으로 해결했지만 view.recycler_view.post사용했습니다 notifyItemInserted. 제 경우에는 이미 UI 스레드였습니다.
CoolMind

6

이 오류는 사용자가 스크롤하여 항목 홀더의 위치를 ​​변경하고 UI에서 목록과 항목 사이의 참조가 손실되고 다음 "notifyDataSetChanged" 요청 에서 오류가 발생하면 어댑터의 목록이 지워질 때 발생 합니다.

고치다:

업데이트 목록 방법을 검토하십시오. 당신이 같은 것을하면

mainList.clear();
...
mainList.add() or mainList.addAll()
...
notifyDataSetChanged();

===> Error occur

어떻게 고치는 지. 버퍼 처리를위한 새로운리스트 객체를 생성하고 그 후 메인리스트에 다시 할당

List res = new ArrayList();
…..
res.add();  //add item or modify list
….
mainList = res;
notifyDataSetChanged();

이 큰 도움 을 주신 Nhan Cao 에게 감사드립니다. :)


4

Adapter참조 대신 항목 배열의 복사본을 사용하도록 구현을 수정 한 후 문제가 해결되었습니다 . 이 setItems()메소드는 새로운 아이템이 나타날 때마다 호출됩니다.RecyclerView .

대신에:

private class MyAdapter extends RecyclerView.Adapter<ItemHolder> {
     private List<MyItem> mItems;  

    (....)

    void setItems(List<MyItem> items) {
        mItems = items;
    }
}

나는했다 :

void setItems(List<MyItem> items) {
    mItems = new ArrayList<>(items);
}

이렇게하면 문제가 해결되지만 원래 메모리의 두 배를 차지하지 않습니까?
Sreekanth Karumanaghat

@ MiguelA.Gabriel 이것이 성능에 영향을 미칩니 까? 예를 들어 내 경우에는 recylerview의 배열을 너무 자주 업데이트하므로 현재 이것을 suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true); 하고 있으며 이것이 내 생성자입니다 public CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Mateen Chaudhry

@ mateen-chaudhry 아마 그럴 것입니다. 귀하의 경우 테스트하고 제안 된 솔루션 중 다른 솔루션을 사용하여 결정하거나 시도해야합니다. 내가 말했듯이, 그것은 단지 해결 방법 일 뿐이며 제 경우에는 저에게 효과적입니다.
미겔 A. 가브리엘

3

나는 같은 상황에 직면했다. 그리고 컬렉션을 지우기 전에 코드를 추가하여 해결되었습니다.

mRecyclerView.getRecycledViewPool().clear();


3

필자의 경우 항목을 업데이트하고 notifyDataSetChangedUI가 아닌 스레드를 호출 했습니다. 대부분의 경우 효과가 있었지만 많은 변경 사항이 빠르게 발생하면 충돌이 발생했습니다. 내가 대신에, 기본적으로

activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        changeData();
        notifyDataSetChanged();
    }
});

그런 다음 충돌이 중지되었습니다.


3

당신은 목록을 지우고 할 필요는 OnPostExecute()없습니다.Pull to Refresh

// Setup refresh listener which triggers new data loading
        swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {

                AsyncTask<String,Void,String> task = new get_listings();
                task.execute(); // clear listing inside onPostExecute

            }
        });

난 당신이 중 스크롤 할 때 이런 일이 발생한다는 것을 발견 새로 고침을 끌어 나는 전에 목록을 삭제 한 이후 async task에 그 결과, java.lang.IndexOutOfBoundsException: Inconsistency detected.

        swipeContainer.setRefreshing(false);
        //TODO : This is very crucial , You need to clear before populating new items 
        listings.clear();

그렇게하면 불일치로 끝나지 않을 것입니다


2

또한 어댑터를 동시에 여러 번 설정하는 것과 관련 될 수 있습니다. 나는 동시에 5-6 번 트리거 된 콜백 메소드를 가지고 있었고 그 콜백에서 어댑터를 설정하여 RecycledViewPool이 모든 데이터를 동시에 처리 할 수 ​​없었습니다. 뚱뚱한 기회이지만 어쨌든 확인하는 것이 좋습니다.


1
예, 같은 문제입니다.하지만 해결책 은요? 당신은 이유를 제공합니다. 어떻게 고쳐?
Ranjith Kumar

@RanjithKumar, 위의 문제를 해결하는 방법을 공유하십시오. mRecyclerView.getRecycledViewPool (). clear ();를 사용하여 해결했습니다. notifyDataSetChanged 전에 어댑터의 업데이트 기능 동기화 주변 블록을 이용하여
Attiq 레흐만 UR

당신이 내 코드를 살펴주십시오 수 있습니다 난 내 문제는 당신이 당신이 나를 도울 수있는 것처럼 생각 stackoverflow.com/questions/50213362/...
Mateen 드리

2

사용하다

notifyDataSetChanged()

대신에

notifyItemRangeInserted(0, YourArrayList.size())

이 경우


1
그러나 이것은 성능에 좋지 않습니다. notifyItemRangeInserted가 더 좋습니다, 문제는 여기에 있지 않습니다
Derekyy

2

이 문제를 해결하려면 재활용보기를 업데이트하기 전에 빈 목록으로 notifyDataSetChanged ()를 호출하십시오.

예를 들어

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear (); // 업데이트 재활용보기 목록

adapter.notifyDataSetChanged();

2
해결책이 아닙니다.
Miha_x64

@ Milla 충돌 문제를 해결하는 다른 솔루션을 얻지 못했습니다. 그러나 위의 해결책은 나를 위해 일했습니다. 해결책이 아닌 경우 적절한 수정 방법을 알려주십시오.
EKN

때에 따라 다르지. RecyclerView 컨텐츠를 업데이트하기위한 범용 도구 인 DiffUtil을 사용해보십시오.
Miha_x64

2

내 경우에는 방금 줄을 제거했습니다. setHasStableIds(true);


그러나 HasStableIds (true)는 Rv의 성능을 향상시킵니다. 대체 솔루션이 있습니까?
Sreekanth Karumanaghat

실제로 다른 이유로 인해 발생할 수 있다고 생각하므로 근본 원인이 무엇인지에 따라이 문제에 대한 다른 해결책이있을 수 있습니다.
Sreekanth Karumanaghat

2

필자의 경우 백그라운드 스레드에서 어댑터 내용변경 하려고 했지만 main / ui 스레드에서 notify *라고했습니다.

불가능합니다! notify가 주 스레드로 강제되는 이유는 recyclerview가 동일한 호출 스택에서도 주 스레드에서 백업 어댑터를 편집하기를 원하기 때문입니다.

문제를 해결하려면 어댑터에 대한 모든 작업과 모든 notify ... 호출이 ui / main 스레드에서 수행되어야합니다 !


2
어댑터의 목록에 항목을 추가하는 작업은 백그라운드 스레드에서 수행하고 postexecute에 대해 notify를 호출해야합니다. 응용 프로그램의 몇 밀리 초에 대한 동결 또는 초 UI 스레드에서 데이터하게 추가 할 경우 추가 많은 데이터
디온 llorera

@dionellorera와 동의 한 경우, "어댑터 내용의 변경"은 기본 값, 객체의 속성 또는 객체 자체와 상관없이 데이터를 직접 수정하는 것을 의미합니다.
OzzyTheGiant

2

최근에 새로운 Android Architecture Components를 사용하여이 불쾌한 스택 추적에 부딪 쳤습니다. 본질적으로 ViewData에는 LiveData를 사용하여 Fragment에서 관찰되는 항목 목록이 있습니다. ViewModel이 데이터에 대한 새 값을 게시하면 프래그먼트가 어댑터를 업데이트하여 새 데이터 요소를 전달하고 변경 사항이 있음을 어댑터에 알립니다.

불행히도 새로운 데이터 요소를 어댑터에 전달할 때 ViewModel과 Adapter가 동일한 객체 참조를 가리키는 사실을 설명하지 못했습니다! 데이터를 업데이트하고 전화하면postValue() ViewModel 내에서 하면 데이터가 업데이트되고 어댑터에 아직 알림이 표시되지 않는 매우 작은 창이 있습니다!

내 수정은 어댑터에 전달 될 때 요소의 새로운 사본을 인스턴스화하는 것이 었습니다.

mList = new ArrayList<>(passedList);

이 매우 쉬운 수정 사항을 사용하면 어댑터에 통지하기 직전까지 어댑터 데이터가 변경되지 않도록 할 수 있습니다.


2

이것은 위의 솔루션에서 많은 것을 시도해도 나를 위해 일한 솔루션입니다.

1.) Intilization

CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);

이 방법을 어댑터에 작성

public void updateList(List<StockListModel> list) {
stockListModels.clear();
stockListModels.addAll(list);
notifyDataSetChanged();
}

stockListModels->이 목록은 어댑터에서 사용중인 것입니다.


2

나를 위해 다음 코드 줄을 추가 한 후 작동했습니다.

mRecyclerView.setItemAnimator(null);

2
애니메이션을 원한다면 어댑터 코드를 다시 작성하고 변경 사항을 알리는 데 오류가 있는지 확인하십시오.
Dragos Rachieru

나는 실제 어댑터를 사용하고있어 흐름을 제어 할 수 없으며 windowActivityTransitions를 활성화 하여이 문제를 일으켜 하루 종일 저에게 감사합니다.
Arul Mani

1

이 문제는 목록을 지우려고 할 때 발생할 수 있습니다. 특히 데이터를 새로 고치려고 할 때 풀을 새로 고칠 때 부울 플래그를 사용하고 false로 초기화하고 OnRefresh 메서드 내에서 true로 설정하면 dataList가 지워집니다. 새 데이터를 추가하기 직전에 플래그가 참이면 거짓으로 만듭니다.

당신의 코드는 다음과 같습니다

 private boolean pullToRefreshFlag = false ;
 private ArrayList<your object> dataList ;
 private Adapter adapter ;

 public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{

 private void requestUpdateList() {

     if (pullToRefresh) {
        dataList.clear
        pullToRefreshFlag = false;
     }

     dataList.addAll(your data);
     adapter.notifyDataSetChanged;


 @Override
 OnRefresh() {
 PullToRefreshFlag = true
 reqUpdateList() ; 
 }

}

1

나는 이전에 같은 문제가있었습니다. 마침내 그 해결 방법을 찾았습니다.

내가하는 일은 어댑터에 항목이 제거되었음을 알리고 어댑터 데이터 세트 범위가 변경되었음을 알리는 것입니다.

 public void setData(List<Data> dataList) {
      if (this.dataList.size() > 0) {
          notifyItemRangeRemoved(0, dataList.size());
          this.dataList.clear();
      }
      this.dataList.addAll(dataList)
      notifyItemRangeChanged(0, dataList.size());

 }

1

비슷한 문제가 발생하여 방금 알아 냈습니다. 테스트 사례에 대한 몇 가지 예를 하드 코딩했지만 각각 고유 한 ID를 반환했는지 확인하지 않아 아래의 충돌이 발생했습니다. ID를 수정하면 문제가 해결되었습니다. 다른 사람에게 도움이 되었기를 바랍니다.


1

한 번도 오류가 발생했습니다.

원인 : 오래된 삭제 된 viewHolders를 가져 오는 동안 비동기 작업에서 Recycler View를 업데이트하려고했습니다.

코드 : 버튼을 누르면 데이터가 생성됩니다.

  1. 리사이클 러보기에서 마지막 항목 지우기
  2. 비동기 작업을 호출하여 데이터 생성
  3. OnPostExecute 리사이클 러보기 및 NotifyDataSetChanged 업데이트

문제 : 데이터를 생성하기 전에 빠르게 스크롤 할 때마다

불일치가 감지되었습니다. 유효하지 않은 뷰 홀더 어댑터 positionViewHolder java.lang.IndexOutOfBoundsException : 불일치가 발견되었습니다. 잘못된 품목 위치 20 (오프셋 : 2). 상태 : 3

솔루션 : 내 데이터를 생성하기 전에 RecyclerView를 지우는 대신 대신 그대로두고 다음과 같이 새 데이터 Call NotifyDatasetChanged로 바꾸십시오.

       @Override
        protected void onPostExecute(List<Objects> o) {
            super.onPostExecute(o);
            recyclerViewAdapter.setList(o);
            mProgressBar.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
        }

내 코드를 살펴볼 수 있습니까? 제 문제는 당신과 같다고 생각합니다. [link] ( stackoverflow.com/questions/50213362/… )
Mateen Chaudhry

1

통지하기 전에 레이아웃 관리자의 모든 뷰를 제거하십시오. 처럼:

myLayoutmanager.removeAllViews();

효과가있다. 스크롤로드 및 탭 변경에 문제가있었습니다.
Warwicky

1

사용하여 ListAdapter (androidx.recyclerview.widget.ListAdapter)전화를 adapter.submitList(null)호출하기 전에 adapter.submitList(list):

adapter.submitList(null)
adapter.submitList(someDataList)

0

설정 mRecycler.setLayoutFrozen (true); swipeContainer의 onRefresh 메소드에서

나를 위해 문제를 해결했습니다.

swipeContainer.setOnRefreshListener(new   SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            orderlistRecycler.setLayoutFrozen(true);
            loadData(false);

        }
    });

0

이것은 꽤 나쁜 버그입니다.

내 항목 클릭을 처리하기 위해이 질문RecyclerView.OnItemTouchListener 에서 찾은 솔루션과 유사한 구현을 사용했습니다 .

RecyclerView의 데이터 소스를 여러 번 새로 고치고 항목을 클릭하면 IndexOutOfBoundsException응용 프로그램이 중단됩니다. 항목을 클릭하면 RecyclerView내부에서 올바른 기본보기를 찾고 해당 위치를 다시 제공합니다. 소스 코드를 확인하면서 일정 이 Tasks있고 Threads일정 이 있음을 알았습니다 . 이야기를 짧게 말하면 기본적으로 두 개의 데이터 소스가 혼합되어 동기화되지 않고 모든 것이 열악한 불법 상태입니다.

이를 바탕으로 구현을 제거 하고 나 자신 RecyclerView.OnItemTouchListener의 클릭을 포착했습니다 .ViewHolderAdapter

public void onBindViewHolder (final BaseContentView holder, final int position) {

    holder.itemView.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick (View view) {

        // do whatever you like here
      }
    });

}

이것은 최선의 해결책은 아니지만 지금은 무 충돌입니다.. 희망적으로 이것은 당신에게 시간을 절약 할 것입니다 :).


onBind가 호출 될 때마다 새 객체를 만들면 많은 객체가 가비지 수집되고 사용자가 정지 될 수 있습니다.
dephinera

0

Lint는 불일치에 관한 조언을주었습니다. (onBindViewHolder ()) :

pholder.mRlayout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        doStuff(position);
                    }
                });

이것은 다음으로 대체되어야했습니다.

pholder.mRlayout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        doStuff(pholder.getAdapterPosition());
                    }
                });

코드에서 두 코드를 모두 실행 한 다음 전체 설명을 위해 Lint를 실행하십시오!

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