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

RecyclerView и java.lang.IndexOutOfBoundsException: обнаружена несогласованность. Недействительный адаптер держателя держателя ViewView в устройствах Samsung

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

java.lang.IndexOutOfBoundsException: обнаружена несогласованность. Недействительный адаптер держателя держателя ViewViewer

когда я возвращаюсь к фрагменту с видом ресайклера из другого вида деятельности.

Код адаптера:

public class FeedRecyclerAdapter extends RecyclerView.Adapter<FeedRecyclerAdapter.MovieViewHolder> {
    public static final String getUserPhoto = APIConstants.BASE_URL + APIConstants.PICTURE_PATH_SMALL;
    Movie[] mMovies = null;
    Context mContext = null;
    Activity mActivity = null;
    LinearLayoutManager mManager = null;
    private Bus uiBus = null;
    int mCountOfLikes = 0;

    //Constructor
    public FeedRecyclerAdapter(Movie[] movies, Context context, Activity activity,
                               LinearLayoutManager manager) {
        mContext = context;
        mActivity = activity;
        mMovies = movies;
        mManager = manager;
        uiBus = BusProvider.getUIBusInstance();
    }

    public void setMoviesAndNotify(Movie[] movies, boolean movieIgnored) {
        mMovies = movies;
        int firstItem = mManager.findFirstVisibleItemPosition();
        View firstItemView = mManager.findViewByPosition(firstItem);
        int topOffset = firstItemView.getTop();
        notifyDataSetChanged();
        if(movieIgnored) {
            mManager.scrollToPositionWithOffset(firstItem - 1, topOffset);
        } else {
            mManager.scrollToPositionWithOffset(firstItem, topOffset);
        }
    }

