Saya memiliki pandangan pendaur ulang yang bekerja dengan baik pada semua perangkat kecuali Samsung. Di Samsung, saya mengerti
java.lang.IndexOutOfBoundsException: Ketidakkonsistenan terdeteksi. Tampilan adapter pemegang viewViewHolder tidak valid
ketika saya akan kembali ke fragmen dengan tampilan pendaur ulang dari aktivitas lain.
Kode adaptor:
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);
}
}
}
Pengecualian:
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)
Bagaimana saya bisa memperbaikinya?
android
android-recyclerview
Владимир Фишер
sumber
sumber
Jawaban:
Masalah ini disebabkan oleh
RecyclerView
data yang dimodifikasi di utas berbeda. Cara terbaik adalah memeriksa semua akses data. Dan solusinya sedang membungkusLinearLayoutManager
.Jawaban sebelumnya
Sebenarnya ada bug di RecyclerView dan dukungan 23.1.1 masih belum diperbaiki.
Untuk solusinya, perhatikan bahwa backtrace menumpuk, jika kita dapat menangkap ini
Exception
di salah satu kelas tertentu mungkin melewatkan kecelakaan ini. Bagi saya, saya membuatLinearLayoutManagerWrapper
dan menimpaonLayoutChildren
:Kemudian atur ke
RecyclerView
:Sebenarnya menangkap pengecualian ini, dan tampaknya belum ada efek samping.
Juga, jika Anda menggunakan
GridLayoutManager
atauStaggeredGridLayoutManager
Anda harus membuat pembungkus untuk itu.Perhatikan: Kondisi
RecyclerView
internal mungkin salah.sumber
LinearLayoutManager
dan menimpa ini. Saya akan menambahkan jawaban saya.Ini adalah contoh untuk menyegarkan data dengan konten yang sama sekali baru. Anda dapat dengan mudah memodifikasinya agar sesuai dengan kebutuhan Anda. Saya memecahkan ini dalam kasus saya dengan menelepon:
sebelum:
Ini adalah solusi yang benar dan juga disebutkan dalam posting ini oleh anggota proyek AOSP.
sumber
notifyItemRangeInserted
dan memiliki masalah ini dengan beberapa perangkat SamsungnotifyItemRangeInserted
Saya menghadapi masalah ini satu kali, dan saya menyelesaikannya dengan membungkus
LayoutManager
dan menonaktifkan animasi prediksi.Berikut sebuah contoh:
Dan atur ke
RecyclerView
:sumber
public boolean supportsPredictiveItemAnimations() { return false; }
LinearLayoutManager
mengatakan default adalah salah, tetapi pernyataan itu salah :-( Kode yang didekompilasi untukLinearLayoutManager
memiliki ini: public boolean supportPredictiveItemAnimations () {return this.mPendingSavedState == null && this.mLastStackFromEnd == this.mStackFromEnd ;}Jawaban baru: Gunakan DiffUtil untuk semua pembaruan RecyclerView. Ini akan membantu dengan kinerja dan bug di atas. Lihat disini
Jawaban sebelumnya: Ini berhasil untuk saya. Kuncinya adalah untuk tidak menggunakan
notifyDataSetChanged()
dan melakukan hal-hal yang benar dalam urutan yang benar:sumber
Alasan yang menyebabkan masalah ini:
LARUTAN:
----------------- SOLUSI 1 ---------------
Buat LinearLayoutManager kustom sebagai berikut dan atur ke ReyclerView
Kemudian atur RecyclerVIew Layout Manager sebagai berikut:
----------------- SOLUSI 2 ---------------
Sekali lagi, buat Manajer Tata Letak Linear kustom sebagai berikut:
Kemudian atur RecyclerVIew Layout Manager sebagai berikut:
----------------- SOLUSI 3 ---------------
----------------- SOLUSI 4 ---------------
sumber
Saya punya masalah serupa.
Masalah dalam kode kesalahan di bawah ini:
Larutan:
sumber
newList.size() - 1
.Menurut masalah ini , masalahnya telah diselesaikan dan kemungkinan dirilis beberapa waktu di dekat awal 2015. Kutipan dari utas yang sama :
Jika Anda masih mengalami masalah dengan versi terbaru dari pustaka dukungan, saya sarankan meninjau panggilan Anda untuk
notifyXXX
(khususnya, penggunaan AndanotifyDataSetChanged
) di dalam adaptor Anda, untuk memastikan Anda mematuhiRecyclerView.Adapter
kontrak (agak rumit / tidak jelas) . Pastikan juga untuk mengeluarkan notifikasi tersebut di utas utama.sumber
Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
Saya memiliki masalah yang sama. Ini disebabkan karena saya menunda pemberitahuan untuk adaptor tentang penyisipan item.
Tetapi
ViewHolder
mencoba untuk menggambar ulang beberapa data dalam pandangan itu dan mulaiRecyclerView
menghitung dan menghitung menghitung anak - pada saat itu crash (daftar item dan ukurannya sudah diperbarui, tetapi adaptor belum diberitahu).sumber
Ini terjadi ketika Anda menentukan posisi yang salah untuk notifyItemChanged, notifyItemRangeInserted etc.For me:
Sebelum: (Salah)
Setelah: (Benar)
sumber
notifyItemRangeInserted(initialSize, mChannelItemList.size()-1);
dan tidaknotifyItemRangeInserted(initialSize, list.size());
?initialSize
danlist
ukuran. Jadi, kedua varian Anda salah.notifyItemRangeInserted(initialSize, list.size()-1);
tetapi saya tidak mengerti. Mengapa saya harus mengurangi ukuran yang dimasukkan satu demi satu untuk itemCount?alasan lain masalah ini terjadi adalah ketika Anda memanggil metode ini dengan indeks yang salah (indeks yang TIDAK ada masukkan atau hapus di dalamnya)
-notifyItemRangeRemoved
-notifyItemRemoved
-notifyItemRangeInerterted
-notifyItemInserted
periksa parameter indexe untuk metode ini dan pastikan mereka tepat dan benar.
sumber
Bug ini masih belum diperbaiki di 23.1.1, tetapi solusi yang umum adalah menangkap pengecualian.
sumber
Dapat mengkonfirmasi threading sebagai satu masalah dan karena saya mengalami masalah dan RxJava menjadi semakin populer: pastikan bahwa Anda menggunakan
.observeOn(AndroidSchedulers.mainThread())
setiap kali Anda meneleponnotify[whatever changed]
contoh kode dari adaptor:
sumber
Dalam kasus saya setiap kali saya menelepon notifyItemRemoved (0), crash. Ternyata saya atur
setHasStableIds(true)
dangetItemId
saya baru saja mengembalikan posisi item. Saya akhirnya memperbarui untuk mengembalikan itemhashCode()
atau id unik yang ditentukan sendiri, yang menyelesaikan masalah.sumber
Dalam kasus saya, saya mendapatkan masalah ini karena mendapatkan pembaruan data dari server (saya menggunakan Firebase Firestore) dan sementara set data pertama sedang diproses oleh DiffUtil di latar belakang, serangkaian pembaruan data lainnya datang dan menyebabkan masalah konkurensi. dengan memulai DiffUtil lain.
Singkatnya, jika Anda menggunakan DiffUtil pada utas Latar belakang yang kemudian kembali ke Utas Utama untuk mengirimkan hasilnya ke RecylerView, maka Anda menjalankan peluang untuk mendapatkan kesalahan ini ketika beberapa pembaruan data datang dalam waktu singkat.
Saya menyelesaikan ini dengan mengikuti saran dalam penjelasan yang luar biasa ini: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2
Untuk menjelaskan solusinya adalah dengan mendorong pembaruan sementara yang saat ini berjalan ke Deque. Deque kemudian dapat menjalankan pembaruan yang tertunda setelah yang saat ini selesai, maka menangani semua pembaruan berikutnya tetapi menghindari kesalahan inkonsistensi juga!
Semoga ini bisa membantu karena yang ini membuatku menggaruk kepalaku!
sumber
Masalah terjadi bagi saya hanya ketika:
Saya membuat Adaptor dengan daftar kosong . Kemudian saya memasukkan item dan menelepon
notifyItemRangeInserted
.Larutan:
Saya memecahkan masalah ini dengan membuat Adaptor hanya setelah saya memiliki potongan data pertama dan menginisialisasi dengan itu segera. Potongan selanjutnya bisa dimasukkan dan
notifyItemRangeInserted
dipanggil tanpa masalah.sumber
notifyItemRangeInserted
, tetapi tidak pernah memiliki pengecualian ini di sana.Masalah saya adalah bahwa meskipun saya menghapus kedua daftar array yang berisi model data untuk tampilan pendaur ulang, saya tidak memberi tahu adaptor tentang perubahan itu, sehingga memiliki data basi dari model sebelumnya. Yang menyebabkan kebingungan tentang posisi pemegang tampilan. Untuk Memperbaiki ini selalu beri tahu adaptor bahwa set data diubah sebelum memperbarui lagi.
sumber
Dalam kasus saya, saya mengubah data sebelumnya di dalam utas dengan mRecyclerView.post (Runnable baru ...) dan kemudian lagi mengubah data di utas UI, yang menyebabkan inkonsistensi.
sumber
Kesalahan dapat disebabkan oleh perubahan Anda yang tidak konsisten dengan apa yang Anda beri tahu. Dalam kasus saya:
Apa yang tentu saja harus saya lakukan:
sumber
Dalam kasus saya masalahnya adalah saya menggunakan notifyDataSetChanged ketika jumlah data yang baru dimuat kurang dari data awal. Pendekatan ini membantu saya:
sumber
notifyDataSetChanged
tergantung pada data baru? Saya pikir ini akan menyegarkan seluruh daftar.Saya mengalami masalah yang sama.
Aplikasi saya menggunakan komponen Navigasi dengan sebuah fragmen yang mengandung recyclerView saya. Daftar saya ditampilkan dengan baik pertama kali fragmen dimuat ... tetapi setelah menavigasi dan kembali kesalahan ini terjadi.
Ketika menavigasi siklus hidup fragmen hanya melalui onDestroyView dan setelah kembali dimulai pada onCreateView. Namun, adaptor saya diinisialisasi di onCreate fragmen dan tidak menginisialisasi ulang ketika kembali.
Cara mengatasinya adalah menginisialisasi adaptor di onCreateView.
Semoga ini bisa membantu seseorang.
sumber
Saya mendapatkan kesalahan ini karena saya keliru memanggil metode untuk menghapus baris tertentu dari recyclerview saya beberapa kali. Saya punya metode seperti:
Saya tidak sengaja memanggil metode ini tiga kali alih-alih sekali, jadi yang kedua
loc
adalah -1 dan kesalahan diberikan ketika mencoba menghapusnya. Kedua perbaikan itu untuk memastikan metode hanya dipanggil sekali, dan juga untuk menambahkan cek kewarasan seperti ini:sumber
Saya mendapat masalah yang sama dan saya telah membaca bahwa ini hanya terjadi pada ponsel Samsung ... Tetapi kenyataannya menunjukkan bahwa ini terjadi pada banyak merek.
Setelah pengujian saya menyadari bahwa ini hanya terjadi ketika Anda menggulir cepat RecyclerView dan kemudian Anda kembali dengan tombol kembali atau tombol Naik. Jadi saya masukkan ke dalam tombol Atas dan onBacked potongan di bawah ini:
Dengan solusi ini, Anda cukup memuat Arraylist baru ke adaptor dan adaptor baru ke recyclerView dan kemudian Anda menyelesaikan aktivitas.
Semoga ini bisa membantu seseorang
sumber
Saya mendapat kesalahan ini karena saya memanggil "notifyItemInserted" dua kali karena kesalahan.
sumber
Dalam kasus saya, saya memiliki lebih dari 5000 item dalam daftar. Masalah saya adalah ketika menggulir tampilan pendaur ulang, kadang-kadang "onBindViewHolder" dipanggil sementara metode "myCustomAddItems" mengubah daftar.
Solusi saya adalah menambahkan "disinkronkan (syncObject) {}" ke semua metode yang mengubah daftar data. Dengan cara ini kapan saja hanya satu metode yang dapat membaca daftar ini.
sumber
Dalam kasus saya, data adaptor berubah. Dan saya salah menggunakan notifyItemInserted () untuk perubahan ini. Ketika saya menggunakan notifyItemChanged, kesalahan telah hilang.
sumber
Saya mengalami masalah yang sama ketika saya telah menghapus dan memperbarui item dalam daftar ... Setelah beberapa hari menyelidiki saya pikir saya akhirnya menemukan solusi.
Yang perlu Anda lakukan adalah pertama-tama lakukan semua
notifyItemChanged
daftar Anda dan baru kemudian lakukan semuanotifyItemRemoved
dalam urutan menurunSaya harap ini akan membantu orang yang mengalami masalah yang sama ...
sumber
Saya menggunakan kursor jadi saya tidak bisa menggunakan DiffUtils seperti yang diusulkan dalam jawaban populer. Untuk membuatnya berfungsi bagi saya, saya menonaktifkan animasi ketika daftar tidak idle. Ini adalah ekstensi yang memperbaiki masalah ini:
Kemudian Anda dapat memperbarui adaptor Anda seperti itu
sumber
Jika masalah terjadi setelah multi touch Anda dapat menonaktifkan multi touch dengan
dalam file tata letak.
sumber
Jika data Anda banyak berubah, Anda dapat menggunakan
atau beberapa item tunggal dalam perubahan set data Anda, dapat Anda gunakan
Untuk penggunaan metode terperinci, Anda dapat merujuk dokumen , dengan cara, cobalah untuk tidak langsung menggunakan
mAdapter.notifyDataSetChanged()
.sumber
notifyItemRangeChanged
juga menghasilkan crash yang sama.