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
KarolDepka
источник
2
Вопрос: насколько последовательна ваша репро? Я знаю, что это ошибка в коде Google здесь и здесь . Но этого можно избежать. Так происходит ли это при каждом повороте?
VicVu
Здравствуй. Это случается редко, но когда это происходит, это фатально для приложения.
KarolDepka
Спасибо за ссылки на ошибки. Первый кажется более актуальным, чем второй.
KarolDepka
1
Да, я думаю, что вам лучше всего не допускать изменений в просмотре списка во время ротации.
VicVu
1
Если бы вы могли легко воспроизвести, я бы предложил напечатать значение «getItemCount» до того, как все вызовы «уведомить *» ... вы можете обнаружить, что количество элементов не соответствует вашим предположениям.
Рич Эмер

Ответы:

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
Это сработало для меня, и отключение прогностической анимации не приводит к потере анимации вместе. Браво.
Роберт Либераторе
8
Большое спасибо! Работал мгновенно с LinearLayoutManager, вероятно, сэкономив мне дни.
Леваваре
8
Большое спасибо. Это решение работает с LinearLayoutManager.
Pruthviraj
8
Я думаю, что этот парень заслуживает того, чтобы мы построили статую в честь его драгоценной помощи ... Это одна из худших задокументированных проблем в Интернете, но, похоже, многие разработчики сталкиваются с этой проблемой ... Мне просто интересно, как вы узнали, что это могло быть пропущено, если PredictiveAnimations ложны, @KasHunt? Потому что трассировка стека очень неясна ...
PAD
4
Кто-нибудь знает, как это исправить без этого взлома? Потому что notifyDatasetChanged был отброшен в пользу DiffUtil
Антон Шкуренко
83

В моем случае (удалить / вставить данные в моей структуре данных) мне нужно было очистить пул утилизации и затем уведомить об изменении набора данных!

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

MatejC
источник
6
Обычно я так не говорю, НО СПАСИБО. Я пытался ВСЕ, чтобы исправить эту аварию, которая случается время от времени, когда я перемещаю кучу элементов в списке в быстром порядке. Я потратил буквально целую неделю, пытаясь решить эту проблему. Я отошел от него на пару месяцев, чтобы попытаться дать своему мозгу возможность подойти к нему по-другому, и обнаружил это в своей первой попытке Google. Будьте здоровы!
Chantell Osejo
Позвольте мне принести вам тонну печенья, потому что вы заслуживаете каждого из них. Спасибо.
antonis_st
зачем тебе это делать?
Даблук
9
Это довольно тяжелая операция, которая побеждает цель повторного использования представлений.
gjsalot
@gjsalot Так что, если я использую это, это может вызвать некоторые проблемы?
Sreekanth Karumanaghat
38

Используйте notifyDataSetChanged()вместо этого notifyItem...в этом случае.

khaintt
источник
5
В некоторых случаях это путь. У меня была ситуация, когда я заменил все свои элементы, но я не был честен с адаптером, просто сказал, что я вставил некоторые новые элементы (notifyItemRangeInserted), не говоря сначала, что я также удалил элементы. Затем адаптер ожидал, что будет больше элементов, чем было на самом деле. Если при использовании какого-либо из методов уведомления адаптера ожидается notifyDataSetChanged, например notifyItemRangeRemoved / Inserted / Обновлено, вызывающая сторона несет полную ответственность за то, чтобы сообщить адаптеру именно то , что было изменено, или вы можете столкнуться с этим «несовместимым состоянием».
JHH
19
Это не решение вообще.
Miha_x64
Это не путь. Если это работает, это означает, что вы просто перепутали диапазон notifyItem...и исправили, что начнет работать вместо того, чтобы перерисовывать все элементы.
Ранжан
12

Я решил это, отложив mRecycler.setAdapter(itemsAdapter)кассу после добавления всех элементов к адаптеру, mRecycler.addAll(items)и это сработало. Понятия не имею, почему я сделал это с самого начала, это было из кода библиотеки, который я просмотрел и увидел эти строки в «неправильном порядке», но я почти уверен, что это так, пожалуйста, если кто-то может подтвердить это, объясните, почему это так? Не уверен, если это правильный ответ, даже