    // Create new views (called by layout manager)
    @Override
    public MovieViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.feed_one_recommended_movie_layout, parent, false);

        return new MovieViewHolder(view);
    }

    // Replaced contend of each view (called by layout manager)
    @Override
    public void onBindViewHolder(MovieViewHolder holder, int position) {
        setLikes(holder, position);
        setAddToCollection(holder, position);
        setTitle(holder, position);
        setIgnoreMovieInfo(holder, position);
        setMovieInfo(holder, position);
        setPosterAndTrailer(holder, position);
        setDescription(holder, position);
        setTags(holder, position);
    }

    // returns item count (called by layout manager)
    @Override
    public int getItemCount() {
        return mMovies != null ? mMovies.length : 0;
    }

    private void setLikes(final MovieViewHolder holder, final int position) {
        List<Reason> likes = new ArrayList<>();
        for(Reason reason : mMovies[position].reasons) {
            if(reason.title.equals("Liked this movie")) {
                likes.add(reason);
            }
        }
        mCountOfLikes = likes.size();
        holder.likeButton.setText(mContext.getString(R.string.like)
            + Html.fromHtml(getCountOfLikesString(mCountOfLikes)));
        final MovieRepo repo = MovieRepo.getInstance();
        final int pos = position;
        final MovieViewHolder viewHolder = holder;
        holder.likeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mMovies[pos].isLiked) {
                    repo.unlikeMovie(AuthStore.getInstance()
                        .getAuthToken(), mMovies[pos].id, new Callback<Movie>() {
                        @Override
                        public void success(Movie movie, Response response) {
                            Drawable img = mContext.getResources().getDrawable(R.drawable.ic_like);
                            viewHolder.likeButton
                                .setCompoundDrawablesWithIntrinsicBounds(img, null, null, null);
                            if (--mCountOfLikes <= 0) {
                                viewHolder.likeButton.setText(mContext.getString(R.string.like));
                            } else {
                                viewHolder.likeButton
                                    .setText(Html.fromHtml(mContext.getString(R.string.like)
                                        + getCountOfLikesString(mCountOfLikes)));
                            }
                            mMovies[pos].isLiked = false;
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext.getApplicationContext(),
                                mContext.getString(R.string.cannot_like), Toast.LENGTH_LONG)
                                .show();
                        }
                    });
                } else {
                    repo.likeMovie(AuthStore.getInstance()
                        .getAuthToken(), mMovies[pos].id, new Callback<Movie>() {
                        @Override
                        public void success(Movie movie, Response response) {
                            Drawable img = mContext.getResources().getDrawable(R.drawable.ic_liked_green);
                            viewHolder.likeButton
                                .setCompoundDrawablesWithIntrinsicBounds(img, null, null, null);
                            viewHolder.likeButton
                                .setText(Html.fromHtml(mContext.getString(R.string.like)
                                    + getCountOfLikesString(++mCountOfLikes)));
                            mMovies[pos].isLiked = true;
                            setComments(holder, position);
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext,
                                mContext.getString(R.string.cannot_like), Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
        });
    }

    private void setComments(final MovieViewHolder holder, final int position) {
        holder.likeAndSaveButtonLayout.setVisibility(View.GONE);
        holder.commentsLayout.setVisibility(View.VISIBLE);
        holder.sendCommentButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (holder.commentsInputEdit.getText().length() > 0) {
                    CommentRepo repo = CommentRepo.getInstance();
                  repo.sendUserComment(AuthStore.getInstance().getAuthToken(), mMovies[position].id,
                        holder.commentsInputEdit.getText().toString(), new Callback<Void>() {
                            @Override
                            public void success(Void aVoid, Response response) {
                                Toast.makeText(mContext, mContext.getString(R.string.thanks_for_your_comment),
                                    Toast.LENGTH_SHORT).show();
                                hideCommentsLayout(holder);
                            }

                            @Override
                            public void failure(RetrofitError error) {
                                Toast.makeText(mContext, mContext.getString(R.string.cannot_add_comment),
                                    Toast.LENGTH_LONG).show();
                            }
                        });
                } else {
                    hideCommentsLayout(holder);
                }
            }
        });
    }

    private void hideCommentsLayout(MovieViewHolder holder) {
        holder.commentsLayout.setVisibility(View.GONE);
        holder.likeAndSaveButtonLayout.setVisibility(View.VISIBLE);
    }

    private void setAddToCollection(final MovieViewHolder holder, int position) {
        final int pos = position;
        if(mMovies[position].isInWatchlist) {
            holder.saveButton
              .setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_check_green, 0, 0, 0);
        }
        final CollectionRepo repo = CollectionRepo.getInstance();
        holder.saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!mMovies[pos].isInWatchlist) {
                   repo.addMovieToCollection(AuthStore.getInstance().getAuthToken(), 0, mMovies[pos].id, new Callback<MovieCollection[]>() {
                            @Override
                            public void success(MovieCollection[] movieCollections, Response response) {
                                holder.saveButton
                                    .setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_check_green, 0, 0, 0);

                                mMovies[pos].isInWatchlist = true;
                            }

                            @Override
                            public void failure(RetrofitError error) {
                                Toast.makeText(mContext, mContext.getString(R.string.movie_not_added_to_collection),
                                    Toast.LENGTH_LONG).show();
                            }
                        });
                } else {
                 repo.removeMovieFromCollection(AuthStore.getInstance().getAuthToken(), 0,
                        mMovies[pos].id, new Callback<MovieCollection[]>() {
                        @Override
                        public void success(MovieCollection[] movieCollections, Response response) {
                            holder.saveButton
                                .setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_plus, 0, 0, 0);

                            mMovies[pos].isInWatchlist = false;
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext,
                                mContext.getString(R.string.cannot_delete_movie_from_watchlist),
                                Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
        });
    }

    private String getCountOfLikesString(int countOfLikes) {
        String countOfLikesStr;
        if(countOfLikes == 0) {
            countOfLikesStr = "";
        } else if(countOfLikes > 999) {
            countOfLikesStr = " " + (countOfLikes/1000) + "K";
        } else if (countOfLikes > 999999){
            countOfLikesStr = " " + (countOfLikes/1000000) + "M";
        } else {
            countOfLikesStr = " " + String.valueOf(countOfLikes);
        }
        return "<small>" + countOfLikesStr + "</small>";
    }

    private void setTitle(MovieViewHolder holder, final int position) {
        holder.movieTitleTextView.setText(mMovies[position].title);
        holder.movieTitleTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieDetailActivity.openView(mContext, mMovies[position].id, true, false);
            }
        });
    }

    private void setIgnoreMovieInfo(MovieViewHolder holder, final int position) {
        holder.ignoreMovie.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieRepo repo = MovieRepo.getInstance();
                repo.hideMovie(AuthStore.getInstance().getAuthToken(), mMovies[position].id,
                    new Callback<Void>() {
                        @Override
                        public void success(Void aVoid, Response response) {
                            Movie[] newMovies = new Movie[mMovies.length - 1];
                            for (int i = 0, j = 0; j < mMovies.length; i++, j++) {
                                if (i != position) {
                                    newMovies[i] = mMovies[j];
                                } else {
                                    if (++j < mMovies.length) {
                                        newMovies[i] = mMovies[j];
                                    }
                                }
                            }
                            uiBus.post(new MoviesChangedEvent(newMovies));
                            setMoviesAndNotify(newMovies, true);
                            Toast.makeText(mContext, mContext.getString(R.string.movie_ignored),
                                Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void failure(RetrofitError error) {
                            Toast.makeText(mContext, mContext.getString(R.string.movie_ignored_failed),
                                Toast.LENGTH_LONG).show();
                        }
                    });
            }
        });
    }

    private void setMovieInfo(MovieViewHolder holder, int position) {
        String imdp = "IMDB: ";
        String sources = "", date;
        if(mMovies[position].showtimes != null && mMovies[position].showtimes.length > 0) {
            int countOfSources = mMovies[position].showtimes.length;
            for(int i = 0; i < countOfSources; i++) {
                sources += mMovies[position].showtimes[i].name + ", ";
            }
            sources = sources.trim();
            if(sources.charAt(sources.length() - 1) == ',') {
                if(sources.length() > 1) {
                    sources = sources.substring(0, sources.length() - 2);
                } else {
                    sources = "";
                }
            }
        } else {
            sources = "";
        }
        imdp += mMovies[position].imdbRating + " | ";
        if(sources.isEmpty()) {
            date = mMovies[position].releaseYear;
        } else {
            date = mMovies[position].releaseYear + " | ";
        }

        holder.movieInfoTextView.setText(imdp + date + sources);
    }

    private void setPosterAndTrailer(final MovieViewHolder holder, final int position) {
        if (mMovies[position] != null && mMovies[position].posterPath != null
            && !mMovies[position].posterPath.isEmpty()) {
            Picasso.with(mContext)
                .load(mMovies[position].posterPath)
             .error(mContext.getResources().getDrawable(R.drawable.noposter))
                .into(holder.posterImageView);
        } else {
            holder.posterImageView.setImageResource(R.drawable.noposter);
        }
        holder.posterImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieDetailActivity.openView(mActivity, mMovies[position].id, false, false);
            }
        });
        if(mMovies[position] != null && mMovies[position].trailerLink  != null
            && !mMovies[position].trailerLink.isEmpty()) {
            holder.playTrailer.setVisibility(View.VISIBLE);
            holder.playTrailer.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    MovieDetailActivity.openView(mActivity, mMovies[position].id, false, true);
                }
            });
        }
    }

    private void setDescription(MovieViewHolder holder, int position) {
        String text = mMovies[position].overview;
        if(text == null || text.isEmpty()) {
       holder.descriptionText.setText(mContext.getString(R.string.no_description));
        } else if(text.length() > 200) {
            text = text.substring(0, 196) + "...";
            holder.descriptionText.setText(text);
        } else {
            holder.descriptionText.setText(text);
        }
        final int pos = position;
        holder.descriptionText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MovieDetailActivity.openView(mActivity, mMovies[pos].id, false, false);
            }
        });
    }

    private void setTags(MovieViewHolder holder, int position) {
        List<String> tags = Arrays.asList(mMovies[position].tags);
        if(tags.size() > 0) {
            CastAndTagsFeedAdapter adapter = new CastAndTagsFeedAdapter(tags,
                mContext, ((FragmentActivity) mActivity).getSupportFragmentManager());
            holder.tags.setItemMargin(10);
            holder.tags.setAdapter(adapter);
        } else {
            holder.tags.setVisibility(View.GONE);
        }
    }

    // class view holder that provide us a link for each element of list
    public static class MovieViewHolder extends RecyclerView.ViewHolder {
        TextView movieTitleTextView, movieInfoTextView, descriptionText, reasonsCountText;
        TextView reasonText1, reasonAuthor1, reasonText2, reasonAuthor2;
        EditText commentsInputEdit;
        Button likeButton, saveButton, playTrailer, sendCommentButton;
        ImageButton ignoreMovie;
        ImageView posterImageView, userPicture1, userPicture2;
        TwoWayView tags;
        RelativeLayout mainReasonsLayout, firstReasonLayout, secondReasonLayout, reasonsListLayout;
        RelativeLayout commentsLayout;
        LinearLayout likeAndSaveButtonLayout;
        ProgressBar progressBar;

        public MovieViewHolder(View view) {
            super(view);
            movieTitleTextView = (TextView)view.findViewById(R.id.movie_title_text);
            movieInfoTextView = (TextView)view.findViewById(R.id.movie_info_text);
            descriptionText = (TextView)view.findViewById(R.id.text_description);
            reasonsCountText = (TextView)view.findViewById(R.id.reason_count);
            reasonText1 = (TextView)view.findViewById(R.id.reason_text_1);
            reasonAuthor1 = (TextView)view.findViewById(R.id.author_1);
            reasonText2 = (TextView)view.findViewById(R.id.reason_text_2);
            reasonAuthor2 = (TextView)view.findViewById(R.id.author_2);
            commentsInputEdit = (EditText)view.findViewById(R.id.comment_input);
            likeButton = (Button)view.findViewById(R.id.like_button);
            saveButton = (Button)view.findViewById(R.id.save_button);
            playTrailer = (Button)view.findViewById(R.id.play_trailer_button);
            sendCommentButton = (Button)view.findViewById(R.id.send_button);
            ignoreMovie = (ImageButton)view.findViewById(R.id.ignore_movie_imagebutton);
            posterImageView = (ImageView)view.findViewById(R.id.poster_image);
            userPicture1 = (ImageView)view.findViewById(R.id.user_picture_1);
            userPicture2 = (ImageView)view.findViewById(R.id.user_picture_2);
            tags = (TwoWayView)view.findViewById(R.id.list_view_feed_tags);
            mainReasonsLayout = (RelativeLayout)view.findViewById(R.id.reasons_main_layout);
            firstReasonLayout = (RelativeLayout)view.findViewById(R.id.first_reason);
            secondReasonLayout = (RelativeLayout)view.findViewById(R.id.second_reason);
            reasonsListLayout = (RelativeLayout)view.findViewById(R.id.reasons_list);
            commentsLayout = (RelativeLayout)view.findViewById(R.id.comments_layout);
            likeAndSaveButtonLayout = (LinearLayout)view
                .findViewById(R.id.like_and_save_buttons_layout);
            progressBar = (ProgressBar)view.findViewById(R.id.centered_progress_bar);
        }
    }
}

