Перегляд утилізатора: виявлено невідповідність. Недійсне положення адаптера тримача подання ViewVolder


84

Виявлено невідповідність подання Recycler Помилка, виявлена ​​під час швидкої прокрутки або прокрутки під час завантаження більше елементів ..

FATAL EXCEPTION: main
Process: com.pratap.endlessrecyclerview, PID: 21997
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{56a082c position=40 id=-1, oldPos=39, pLpos:39 scrap [attachedScrap] tmpDetached no parent}
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4251)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4382)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2864)
at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1445)
at android.support.v7.widget.RecyclerView.access$400(RecyclerView.java:144)
at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:282)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:603)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)

Перехідник

public class DataAdapter extends RecyclerView.Adapter {
    private final int VIEW_ITEM = 1;
    private final int VIEW_PROG = 0;

    private List<Feed> mFeed;
    // The minimum amount of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 5;
    private int lastVisibleItem, totalItemCount;
    private boolean loading;
    private OnLoadMoreListener onLoadMoreListener;

    public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {

        mFeed = feeds;

        if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {

            final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
                .getLayoutManager();

            recyclerView
                .addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView,
                        int dx, int dy) {
                        super.onScrolled(recyclerView, dx, dy);

                        totalItemCount = linearLayoutManager.getItemCount();
                        lastVisibleItem = linearLayoutManager
                            .findLastVisibleItemPosition();
                        if (!loading
                            && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
                            // End has been reached
                            // Do something
                            if (onLoadMoreListener != null) {
                                onLoadMoreListener.onLoadMore();
                            }
                            loading = true;
                        }
                    }
                });
        }
    }

    @Override
    public int getItemViewType(int position) {
        return mFeed.get(position) == null ? VIEW_PROG : VIEW_ITEM;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
        int viewType) {
        RecyclerView.ViewHolder vh;
        if (viewType == VIEW_ITEM) {
            View v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.list_row, parent, false);

            vh = new StudentViewHolder(v);
        }
        else {
            View v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.progress_item, parent, false);

            vh = new ProgressViewHolder(v);
        }
        return vh;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof StudentViewHolder) {

            Feed singleStudent= (Feed) mFeed.get(position);
            ((StudentViewHolder) holder).tvName.setText(singleStudent.getTitle());
            ((StudentViewHolder) holder).student= singleStudent;
        } else {
            ProgressViewHolder.PROGRESS_BAR.setIndeterminate(true);
        }
    }

    public void setLoaded() {
        loading = false;
    }

    public void  addFeed(Feed feed) {
        mFeed.add(feed);
        //mFeed.addAll(0, (Collection<? extends Feed>) feed);
        notifyItemInserted(mFeed.size());
        //notifyItemRangeInserted(0,mFeed.size());
        notifyDataSetChanged();
        //notifyItemInserted(mFeed.size());
        //setLoaded();
        //notifyItemInserted(mFeed.size());
    }

    public void removeAll(){
        mFeed.clear();
        notifyDataSetChanged();
    }

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

    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
        this.onLoadMoreListener = onLoadMoreListener;
    }

    public static class StudentViewHolder extends RecyclerView.ViewHolder {
        public TextView tvName;

        public Feed student;
        public StudentViewHolder(View v) {
            super(v);
            tvName = (TextView) v.findViewById(R.id.tvName);

            //tvEmailId = (TextView) v.findViewById(R.id.tvEmailId);
        }
    }

    public static class ProgressViewHolder extends RecyclerView.ViewHolder {
        //public ProgressBar progressBar;
        public static ProgressBar PROGRESS_BAR;
        public ProgressViewHolder(View v) {
            super(v);
            PROGRESS_BAR = (ProgressBar) v.findViewById(R.id.progressBar1);
            //  progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
        }
    }
}

Діяльність

