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
.
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.
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.
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());
}
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);
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);
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.
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).
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.
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
}
Dieser Fehler ist in 23.1.1 immer noch nicht behoben, aber eine allgemeine Problemumgehung wäre, die Ausnahme abzufangen.
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]
}
});
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.
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.
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.
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!
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.
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);
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);
Gründe verursachten dieses Problem:
LÖSUNG:
----------------- LÖSUNG 1 ---------------
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 ---------------
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 ---------------
----------------- LÖSUNG 4 ---------------
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.
Ich habe diese Fehlermeldung erhalten, weil ich aus Versehen zweimal "notifyItemInserted" aufgerufen habe.
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.
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
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);
}
}
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.