Исключение:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{42319ed8 position=1 id=-1, oldPos=0, pLpos:0 scrap tmpDetached no parent}
 at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4166)
 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4297)
 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4278)
 at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1947)
 at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:434)
 at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1322)
 at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:556)
 at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:171)
 at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2627)
 at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2971)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:562)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
07-30 12:48:22.688    9590-9590/com.Filmgrail.android.debug W/System.err? at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
 at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
 at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 at android.view.View.layout(View.java:15746)
 at android.view.ViewGroup.layout(ViewGroup.java:4867)
 at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2356)
 at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2069)
 at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
 at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6630)
 at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803)
 at android.view.Choreographer.doCallbacks(Choreographer.java:603)
 at android.view.Choreographer.doFrame(Choreographer.java:573)
 at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789)
 at android.os.Handler.handleCallback(Handler.java:733)
 at android.os.Handler.dispatchMessage(Handler.java:95)
 at android.os.Looper.loop(Looper.java:136)
 at android.app.ActivityThread.main(ActivityThread.java:5479)
 at java.lang.reflect.Method.invokeNative(Native Method)
 at java.lang.reflect.Method.invoke(Method.java:515)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
 at dalvik.system.NativeStart.main(Native Method)

Как я могу это исправить?

4b9b3361

Ответ 1

