Подтвердить что ты не робот

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

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

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

Но если я попытаюсь изменить содержимое mItemList, в то время как RecyclerView прокручивается, иногда это дает мне  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;
    }
}
4b9b3361

Ответ 1

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

Старый ответ: Кажется, это ошибка в RecyclerView, она сообщила здесь и здесь, Надеюсь, он будет исправлен в следующем выпуске.

Ответ 2

Нет проблем для меня. Используйте 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();
    }
}

Ответ 4

Хотя в принятом ответе дается несколько полезных гиперссылок об этой проблеме, неверно, что это поведение 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. Чтобы устранить эту проблему, вы должны уведомить адаптер сразу после изменения набора данных.

Ответ 5

Проблема, конечно, не из-за прокрутки 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)

Я назвал adapter.notifyDataSetChanged() после list.clear(), где бы это ни было необходимо.

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

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

Ответ 6

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

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

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

Ответ 7

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

Создайте Пользовательский 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);

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

Ответ 8

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

1) Прокрутка recyclerview

2) изменение набора данных

Итак, я решил эту проблему, отключив прокрутку до вызова notifydatasetchanged.

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

Чтобы отключить прокрутку, я использовал диалог прогресса, чей setCancelable является ложным.

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

Хитрость здесь заключается в том, чтобы включить прокрутку только тогда, когда набор данных был обновлен.

Ответ 9

Эта проблема подходит для recyclerview, если вы используете

adapter.setHasStableIds(true);

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

Ответ 10

Я получил это, чтобы работать с использованием предложения Cocorico в предыдущем ответе (fooobar.com/questions/104971/...), но есть уловка: поскольку я использую 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();
}  

Ответ 11

Вы должны использовать в своем подсчете 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);
        }
    }

Ответ 12

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

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

Ответ 13

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

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

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

Кажется, что Recyclerview вызывает getItemCount() для проверки состояния.

Ответ 14

Избегайте 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());
}

Ответ 15

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

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

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

Ответ 16

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

Странная проблема, надеюсь, что скоро она будет исправлена!

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

Ответ 17

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

Ответ 18

В моем случае он был решен путем изменения mRecyclerView.smoothScrollToPosition(0) до

mRecyclerView.scrollToPosition(0)

Ответ 20

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

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

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

Ответ 21

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

Ответ 22

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

ваш код может выглядеть следующим образом

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

}

Ответ 23

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

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

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

supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)

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

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

Ответ 24

Я изменяю данные для RecyclerView в фоновом режиме Thread. Я получил тот же Exception, что и OP. Я добавил это после изменения данных:

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

Надеюсь, что это поможет

Ответ 25

делайте это, когда хотите добавить представление (например, notifyData или addView или что-то в этом роде)

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