public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {

    private Toolbar toolbar;

    private TextView tvEmptyView;
    private RecyclerView mRecyclerView;
    private DataAdapter mAdapter;
    private LinearLayoutManager mLayoutManager;
    private RestManager mManager;
    private List<Feed> mFeed;
    SwipeRefreshLayout mSwipeRefreshLayout;
    protected Handler handler;
    private int currentPage=1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        tvEmptyView = (TextView) findViewById(R.id.empty_view);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        mSwipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        //studentList = new ArrayList<Student>();
        mFeed = new ArrayList<Feed>();
        handler = new Handler();
        if (toolbar != null) {
            setSupportActionBar(toolbar);
            getSupportActionBar().setTitle("Android Students");

        }
        mManager = new RestManager();

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this);

        // use a linear layout manager
        mRecyclerView.setLayoutManager(mLayoutManager);

        // create an Object for Adapter
        mAdapter = new DataAdapter(mFeed,mRecyclerView);

        // set the adapter object to the Recyclerview
        mRecyclerView.setAdapter(mAdapter);
        //   mAdapter.notifyDataSetChanged();

        loadData(false);

        //        if (mFeed.isEmpty()) {
        //            mRecyclerView.setVisibility(View.GONE);
        //            tvEmptyView.setVisibility(View.VISIBLE);
        //
        //        } else {
        //            mRecyclerView.setVisibility(View.VISIBLE);
        //            tvEmptyView.setVisibility(View.GONE);
        //        }

        mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                //add null , so the adapter will check view_type and show progress bar at bottom
                mFeed.add(null);
                mAdapter.notifyItemInserted(mFeed.size() - 1);

                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //   remove progress item
                        mFeed.remove(mFeed.size() - 1);
                        // mAdapter.notifyItemRemoved(mFeed.size());
                        //add items one by one
                        int start = mFeed.size();
                        currentPage++;

                        Log.d("CurrentPage", String.valueOf(currentPage));
                        Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);

                        listCall.enqueue(new Callback<Results>() {

                            @Override
                            public void onResponse(Call<Results> call, Response<Results> response) {
                                mSwipeRefreshLayout.setRefreshing(false);
                                if (response.isSuccess()) {
                                    if (response.body() != null) {
                                        Results feedList = response.body();

                                        // List<Results> newUsers = response.body();

                                        Log.d("Retrofut", String.valueOf(feedList));

                                        for (int i = 0; i < feedList.results.size(); i++) {
                                            Feed feed = feedList.results.get(i);
                                            // mFeed.add(feed);
                                            mAdapter.addFeed(feed);
                                            //                                        mAdapter.notifyDataSetChanged();


                                            //mAdapter.notifyItemInserted(mFeed.size());

                                        }
                                        //    mAdapter.notifyDataSetChanged();
                                    }
                                }
                            }

                            @Override
                            public void onFailure(Call<Results> call, Throwable t) {
                                Log.d("Retrofut", "Error");
                                mFeed.remove(mFeed.size() - 1);
                                mAdapter.notifyItemRemoved(mFeed.size());

                                mAdapter.setLoaded();
                                mSwipeRefreshLayout.setRefreshing(false);
                            }
                        });

                        //        for (int i = 1; i <= 20; i++) {
                        //            studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
                        //
                        //        }

                        mAdapter.setLoaded();
                        //or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();

                    }
                }, 2000);
            }
        });
    }

    // load initial data
    private void loadData(final boolean removePreData) {

        Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);

        listCall.enqueue(new Callback<Results>() {

                             @Override
                             public void onResponse(Call<Results> call, Response<Results> response) {

                                 if (response.isSuccess()) {
                                     if (response.body() != null) {
                                         //  if(removePreData) mAdapter.removeAll();
                                         Results feedList = response.body();
                                         Log.d("Retrofut", String.valueOf(feedList));

                                         for (int i = 0; i < feedList.results.size(); i++) {
                                             Feed feed = feedList.results.get(i);
                                             // mFeed.add(feed);
                                             //mAdapter.notifyDataSetChanged();
                                             mAdapter.addFeed(feed);
                                         }

                                         mSwipeRefreshLayout.setRefreshing(false);
                                     }
                                 }
                             }

                             @Override
                             public void onFailure(Call<Results> call, Throwable t) {
                                 Log.d("Retrofut", String.valueOf(t));
                                 mFeed.remove(mFeed.size() - 1);
                                 mAdapter.notifyItemRemoved(mFeed.size());
                                 mAdapter.setLoaded();
                                 mSwipeRefreshLayout.setRefreshing(false);
                             }
                         }
        );

        //        for (int i = 1; i <= 20; i++) {
        //            studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
        //
        //        }

        mSwipeRefreshLayout.setRefreshing(true);
    }

    @Override
    public void onRefresh() {
        mFeed.clear();
        mAdapter.notifyDataSetChanged();
        loadData(true);
        currentPage=1;
    }
}