Эта проблема вызвана изменением данных RecyclerView в другом потоке. Лучший способ - это проверка доступа к данным. И обходной путь - это обертывание LinearLayoutManager.

Предыдущий ответ

На самом деле была ошибка в RecyclerView, и поддержка 23.1.1 все еще не исправлена.

Для обходного пути, обратите внимание, что стеки обратной трассировки, если мы можем перехватить это Exception в одном из классов, могут пропустить этот сбой. Для меня я создаю LinearLayoutManagerWrapper и переопределяю onLayoutChildren:

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("TAG", "meet a IOOBE in RecyclerView");
        }
    }
}

Затем установите его в RecyclerView:

RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);

recyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));

На самом деле поймать это исключение, и, похоже, никаких побочных эффектов еще нет.

Кроме того, если вы используете GridLayoutManager или StaggeredGridLayoutManager вы должны создать для него оболочку.

Примечание: RecyclerView может быть в неправильном внутреннем состоянии.

Ответ 2

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

notifyItemRangeRemoved(0, previousContentSize);

перед:

notifyItemRangeInserted(0, newContentSize);

Это правильное решение, которое также упоминается в этот пост членом проекта AOSP.

Ответ 3

Новый ответ: используйте DiffUtil для всех обновлений RecyclerView. Это поможет как с производительностью, так и с ошибкой выше. Посмотреть здесь