Odaym
источник
Я думаю, что это решение, после того, как я задержал адаптер, все было в порядке, я верю ... теперь он появляется, когда устанавливает адаптер в поток пользовательского интерфейса и добавляет к нему элементы.
EngineSense
18
Я использовал swapAdapter(adapter, true)вместо, setAdapter(adapter)и это помогло.
франгулян
11

У меня была похожая проблема, но не точно такая же. В моем случае в 1 момент я очищал массив, который был передан в просмотрщик

mObjects.clear();

и не вызывая notifyDataSetChanged, поскольку я не хотел, чтобы просмотрщик-очиститель немедленно очищал представления. Я заново заполнял массив mObjects в AsyncTask.

Aalap
источник
9

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

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

mList.addAll(newData);
mAdapter.notifyDataSetChanged();
Реза
источник
1
Эта простая ошибка заставила меня потерять так много времени, большое спасибо!
leb1755
7

У меня та же проблема. Это происходило, когда я быстро прокручивал и вызывал API и обновлял данные. После попытки сделать все, чтобы предотвратить сбой, я нашел решение.

mRecyclerView.stopScroll();

Это сработает.

Ананд Савьяни
источник
Это обходной путь, а не исправление. Вы заставляете его остановить прокрутку. Bad UX
Доктор АНДРО
1
@ Dr.aNdRO: Адаптер должен установить положение, и если вы продолжите прокручивать recylerview, то адаптер не сможет установить данные, которые являются причиной сбоя. Это не плохо UX
Ананд Савьяни
1
имеет смысл. Остановка прокрутки не так уж плоха, так как происходит обновление данных.
Sush
6

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

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

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

Кашьяп Джимулия
источник
Спасибо чувак! это единственный ответ, который имеет смысл с точки зрения разработки Android.
user347187
Хотя я тоже решил с помощью view.recycler_view.post, я использовал notifyItemInserted. В моем случае это был уже поток пользовательского интерфейса.
CoolMind
6

Эта ошибка возникает, когда список в адаптере очищается при прокрутке пользователя, при которой изменяется положение держателя элемента, теряется ссылка между списком и элементом на пользовательском интерфейсе, возникает ошибка в следующем запросе «notifyDataSetChanged» .

Fix:

Просмотрите ваш метод обновления списка. Если вы делаете что-то вроде

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

===> Error occur

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

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

Спасибо Нхан Цао за эту большую помощь :)

Рамеш Покхарел
источник
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. Габриэль, это повлияет на производительность? например, в моем случае я обновляю массив recylerview слишком часто, поэтому в настоящее время я делаю это, suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true); и это мой конструктор public CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Mateen Chaudhry
@ mateen-chaudhry Вероятно, так и будет. Вам нужно будет проверить это в вашем случае и решить, или попробуйте использовать другое из предложенных решений. Как я уже сказал, это только обходной путь, и он работает для меня в моем случае.
Мигель А. Габриэль
3

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

mRecyclerView.getRecycledViewPool().clear();

MagicDroidX
источник
3

В моем случае я обновлял элементы и вызывал notifyDataSetChangedих не в пользовательском интерфейсе. Большую часть времени это работало, но когда много изменений происходило быстро, это приводило к краху. Когда я сделал, вместо этого, в основном

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

затем он перестал сбой.

Erhannis
источник
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();

Таким образом, вы не закончите с непоследовательностью

Fahad
источник
2

Это также может быть связано с настройкой адаптера несколько раз одновременно. У меня был метод обратного вызова, который запускался 5-6 раз в одно и то же время, и я устанавливал адаптер в этом обратном вызове, чтобы RecycledViewPool не мог обрабатывать все эти данные одновременно. Это большой шанс, но лучше все равно проверить.

Мустафа Гювен
источник
1
да та же проблема .. но решение? Вы приводите причину только .. Как исправить?
Ранджит Кумар
@RanjithKumar, пожалуйста, поделитесь своим способом решения вышеуказанной проблемы. Я решил это с помощью mRecyclerView.getRecycledViewPool (). Clear (); перед notifyDataSetChanged и использованием синхронизированного блока вокруг функции обновления адаптера
Attiq ur Rehman
Можете ли вы взглянуть на мой код, я думаю, что моя проблема, как ваша, вы можете помочь мне stackoverflow.com/questions/50213362/…
Mateen Chaudhry
2