У мене така сама помилка, оскільки вона з’являється, тому що я використовую нескінченний вторинний переробник, оскільки мої дані потрапляють в останню позицію, вони не отримують кількість елементів, як якщо ви встановите її, отримайте 27 підрахунків елементів у кожному прокрутці, нарешті, можливо, ви цього не зробите отримати 27 елементів, так що це відбувається потім після цього, коли я отримую, в чому проблема щойно створеної логіки, що якщо я модифікую (%) list.size () за елементом, я отримую, якщо воно дорівнює 0, просто використовуйте notifyItemrangeInserted, інакше notifyDataSetChanged ();
Shashank Verma

Чи є спосіб відтворити це? Я отримую цей збій у своєму виробничому додатку, але не можу відтворити його локально
Еціо,

якщо ви виробляєте місцево, використовуйте пристрій низького класу з 1 ГБ оперативної пам'яті
Хамза,

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

Відповіді:


41

Це схоже на відому помилку Android

Є досить потворний, але робочий підхід

public class WrapContentLinearLayoutManager extends LinearLayoutManager {
    //... constructor
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e) {
            Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
        }
    }
}


mRecyclerView.setLayoutManager(new WrapContentGridLayoutManager(getContext(), spanCount));

Для мене це працює без будь-яких побічних ефектів.


1
Додаток не виходить з ладу після використання цього, але помилка все ще є. Яка точна причина цієї помилки?
Кришна Міна,

13
це дійсно погана ідея, прив'язка винятку та приглушення його ... issueetracker.google.com/issues/37030377#comment9 @Angad Tiwari
marcos E.

1
@payyd що таке spanCount?
Віджай Раджпут

3
Я використовую останню версію RecyclerView: 26.0.2, і ця помилка все одно відображатиметься іноді, тому, гадаю, вони її не виправили.
vovahost

4
Чи є спосіб відтворити цей збій, тому що ми отримуємо цей збій у виробництві, але не можемо відтворити його локально
Еціо

35

Ця проблема - відома помилка RecyclerView. Найкраще рішення - очищати список кожного разу перед оновленням RecyclerView.

Для виправлення цієї проблеми просто зателефонуйте notifyDataSetChanged () з порожнім списком перед оновленням перегляду утилізації.

Наприклад

//Method for refresh recycle view

if (!yourList.isEmpty())

yourList.clear(); //The list for update recycle view

adapter.notifyDataSetChanged(); 

Я впадаю в цю проблему reciclerview LoadMore items
Ahamadullah Saikat

@AhamadullahSaikat ти отримав відповідь на це?
Аншул Тягі

Я працюю з переглядачем матеріалів, адаптерами та фрагментами, і це працює для мене. Я просто додаю метод на своєму адаптері, щоб перевірити за фрагментом, чи мій список порожній. boolean isEmpty(){return mylist.isEmpty;}а потім у моєму фрагменті дзвінкаmyadapter.isEmpty()
Кімбо,

1
Ну це дивно, але очищення списку після оновлення - це насправді те, що ПРИЧИНИЛО проблему для мене. Однак я не дзвонив до notifyDataSetChanged. Якщо хтось збирається використовувати це рішення, СКОНЕЦЬ, обов’язково оновіть його.
Riot Goes

1
Це працює! Ви врятували мій день після години марних досліджень!
Маттіа Руджеро,

17

поставте цей рядок разом із налаштуванням reciclerView. проблема була виправлена, встановивши для RecyclerView значення ItemAnimator значенням null.

