program story

RecyclerView에서 불일치가 감지되었습니다. 스크롤하는 동안 RecyclerView의 내용을 변경하는 방법

inputbox 2020. 12. 24. 23:41
반응형

RecyclerView에서 불일치가 감지되었습니다. 스크롤하는 동안 RecyclerView의 내용을 변경하는 방법


RecyclerView항목의 이름을 표시 하는 사용 하고 있습니다. 내 행에는 단일 TextView. 항목 이름은에 저장됩니다 List<String> mItemList.

의 내용을 변경하려면 RecyclerView에서 문자열을 바꾸고에서 ()를 mItemList호출 notifyDataSetChanged합니다 RecyclerViewAdapter.

하지만 mItemListRecyclerView가 스크롤 되는 동안 내용을 변경하려고하면 가끔java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588

의 크기 mItemList가 이전보다 작을 경우 발생합니다 . 그래서의 내용을 변경하는 올바른 방법은 무엇 RecyclerView입니까? 이 버그 RecyclerView입니까?

다음은 Exception의 전체 스택 추적입니다.

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3300)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258)
        at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803)
        at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302)
        at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
        at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1093)
        at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:956)
        at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:2715)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
        at android.view.Choreographer.doCallbacks(Choreographer.java:555)
        at android.view.Choreographer.doFrame(Choreographer.java:524)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
        at android.os.Handler.handleCallback(Handler.java:615)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4921)
        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:1027)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
        at dalvik.system.NativeStart.main(Native Method)

AdapterView 코드 :

private static class FileListAdapter extends RecyclerView.Adapter<FileHolder> {
    private final Context mContext;
    private final SparseBooleanArray mSelectedArray;
    private final List<String> mList;

    FileListAdapter(Context context, List<String> list, SparseBooleanArray selectedArray) {
        mList = list;
        mContext = context;
        mSelectedArray = selectedArray;
    }


    @Override
    public FileHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        View view = LayoutInflater.from(viewGroup.getContext()).inflate(
                R.layout.file_list_item, viewGroup, false);

        TextView tv = (TextView) view
                .findViewById(R.id.file_name_text);
        Typeface font = Typeface.createFromAsset(viewGroup.getContext().getAssets(),
                viewGroup.getContext().getString(R.string.roboto_regular));
        tv.setTypeface(font);

        return new FileHolder(view, tv);
    }

    @Override
    public void onBindViewHolder(FileHolder fileHolder, final int i) {

        String name = mList.get(i);

        // highlight view if selected
        setSelected(fileHolder.itemView, mSelectedArray.get(i));

        // Set text
        fileHolder.mTextView.setText(name);
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }
}

private static class FileHolder extends RecyclerView.ViewHolder {

    public final TextView mTextView;

    public FileHolder(View itemView, TextView tv) {
        super(itemView);
        mTextView = tv;
    }
}

편집 : 버그가 수정되었습니다. 동일한 예외가 계속 발생하면 메인 스레드에서만 어댑터 데이터 소스를 업데이트하고 그 후에 적절한 어댑터 알림 메소드를 호출하는지 확인하세요.

이전 답변 : 의 버그 인 것 같습니다 . 여기여기에RecyclerView 보고 되었습니다 . 다음 릴리스에서 수정되기를 바랍니다.


나에겐 문제가 아니야. NotifyDataSetChanged (); 사용

public class MyFragment extends Fragment{

    private MyAdapter adapter;

    // Your code

    public void addArticle(){
        ArrayList<Article> list = new ArrayList<Article>();
        //Add one article in this list

        adapter.addArticleFirst(list); // or adapter.addArticleLast(list);
    }
}

public class ArticleAdapterRecycler extends RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder> {

    private ArrayList<Article> Articles = new ArrayList<Article>();
    private Context context;


    // Some functions from RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder>    

    // Add at the top of the list.

    public void addArticleFirst(ArrayList<Article> list) {
        Articles.addAll(0, list);
        notifyDataSetChanged();
    }

