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

Привіт. Це трапляється лише рідко, але коли це трапляється, це фатально для програми.
КарольДепка

Дякуємо за посилання на помилки. Перший здається більш актуальним, ніж другий.
КарольДепка

1
Так, я думаю, що найкраще зробити, щоб не допускати змін до перегляду списку під час обертання.
VicVu

1
Якщо ви зможете легко відтворити, я б запропонував роздрукувати значення для "getItemCount" перед усіма дзвінками на "сповіщення *" ... Ви можете виявити, що кількість ваших елементів не відповідає вашим припущенням.
Річ Емер

Відповіді:


209

У мене виникла проблема (можливо) - введення нового екземпляра діяльності з RecyclerView, але менший адаптер викликав цю аварію для мене.

RecyclerView.dispatchLayout()можна спробувати витягнути предмети з брухту, перш ніж дзвонити mRecycler.clearOldPositions(). Наслідком цього є те, що він витягував елементи із загального пулу, які мали позиції вище розміру адаптера.

На щастя, це робиться лише в тому випадку PredictiveAnimations, якщо вони включені, тому моє рішення полягало в підкласі GridLayoutManager( LinearLayoutManagerмає ту саму проблему і 'виправити'), і замінити, supportsPredictiveItemAnimations()щоб повернути помилкове:

/**
 * 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, ймовірно, економив мені дні.
levavare

8
Дуже дякую. Це рішення працює з LinearLayoutManager.
Прутвірадж

8
Я думаю, що цей хлопець заслуговує на те, що ми споруджуємо статую на честь його дорогоцінної допомоги ... Це одна з найгірших задокументованих проблем в Інтернеті, але, здається, багато проблем зіткнулося з цим питанням ... Мені просто цікаво, як ти це знайшов? пропустити, якщо PredictiveAnimations помилкові, @KasHunt? Тому що, стек-трек дуже незрозумілий ...
ПАД

4
Хтось знає, як це виправити без цього злому? Тому що notifyDatasetChanged було відхилено на користь DiffUtil
Антон Шкуренко

83

У моєму випадку (видалити / вставити дані в мою структуру даних) мені потрібно було очистити пул рециркуляції, а потім повідомити про зміну набору даних!

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


6
Я зазвичай цього не кажу, але ДЯКУЙТЕ ТАКОЖ МНОГО. Я намагався ВСЕ, щоб виправити цю аварію, яка виникає епізодично, коли я швидко переміщую купу елементів у списку. Я витратив буквально, мабуть, цілий тиждень на спробу вирішити це питання. Я пішов від цього на кілька місяців, щоб спробувати дати моєму моменту можливість підійти до нього по-іншому, а потім виявив це у своїй першій спробі Google. Будь здоровий!
Chantell Osejo

Дозвольте мені отримати тони печива, тому що ви заслуговуєте на кожне з них. Дякую.
antonis_st

чому ти повинен це робити?
dabluck

9
Це досить важка операція, яка перемагає мету переробки видів.
gjsalot

@gjsalot Отож, якщо я використовую це, це може викликати деякі проблеми?
Sreekanth Karumanaghat

38

Використовуйте notifyDataSetChanged()замість цього notifyItem...в цьому випадку.


5
У деяких випадках це шлях. У мене виникла ситуація, коли я замінив усі свої предмети, але я не був чесний з адаптером, лише сказавши це, що я вставив нові елементи (notifyItemRangeInserted), не попередньо сказавши, що я також видалив предмети. Тоді адаптер очікував, що там буде більше предметів, ніж насправді. Якщо при використанні будь-якого з методів сповіщення адаптера очікується notifyDataSetChanged, наприклад notifyItemRangeRemoved / Inserted / Update, абонент несе повну відповідальність повідомити адаптер , що саме було змінено, або ви можете закінчитися цим "невідповідним станом".
JHH

19
Це зовсім не рішення.
Miha_x64

Це не шлях. Якщо це працює, це означає, що ви просто зіпсували діапазон notifyItem...та виправите, що почне працювати замість того, щоб повторно рендерінг усіх елементів.
Ranjan

12

Я вирішив це, відклавши mRecycler.setAdapter(itemsAdapter)до після додавання всіх елементів до адаптера, mRecycler.addAll(items)і це спрацювало. Не маю ідеї, чому я це зробив для початку, саме з коду бібліотеки я переглянув і побачив ці рядки в "неправильному порядку", я впевнений, що це все-таки, будь ласка, якщо хтось може підтвердити це, поясніть, чому це так? Не впевнений, чи справжня це відповідь


Я думаю, що це рішення, як тільки я затримав адаптер, це було добре, я вірю ... зараз він спливає, коли встановлюється адаптер у потоці інтерфейсу та додає до нього елементи.
EngineSense

18
Я використовував swapAdapter(adapter, true)замість цього, setAdapter(adapter)і це допомогло.
frangulyan

11

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

mObjects.clear();

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


9

У мене була така ж проблема з recilerView. Тому я щойно повідомив адаптер про зміну набору даних відразу після очищення списку.

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

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

1
Ця проста помилка змусила мене втратити стільки часу, дуже дякую!
leb1755

7

У мене така ж проблема. Це трапилося, коли я швидко прокручував дзвінки API та оновлював дані. Після того, як намагався запобігти аварійним ситуаціям, я знайшов рішення.

mRecyclerView.stopScroll();

Це спрацює.


Це рішення не є виправленням. Ви змушуєте його зупинити прокрутку. Bad UX
доктор aNdRO

1
@ Dr.aNdRO: Адаптеру потрібно встановити положення, і якщо ви продовжуєте прокрутку recylerview, адаптер не може встановити дані, що є причиною збоїв. Непогано UX
Ананд Савяні

1
мати сенс. Зупинка прокрутки не є поганою, оскільки оновлення даних відбувається.
Суш

6

Я змінюю дані для RecyclerViewфона Thread. Я отримав те саме Exception, що і ОП. Я додав це після зміни даних:

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

Сподіваюся, це допомагає


дякую людино! це єдина відповідь, яка має сенс з точки зору розвитку Android.
користувач347187

Хоча я також вирішував за допомогою view.recycler_view.post, я використовував notifyItemInserted. У моєму випадку це вже потік інтерфейсу користувача.
CoolMind

6

Ця помилка виникає, коли список у адаптері чистий при прокрутці користувача, який робить зміну позиції власника елемента, втрачається пересилання між списком та елементом в інтерфейсі, виникає помилка в наступному запиті "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 Це, мабуть, буде. Вам потрібно буде протестувати його у вашому випадку та вирішити, або спробувати скористатися іншим із запропонованих рішень. Як я вже сказав, це лише обхід, і він працює для мене в моєму випадку.
Мігель А. Габріель

3

Я зіткнувся з тією ж ситуацією. І це було вирішено шляхом додавання кодів до очищення колекції.

mRecyclerView.getRecycledViewPool().clear();


3

У моєму випадку я оновлював елементи та викликав notifyDataSetChangedпотоки, що не належать до інтерфейсу користувача. Більшу частину часу він працював, але коли багато змін відбулося швидко, він вийшов з ладу. Коли я, натомість, в основному

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
так-таки проблема .. Але рішення? Ви вказуєте лише причину .. Як виправити?
Ранджіт Кумар

@ RanjithKumar, Будь ласка, поділіться своїм способом вирішення вищезазначеної проблеми. Я вирішив це за допомогою mRecyclerView.getRecycledViewPool (). Clear (); перед тим, як сповіститиDataSetChanged та використовувати синхронізований блок навколо функції оновлення адаптера
Attiq ur Rehman

Ви можете, будь ласка, подивіться на мій код, я думаю, що моя проблема як ваша, чи можете ви допомогти мені stackoverflow.com/questions/50213362/…
Mateen Chaudhry

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

@Milha Я не знайшов іншого рішення, щоб виправити проблему з аварією. Але вищевказане рішення працює для мене. Якщо це не рішення, скажіть мені правильне виправлення.
EKN

Це залежить. Ви можете спробувати використовувати DiffUtil - інструмент загального призначення для оновлення вмісту RecyclerView.
Miha_x64

2

У моєму випадку я просто видалив рядок із setHasStableIds(true);


Але HasStableIds (true) покращує продуктивність Rv, чи є альтернативне рішення?
Sreekanth Karumanaghat

Насправді я думаю, що це може бути пов’язано з різними причинами, тому можуть бути різні рішення цієї проблеми, виходячи з першопричини.
Sreekanth Karumanaghat

2

У моєму випадку я намагався змінити вміст свого адаптера на фоновому потоці, але зателефонував notify * на основний / ui потік.

Це неможливо! Причина, через яку сповіщення змушене провести основний потік, полягає в тому, що рециркулятор хоче, щоб ви відредагували резервний адаптер на головному потоці навіть у тому ж стеці викликів.

Щоб вирішити проблему, переконайтеся, що кожна операція з вашим адаптером, а також кожне повідомлення ... дзвінок здійснюються на ui / main thread !


2
додавання елементів до списку у вашому адаптері слід робити на фоновій нитці та повідомляти про дзвінки на postexecute. додавання даних у потік інтерфейсу змушує додаток заморожуватись на кілька мілісекунд або секунд, якщо додавати багато даних
діонна ллорера

погоджено з @dionellorera, слід уточнити, що "зміна вмісту адаптера" конкретно означає зміну будь-яких даних безпосередньо, будь то примітивні значення, властивості об'єктів чи самі об'єкти
OzzyTheGiant

2

Нещодавно я зіткнувся з цим неприємним слідом стека з новими компонентами архітектури Android. По суті, у мене є список елементів у моєму ViewModel, які спостерігаються моїм фрагментом, використовуючи LiveData. Коли ViewModel публікує нове значення для даних, Фрагмент оновлює адаптер, передаючи ці нові елементи даних та повідомляючи адаптер про зміни.

На жаль, при передачі нових елементів даних до адаптера я не зміг врахувати той факт, що і ViewModel, і адаптер вказували б на одну і ту ж посилання на об'єкт! Це означає, що якщо я оновлюю дані та дзвоню 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" для оновлення, спробуйте використовувати булевий прапор, ініціалізувати його як помилковий, і всередині методу OnRefresh зробити це справжнім, очистити свій список даних якщо прапор є істинним безпосередньо перед додаванням нових даних до нього, а після цього зробіть його помилковим.

Ваш код може бути таким

 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

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


1

Я колись також отримав помилку:

Причина: я намагався оновити перегляд Recycler із завдання Async, одночасно намагаючись отримати старі видалені viewHolders;

Код: Я генерую дані при натисканні кнопки, логіка наступна

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

Проблема: Щоразу, коли я швидко прокручуюсь, перш ніж отримувати свої дані, я отримую

Виявлена ​​невідповідність. Недійсне положення адаптера власника видуViewHolder 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);
        }

ви можете, будь ласка, погляньте на мій код, я думаю, що моя проблема подібна до вашого [посилання] ( 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 (вірно); у методі 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
      }
    });

}

Це може бути не найкращим рішенням, але на даний момент без збоїв. Сподіваємось, це заощадить вам якийсь час :).


Створення нового об'єкта щоразу, коли викликуме onBind, призведе до появи багатьох об'єктів, які збиратимуть сміття, і користувач може зазнати замерзання.
dephinera

0

Лінт дав мені поради щодо невідповідності: я написав (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 для повного пояснення !!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.