Mam widok recyklera, który działa idealnie na wszystkich urządzeniach oprócz Samsunga. Na Samsunga dostaję
java.lang.IndexOutOfBoundsException: Wykryto niespójność. Nieprawidłowa pozycja adaptera uchwytu widoku ViewHolder
kiedy wracam do fragmentu z widokiem recyklera z innej działalności.
Kod adaptera:
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);
}
}
}
Wyjątek:
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)
Jak mogę to naprawić?
android
android-recyclerview
Владимир Фишер
źródło
źródło
Odpowiedzi:
Przyczyną tego problemu są
RecyclerView
Dane zmodyfikowane w innym wątku. Najlepszym sposobem jest sprawdzenie dostępu do wszystkich danych. Obejście tego problemu się kończyLinearLayoutManager
.Poprzednia odpowiedź
W RecyclerView rzeczywiście wystąpił błąd, a obsługa 23.1.1 nadal nie została naprawiona.
Aby obejść ten problem, zwróć uwagę, że stosy śledzenia wstecznego mogą zostać przechwycone przez
Exception
jedną z klas, co może spowodować pominięcie tej awarii. Dla mnie tworzęLinearLayoutManagerWrapper
i zastępujęonLayoutChildren
:Następnie ustaw na
RecyclerView
:Właściwie złap ten wyjątek i wydaje się, że nie ma jeszcze żadnych skutków ubocznych.
Ponadto, jeśli używasz
GridLayoutManager
lubStaggeredGridLayoutManager
musisz utworzyć opakowanie dla niego.Uwaga:
RecyclerView
Może być w złym stanie wewnętrznym.źródło
LinearLayoutManager
i zastąpić to. Dodam do mojej odpowiedzi.To jest przykład odświeżania danych przy użyciu całkowicie nowej zawartości. Możesz go łatwo zmodyfikować, aby dopasować do swoich potrzeb. Rozwiązałem to w moim przypadku, dzwoniąc:
przed:
Jest to poprawne rozwiązanie, a także jest mowa w tym poście przez państwo projektu AOSP.
źródło
notifyItemRangeInserted
i mam ten problem z niektórymi urządzeniami SamsungnotifyItemRangeInserted
Raz zmierzyłem się z tym problemem i rozwiązałem go, włączając
LayoutManager
i wyłączając animacje predykcyjne.Oto przykład:
I ustaw na
RecyclerView
:źródło
public boolean supportsPredictiveItemAnimations() { return false; }
LinearLayoutManager
mówi, że wartością domyślną jest fałsz, ale ta instrukcja jest fałszywa :-( Dekompilowany kod dlaLinearLayoutManager
ma to: public boolean obsługujePredictiveItemAnimations () {return this.mPendingSavedState == null && this.mLastStackFromEnd == this.mStackFromEnd ;}Nowa odpowiedź: użyj DiffUtil dla wszystkich aktualizacji RecyclerView. Pomoże to zarówno pod względem wydajności, jak i powyższego błędu. Spójrz tutaj
Poprzednia odpowiedź: To zadziałało dla mnie. Kluczem jest, aby nie używać
notifyDataSetChanged()
i robić właściwe rzeczy we właściwej kolejności:źródło
Przyczyny spowodowały ten problem:
ROZWIĄZANIE:
----------------- ROZWIĄZANIE 1 ---------------
Utwórz niestandardowy menedżer LinearLayoutManager w następujący sposób i ustaw go na ReyclerView
Następnie ustaw RecyclerVIew Layout Manager w następujący sposób:
----------------- ROZWIĄZANIE 2 ---------------
Ponownie utwórz niestandardowego menedżera układu liniowego w następujący sposób:
Następnie ustaw RecyclerVIew Layout Manager w następujący sposób:
----------------- ROZWIĄZANIE 3 ---------------
----------------- ROZWIĄZANIE 4 ---------------
źródło
Miałem podobny problem.
Problem z kodem błędu poniżej:
Rozwiązanie:
źródło
newList.size() - 1
.Według tego problemu problem został rozwiązany i prawdopodobnie został wydany jakiś czas na początku 2015 roku . Cytat z tego samego wątku :
Jeśli nadal masz problemy z najnowszą wersją biblioteki wsparcia, sugeruję przejrzenie twoich połączeń
notifyXXX
(w szczególności użycianotifyDataSetChanged
) wewnątrz adaptera, aby upewnić się, że przestrzegasz (nieco delikatnej / niejasnej)RecyclerView.Adapter
umowy. Pamiętaj również, aby wydawać powiadomienia w głównym wątku.źródło
Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
Miałem ten sam problem. Było to spowodowane opóźnionym powiadomieniem adaptera o wstawieniu elementu.
Ale
ViewHolder
spróbowałem przerysować niektóre dane w jego widoku i zaczęło sięRecyclerView
mierzenie i liczenie dzieci - w tym momencie uległo awarii (lista elementów i rozmiar zostały już zaktualizowane, ale adapter nie został jeszcze powiadomiony).źródło
Dzieje się tak, gdy podasz niepoprawną pozycję w powiadomieniuItemChanged, powiadomieniuItemRangeInserted itp. Dla mnie:
Przed: (błędne)
Po: (poprawnie)
źródło
notifyItemRangeInserted(initialSize, mChannelItemList.size()-1);
nienotifyItemRangeInserted(initialSize, list.size());
?initialSize
ilist
rozmiar. Oba warianty są błędne.notifyItemRangeInserted(initialSize, list.size()-1);
ale nie rozumiem. Dlaczego muszę zmniejszyć wstawiony rozmiar o jeden dla itemCount?innym powodem tego problemu jest wywoływanie tych metod z niewłaściwymi indeksami (indeksy, których NIE zdarzyło się, wstawiaj lub usuwaj w nich)
-notifyItemRangeRemoved
-notifyItemRemoved
-notifyItemRangeInserted
-notifyItemInserted
sprawdź parametry indexe tych metod i upewnij się, że są dokładne i poprawne.
źródło
Ten błąd nie został jeszcze naprawiony w 23.1.1, ale powszechnym obejściem jest wychwycenie wyjątku.
źródło
Potwierdza wątki jako jeden problem, a ponieważ natknąłem się na problem, a RxJava staje się coraz bardziej popularny: upewnij się, że używasz za
.observeOn(AndroidSchedulers.mainThread())
każdym razem, gdy dzwonisznotify[whatever changed]
przykładowy kod z adaptera:
źródło
W moim przypadku za każdym razem, gdy wywołuję powiadomieniaItemRemoved (0), zawieszał się. Okazało się, że ustawiłem
setHasStableIds(true)
igetItemId
właśnie zwróciłem pozycję przedmiotu. Skończyłem aktualizować go, aby zwrócićhashCode()
unikatowy identyfikator produktu lub samodzielnie zdefiniowany identyfikator, co rozwiązało problem.źródło
W moim przypadku występował ten problem z powodu pobierania aktualizacji danych z serwera (korzystam z Firebase Firestore) i podczas gdy pierwszy zestaw danych jest przetwarzany przez DiffUtil w tle, pojawia się inny zestaw aktualizacji danych i powoduje problem z współbieżnością uruchamiając kolejny DiffUtil.
Krótko mówiąc, jeśli używasz DiffUtil w wątku tła, który następnie wraca do głównego wątku, aby wysłać wyniki do RecylerView, wtedy masz szansę na uzyskanie tego błędu, gdy wiele aktualizacji danych nastąpi w krótkim czasie.
Rozwiązałem to, postępując zgodnie ze wskazówkami zawartymi w tym cudownym wyjaśnieniu: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2
Wyjaśnienie rozwiązania polega na wypchnięciu aktualizacji, gdy bieżąca jest uruchomiona na Deque. Deque może następnie uruchomić oczekujące aktualizacje po zakończeniu bieżącej, a tym samym obsługiwać wszystkie kolejne aktualizacje, ale także unikać błędów niespójności!
Mam nadzieję, że to pomaga, ponieważ to zmusiło mnie do podrapania się w głowę!
źródło
Problem wystąpił dla mnie tylko wtedy, gdy:
Stworzyłem adapter z pustą listą . Potem wstawiłem przedmioty i zadzwoniłem
notifyItemRangeInserted
.Rozwiązanie:
Rozwiązałem to, tworząc adapter dopiero po tym, jak mam pierwszy fragment danych i od razu go zainicjowałem. Następną porcję można następnie
notifyItemRangeInserted
bez problemu wstawić i wywołać .źródło
notifyItemRangeInserted
, ale nigdy nie miałem tego wyjątku.Mój problem polegał na tym, że chociaż wyczyściłem zarówno listę macierzy zawierającą model danych dla widoku programu do recyklingu, nie powiadomiłem adaptera o tej zmianie, więc zawierały nieaktualne dane z poprzedniego modelu. Co spowodowało zamieszanie dotyczące pozycji uchwytu widoku. Aby to naprawić, należy zawsze powiadomić adapter o zmianie zestawu danych przed ponowną aktualizacją.
źródło
W moim przypadku zmieniłem dane wcześniej w wątku za pomocą mRecyclerView.post (nowy Runnable ...), a następnie ponownie zmieniłem dane w wątku interfejsu użytkownika, co spowodowało niespójność.
źródło
Błąd może być spowodowany niezgodnością wprowadzanych zmian z tym, co powiadamiasz. W moim przypadku:
Co oczywiście musiałem zrobić:
źródło
W moim przypadku problem polegał na tym, że użyłem powiadomieniaDataSetChanged, gdy ilość nowo załadowanych danych była mniejsza niż dane początkowe. To podejście pomogło mi:
źródło
notifyDataSetChanged
zależy od nowych danych? Myślałem, że odświeży całą listę.Natrafiłem na ten sam problem.
Moja aplikacja korzysta ze składników Nawigacji z fragmentem zawierającym mój recyklerView. Moja lista wyświetlała się dobrze przy pierwszym załadowaniu fragmentu ... ale po opuszczeniu i powrocie wystąpił błąd.
Podczas nawigowania cykl życia fragmentu przebiegał tylko przez onDestroyView, a po zwróceniu zaczął się w onCreateView. Jednak mój adapter został zainicjowany w elemencie onCreate tego fragmentu i nie został ponownie zainicjowany podczas powrotu.
Rozwiązaniem była inicjalizacja adaptera w onCreateView.
Mam nadzieję, że to może komuś pomóc.
źródło
Wystąpił ten błąd, ponieważ przez pomyłkę wielokrotnie wywoływałem metodę usuwania określonego wiersza z widoku recyclingu. Miałem taką metodę jak:
Przypadkowo wywoływałem tę metodę trzy razy zamiast raz, więc za drugim razem
loc
było -1, a przy próbie jej usunięcia wystąpił błąd. Dwie poprawki miały zapewnić, że metoda została wywołana tylko raz, a także dodać kontrolę poprawności w następujący sposób:źródło
Mam ten sam problem i czytałem, że stało się to tylko w telefonach Samsung ... Ale rzeczywistość pokazała, że dzieje się tak w wielu markach.
Po testach zdałem sobie sprawę, że dzieje się tak tylko wtedy, gdy przewijasz szybko RecyclerView, a następnie wracasz za pomocą przycisku Wstecz lub przycisku W górę. Więc umieściłem przycisk „w górę” i ponownie włączyłem poniższy fragment kodu:
Dzięki temu rozwiązaniu wystarczy załadować nową Arraylist do adaptera i nowy adapter do recyclinglerView, a następnie zakończyć działanie.
Mam nadzieję, że to komuś pomoże
źródło
Wystąpił ten błąd, ponieważ przez pomyłkę wywoływałem „powiadomItemInserted”.
źródło
W moim przypadku na liście było ponad 5000 pozycji. Mój problem polegał na tym, że podczas przewijania widoku recyklera czasami wywoływana jest metoda „onBindViewHolder”, a metoda „myCustomAddItems” zmienia listę.
Moim rozwiązaniem było dodanie „synchronized (syncObject) {}” do wszystkich metod zmieniających listę danych. W ten sposób tylko jedna metoda może odczytać tę listę.
źródło
W moim przypadku dane adaptera uległy zmianie. I niesłusznie użyłem powiadomieniaItemInserted () dla tych zmian. Gdy korzystam z powiadomieniaItemChanged, błąd zniknął.
źródło
Zetknąłem się z tym samym problemem, gdy usunąłem i zaktualizowałem elementy na liście ... Po wielu dniach badań wydaje mi się, że w końcu znalazłem rozwiązanie.
To, co musisz zrobić, to najpierw zrobić całą
notifyItemChanged
listę, a dopiero potem wszystkonotifyItemRemoved
w kolejności malejącejMam nadzieję, że pomoże to osobom, które mają ten sam problem ...
źródło
Używam kursora, więc nie mogę używać DiffUtils, jak zaproponowano w popularnych odpowiedziach. Aby działało dla mnie, wyłączam animacje, gdy lista nie jest bezczynna. To rozszerzenie rozwiązuje ten problem:
Następnie możesz zaktualizować adapter w ten sposób
źródło
Jeśli problem występuje po Multi Touch, możesz wyłączyć Multi Touch za pomocą
w pliku układu.
źródło
Jeśli Twoje dane bardzo się zmieniają, możesz użyć
lub niektóre pojedyncze elementy w zmianach zestawu danych, możesz użyć
Aby uzyskać szczegółowe informacje na temat użycia metod, możesz odwołać się do dokumentu , w pewien sposób staraj się nie używać bezpośrednio
mAdapter.notifyDataSetChanged()
.źródło
notifyItemRangeChanged
powoduje również tę samą awarię.