    // Add at the end of the list.

    public void addArticleLast(ArrayList<Article> list) {
        Articles.addAll(Articles.size(), list);
        notifyDataSetChanged();
    }
}

데이터가 변경 될 때 RecyclerView의 스크롤을 금지하십시오.

내 코드처럼 :

mRecyclerView.setOnTouchListener(
        new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (mIsRefreshing) {
                    return true;
                } else {
                    return false;
                }
            }
        }
);

추가 정보 : http://drakeet.me/recyclerview-bug-indexoutofboundsexception-inconsistency-detected-invalid-item-position-solution


이 문제에 대한 몇 가지 유용한 하이퍼 링크가 허용 된 답변 에 제공 RecyclerView되지만 스크롤하는 동안 의이 동작이 버그 라는 것은 사실이 아닙니다 .

이 예외가 표시되면의 내용 RecyclerView이 "변경"된 후 어댑터에 알리는 것을 잊었을 것입니다 . 사람들 notifyDataSetChanged()은 항목이 데이터 세트에 추가 된 후에 만 호출 합니다. 그러나 어댑터를 다시 채운 후에도 불일치가 발생하는 것은 물론 항목을 제거하거나 데이터 세트를 지울 때도 어댑터에이 변경 사항을 알려 뷰를 새로 고쳐야합니다.

public void refillAdapter(Item item) {

    adapter.add(item);
    notifyDataSetChanged();

}

public void cleanUpAdapter() {

    adapter.clear();
    notifyDataSetChanged(); /* Important */

}

제 경우에는에서 어댑터를 정리하고에서 onStop()다시 채우려 고했습니다 onStart(). notifyDataSetChanged()을 사용하여 어댑터를 청소 한 후 전화하는 것을 잊었습니다 clear(). 그런 다음 데이터 세트가 다시로드 되는 동안 상태를에서 onStop()변경 onStart()하고 신속하게 스크롤 할 때마다이 RecyclerView예외가 발생했습니다. 스크롤하지 않고 다시로드가 끝날 때까지 기다리면 이번에는 어댑터를 원활하게 복원 할 수 있기 때문에 예외는 없을 것입니다.

요컨대, RecyclerView뷰 변경시 일관성이 없습니다. 데이터 세트의 변경 사항이 처리되는 동안보기를 스크롤하려고하면이 표시 java.lang.IndexOutOfBoundsException: Inconsistency detected됩니다. 이 문제를 해결하려면 데이터 세트가 변경된 후 즉시 어댑터에 알려야합니다.


문제는 확실히 recyclerview 스크롤링 때문이 아니라 notifyDataSetChanged () 와 관련이 있습니다. 데이터를 추가하고 제거하는 등 데이터를 지속적으로 변경하는 리사이클 러 뷰가있었습니다. 목록에 항목을 추가 할 때마다 notifyDataSetChanged ()를 호출 했지만 항목이 제거되거나 목록이 지워질 때마다 어댑터를 새로 고치지 않았습니다 .

따라서 수정하려면 :

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:12 at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5456)

나는 그것이 필요한 곳마다 list.clear () 이후 adapter.notifyDataSetChanged ()를 호출 했습니다.

if (!myList.isEmpty()) {
        myList.clear();
        myListAdapter.notifyDataSetChanged();
    }

그 이후로 예외가 발생하지 않았습니다. 다른 사람들에게도 똑같이 작동하기를 바랍니다. :)


나는 같은 문제에 직면했다, java.lang.IndexOutOfBoundsException : Inconsistency detected .

사용자 지정 LinearLayoutManager를 만듭니다 .

HPLinearLayoutManager.java

public class HPLinearLayoutManager extends LinearLayoutManager {

    public HPLinearLayoutManager(Context context) {
        super(context);
    }

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

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

    /**
     * Magic here
     */
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }
}

인스턴스만듭니다 HPLinearLayoutManager.

