Ich habe eine Recycler-Ansicht, die auf allen Geräten außer Samsung einwandfrei funktioniert. Auf Samsung bekomme ich
java.lang.IndexOutOfBoundsException: Inkonsistenz festgestellt. Ungültiger Ansichtshalteradapter positionViewHolder
wenn ich mit einer anderen Aktivität zum Fragment mit der Recycler-Ansicht 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?
android
android-recyclerview
Владимир Фишер
quelle
quelle
Antworten:
Dieses Problem wird durch
RecyclerView
Daten verursacht, die in einem anderen Thread geändert wurden. Am besten überprüfen Sie den gesamten Datenzugriff. Und eine Problemumgehung wird abgeschlossenLinearLayoutManager
.Vorherige Antwort
Es gab tatsächlich einen Fehler in RecyclerView und die Unterstützung 23.1.1 wurde immer noch nicht behoben.
Beachten Sie für eine Problemumgehung, dass Backtrace-Stapel
Exception
diesen Absturz möglicherweise überspringen , wenn wir dies in einer bestimmten Klasse abfangen können. Für mich erstelle ich einLinearLayoutManagerWrapper
und überschreibe dasonLayoutChildren
:Stellen Sie dann Folgendes ein
RecyclerView
:Fangen Sie tatsächlich diese Ausnahme und scheint noch keine Nebenwirkung.
Auch wenn Sie einen Wrapper dafür verwenden
GridLayoutManager
oderStaggeredGridLayoutManager
erstellen müssen.Hinweis:
RecyclerView
Möglicherweise befindet sich das Gerät in einem falschen internen Zustand.quelle
LinearLayoutManager
und überschreiben Sie dies. Ich werde eine Ergänzung in meiner Antwort.Dies ist ein Beispiel für das Aktualisieren von Daten mit völlig neuen Inhalten. Sie können es leicht an Ihre Bedürfnisse anpassen. Ich habe dies in meinem Fall gelöst, indem ich angerufen habe:
Vor:
Dies ist die richtige Lösung und wird in diesem Beitrag auch von einem AOSP-Projektmitglied erwähnt.
quelle
notifyItemRangeInserted
und habe dieses Problem nicht mit einigen Samsung-GerätennotifyItemRangeInserted
Ich war einmal mit diesem Problem konfrontiert und habe es gelöst, indem ich die
LayoutManager
prädiktiven Animationen verpackt und deaktiviert habe.Hier ein Beispiel:
Und setzen Sie es auf
RecyclerView
:quelle
public boolean supportsPredictiveItemAnimations() { return false; }
LinearLayoutManager
sagt, dass der Standardwert falsch ist, aber diese Anweisung ist falsch :-( Der dekompilierte Code fürLinearLayoutManager
hat Folgendes : public boolean supportPredictiveItemAnimations () {return this.mPendingSavedState == null && this.mLastStackFromEnd == this.mStackFromEnd ;}Neue Antwort: Verwenden Sie DiffUtil für alle RecyclerView-Updates. Dies hilft sowohl bei der Leistung als auch bei dem oben genannten Fehler. Siehe hier
Vorherige Antwort: Das hat bei mir funktioniert. Der Schlüssel ist,
notifyDataSetChanged()
die richtigen Dinge nicht in der richtigen Reihenfolge zu verwenden und zu tun:quelle
Gründe für dieses Problem:
LÖSUNG:
----------------- LÖSUNG 1 ---------------
Erstellen Sie einen benutzerdefinierten LinearLayoutManager wie folgt und setzen Sie ihn auf ReyclerView
Stellen Sie dann RecyclerVIew Layout Manager wie folgt ein:
----------------- LÖSUNG 2 ---------------
Erstellen Sie erneut einen benutzerdefinierten linearen Layout-Manager wie folgt:
Stellen Sie dann RecyclerVIew Layout Manager wie folgt ein:
----------------- LÖSUNG 3 ---------------
----------------- LÖSUNG 4 ---------------
quelle
Ich hatte ein ähnliches Problem.
Problem im Fehlercode unten:
Lösung:
quelle
newList.size() - 1
.Laut diesem Problem wurde das Problem behoben und wahrscheinlich Anfang 2015 veröffentlicht. Ein Zitat aus demselben Thread :
Wenn Sie immer noch Probleme mit einer aktuellen Version der Support-Bibliothek haben, würde ich empfehlen, Ihre Anrufe
notifyXXX
(insbesondere Ihre Verwendung vonnotifyDataSetChanged
) in Ihrem Adapter zu überprüfen , um sicherzustellen, dass Sie den (etwas heiklen / undurchsichtigen)RecyclerView.Adapter
Vertrag einhalten . Stellen Sie außerdem sicher, dass diese Benachrichtigungen im Hauptthread ausgegeben werden.quelle
Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
Ich hatte das gleiche Problem. Dies wurde verursacht, weil ich die Benachrichtigung des Adapters über das Einfügen von Elementen verzögert habe.
Es wurde jedoch
ViewHolder
versucht, einige Daten in der Ansicht neu zu zeichnen, und dieRecyclerView
Messung und Nachzählung der Anzahl der Kinder wurde gestartet. In diesem Moment stürzte sie ab (die Artikelliste und ihre Größe wurden bereits aktualisiert, der Adapter wurde jedoch noch nicht benachrichtigt).quelle
Dies geschieht, wenn Sie die falsche Position für notifyItemChanged, notifyItemRangeInserted usw. angeben. Für mich:
Vorher: (fehlerhaft)
Nachher: (Richtig)
quelle
notifyItemRangeInserted(initialSize, mChannelItemList.size()-1);
und nichtnotifyItemRangeInserted(initialSize, list.size());
?initialSize
undlist
Größe. Beide Varianten sind also falsch.notifyItemRangeInserted(initialSize, list.size()-1);
aber ich verstehe es nicht. Warum muss ich die eingefügte Größe für itemCount um eins reduzieren?Ein weiterer Grund für dieses Problem ist, dass Sie diese Methoden mit falschen Indizes aufrufen (Indizes, bei denen es NICHT vorgekommen ist, fügen sie ein oder entfernen sie).
-notifyItemRangeRemoved
-notifyItemRemoved
-notifyItemRangeInserted
-notifyItemInserted
Überprüfen Sie die Indexparameter dieser Methoden und stellen Sie sicher, dass sie präzise und korrekt sind.
quelle
Dieser Fehler ist in 23.1.1 immer noch nicht behoben, aber eine häufige Problemumgehung besteht darin, die Ausnahme abzufangen.
quelle
Kann das 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 es verwenden,
.observeOn(AndroidSchedulers.mainThread())
wenn Sie anrufennotify[whatever changed]
Codebeispiel vom Adapter:
quelle
In meinem Fall stürzte es jedes Mal ab, wenn ich notifyItemRemoved (0) aufrufte. Es stellte sich heraus, dass ich eingestellt
setHasStableIds(true)
undgetItemId
gerade die Artikelposition zurückgegeben habe. Am Ende habe ich es aktualisiert, um diehashCode()
oder die selbst definierte eindeutige ID des Elements zurückzugeben , wodurch das Problem behoben wurde.quelle
In meinem Fall trat dieses Problem auf, weil Datenaktualisierungen vom Server abgerufen wurden (ich verwende Firebase Firestore), und während der erste Datensatz von DiffUtil im Hintergrund verarbeitet wird, kommt ein weiterer Satz von Datenaktualisierungen und verursacht ein Parallelitätsproblem durch Starten eines anderen DiffUtil.
Kurz gesagt, wenn Sie DiffUtil in einem Hintergrundthread verwenden, der dann zum Hauptthread zurückkehrt, um die Ergebnisse an RecylerView zu senden, besteht die Möglichkeit, dass dieser Fehler auftritt, wenn mehrere Datenaktualisierungen in kurzer Zeit erfolgen.
Ich habe dieses Problem gelöst, indem ich den Ratschlägen in dieser wunderbaren Erklärung gefolgt bin: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2
Um die Lösung zu erklären, müssen Sie die Updates pushen, während das aktuelle auf einem Deque ausgeführt wird. Die Deque kann dann die ausstehenden Aktualisierungen ausführen, sobald die aktuelle abgeschlossen ist, wodurch alle nachfolgenden Aktualisierungen verarbeitet werden, aber auch Inkonsistenzfehler vermieden werden!
Hoffe das hilft, denn dieser hat mich am Kopf kratzen lassen!
quelle
Problem trat bei mir nur auf, wenn:
Ich habe den Adapter mit einer leeren Liste erstellt . Dann habe ich Artikel eingefügt und angerufen
notifyItemRangeInserted
.Lösung:
Ich habe dieses Problem gelöst, indem ich den Adapter erst erstellt habe, nachdem ich den ersten Datenblock habe und ihn sofort damit initialisiert habe. Der nächste Block konnte dann problemlos eingefügt und
notifyItemRangeInserted
aufgerufen werden.quelle
notifyItemRangeInserted
, hatte aber dort nie diese Ausnahme.Mein Problem war, dass ich, obwohl ich beide Array-Listen mit dem Datenmodell für die Recycler-Ansicht löschte, den Adapter nicht über diese Änderung benachrichtigte, sodass veraltete Daten aus dem vorherigen Modell vorhanden waren. Was die Verwirrung über die Position des Sichthalters verursachte. Um dies zu beheben, benachrichtigen Sie den Adapter immer, dass der Datensatz geändert wurde, bevor Sie ihn erneut aktualisieren.
quelle
In meinem Fall habe ich die Daten zuvor in einem Thread mit mRecyclerView.post (new Runnable ...) geändert und später die Daten im UI-Thread erneut geändert, was zu Inkonsistenzen führte.
quelle
Der Fehler kann dadurch verursacht werden, dass Ihre Änderungen nicht mit dem übereinstimmen, was Sie benachrichtigen. In meinem Fall:
Was ich natürlich tun musste:
quelle
In meinem Fall bestand das Problem darin, dass ich notifyDataSetChanged verwendet habe, wenn die Menge der neu geladenen Daten geringer war als die ursprünglichen Daten. Dieser Ansatz hat mir geholfen:
quelle
notifyDataSetChanged
hängt es von neuen Daten ab? Ich dachte, es würde die ganze Liste aktualisieren.Ich bin auf das gleiche Problem gestoßen.
Meine App verwendet Navigationskomponenten mit einem Fragment, das meine recyclerView enthält. Meine Liste wurde beim ersten Laden des Fragments gut angezeigt ... aber beim Navigieren und Zurückkommen trat dieser Fehler auf.
Beim Wegnavigieren durchlief der Fragmentlebenszyklus nur onDestroyView und startete bei der Rückkehr bei onCreateView. 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.
quelle
Ich habe diesen Fehler erhalten, weil ich fälschlicherweise eine Methode aufgerufen habe, um eine bestimmte Zeile mehrmals aus meiner Recycling-Ansicht zu entfernen. Ich hatte eine Methode wie:
Ich habe diese Methode versehentlich dreimal statt einmal aufgerufen, daher war das zweite Mal
loc
-1 und der Fehler wurde angegeben, als versucht wurde, sie zu entfernen. Die beiden Korrekturen sollten sicherstellen, dass die Methode nur einmal aufgerufen wurde, und auch eine Überprüfung der Integrität wie folgt hinzufügen:quelle
Ich habe das gleiche Problem und habe gelesen, dass dies nur bei Samsung-Handys passiert ist ... Aber die Realität hat gezeigt, dass dies bei vielen Marken passiert.
Nach dem Testen wurde mir klar, dass dies nur passiert, wenn Sie schnell durch die RecyclerView scrollen und dann entweder mit der Zurück-Taste oder der Aufwärts-Taste zurückgehen. Also habe ich den Up-Button hineingesteckt und onBackpressed das folgende Snippet:
Mit dieser Lösung laden Sie einfach eine neue Arraylist in den Adapter und einen neuen Adapter in recyclerView und beenden dann die Aktivität.
Hoffe es hilft jemandem
quelle
Ich habe diesen Fehler erhalten, weil ich versehentlich zweimal "notifyItemInserted" aufgerufen habe.
quelle
In meinem Fall hatte ich mehr als 5000 Artikel in der Liste. Mein Problem war, dass beim Scrollen in der Recycler-Ansicht manchmal der "onBindViewHolder" aufgerufen wird, während die "myCustomAddItems" -Methode die Liste ändert.
Meine Lösung bestand darin, allen Methoden, die die Datenliste ändern, "synchronized (syncObject) {}" hinzuzufügen. Auf diese Weise kann zu jedem Zeitpunkt nur eine Methode diese Liste lesen.
quelle
In meinem Fall haben sich die Adapterdaten geändert. Und ich habe fälschlicherweise notifyItemInserted () für diese Änderungen verwendet. Wenn ich notifyItemChanged verwende, ist der Fehler verschwunden.
quelle
Ich bin auf dasselbe Problem gestoßen, als ich Elemente in der Liste entfernt und aktualisiert habe ... Nach Tagen der Untersuchung denke ich, dass ich endlich eine Lösung gefunden habe.
Was Sie tun müssen, ist zuerst die gesamte
notifyItemChanged
Liste und erst dann allenotifyItemRemoved
in absteigender ReihenfolgeIch hoffe, dies wird Menschen helfen, die auf dasselbe Problem stoßen ...
quelle
Ich verwende einen Cursor, daher kann ich die DiffUtils nicht wie in den gängigen Antworten vorgeschlagen verwenden. Damit es für mich funktioniert, deaktiviere ich Animationen, wenn die Liste nicht inaktiv ist. Dies ist die Erweiterung, die dieses Problem behebt:
Dann können Sie Ihren Adapter so aktualisieren
quelle
Wenn das Problem nach Multi-Touch auftritt, können Sie Multi-Touch mit deaktivieren
in der Layoutdatei.
quelle
Wenn sich Ihre Daten stark ändern, können Sie verwenden
oder einige einzelne Elemente in Ihrem Datensatz können geändert werden
Für eine detaillierte Verwendung der Methoden können Sie auf das Dokument verweisen, indem Sie versuchen, es nicht direkt zu verwenden
mAdapter.notifyDataSetChanged()
.quelle
notifyItemRangeChanged
erzeugt auch den gleichen Absturz.