it-swarm.com.de

RecyclerView und Java.lang.IndexOutOfBoundsException: Inkonsistenz festgestellt. Ungültige Ansichtshalter-AdapterpositionViewHolder in Samsung-Geräten

Ich habe eine Recycler-Ansicht, die auf allen Geräten außer Samsung einwandfrei funktioniert. Bei Samsung bekomme ich es

Java.lang.IndexOutOfBoundsException: Inkonsistenz festgestellt. Ungültiger Ansichtshalteradapter positionViewHolder

wenn ich mit der Recycler-Ansicht von einer anderen Aktivität zum Fragment zurückkehre.

Adaptercode: 

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

Ausnahme:

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)

Wie kann ich das beheben?

Dieses Problem wird durch RecyclerView Daten verursacht, die in einem anderen Thread geändert wurden. Am besten prüfen Sie den gesamten Datenzugriff. Und eine Problemumgehung ist das Umschließen von LinearLayoutManager.

Vorherige Antwort

Es gab einen Fehler in RecyclerView und die Unterstützung 23.1.1 wurde immer noch nicht behoben. 

Beachten Sie für eine Problemumgehung die Rückverfolgung von Stapeln. Wenn Sie diese Exception in einer Klasse abfangen können, kann dieser Absturz übersprungen werden. Für mich erstelle ich eine LinearLayoutManagerWrapper und überschreibe die 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");
        }
    }
}

Dann setzen Sie es auf RecyclerView:

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

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

Fangen Sie diese Ausnahme tatsächlich ab und scheint noch keine Nebenwirkung zu haben. 

Wenn Sie GridLayoutManager oder StaggeredGridLayoutManager verwenden, müssen Sie einen Wrapper dafür erstellen.

Hinweis: Die RecyclerView befindet sich möglicherweise in einem falschen internen Status.

149
sakiM

Dies ist ein Beispiel für die Aktualisierung von Daten mit völlig neuem Inhalt. Sie können sie leicht an Ihre Bedürfnisse anpassen. Ich habe dies in meinem Fall gelöst, indem Sie Folgendes aufgerufen haben:

notifyItemRangeRemoved(0, previousContentSize);

vor:

notifyItemRangeInserted(0, newContentSize);

Dies ist die richtige Lösung und wird auch in diesem Beitrag von einem AOSP-Projektmitglied erwähnt.

56
box

Neue Antwort: Verwenden Sie DiffUtil für alle RecyclerView-Updates. Dies wird sowohl bei der Leistung als auch beim oben genannten Fehler helfen. Siehe hier

Vorherige Antwort: Das hat für mich funktioniert. Der Schlüssel ist, notifyDataSetChanged() nicht zu verwenden und die richtigen Dinge in der richtigen Reihenfolge zu tun:

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

Ich war einmal mit diesem Problem konfrontiert und konnte dieses Problem lösen, indem ich LayoutManager einwickelte und vorhersagende Animationen deaktiviert.

Hier ein Beispiel:

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;
  }
}

Und stell es auf RecyclerView:

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

Ich hatte ein ähnliches Problem.

Problem im Fehlercode unten:

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

Lösung:

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

Laut diesem Problem wurde das Problem behoben und wurde wahrscheinlich Anfang 2015 veröffentlicht. Ein Zitat aus demselben Thread :

Es bezieht sich speziell auf den Aufruf von notifyDataSetChanged. [...]

Übrigens, ich empfehle dringend, notifyDataSetChanged nicht zu verwenden, da Animationen und Leistung dadurch zerstört werden. In diesem Fall wird das Problem auch durch die Verwendung bestimmter Benachrichtigungsereignisse behoben.

Wenn Sie immer noch Probleme mit einer aktuellen Version der Support-Bibliothek haben, empfiehlt es sich, Ihre Aufrufe an notifyXXX (insbesondere Ihre Verwendung von notifyDataSetChanged) in Ihrem Adapter zu überprüfen, um sicherzustellen, dass Sie sich an den (etwas empfindlichen/obskuren) RecyclerView.Adapter Vertrag. Stellen Sie außerdem sicher, dass Sie diese Benachrichtigungen im Haupt-Thread ausgeben.

13
stkent

Ich hatte das gleiche Problem. Ursache war, dass ich die Benachrichtigung des Adapters über das Einfügen von Artikeln verzögerte.

ViewHolder hat jedoch versucht, einige Daten in seiner Ansicht neu zu zeichnen, und es begann die RecyclerView-Messung und die Zählung der untergeordneten Kinder - in diesem Moment stürzte sie ab (Artikelliste und ihre Größe wurde bereits aktualisiert, der Adapter wurde jedoch noch nicht benachrichtigt).

10
porfirion

