it-roy-ru.com

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

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

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

когда я вернусь к фрагменту с видом переработчика из другого действия.

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

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)

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

174
Владимир Фишер

Эта проблема вызвана 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 может быть в неправильном внутреннем состоянии.

149
sakiM

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

notifyItemRangeRemoved(0, previousContentSize);

до:

notifyItemRangeInserted(0, newContentSize);

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

56
box

Новый ответ: Используйте 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());
}
17
Bolling

Однажды я столкнулся с этой проблемой и решил ее, обернув переменную 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);
15
hcknl

У меня была похожая проблема.

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

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);
14
Vandai Doan

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

Это конкретно связано с вызовом notifyDataSetChanged. [...]

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

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

13
stkent

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

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

10
porfirion

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

-notifyItemRangeRemoved 

-notifyItemRemoved

-notifyItemRangeInserted

-notifyItemInserted

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

7
Amir Ziarati

Это происходит, когда вы указываете неправильную позицию для 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 
 }
6
Saurabh Padwekar

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

6
Farooq AR

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

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

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

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

    [...]

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

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

4
Arst

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

3
Niroj Shr

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

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

Решение:

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

3
Willi Mentzel

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

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

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

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

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

3
dejavu89

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

2
Remario

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

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

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

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

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

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

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

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

РЕШЕНИЕ:

----------------- РЕШЕНИЕ 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));

----------------- РЕШЕНИЕ 2 ---------------

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

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

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

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

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

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- РЕШЕНИЕ 3 ---------------

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

----------------- РЕШЕНИЕ 4 ---------------

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

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

0
Feuby

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

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

0
user3193413

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

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

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

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

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

0
Farmaker

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

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

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

0
oiyio