использование

notifyDataSetChanged()

вместо

notifyItemRangeInserted(0, YourArrayList.size())

в таком случае.

Панкадж Талавия
источник
1
но это не хорошо для производительности, верно? notifyItemRangeInserted лучше, проблема здесь не
кроется
2

Для решения этой проблемы просто вызовите notifyDataSetChanged () с пустым списком перед обновлением представления корзины.

Например

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear (); // Список для вида обновления корзины

adapter.notifyDataSetChanged();
EKN
источник
2
Не решение
Miha_x64
@Milha У меня не было другого решения, чтобы решить проблему с аварией. Но вышеуказанное решение работает для меня. Если это не решение, скажите мне правильное решение.
EKN
Это зависит. Вы можете попробовать использовать DiffUtil - универсальный инструмент для обновления содержимого RecyclerView.
Miha_x64
2

В моем случае я просто удалил строку с setHasStableIds(true);

darkchaos
источник
Но HasStableIds (true) улучшает производительность Rv, есть ли альтернативное решение?
Sreekanth Karumanaghat
На самом деле, я думаю, что это может быть связано с разными причинами, поэтому могут быть разные решения этой проблемы в зависимости от первопричины.
Sreekanth Karumanaghat
2

В моем случае я пытался изменить содержимое моего адаптера в фоновом потоке, но вызвал notify * в основном / пользовательском потоке.

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

Чтобы решить эту проблему, убедитесь, что каждая операция с вашим адаптером, а также каждый вызов notify ... выполняется в потоке пользовательского интерфейса / main !

Йоханнес Мюенихсдорфер
источник
2
добавление элементов в список в вашем адаптере должно выполняться в фоновом потоке и вызывать notify на postexecute. добавление данных в потоке пользовательского интерфейса приводит к зависанию приложения на несколько миллисекунд или секунд при добавлении большого количества данных
dione llorera
согласившись с @dionellorera, следует четко указать, что «изменение содержимого адаптера», в частности, означает непосредственное изменение любых данных, будь то примитивные значения, свойства объектов или сами объекты
OzzyTheGiant,
2

Недавно я столкнулся с этой неприятной трассировкой стека с новыми компонентами архитектуры Android. По сути, у меня есть список элементов в моей ViewModel, которые наблюдаются моим фрагментом с использованием LiveData. Когда ViewModel публикует новое значение для данных, фрагмент обновляет адаптер, передавая эти новые элементы данных и уведомляя адаптер об изменениях.

К сожалению, при передаче новых элементов данных в адаптер я не смог учесть тот факт, что и ViewModel, и Adapter будут указывать на одну и ту же ссылку на объект! Это означает, что если я обновлю данные и вызову postValue()из ViewModel, появится очень маленькое окно, где данные могут быть обновлены, а адаптер еще не уведомлен!

Мое исправление состояло в том, чтобы создать новую копию элементов при передаче в адаптер:

mList = new ArrayList<>(passedList);

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

Стив
источник
2

Это единственное решение, которое сработало для меня, даже пробуя многие решения сверху.

1.) Использование

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

2.) Напишите этот метод в адаптере

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

stockListModels -> этот список, который вы используете в адаптере.

Рамкеш Ядав
источник
2

Для меня это сработало после добавления этой строки кода:

mRecyclerView.setItemAnimator(null);
Фатих Джи
источник
2
в большинстве случаев это не является исправлением, если вам нужна анимация, то вы переписываете код своего адаптера и обнаруживаете ошибки в уведомлении об изменениях
Dragos Rachieru
Он отлично работает для меня. Я использую настоящий адаптер, поэтому я не могу контролировать поток, и я включил windowActivityTransitions в стиле, который вызывает эту проблему, спасибо человеку, что вы сохранили мой день.
Арул Мани
1

эта проблема может возникнуть, когда вы пытаетесь очистить свой список, если вы собираетесь очистить свой список данных, особенно когда вы используете команду pull для обновления, попробуйте использовать логический флаг, инициализируйте его как false и внутри метода OnRefresh сделайте его истинным, очистите свой dataList Если флаг равен 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() ; 
 }

}
Моаз Н
источник
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());

 }
