首页 文章

RecyclerView和java.lang.IndexOutOfBoundsException:检测到不一致 . 三星设备中的视图持有者适配器positionViewHolder无效

提问于
浏览
164

我有一个回收站视图,可以在除三星以外的所有设备上完美运行 . 在三星,我明白了

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)

我怎样才能解决这个问题?

25 回答

  • 0

    在我的情况下,问题是当新加载的数据量小于初始数据时我使用notifyDataSetChanged . 这种方法帮助我:

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

    在我的情况下,我遇到了这个问题,因为从服务器获取数据更新(我正在使用Firebase Firestore),而第一组数据正在后台由DiffUtil处理,另一组数据更新到来并导致并发问题通过启动另一个DiffUtil .

    简而言之,如果您在后台线程上使用DiffUtil然后返回到主线程以将结果分派给RecylerView,那么当多个数据更新在短时间内到达时,您就有机会获得此错误 .

    我按照这个精彩解释中的建议解决了这个问题:https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

    只是解释解决方案是在当前的更新运行到Deque时推送更新 . 然后,deque可以在当前更新完成后运行挂起的更新,从而处理所有后续更新,同时避免不一致错误!

    希望这会有所帮助,因为这个让我抓狂!

  • 54

    我有同样的问题 . 这是因为我推迟了关于项目插入的适配器的通知 .

    但是 ViewHolder 试图在它的视图中重绘一些数据并且它开始测量并重新计算子计数 - 在那一刻它崩溃了(项目列表并且它的大小已经更新,但是适配器还没有得到通知) .

  • 13

    这对我有用 . 关键是不要使用 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());
    }
    
  • 11

    我的问题是,即使我清除包含回收器视图的数据模型的数组列表,我也没有通知适配器该更改,因此它具有来自先前模型的陈旧数据 . 这引起了对观点持有人立场的困惑 . 要解决此问题,请始终在再次更新之前通知适配器数据集已更改 .

  • 10

    我遇到了同样的问题,我已经读到这只发生在三星手机上......但实际情况表明,这种情况发生在很多品牌中 .

    经过测试后,我意识到只有当您快速滚动RecyclerView然后使用后退按钮或向上按钮返回时才会发生这种情况 . 所以我把里面的按钮和onBackpressed放在下面的片段:

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

    使用此解决方案,您只需将新的Arraylist加载到适配器,将新适配器加载到recyclerView,然后完成活动 .

    希望它可以帮到某人

  • 3

    我收到此错误是因为我错误地调用了一个方法来多次从recyclelerview中删除特定行 . 我有一个方法,如:

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

    根据this issue,问题已得到解决,并可能在2015年初附近发布.A quote from that same thread

    它特别与调用notifyDataSetChanged有关 . [...]顺便说一句,我强烈建议不要使用notifyDataSetChanged,因为它会杀死动画和性能 . 同样对于这种情况,使用特定的通知事件将解决此问题 .

    如果您仍然遇到最新版本的支持库问题,我建议您在适配器内查看 notifyXXX (具体来说,您使用 notifyDataSetChanged )的调用,以确保您坚持(有些微妙/模糊) RecyclerView.Adapter Contract . 还要确保在主线程上发出这些通知 .

  • 2

    如果您的数据发生了很大变化,您可以使用

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

    或者您的数据集中的某些单项会发生变化,您可以使用

    mAdapter.notifyItemChanged(pos);
    

    有关详细的方法用法,您可以参考doc,在某种程度上,尽量不要直接使用 mAdapter.notifyDataSetChanged() .

  • 13

    在我的情况下,每当我调用notifyItemRemoved(0)时,它都会崩溃 . 原来我设置 setHasStableIds(true) 并且在 getItemId 我刚刚返回了项目位置 . 我最终更新它以返回项目的 hashCode() 或自定义的唯一ID,这解决了问题 .

  • 0

    在我的情况下,我使用mRecyclerView.post(新的Runnable ...)更改先前在线程内的数据,然后再次更改UI线程中的数据,这导致不一致 .

  • -1

    我曾经遇到过这个问题,我通过包装 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

    Problem occured for me only when:

    我用 empty list 创建了适配器 . 然后我插入了项目并调用 notifyItemRangeInserted .

    Solution:

    我通过创建适配器解决了这个问题,只有在我拥有第一块数据并立即用它初始化之后 . 然后可以插入下一个块并且没有问题地调用 notifyItemRangeInserted .

  • 138

    此问题是由 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));
    

    实际上 grab 这个例外,似乎没有任何副作用 .

    此外,如果您使用 GridLayoutManagerStaggeredGridLayoutManager ,则必须为其创建包装器 .

    注意: RecyclerView 可能处于错误的内部状态 .

  • 4

    我有类似的问题 .

    Problem in error code below:

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

    Solution:

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

    错误可能是由于您的更改与您通知的内容不一致而导致的 . 就我而言:

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

    我当然要做的事情:

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

    就我而言,适配器数据已更改 . 我错误地使用notifyItemInserted()来进行这些更改 . 当我使用notifyItemChanged时,错误消失了 .

  • 4

    我收到此错误是因为我错误地两次调用“notifyItemInserted” .

  • 3

    这个问题发生的另一个原因是当你用错误的索引调用这些方法时(没有插入或删除的索引)

    -notifyItemRangeRemoved

    -notifyItemRemoved

    -notifyItemRangeInserted

    -notifyItemInserted

    检查这些方法的索引参数,并确保它们是准确和正确的 .

  • -1

    我有同样的问题 . 像这样重新创建新列表可以完美地解决问题:

    //在 Adapter

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

    在我的情况下,我在列表中有超过5000个项目 . 我的问题是,当滚动循环器视图时,有时会调用“onBindViewHolder”而“myCustomAddItems”方法正在改变列表 .

    我的解决方案是将“synchronized(syncObject){}”添加到改变数据列表的所有方法中 . 这种方式在任何时候只有一种方法可以读取此列表 .

  • 2

    这是使用全新内容刷新数据的示例 . 您可以轻松修改它以满足您的需求 . 我通过调用以下方式解决了这个问题:

    notifyItemRangeRemoved(0, previousContentSize);
    

    之前:

    notifyItemRangeInserted(0, newContentSize);
    

    这是正确的解决方案,也是AOSP项目成员在this post中提到的 .

  • 0

    当你为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 
     }
    
  • 0

    这个错误在23.1.1中仍然没有修复,但常见的解决方法是捕获异常 .

  • 0

    此问题是由在不同线程中修改的RecyclerView数据引起的

    可以确认线程是一个问题,因为我遇到了问题并且RxJava变得越来越流行:确保在调用 notify[whatever changed] 时使用 .observeOn(AndroidSchedulers.mainThread())

    适配器的代码示例:

    myAuxDataStructure.getChangeObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<AuxDataStructure>() {
    
        [...]
    
        @Override
        public void onNext(AuxDataStructure o) {
            [notify here]
        }
    });
    

相关问题