Предыдущий ответ: это сработало для меня. Ключ должен не использовать notifyDataSetChanged() и делать правильные вещи в правильном порядке:

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, newArticles.size());
}

Ответ 4

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

Вот пример:

public class LinearLayoutManagerWrapper extends LinearLayoutManager {

  public LinearLayoutManagerWrapper(Context context) {
    super(context);
  }

  public LinearLayoutManagerWrapper(Context context, int orientation, boolean reverseLayout) {
    super(context, orientation, reverseLayout);
  }

  public LinearLayoutManagerWrapper(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }

  @Override
  public boolean supportsPredictiveItemAnimations() {
    return false;
  }
}

И установите его в RecyclerView:

RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManagerWrapper(context, LinearLayoutManager.VERTICAL, false);

Ответ 5

Согласно этой проблеме, проблема была решена и, скорее всего, была выпущена некоторое время в начале 2015 года. A цитата из той же темы:

Он специально связан с вызовом notifyDataSetChanged. [...]

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

Если у вас по-прежнему возникают проблемы с последней версией библиотеки поддержки, я бы предложил проверить ваши вызовы на notifyXXX (в частности, использование notifyDataSetChanged) внутри вашего адаптера, чтобы убедиться, что вы придерживаетесь (несколько тонкий/неясный) RecyclerView.Adapter контракт. Также не забудьте оставить эти уведомления в основном потоке.

Ответ 6

У меня была аналогичная проблема.

Проблема с кодом ошибки ниже:

int prevSize = messageListHistory.size();
// some insert
adapter.notifyItemRangeInserted(prevSize - 1, messageListHistory.size() -1);

Решение:

int prevSize = messageListHistory.size();
// some insert
adapter.notifyItemRangeInserted(prevSize, messageListHistory.size() -prevSize);

Ответ 7

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

Но ViewHolder попытался перерисовать некоторые данные в этом представлении и начал отсчет подсчета и пересчета числа RecyclerView, и в этот момент он разбился (список элементов и размер уже был обновлен, но адаптер еще не был уведомлен).

Ответ 8

Это происходит, когда вы указываете неправильную позицию для notifyItemChanged, notifyItemRangeInserted и т.д. Для меня:

До: (Неверно)