у котліні

    recyclerView.layoutManager = LinearLayoutManager(this)
    recyclerView.itemAnimator = null

в Java

    recyclerView.setItemAnimator(null);

9

Використовуйте це, щоб оновити RecyclerView

items.clear(); //here items is an ArrayList populating the RecyclerView
adapter.notifyDataSetChanged();
items.addAll(list);// add new data 
adapter.notifyItemRangeInserted(0, items.size);// notify adapter of new data

`


1
дякую, брате, ця відповідь на 100% працює для мене. коли мій reciclerview знаходиться всередині фрагмента. Дуже дякую .
Куналь Нікам

7

У мене була подібна проблема, а також це рішення мені допомогло після того, як я додав новий елемент до свого RV:

recyclerView.getRecycledViewPool().clear();
adapter.notifyDataSetChanged();

3

У моєму випадку я робив це, оскільки notifyItemInserted(position); це викликало у мене цю проблему, тоді я використовував як і це спрацювало чудово.notifyItemRangeInserted(startIndex,endIndex);


3

Можливо, ви можете спробувати це перед оновленням адаптера:

dataList.clear(); 
patrolListAdapter.notifyDataSetChanged();

2

Проблема полягає в цьому рядку коду:

 mFeed = feeds;

Ви призначаєте mFeed екземпляр абонента, feedsтому кожен раз, коли абонент змінює свою змінну (можливо, додає, очищає або видаляє елементи), ваш місцевий орган mFeedзміниться

спробуйте змінити на

mFeed.addAll(feeds);

не забудьте ініціалізувати mFeedбудь-який список, який відповідає вашим потребам, наприкладmFeed = new ArrayList<>();


1

У мене була ця проблема при швидкому прокручуванні нескінченної сторінки / сторінки RecyclerView. Корінь моєї проблеми виходив з того, що у мене на початку списку був елемент “header”, цей елемент заголовка не входив до джерела даних, він був просто вставлений на початку adapterсписку. Тож, швидко прокручуючи та додаючи нові сторінки елементів до RecyclerView Adapterі повідомляючи про adapterте, що нові дані були вставлені, я не враховував додатковий елемент заголовка, роблячи таким чином розмір списку адаптера неправильним ... і спричиняючи це виняток. ..

Отже, якщо коротко, якщо ви використовуєте верхній / нижній колонтитул у нашому RecyclerViewадаптері, обов’язково враховуйте це під час оновлення даних адаптерів.

Приклад:

public void addNewPageToList(List<MyData> list)
{   //
    // Make sure you account for any header/footer in your list!
    //
    // Add one to the currentSize to account for the header item.
    //
    int currentSize = this.adapterList.size() + 1;
    this.adapterList.addAll(list);
    notifyItemRangeInserted(currentSize, this.adapterList.size());
}

Редагувати: Я думаю, ви завжди можете просто скористатися методом адаптера, getItemCount()щоб отримати розмір, замість того, щоб отримати розмір із “списку даних” і додати до нього. Ваш getItemCount()метод вже повинен враховувати всі додаткові колонтитули / колонтитули / тощо, які є у вашому списку.


1

поставте цей рядок разом із налаштуванням reciclerView. проблема була виправлена, встановивши для RecyclerView значення ItemAnimator значенням null.

in kotlin
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.itemAnimator = null

0

У мене була подібна проблема. Видалення всіх подань із RecyclerView допомогло мені:

RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();

0

Для мене проблема полягала в тому, що я не публікував notifyDatasetChanged, коли набір даних змінювався, коли я реалізовував додатковий пошук.

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

Мені довелося зробити обидва сповіщення, щоб режим переробки працював

Відфільтруйте вихідний набір даних, а потім опублікуйте зміну набору даних

this.searchResultTable?.post {
    this.searchResultTable?.adapter?.notifyDataSetChanged()
}

Отримавши відповідь, знову опублікуйте сповіщення

this.searchResultTable?.post {
    this.searchResultTable?.adapter?.notifyItemChanged(index, updateDataHashMap)
}

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

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

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