Несоответствие обнаружено в RecyclerView, как изменить содержимое RecyclerView при прокрутке



я использую RecyclerView для отображения названия элементов. Моя строка содержит один TextView. Имена элементов хранятся в List<String> mItemList.



изменить содержимое RecyclerView, я заменяю строки в mItemList и звонок notifyDataSetChanged () on RecyclerViewAdapter.



но если я попытаюсь изменить содержимое mItemList пока RecyclerView прокручивается, иногда это дает мне
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588



это происходит, если размер mItemList меньше, чем раньше. Итак, как правильно изменить содержимое из RecyclerView ? Это ошибка в RecyclerView ?



вот полный стек трассировки исключения:



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;
}
}
733   24  

24 ответов:

Edit: Теперь ошибка исправлена, если вы все еще получаете то же исключение, убедитесь, что вы обновляете источник данных адаптера только из основного потока и вызываете соответствующий метод уведомления адаптера после него.

ответ: кажется, это ошибка в 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(). У меня было представление recycler, в котором я постоянно изменял данные, т. е. добавлял и удалял данные. Я звонил 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)

I называется адаптер.notifyDataSetChanged() после списка.очистить(), где это было необходимо.

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

С тех пор я никогда не сталкивался с исключением. Надеюсь, что это работает для других. :)

У меня была аналогичная проблема, но с удалением содержимого. Я также хотел сохранить анимацию тоже. Я закончил тем, что использовал notifyRemove, а затем передал диапазон. Это, кажется, исправить любые проблемы...

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

Кажется, работает и избавляется от исключения IOB...

я столкнулся с той же проблемой, java.ленг.IndexOutOfBoundsException: обнаружена несогласованность.

создать Custom 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);

надеюсь, что это поможет вам.

Я думаю, что для меня это исключение происходит, когда две вещи происходят одновременно, т. е.

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

Если вы установили так, удалите это и обновите свой набор данных внутри адаптера;
если вы все еще сталкиваетесь с этой проблемой, аннулируйте все представления после получения новых данных, а затем обновите свой набор данных.

я получил эту работу, используя предложение Cocorico в предыдущем ответе (https://stackoverflow.com/a/26927186/3660638) но есть загвоздка: так как я использую 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 count

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.ленг.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);
        }
    }

У меня была такая же проблема, когда я устанавливал новый экземпляр адаптера на основе некоторых критериев выбора.

я исправил свою проблему с помощью RecyclerView.swapAdapter(adapter, true) Когда мы устанавливаем новый адаптер.

Я повторил эту проблему. Это произошло, когда мы удаляем элементы в фоновом потоке из mList, но не вызываем notifyDataSetChanged(). Теперь, если мы прокручиваем это исключение идет.

java.ленг.IndexOutOfBoundsException: обнаружена несогласованность. Недопустимое положение элемента 86 (смещение:86).состояние:100

первоначально у меня было 100 элементов и удалил несколько элементов из фонового потока.

похоже, Recyclerview вызывает getItemCount () сам для проверки государство.

избегайте notifyDatasetHasChanged () и выполните следующие действия:

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

Я не вижу ничего плохого в коде, который вы опубликовали. Единственное, что странно для меня это линия

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

в вашем методе onBindViewHolder в адаптере.. вы также обновляете этот массив при изменении размера списка элементов в массиве?

У меня была аналогичная проблема, когда я пытаюсь добавить первый элемент в recyclerView с помощью метода notifyItemInserted, поэтому я изменил функцию addItem на своем адаптере, как показано ниже, и она разрешилась.

странная проблема, надеюсь, что это будет исправлено в ближайшее время 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);
    }
}

в звуковом коде есть одно предложение:
/** * Используется, когда LayoutState строится в состоянии прокрутки. Он должен * установить время прокрутки, мы можем сделать без создания нового * вид. Настройки это необходимо для эффективной утилизации вида. */ int mScrollingOffset;

в моем случае это решается изменением mRecyclerView.smoothScrollToPosition(0) to

mRecyclerView.scrollToPosition(0)

У меня также была та же проблема, и я исправил ее, не используя метод notifyItemRangeChanged (). Это хорошо объяснено в

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

У меня такая же проблема с этой проблемой, я очень устал искать и решать ее. Но я нашел ответ, чтобы решить и исключения не были выброшены снова.

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

Я надеюсь, что этот ответ будет решить вашу проблему.

создать 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;
    }
}

попробуйте использовать логический флаг, инициализируйте его как false и внутри метода OnRefresh сделайте его true, очистите свой dataList, если флаг true непосредственно перед добавлением новых данных к нему и после этого сделайте его false.

ваш код может быть такой

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

}

в моем случае проблема была во мне.

моя настройка-это механизм Recyclerview, Adapter & Cursor / Loader.

в какой-то момент в моем приложении загрузчик уничтожается.

supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)

Я ожидал, что Recyclerview будет отображать пустой список, так как я только что удалил их источник данных. Что делает поиск ошибок более сложным, так это то, что список был виден, и хорошо известное исключение произошло только на a бросок / прокрутка / анимация.

это стоило мне нескольких часов. :)

Я изменяю данные для RecyclerView на фоне Thread. Я получил то же самое Exception как ОП. Я добавил Это после изменения данных:

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

надеюсь, что это помогает

Comments

    Ничего не найдено.