public void addData(List<ChannelItem> list) {
  int initialSize = list.size();
  mChannelItemList.addAll(list);
  notifyItemRangeChanged(initialSize - 1, mChannelItemList.size());
 } 

После: (Правильно)

 public void addData(List<ChannelItem> list) {
  int initialSize = mChannelItemList.size();
  mChannelItemList.addAll(list);
  notifyItemRangeInserted(initialSize, mChannelItemList.size()-1); //Correct position 
 }

Ответ 9

Другая причина, по которой эта проблема возникает, - это когда вы вызываете эти методы с неправильными индексами (индексы, которые там не были вставлены или удалены в них)

-notifyItemRangeRemoved

-notifyItemRemoved

-notifyItemRangeInserted

-notifyItemInserted

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

Ответ 10

Эта ошибка до сих пор не исправлена ​​в 23.1.1, но обходным решением было бы исключение.

Ответ 11

Причины вызвали эту проблему:

  1. Внутренняя проблема в Recycler, когда включена анимация элементов
  2. Модификация данных Recycler в другом потоке
  3. Неправильный вызов методов уведомления

РЕШЕНИЕ:

-----------------SOLUTION 1---------------

  • Поймать исключение (не рекомендуется, особенно по причине № 3)

Создайте пользовательский LinearLayoutManager следующим образом и установите для него ReyclerView.

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

            @Override
            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

                try {

                    super.onLayoutChildren(recycler, state);

                } catch (IndexOutOfBoundsException e) {

                    Log.e(TAG, "Inconsistency detected");
                }

            }
        }

Затем установите RecyclerVIew Layout Manager следующим образом:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

-----------------SOLUTION 2---------------

  • Отключить анимацию элементов (исправляет проблему, если она вызвана причиной № 1):

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

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

             @Override
             public boolean supportsPredictiveItemAnimations() {
                 return false;
             }
        }

Затем установите RecyclerVIew Layout Manager следующим образом:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

-----------------SOLUTION 3---------------

  • Это решение устраняет проблему, если она вызвана причиной № 3. Вы должны убедиться, что вы используете методы уведомления правильно. Кроме того, используйте DiffUtil, чтобы обрабатывать изменения разумным, простым и плавным способом. Использование DiffUtil в Android RecyclerView

-----------------SOLUTION 4---------------

  • По причине №2 вам необходимо проверить все данные доступа к списку переработчика и убедиться, что нет изменений в другом потоке.

Ответ 12

Эта проблема вызвана данными RecyclerView, измененными в разных нить

Можно проверить threading как одну проблему, и поскольку я столкнулся с проблемой, и RxJava становится все более популярным: убедитесь, что вы используете .observeOn(AndroidSchedulers.mainThread()) всякий раз, когда вы вызываете notify[whatever changed]

пример кода из адаптера:

myAuxDataStructure.getChangeObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<AuxDataStructure>() {

    [...]

    @Override
    public void onNext(AuxDataStructure o) {
        [notify here]
    }
});

Ответ 13

В моем случае каждый раз, когда я вызываю notifyItemRemoved (0), он разбился. Оказалось, что я установил setHasStableIds(true), а в getItemId я только что вернул позицию позиции. Я закончил обновление, чтобы вернуть элемент hashCode() или самоопределенный уникальный идентификатор, который решил проблему.

Ответ 14

В моем случае я получал эту проблему из-за получения обновлений данных с сервера (я использую Firebase Firestore), и, когда первый набор данных обрабатывается DiffUtil в фоновом режиме, появляется еще один набор обновлений данных и вызывает проблему параллелизма путем запуска другого DiffUtil.

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

Я решил это, следуя советам в этом замечательном объяснении: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

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

Надеюсь, это поможет, потому что это заставило меня почесать голову!

Ответ 15

Проблема возникла для меня только тогда, когда:

Я создал адаптер с пустым списком. Затем я вставил элементы и набрал notifyItemRangeInserted.

Решение:

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

Ответ 16