HPLinearLayoutManager hpLinearLayoutManager = new HPLinearLayoutManager(mContext);
recyclerView.setLayoutManager(hpLinearLayoutManager);

이것이 당신을 도울 수 있기를 바랍니다.


비슷한 문제가 있었지만 내용을 삭제했습니다. 애니메이션도 유지하고 싶었습니다. 나는 notifyRemove를 사용한 다음 범위를 전달했습니다. 이것은 문제를 해결하는 것 같습니다 ...

public void deleteItem(int index) {
    try{
        mDataset.remove(index);
        notifyItemRemoved(index);
        notifyItemRangeRemoved(index,1);
    } catch (IndexOutOfBoundsException e){
        notifyDataSetChanged();
        e.printStackTrace();
    }
}

작동하고 IOB 예외를 제거하는 것 같습니다 ...


나는 나 에게이 예외는 두 가지가 동시에 일어날 때 발생한다는 것을 알아 냈습니다.

1) recyclerview 스크롤

2) 데이터 세트 변경

그래서 notifydatasetchanged가 호출 될 때까지 스크롤을 비활성화하여이 문제를 해결했습니다.

leaderAdapter.notifyDataSetChanged();
pDialog.hide();

스크롤을 비활성화하기 위해 setCancelable이 false 인 진행 대화 상자를 사용했습니다.

pDialog = new ProgressDialog(getActivity());
pDialog.setMessage("Please wait...");
pDialog.setCancelable(false);

여기서 트릭은 데이터 세트가 업데이트 된 경우에만 스크롤을 활성화하는 것입니다.


이 문제는 다음을 사용하는 경우 recyclerview에 발생합니다.

adapter.setHasStableIds(true);

그렇게 설정 한 경우이를 제거하고 어댑터 내부의 데이터 세트를 업데이트하십시오.
그래도 문제가 발생하면 새 데이터를 얻은 후 모든보기를 무효화 한 다음 데이터 세트를 업데이트하세요.


이전 답변 ( https://stackoverflow.com/a/26927186/3660638 ) 에서 Cocorico 제안을 사용하여 작동하도록 했지만 캐치 SortedList가 있습니다. 데이터가 변경 될 때마다 notifyDataSetChanged ()를 사용하고 있기 때문에 ( 추가, 제거 등)로 인해 얻은 항목 애니메이션을 잃게 notifyItemXXXXX(position)되므로 결과적으로 데이터를 일괄 적으로 변경할 때만 다음과 같이 사용했습니다.

public void addAll(SortedList<Entity> items) {
    movieList.beginBatchedUpdates();
    for (int i = 0; i < items.size(); i++) {
        movieList.add(items.get(i));
    }
    movieList.endBatchedUpdates();
    notifyDataSetChanged();
}  

getitem 수에 사용해야합니다.

public int getItemCount() {

            if (mList!= null)
                return mList.size();
            else
                return 0;
        }

리사이클 러 뷰를 새로 고침하면 이것을 사용하십시오

if (recyclerView.getAdapter() == null) {

            recyclerView.setHasFixedSize(true);
            mFileListAdapter= new FileListAdapter(this);
            recyclerView.setAdapter(mFileListAdapter);
            recyclerView.setItemAnimator(new DefaultItemAnimator());
        } else {
            mFileListAdapter.notifyDataSetChanged();        

        }

이 솔루션을 사용하면 문제를 해결할 수 없습니다. onBindViewHolder 내부의 조건을 사용하여 java.lang.IndexOutOfBoundsException을 해결하기 만하면됩니다.

 public void onBindViewHolder(FileHolder fileHolder, final int i) {
        if(i < mList.size)
        {
           String name = mList.get(i);       
           setSelected(fileHolder.itemView, mSelectedArray.get(i));
           fileHolder.mTextView.setText(name);
        }
    }

CustomLinearLayoutManager를 만듭니다.

public class CustomLinearLayoutManager extends LinearLayoutManager {

public CustomLinearLayoutManager(Context context) {
        super(context);
    }

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

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

    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();

        }
    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            return super.scrollVerticallyBy(dy, recycler, state);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
}

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

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