Cheng
источник
1

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

DocBot
источник
1

я тоже однажды получил ошибку:

Причина: я пытался обновить представление Recycler из асинхронной задачи, одновременно пытаясь получить старые удаленные viewHolders;

Код: я генерирую данные при нажатии кнопки, логика выглядит следующим образом

  1. Очистить последние элементы в представлении переработчика
  2. Вызов асинхронной задачи для генерации данных
  3. OnPostExecute Обновление представления Recycler и NotifyDataSetChanged

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

Обнаружено несоответствие. Неверный адаптер держателя представления positionViewHolder java.lang.IndexOutOfBoundsException: обнаружено несоответствие. Недопустимая позиция элемента 20 (смещение: 2). Состояние: 3

Решение: вместо очистки RecyclerView перед генерацией моих данных я оставляю их, а затем заменяю их новыми данными, вызовом NotifyDatasetChanged, как показано ниже;

       @Override
        protected void onPostExecute(List<Objects> o) {
            super.onPostExecute(o);
            recyclerViewAdapter.setList(o);
            mProgressBar.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
        }
EdgeDev
источник
Вы можете, пожалуйста, взгляните на мой код я думаю , что моя проблема в том , как ваша [ссылка] ( stackoverflow.com/questions/50213362/... )
Матин Чаудхри
1

Просто удалите все виды вашего менеджера макета, прежде чем уведомить. лайк:

myLayoutmanager.removeAllViews();
BaBaX Ra
источник
Оно работает. У меня была проблема с загрузкой прокрутки и сменой вкладки.
Уорики
1

Использование ListAdapter (androidx.recyclerview.widget.ListAdapter)вызова adapter.submitList(null)перед звонком adapter.submitList(list):

adapter.submitList(null)
adapter.submitList(someDataList)
Сергей
источник
1

Это исключение возникло по API 19, 21 (но не по новому). В сопрограмме Kotlin я загрузил данные (в фоновом потоке) и добавил в поток пользовательского интерфейса и показал их:

adapter.addItem(item)

адаптер:

var list: MutableList<Item> = mutableListOf()

init {
    this.setHasStableIds(true)
}

open fun addItem(item: Item) {
    list.add(item)
    notifyItemInserted(list.lastIndex)
}

По некоторым причинам Android не рендерится достаточно быстро или что-то еще, поэтому я обновляю список в postметоде RecyclerView(добавить, удалить, обновить события элементов):

view.recycler_view.post { adapter.addItem(item) }

Это исключение похоже на «Невозможно вызвать этот метод в обратном вызове прокрутки. Обратные вызовы прокрутки могут выполняться во время этапа измерения и разметки, когда вы не можете изменить данные RecyclerView. Любой вызов метода, который может изменить структуру RecyclerView или содержимое адаптера, следует отложить до следующий кадр. ": Recyclerview - не может вызвать этот метод в обратном вызове прокрутки .

CoolMind
источник
0

Я обнаружил, что настройка mRecycler.setLayoutFrozen (true); в методе onRefresh объекта swipeContainer.

решил проблему для меня.

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

        }
    });
Марк Шики
источник
0

Это довольно неприятная ошибка.

Для обработки моего клика по элементу я использовал реализацию, RecyclerView.OnItemTouchListenerаналогичную решению, найденному в этом вопросе .

После многократного обновления RecyclerViewисточника данных и щелчка по элементу IndexOutOfBoundsExceptionмое приложение вылетало. Когда по элементу щелкают, RecyclerViewвнутренняя часть ищет правильный базовый вид и возвращает его положение. Проверяя исходный код, я увидел, что они есть Tasksи Threadsзапланированы. Короче говоря, по сути, это просто какое-то незаконное состояние, когда два источника данных смешаны, а не синхронизированы, и все это становится диким.

Исходя из этого, я снял с реализации из RecyclerView.OnItemTouchListenerи просто поймал нажмите на ViewHolderо Adapterсебе:

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

}

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

DroidBender
источник
Создание нового объекта каждый раз, когда вызывается onBind, приведет к тому, что многие объекты будут собираться мусором, и пользователь может столкнуться с зависанием.
Dephinera