В моем случае я ранее менял данные внутри потока с помощью mRecyclerView.post(new Runnable...), а затем позже позже менял данные в потоке пользовательского интерфейса, что вызывало несогласованность.

Ответ 17

Ошибка может быть вызвана тем, что ваши изменения не соответствуют тому, что вы уведомляете. В моем случае:

myList.set(position, newItem);
notifyItemInserted(position);

Что я, конечно, должен был сделать:

myList.add(position, newItem);
notifyItemInserted(position);

Ответ 18

Моя проблема заключалась в том, что, хотя я очищаю список массивов, содержащий модель данных для представления recycler, я не уведомлял адаптер об этом изменении, поэтому он имел устаревшие данные из предыдущей модели. Это вызвало путаницу в отношении позиции держателя. Чтобы исправить это, всегда сообщайте адаптеру о том, что набор данных изменился перед обновлением.

Ответ 19

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

adapter.notifyItemRangeChanged(0, newAmountOfData + 1);
adapter.notifyItemRangeRemoved(newAmountOfData + 1, previousAmountOfData);

Ответ 20

Я получил эту ошибку, потому что я ошибочно вызывал метод для удаления определенной строки из моего recyclerview несколько раз. У меня был метод вроде:

void removeFriends() {
    final int loc = data.indexOf(friendsView);
    data.remove(friendsView);
    notifyItemRemoved(loc);
}

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

void removeFriends() {
    final int loc = data.indexOf(friendsView);
    if (loc > -1) {
        data.remove(friendsView);
        notifyItemRemoved(loc);
    }
}

Ответ 21

У меня такая же проблема, и я прочитал, что это произошло только на телефонах Samsung... Но реальность показала, что это происходит во многих брендах.

После тестирования я понял, что это происходит, только когда вы быстро прокручиваете RecyclerView, а затем возвращаетесь либо с помощью кнопки "Назад", либо кнопки "Вверх". Поэтому я набрал кнопку "Вверх" и "Вниз". Снимок ниже:

someList = new ArrayList<>();
mainRecyclerViewAdapter = new MainRecyclerViewAdapter(this, someList, this);
recyclerViewMain.setAdapter(mainRecyclerViewAdapter);
finish();

С помощью этого решения вы просто загружаете новый Arraylist в адаптер и новый адаптер для recyclerView, а затем завершаете активность.

Надеюсь, это поможет кому-то

Ответ 22

Я получил эту ошибку, потому что я дважды вызывал "notifyItemInserted" по ошибке.

Ответ 23

В моем случае у меня было больше 5000 предметов в списке. Моя проблема заключалась в том, что при прокрутке представления recycler иногда вызываются "onBindViewHolder", а метод "myCustomAddItems" изменяет список.

Моим решением было добавить "synchronized (syncObject) {}" ко всем методам, которые изменяют список данных. Таким образом, в любой момент времени только один метод может читать этот список.

Ответ 24

В моем случае данные адаптера изменились. И я ошибочно использовал notifyItemInserted() для этих изменений. Когда я использую notifyItemChanged, ошибка исчезла.

Ответ 25

Я столкнулся с той же проблемой.

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

При перемещении по фрагменту жизненный цикл фрагмента проходил только через onDestroyView, а по возвращении он начинался с onCreateView. Однако, мой адаптер был инициализирован во фрагменте onCreate и не инициализировался при возврате.

Исправление состояло в том, чтобы инициализировать адаптер в onCreateView.

Надеюсь, что это может кому-то помочь.

Ответ 26

Если ваши данные сильно изменяются, вы можете использовать

 mAdapter.notifyItemRangeChanged(0, yourData.size());

или некоторые отдельные элементы в ваших наборах данных, вы можете использовать

 mAdapter.notifyItemChanged(pos);

Для подробного использования методов вы можете ссылаться на doc, чтобы не использовать прямое использование mAdapter.notifyDataSetChanged().

Ответ 27

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

//в классе адаптера

public void updateItems(List<ComponentEntity> newItems) {
    List<ComponentEntity> updatedItems = new ArrayList<>();
    updatedItems = newItems;
    items = updatedItems;
    notifyDataSetChanged();
}