도움이되기를 바랍니다.


일부 선택 기준에 따라 새 어댑터 인스턴스를 설정할 때 동일한 문제가 발생했습니다.

RecyclerView.swapAdapter(adapter, true)새 어댑터를 설정할 때 를 사용하여 문제를 해결했습니다 .


이 문제와 동일한 문제가 있으며 검색하고 해결하는 데 매우 피곤합니다. 그러나 나는 해결에 대한 답을 찾았고 예외는 다시 던져지지 않았습니다.

public class MyLinearLayoutManager extends LinearLayoutManager 
{
    public MyLinearLayoutManager(Context context) {
        super(context);
    }

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

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

    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        //override this method and implement code as below
        try {
            super.onLayoutChildren(recycler, state);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

이 답변으로 문제가 해결되기를 바랍니다.


I have replicated this issue. This happened when we remove items in the background thread from mList but dont call notifyDataSetChanged(). Now If we scroll This exception is comming.

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 86(offset:86).state:100

Initially I had 100 items and removed few items from background thread.

Seems like Recyclerview calls getItemCount() itself to validate the state.


Avoid notifyDatasetHasChanged() and do the following:

public void setItems(ArrayList<Article> newArticles) {
    //get the current items
    int currentSize = articles.size();
    //remove the current items
    articles.clear();
    //add all the new items
    articles.addAll(newArticles);
    //tell the recycler view that all the old items are gone
    notifyItemRangeRemoved(0, currentSize);
    //tell the recycler view how many new items we added
    notifyItemRangeInserted(0, articles.size());
}

I dont see anthing wrong with the code you posted. The only thing weird to me is this line

setSelected(fileHolder.itemView, mSelectedArray.get(i));

in your onBindViewHolder method in the adapter.. are you updating this array too when you change the size of your list of items in the array?


I had similar problem while i try to add first item into recyclerView with notifyItemInserted method, so i modified addItem function on my adapter as below and it resolved.

Weird problem, hope that it'll be fixed soon thoug!

public void addItem(int position, TableItem item) {
    boolean firstEntry = false;
    if (items.size() == 0) {
        firstEntry = true;
    }

    items.add(position, item);

    if (firstEntry) {
        notifyDataSetChanged();
    } else {
        notifyItemInserted(position);
    }
}

there is one sentence in sound code :
/** * Used when LayoutState is constructed in a scrolling state. It should * be set the amount of scrolling we can make without creating a new * view. Settings this is required for efficient view recycling. */ int mScrollingOffset; 


In my case it solved by changing mRecyclerView.smoothScrollToPosition(0) to

mRecyclerView.scrollToPosition(0)

I also had the same issue and I have fixed it with not using notifyItemRangeChanged() method. It is nicely explained at

https://code.google.com/p/android/issues/detail?id=77846#c10


try to use a boolean flag, initialize it as false and inside OnRefresh method make it true, clear your dataList if flag is true just before adding the new data to it and after that make it false.

your code might be like this

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

}

In my case the problem was me.

My setup is a Recyclerview, Adapter & Cursor/Loader mechanism.

At one point in my App the loader is destroyed.

supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)

I was expecting the Recyclerview would display an empty list since i just deleted their datasource. What makes the error finding more complicated was, that the list was visible and the well known Exception occured only on a fling/scroll/animation.

That cost me a few hrs. :)


do this when you want to add view(like notifyData or addView or something like that)

if(isAdded()){ 
    // 
    //  add view like this.
    //
    //  celebrityActionAdapter.notifyItemRangeInserted(pageSize, 10);
    //
    //
}

ReferenceURL : https://stackoverflow.com/questions/26827222/inconsistency-detected-in-recyclerview-how-to-change-contents-of-recyclerview-w

반응형