RecyclerView和java.lang.IndexOutOfBoundsException:检测到不一致。 三星设备中的视图持有人适配器positionViewHolder无效
我有一个回收的观点,除三星以外的所有设备完美的作品。 在三星,我明白了
java.lang.IndexOutOfBoundsException:检测到不一致。 无效的视图持有者适配器positionViewHolder
当我回到与从另一个活动的回收者视图的片段。
适配器代码:
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); } } }
例外:
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)
我怎样才能解决这个问题?
此问题是由RecyclerView
Data在不同线程中修改引起的。 最好的方法是检查所有的数据访问。 一个解决方法是包装LinearLayoutManager
。
以前的答案
RecyclerView中实际上有一个bug,支持23.1.1仍然没有修复。
对于解决方法,请注意回溯堆栈,如果我们可以在某个类中捕获此Exception
,则可能会跳过此崩溃。 对于我来说,我创build了一个LinearLayoutManagerWrapper
并覆盖了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("probe", "meet a IOOBE in RecyclerView"); } } }
然后将其设置为RecyclerView
:
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));
其实赶上这个例外,似乎还没有任何副作用。
另外,如果使用GridLayoutManager
或StaggeredGridLayoutManager
,则必须为其创build一个包装器。
注意: RecyclerView
可能处于错误的内部状态。
这是使用全新内容刷新数据的示例。 您可以轻松修改它以适应您的需求。 我通过打电话给我解决了这个问题:
notifyItemRangeRemoved(0, previousContentSize);
之前:
notifyItemRangeInserted(0, newContentSize);
这是一个正确的解决scheme,AOSP项目成员在这篇文章中也提到了这个问题 。
根据这个问题 ,这个问题已经解决了,并且可能在2015年初发布了一段时间。 同一个post的引用 :
它特别涉及到调用notifyDataSetChanged。 […]
顺便说一句,我强烈build议不要使用notifyDataSetChanged,因为它杀死animation和性能。 同样对于这种情况,使用特定的通知事件将解决这个问题。
如果您在支持库的最新版本中仍然存在问题,我build议您在适配器内检查您的电话,以notifyXXX
(特别是,使用notifyDataSetChanged
),以确保您遵守(有点微妙/模糊)的RecyclerView.Adapter
合同。 另外一定要在主线程上发布这些通知。
我有同样的问题。 这是因为我推迟了关于物品插入的适配器通知。
但ViewHolder
试图重新绘制视图中的一些数据,并启动了RecyclerView
测量和重新计数儿童数 – 在那一刻它崩溃了(项目列表和它的大小已经更新,但适配器还没有通知)。
我有一个类似的问题。
问题在下面的错误代码:
int prevSize = messageListHistory.size(); // some insert adapter.notifyItemRangeInserted(prevSize - 1, messageListHistory.size() -1);
解:
int prevSize = messageListHistory.size(); // some insert adapter.notifyItemRangeInserted(prevSize, messageListHistory.size() -prevSize);
这个bug在23.1.1中仍然没有解决,但是一个常见的解决方法是捕获这个exception。
我遇到过这个问题,我通过包装LayoutManager
和禁用预测animation来解决这个问题。
这里是一个例子:
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; } }
并将其设置为RecyclerView
:
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManagerWrapper(context, LinearLayoutManager.VERTICAL, false);
只有在以下情况下才会出现问题:
我创build了一个空列表的适配器。 然后我插入项目,并调用notifyItemRangeInserted
。
解:
我通过创build适配器来解决这个问题,只有在我有了第一个数据块并立即初始化它之后。 然后可以插入下一个块,并调用notifyItemRangeInserted
,没有问题。
这对我有效。 关键是不要使用notifyDataSetChanged()
并按照正确的顺序做正确的事情:
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()); }
如果你的数据变化很大,你可以使用
mAdapter.notifyItemRangeChanged(0, yourData.size());
或者数据集中的一些单个项目发生更改,您可以使用
mAdapter.notifyItemChanged(pos);
有关详细的方法用法,可以参考doc ,尽量不要直接使用mAdapter.notifyDataSetChanged()
。
此问题是由RecyclerView Data在不同线程中修改引起的
可以确认线程是一个问题,因为我遇到了这个问题,RxJava变得越来越stream行:无论何时调用notify[whatever changed]
.observeOn(AndroidSchedulers.mainThread())
确保使用.observeOn(AndroidSchedulers.mainThread())
适配器的代码示例:
myAuxDataStructure.getChangeObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<AuxDataStructure>() { [...] @Override public void onNext(AuxDataStructure o) { [notify here] } });
在我每次调用notifyItemRemoved(0)时,它都会崩溃。 原来,我设置了setHasStableIds(true)
并在getItemId
我刚刚返回的项目位置。 我结束了更新它返回项目的hashCode()
或自定义唯一的ID,这解决了问题。
错误可能是由于您的更改与您正在通知的内容不一致而导致的。 在我的情况下:
myList.set(position, newItem); notifyItemInserted(position);
我当然必须做的:
myList.add(position, newItem); notifyItemInserted(position);
当您为notifyItemChanged,notifyItemRangeInserted等指定不正确的位置时,会发生这种情况。对于我:
之前:(错误)
public void addData(List<ChannelItem> list) { int initialSize = list.size(); mChannelItemList.addAll(list); notifyItemRangeChanged(initialSize - 1, mChannelItemList.size()); }
之后:(正确)
public void addData(List<ChannelItem> list) { int initialSize = mChannelItemList.size(); mChannelItemList.addAll(list); notifyItemRangeInserted(initialSize, mChannelItemList.size()-1); //Correct position }
我得到这个错误,因为我错误地调用了一个方法从我的recyclerview多次删除特定的行。 我有一个方法,如:
void removeFriends() { final int loc = data.indexOf(friendsView); data.remove(friendsView); notifyItemRemoved(loc); }
我不小心把这个方法调用了三次而不是一次,所以第二次loc
是-1,并且当它试图去除它时给出错误。 这两个修补程序是为了确保该方法只被调用一次,并添加一个这样的理智检查:
void removeFriends() { final int loc = data.indexOf(friendsView); if (loc > -1) { data.remove(friendsView); notifyItemRemoved(loc); } }
我的问题是,即使我清除包含回收视图的数据模型的数组列表,我没有通知适配器的变化,所以它有从以前的模型陈旧的数据。 这引起了对查看者位置的混淆。 要解决这个问题,请在更新之前通知适配器数据集已更改。
在我的情况下,问题是我使用notifyDataSetChanged时,新加载的数据量小于初始数据。 这种方法帮助我:
adapter.notifyItemRangeChanged(0, newAmountOfData + 1); adapter.notifyItemRangeRemoved(newAmountOfData + 1, previousAmountOfData);
在我的情况下,我用mRecyclerView.post(new Runnable …)更改线程之前的数据,然后再次更改UI线程中的数据,从而导致不一致。
- 如何在Eclipse中自动滚动LogCat输出?
- Android:列“_id”不存在
- 开源的Android库? 可重用视图,ViewGroups,适配器等?
- 什么是textview文本的默认颜色?
- Android Android支持库与Android支持库vs Google Repository vs Google Play服务之间有什么区别?
- 在Scroll上隐藏RecyclerView / ListView上的ActionBar
- 使用OAuth2为应用*和*网站进行身份validation
- 在Eclipse中找不到AVD或SDKpipe理器
- NestedScrollView内的Recycler视图使滚动在中间开始