ein weiterer Grund für dieses Problem ist, wenn Sie diese Methoden mit falschen Indizes aufrufen (Indizes, die NICHT geschehen, fügen Sie in sie ein)

-notifyItemRangeRemoved 

-notifyItemRemoved

-notifyItemRangeInserted

-notifyItemInserted

Überprüfen Sie die Indexparameter dieser Methoden und stellen Sie sicher, dass sie genau und korrekt sind.

7
Amir Ziarati

Dies geschieht, wenn Sie die falsche Position für notifyItemChanged angeben, NotifyItemRangeInserted usw.Für mich:

Vorher: (fehlerhaft)

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

Nach: (richtig)

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

Dieser Fehler ist in 23.1.1 immer noch nicht behoben, aber eine allgemeine Problemumgehung wäre, die Ausnahme abzufangen.

6
Farooq AR

Dieses Problem wird durch RecyclerView-Daten verursacht, die in verschiedenen .__-Dateien geändert wurden. Faden

Kann Threading als ein Problem bestätigen und da ich auf das Problem gestoßen bin und RxJava immer beliebter wird: Stellen Sie sicher, dass Sie .observeOn(AndroidSchedulers.mainThread()) verwenden, wenn Sie notify[whatever changed] aufrufen. 

codebeispiel vom Adapter:

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

    [...]

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

In meinem Fall stürzte es jedes Mal ab, wenn ich notifyItemRemoved (0) anrufe. Es stellte sich heraus, dass ich setHasStableIds(true) eingestellt habe und in getItemId die Artikelposition zurückgegeben habe. Ich beendete die Aktualisierung, um die hashCode() des Elements oder die selbst definierte eindeutige ID des Elements zurückzugeben, wodurch das Problem behoben wurde.

4
Arst

In meinem Fall habe ich die Daten zuvor in einem Thread mit mRecyclerView.post (new Runnable ...) geändert und dann später wieder im UI-Thread geändert, was zu Inkonsistenzen führte. 

3
Niroj Shr

Problem trat nur auf, wenn:

Ich habe den Adapter mit einer leeren Liste erstellt . Dann habe ich Elemente eingefügt und notifyItemRangeInserted aufgerufen.

Lösung:

Ich habe dieses Problem gelöst, indem ich den Adapter erst erstellt habe, nachdem ich den ersten Datenblock besessen und ihn sofort initialisiert habe. Der nächste Block könnte dann eingefügt und notifyItemRangeInserted ohne Probleme aufgerufen werden.

3
Willi Mentzel

In meinem Fall bekam ich dieses Problem, weil Daten vom Server abgerufen wurden (ich verwende Firebase Firestore). Während der erste Datensatz von DiffUtil im Hintergrund verarbeitet wird, kommt es zu einem weiteren Datenupdate, das ein Parallelitätsproblem verursacht indem Sie ein anderes DiffUtil starten. 

Wenn Sie DiffUtil in einem Hintergrundthread verwenden, der dann zum Hauptthread zurückkehrt, um die Ergebnisse an RecylerView zu senden, besteht die Möglichkeit, diesen Fehler zu erhalten, wenn mehrere Datenaktualisierungen in kurzer Zeit eintreffen.

Ich löste dies, indem ich den Ratschlag in dieser wunderbaren Erklärung befolgte: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

Um die Lösung zu erklären, müssen Sie lediglich die Aktualisierungen pushen, während das aktuelle Update zu einem Deque läuft. Der Deque kann dann die anstehenden Aktualisierungen ausführen, sobald die aktuelle abgeschlossen ist. Dadurch werden alle nachfolgenden Aktualisierungen gehandhabt, aber auch Inkonsistenzfehler vermieden.

Ich hoffe, das hilft, weil ich mich am Kopf kratzte!

3
dejavu89

Mein Problem war, dass, obwohl ich sowohl die Array-Liste mit dem Datenmodell für die Recycler-Ansicht lösche, ich den Adapter nicht über diese Änderung informiert habe, so dass veraltete Daten aus dem vorherigen Modell vorhanden waren. Was die Verwirrung über die Position des Sichthalters verursacht hat. Um dies zu beheben, benachrichtigen Sie den Adapter immer, dass sich der Datensatz geändert hat, bevor Sie ihn erneut aktualisieren.

2
Remario

In meinem Fall bestand das Problem darin, dass ich notifyDataSetChanged verwendete, wenn die Menge der neu geladenen Daten unter den ursprünglichen Daten lag.

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

Der Fehler kann dadurch verursacht werden, dass Ihre Änderungen nicht mit den Benachrichtigungen übereinstimmen, die Sie benachrichtigen. In meinem Fall:

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

Was ich natürlich tun musste:

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

Gründe verursachten dieses Problem:

  1. Ein internes Problem in Recycler, wenn Elementanimationen aktiviert sind
  2. Änderung der Daten des Recyclers in einem anderen Thread
  3. Benachrichtigungsmethoden falsch aufrufen

LÖSUNG:

----------------- LÖSUNG 1 ---------------

  • Abfangen der Ausnahme (nicht empfohlen, insbesondere aus Grund # 3)}

Erstellen Sie einen benutzerdefinierten LinearLayoutManager wie folgt und setzen Sie ihn auf 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");
                }

            }
        }

Stellen Sie dann RecyclerVIew Layout Manager wie folgt ein:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- LÖSUNG 2 ---------------

  • Elementanimationen deaktivieren (behebt das Problem, wenn es zu Grund 1 kam):

Erstellen Sie erneut einen benutzerdefinierten linearen Layout-Manager wie folgt:

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

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

Stellen Sie dann RecyclerVIew Layout Manager wie folgt ein:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- LÖSUNG 3 ---------------

  • Diese Lösung behebt das Problem, wenn es sich um den Grund Nr. 3 handelt.) Sie müssen sicherstellen, dass Sie die Benachrichtigungsmethoden korrekt verwenden. Alternativ können Sie DiffUtil verwenden, um die Änderungen in einem Smart, Easy und reibungsloser Weg.Verwenden von DiffUtil in Android RecyclerView

----------------- LÖSUNG 4 ---------------

  • Aus Gründen # 2 müssen Sie den gesamten Datenzugriff auf die Recycler-Liste überprüfen und sicherstellen, dass in einem anderen Thread keine Änderungen vorgenommen werden.
1
Islam Assi

Ich bin auf dasselbe Problem gestoßen.

Meine App verwendet Navigationskomponenten mit einem Fragment, das meine recyclerView enthält. Meine Liste wurde beim ersten Laden des Fragments einwandfrei angezeigt. Beim Navigieren und Zurückkehren ist dieser Fehler aufgetreten.

Beim Wegnavigieren durchlief der Fragment-Lebenszyklus nur onDestroyView und bei der Rückkehr wurde er bei onCreateView gestartet. Mein Adapter wurde jedoch in onCreate des Fragments initialisiert und bei der Rückkehr nicht neu initialisiert.

Das Update bestand darin, den Adapter in onCreateView zu initialisieren.

Hoffe das kann jemandem helfen.

1
Loren

Ich habe diese Fehlermeldung erhalten, weil ich aus Versehen zweimal "notifyItemInserted" aufgerufen habe.

0
Feuby

In meinem Fall hatte ich mehr als 5000 Elemente in der Liste ... Mein Problem war, dass beim Scrollen der Recycler-Ansicht manchmal "onBindViewHolder" aufgerufen wird, während die Methode "myCustomAddItems" die Liste ändert.

Meine Lösung bestand darin, "synchronized (syncObject) {}" zu allen Methoden hinzuzufügen, die die Datenliste ändern Auf diese Weise kann zu jeder Zeit nur eine Methode diese Liste lesen.

0
user3193413

Ich habe das gleiche Problem und habe gelesen, dass dies nur bei Samsung-Handys passiert ist. Die Realität hat jedoch gezeigt, dass dies bei vielen Marken der Fall ist.

Nach dem Testen habe ich festgestellt, dass dies nur passiert, wenn Sie die RecyclerView schnell scrollen und dann entweder mit der Zurück-Taste oder der Auf-Taste zurückgehen. Also habe ich den Up-Button und den folgenden Snippet inBackpressed:

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

Mit dieser Lösung laden Sie einfach eine neue Arraylist in den Adapter und einen neuen Adapter in recyclerView. Anschließend beenden Sie die Aktivität.

Hoffe es hilft jemandem

0
Farmaker

Ich habe diesen Fehler erhalten, weil ich versehentlich mehrere Male eine Methode aufgerufen hatte, um eine bestimmte Zeile aus meinem Recycling-Fenster zu entfernen. Ich hatte eine Methode wie:

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

Ich habe diese Methode aus Versehen dreimal statt einmal aufgerufen, daher war loc das zweite Mal -1 und der Fehler wurde angezeigt, als versucht wurde, sie zu entfernen. Die beiden Korrekturen sollten sicherstellen, dass die Methode nur einmal aufgerufen wurde, und außerdem eine Überprüfung der Funktionsfähigkeit wie folgt hinzufügen:

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

In meinem Fall haben sich die Adapterdaten geändert. Und ich habe notifyItemInserted () fälschlicherweise für diese Änderungen verwendet. Wenn ich notifyItemChanged verwende, ist der Fehler verschwunden.

0
oiyio