RecyclerView và java.lang.IndexOutOfBoundException: Phát hiện sự không nhất quán. Vị trí bộ điều hợp chủ sở hữu chế độ xem không hợp lệViewHolder trong các thiết bị Samsung


253

Tôi có chế độ xem tái chế hoạt động hoàn hảo trên tất cả các thiết bị ngoại trừ Samsung. Trên Samsung, tôi nhận được

java.lang.IndexOutOfBoundException: Đã phát hiện sự không nhất quán. Vị trí bộ chuyển đổi chủ sở hữu xem không hợp lệViewHolder

khi tôi quay trở lại đoạn với chế độ xem tái chế từ hoạt động khác.

Mã bộ điều hợp:

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);
        }
    }
}

Ngoại lệ:

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)

Làm thế nào tôi có thể sửa lỗi này?


Khi bạn quay lại, dữ liệu của bạn có giống như khi bạn rời khỏi trang không?
khusrav

tôi đang giải quyết vấn đề tương tự như thế nào bạn giải quyết ....
Ashvin solanki

@ Bạn đã tìm thấy câu trả lời dứt khoát chưa?
Alireza Noorali

Trong trường hợp của tôi, đó là do tôi bắt đầu thực hiện tác vụ không đồng bộ, và khi một trong số chúng hoàn thành trước khi người khác cuộn xuống và trong khi đó, người dùng khác hoàn thành và cập nhật bộ điều hợp có thể nhận được ngoại lệ như vậy vì tác vụ thứ hai trả lại ít dữ liệu hơn
Vasif

Câu trả lời:


195

Vấn đề này là do RecyclerViewdữ liệu sửa đổi trong các chủ đề khác nhau. Cách tốt nhất là kiểm tra tất cả các truy cập dữ liệu. Và một cách giải quyết là gói LinearLayoutManager.

Câu trả lời trước

Thực sự đã có một lỗi trong RecyclerView và hỗ trợ 23.1.1 vẫn chưa được sửa.

Đối với một cách giải quyết, hãy lưu ý rằng các ngăn xếp backtrace, nếu chúng ta có thể bắt gặp điều này Exceptiontrong một trong một số lớp, nó có thể bỏ qua sự cố này. Đối với tôi, tôi tạo LinearLayoutManagerWrappervà ghi đè lên 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");
        }
    }
}

Sau đó đặt thành RecyclerView:

RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);

recyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));

Trên thực tế bắt được ngoại lệ này, và dường như chưa có tác dụng phụ nào.

Ngoài ra, nếu bạn sử dụng GridLayoutManagerhoặc StaggeredGridLayoutManagerbạn phải tạo một trình bao bọc cho nó.

Lưu ý: Có RecyclerViewthể ở trạng thái nội bộ sai.


1
Chính xác thì bạn đặt cái này ở đâu? trên bộ chuyển đổi hoặc hoạt động?
Steve Kamau

mở rộng LinearLayoutManagervà ghi đè lên điều này. Tôi sẽ bổ sung trong câu trả lời của tôi.
sakiM

14
code.google.com/p/android/issues/detail?id=158046 câu trả lời # 12 nói không làm điều đó.
Robert

Ừm, bạn nói đúng. Dường như khó có thể xoa dịu tất cả các sửa đổi chủ đề phi UI trong ứng dụng của tôi, tôi sẽ chỉ giữ điều này như một cách giải quyết.
sakiM

1
Đối với trường hợp của tôi, tôi đang làm trên cùng một chủ đề. mDataHolder.get (). remove ALL (mHiddenGenre); mAd CHƯƠNG.notifyItemRangeRemond (mExpandButtonP vị trí, mHiddenGenre.size ());
JehandadK

73

Đây là một ví dụ để làm mới dữ liệu với nội dung hoàn toàn mới. Bạn có thể dễ dàng sửa đổi nó để phù hợp với nhu cầu của bạn. Tôi đã giải quyết điều này trong trường hợp của tôi bằng cách gọi:

notifyItemRangeRemoved(0, previousContentSize);

trước:

notifyItemRangeInserted(0, newContentSize);

Đây là giải pháp chính xác và cũng được đề cập trong bài đăng này bởi một thành viên dự án AOSP.


2
Giải pháp này hiệu quả với tôi Tôi đã thử rất nhiều câu trả lời ở đây nhưng chúng không hiệu quả (Tôi đã không thử nghiệm giải pháp đầu tiên thaugh)
AndroLife

Vấn đề là sử dụng các phương thức này tạo ra sự không nhất quán đó, ngay cả khi được thực hiện trên cùng một luồng.
JehandadK

Tôi không sử dụng notifyItemRangeInsertedvà gặp sự cố này với một số thiết bị Samsung
user25

Và khá lạc đề ở đây. Tác giả đã không sử dụngnotifyItemRangeInserted
user25

1
Cảm ơn bạn! Điều đó đã giúp tôi.
DmitryKanunnikoff

35

Tôi đã đối mặt với vấn đề này một lần và tôi đã giải quyết vấn đề này bằng cách gói LayoutManagervà vô hiệu hóa hình ảnh động dự đoán.

Dưới đây là một ví dụ:

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;
  }
}

Và đặt nó thành RecyclerView:

RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManagerWrapper(context, LinearLayoutManager.VERTICAL, false);

Điều này dường như làm việc cho tôi, nhưng bạn có thể cho biết tại sao điều này làm việc?
Dennis Anderson

Đã sửa cho tôi quá. Làm thế nào bạn dự đoán rằng đây có thể là nguyên nhân của vụ tai nạn này.
Rahul Rastogi

1
Lớp cơ sở của phương thức linearLayoutManager hỗ trợPredictiveAnimations () trả về false theo mặc định. Chúng ta nhận được gì khi ghi đè phương thức ở đây? public boolean supportsPredictiveItemAnimations() { return false; }
M. Hig

1
@ M.Hig Tài liệu cho LinearLayoutManagerbiết mặc định là sai, nhưng tuyên bố đó là sai :-( Mã dịch ngược cho LinearLayoutManagercó này: boolean hỗ trợPredictiveItemAnimations () {return this.mPendingSattedState == null && this.mLastStackFromEnd = ;}
Clyde

Tôi sử dụng các tiện ích khác nhau để cập nhật bộ điều hợp xem tái chế của mình và câu trả lời này đã khắc phục sự cố. Cảm ơn rất nhiều, tác giả thân mến!
Eugene P.

29

Câu trả lời mới: Sử dụng DiffUtil cho tất cả các bản cập nhật RecyclerView. Điều này sẽ giúp với cả hiệu suất và lỗi ở trên. Xem tại đây

Câu trả lời trước: Điều này làm việc cho tôi. Điều quan trọng là không sử dụng notifyDataSetChanged()và làm đúng việc theo đúng thứ tự:

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());
}

1
Đây là giải pháp kỹ lưỡng nhất với một lời giải thích tốt. Cảm ơn!
Sakiboy

vậy thì mục đích của việc sử dụng notifyitemrangeinserted thay vì notifydatasetchanged (), @Bolling là gì.
Ankur_009

@FilipLuch Bạn có thể giải thích tại sao?
Saletanth Karumanaghat

3
@SaletanthKarumanaghat chắc chắn, dunno tại sao tôi không giải thích lý do. Về cơ bản, anh ta xóa sau đó tạo lại tất cả các mục trong danh sách. Giống như trong kết quả tìm kiếm, rất thường xuyên bạn nhận được các mục tương tự hoặc khi hoàn thành việc làm mới, bạn nhận được các mục tương tự và sau đó bạn sẽ tạo lại mọi thứ, điều này gây lãng phí hiệu suất. Sử dụng DiffUtils thay thế và chỉ cập nhật các thay đổi thay vì tất cả các mục. Nó giống như đi từ A đến Z mỗi lần, nhưng bạn chỉ thay đổi F trong đó.
Filip Luchianenco

2
DiffUtil là một kho báu ẩn. Cám ơn vì đã chia sẻ!
Sileria

22

Lý do gây ra vấn đề này:

  1. Một vấn đề nội bộ trong Recycler khi hoạt ảnh vật phẩm được kích hoạt
  2. Sửa đổi dữ liệu Recycler trong một luồng khác
  3. Gọi phương thức thông báo sai cách

GIẢI PHÁP:

----------------- GIẢI PHÁP 1 ---------------

  • Bắt ngoại lệ (Không được đề xuất đặc biệt là vì lý do # 3)

Tạo một linearLayoutManager tùy chỉnh như sau và đặt nó vào 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");
                }

            }
        }

Sau đó, đặt Trình quản lý bố cục RecyclerVIew như sau:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- GIẢI PHÁP 2 ---------------

  • Vô hiệu hóa hình ảnh động mục (khắc phục sự cố nếu nó gây ra lý do số 1):

Một lần nữa, tạo Trình quản lý bố cục tuyến tính tùy chỉnh như sau:

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

             @Override
             public boolean supportsPredictiveItemAnimations() {
                 return false;
             }
        }

Sau đó, đặt Trình quản lý bố cục RecyclerVIew như sau:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- GIẢI PHÁP 3 ---------------

  • Giải pháp này khắc phục sự cố nếu nguyên nhân số 3 gây ra. Bạn cần đảm bảo rằng bạn đang sử dụng các phương thức thông báo theo cách chính xác. Ngoài ra, sử dụng DiffUtil để xử lý thay đổi một cách thông minh, dễ dàng và trơn tru. Sử dụng DiffUtil trong Android RecyclerView

----------------- GIẢI PHÁP 4 ---------------

  • Vì lý do số 2, bạn cần kiểm tra tất cả quyền truy cập dữ liệu vào danh sách tái chế và đảm bảo rằng không có sửa đổi trên luồng khác.

Điều này hoạt động trong kịch bản của tôi, tôi không thể sử dụng DiffUtil vì tôi có các thành phần tùy chỉnh cho trình tái chế và bộ điều hợp và lỗi xảy ra chính xác trong các tình huống cụ thể đã biết, tôi chỉ cần vá nó mà không cần dùng đến trình gỡ bỏ hoạt hình vật phẩm, vì vậy tôi chỉ gói nó trong một thử và bắt
RJFares

17

Tôi đã có một vấn đề tương tự.

Sự cố trong mã lỗi dưới đây:

int prevSize = messageListHistory.size();
// some insert
adapter.notifyItemRangeInserted(prevSize - 1, messageListHistory.size() -1);

Giải pháp:

int prevSize = messageListHistory.size();
// some insert
adapter.notifyItemRangeInserted(prevSize, messageListHistory.size() -prevSize);

Nó hiệu quả tuyệt vời đối với tôi! Không chắc chắn tại sao chúng ta không thể đơn giản sử dụng newList.size() - 1mặc dù.
waseefakhtar

15

Theo vấn đề này , vấn đề đã được giải quyết và có khả năng được phát hành một thời gian gần đầu năm 2015. Một trích dẫn từ cùng một chủ đề :

Nó đặc biệt liên quan đến việc gọi notifyDataSetChanged. [...]

Btw, tôi khuyên bạn không nên sử dụng notifyDataSetChanged vì nó giết chết hiệu ứng động và hiệu suất. Cũng trong trường hợp này, sử dụng các sự kiện thông báo cụ thể sẽ giải quyết vấn đề.

Nếu bạn vẫn gặp sự cố với phiên bản gần đây của thư viện hỗ trợ, tôi khuyên bạn nên xem lại các cuộc gọi của mình notifyXXX(cụ thể là việc bạn sử dụng notifyDataSetChanged) bên trong bộ chuyển đổi của bạn, để đảm bảo rằng bạn đang tuân thủ hợp đồng (hơi tế nhị / tối nghĩa) RecyclerView.Adapter. Ngoài ra hãy chắc chắn để phát hành các thông báo trên chủ đề chính.


16
không thực sự, tôi đồng ý với phần của bạn về hiệu suất nhưng thông báoDataSetChanged () không giết chết hình ảnh động, để tạo hiệu ứng bằng cách sử dụng notifyDataSetChanged (), a) gọi setHasStableIds (true) trên đối tượng RecyclerView.Ad CHƯƠNG của bạn và ghi đè getItemId giá trị dài duy nhất cho mỗi hàng và kiểm tra xem nó, hoạt ảnh có tác dụng
PirateApp

@PirateApp Bạn nên xem xét nhận xét của mình như một câu trả lời. Tôi đã thử nó và nó hoạt động tốt.
mr5

Không đúng! Vẫn nhận được báo cáo từ Google Console về vấn đề này. Và thiết bị dĩ nhiên là Samsung -Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
user25

10

Tôi đã từng gặp vấn đề tương tự. Nó được gây ra bởi vì tôi đã trì hoãn thông báo cho bộ điều hợp về mục chèn.

Nhưng ViewHolderđã cố vẽ lại một số dữ liệu trong chế độ xem và nó đã bắt đầu RecyclerViewđo và kể lại số trẻ em - tại thời điểm đó, nó bị hỏng (danh sách các mục và kích thước của nó đã được cập nhật, nhưng bộ điều hợp chưa được thông báo).


8

Điều này xảy ra khi bạn chỉ định vị trí không chính xác cho notifyItemChanged, notifyItemRangeInserted, v.v.Đối với tôi:

Trước: (Sai ​​lầm)

public void addData(List<ChannelItem> list) {
  int initialSize = list.size();
  mChannelItemList.addAll(list);
  notifyItemRangeChanged(initialSize - 1, mChannelItemList.size());
 } 

Sau: (Đúng)

 public void addData(List<ChannelItem> list) {
  int initialSize = mChannelItemList.size();
  mChannelItemList.addAll(list);
  notifyItemRangeInserted(initialSize, mChannelItemList.size()-1); //Correct position 
 }

1
Tại sao notifyItemRangeInserted(initialSize, mChannelItemList.size()-1);và không notifyItemRangeInserted(initialSize, list.size());?
CoolMind

Bất hiếu. Bạn trộn lẫn initialSizelistkích thước. Vì vậy, cả hai biến thể của bạn đều sai.
CoolMind

Đối với tôi nó hoạt động với notifyItemRangeInserted(initialSize, list.size()-1);nhưng tôi không nhận được nó. Tại sao tôi phải giảm kích thước chèn xuống một cho itemCount?
plexus

7

Một lý do khác khiến vấn đề này xảy ra là khi bạn gọi các phương thức này với các chỉ mục sai (các chỉ mục KHÔNG xảy ra chèn hoặc xóa trong chúng)

-notifyItemRangeRemond

-notifyItemRemond

-notifyItemRangeInserted

-notifyItemInserted

kiểm tra các tham số indexe cho các phương thức này và đảm bảo chúng chính xác và chính xác.


2
Đây là vấn đề của tôi. Ngoại lệ xảy ra khi thêm không có gì trong danh sách.
Rasel

6

Lỗi này vẫn chưa được sửa trong 23.1.1, nhưng một cách giải quyết chung sẽ là bắt ngoại lệ.


18
Bắt nó ở đâu, chính xác? Mã duy nhất trong theo dõi ngăn xếp là mã Android gốc.
howettl

1
Bắt nó giống như câu trả lời @saki_M.
Renan Bandeira

Điều này thực sự làm việc mặc dù cho bạn mặc dù Renan? Bạn đã thử nghiệm sửa chữa trong một thời gian? Lỗi chỉ thỉnh thoảng xảy ra vì vậy tôi sẽ chỉ xem liệu điều này có hoạt động theo thời gian không.
Simon

Điều này thực sự hoạt động nhưng một số quan điểm trẻ em trong tôi vẫn còn không nhất quán.
david

@david Không nhất quán vẫn còn hậu quả của việc này là gì?
Saletanth Karumanaghat

4

Sự cố này là do Dữ liệu RecyclerView được sửa đổi trong các luồng khác nhau

Có thể xác nhận luồng là một vấn đề và vì tôi gặp phải sự cố và RxJava đang ngày càng trở nên phổ biến: đảm bảo rằng bạn đang sử dụng .observeOn(AndroidSchedulers.mainThread())bất cứ khi nào bạn gọinotify[whatever changed]

ví dụ mã từ bộ chuyển đổi:

myAuxDataStructure.getChangeObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<AuxDataStructure>() {

    [...]

    @Override
    public void onNext(AuxDataStructure o) {
        [notify here]
    }
});

Tôi đang ở trong Chủ đề chính trong khi gọi DiffUtil.calculateDiff (diffUtilForecastItemChangesAnlayser (this.mWeatherForecatsItemWithMainAndWeathers, WeatherForecastItems)). Nhật ký rõ ràng trênThread: Chủ đề [chính, 5, chính]
Mathias Seguy Android2ee

4

Trong trường hợp của tôi mỗi khi tôi gọi notifyItemRemond (0) thì nó bị sập. Hóa ra là tôi đã đặt setHasStableIds(true)và trong getItemIdtôi vừa trả lại vị trí vật phẩm. Tôi đã kết thúc việc cập nhật nó để trả về hashCode()id duy nhất của mục hoặc tự xác định, điều này đã giải quyết được vấn đề.


4

Trong trường hợp của tôi, tôi đã gặp phải sự cố này do nhận được cập nhật dữ liệu từ máy chủ (tôi đang sử dụng Firebase Firestore) và trong khi bộ dữ liệu đầu tiên đang được DiffUtil xử lý ở chế độ nền, một bộ cập nhật dữ liệu khác xuất hiện và gây ra sự cố tương tranh bằng cách bắt đầu một DiffUtil khác.

Nói tóm lại, nếu bạn đang sử dụng DiffUtil trên luồng nền sau đó quay lại Chủ đề chính để gửi kết quả đến RecylerView, thì bạn sẽ có cơ hội gặp lỗi này khi có nhiều cập nhật dữ liệu trong thời gian ngắn.

Tôi đã giải quyết điều này bằng cách làm theo lời khuyên trong lời giải thích tuyệt vời này: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

Chỉ cần giải thích giải pháp là đẩy các bản cập nhật trong khi bản hiện tại đang chạy đến Deque. Sau đó, deque có thể chạy các bản cập nhật đang chờ xử lý sau khi bản cập nhật hiện tại kết thúc, do đó xử lý tất cả các bản cập nhật tiếp theo nhưng cũng tránh được các lỗi không nhất quán!

Hy vọng điều này sẽ giúp vì điều này làm tôi gãi đầu!


Cảm ơn các liên kết!
CoolMind

3

Vấn đề xảy ra với tôi chỉ khi:

Tôi đã tạo ra Adaptor với một danh sách trống . Sau đó, tôi chèn các mục và gọi notifyItemRangeInserted.

Giải pháp:

Tôi đã giải quyết điều này bằng cách tạo Bộ điều hợp chỉ sau khi tôi có đoạn dữ liệu đầu tiên và khởi tạo nó ngay lập tức. Đoạn tiếp theo sau đó có thể được chèn và notifyItemRangeInsertedgọi mà không có vấn đề.


Tôi không nghĩ đó là lý do. Tôi có nhiều bộ điều hợp với danh sách trống, sau đó thêm các mục với notifyItemRangeInserted, nhưng không bao giờ có ngoại lệ này.
CoolMind

3

Vấn đề của tôi là mặc dù tôi xóa cả danh sách mảng chứa mô hình dữ liệu cho chế độ xem tái chế, tôi đã không thông báo cho bộ điều hợp về sự thay đổi đó, vì vậy nó có dữ liệu cũ từ mô hình trước đó. Điều này gây ra sự nhầm lẫn về vị trí chủ sở hữu xem. Để khắc phục điều này luôn thông báo cho bộ điều hợp rằng tập dữ liệu đã được thay đổi trước khi cập nhật lại.


hoặc chỉ đơn giản là thông báo nếu mục bị xóa thay vào đó
Remario

mô hình của tôi sử dụng tham chiếu của container đó là lý do
Remario

3

Trong trường hợp của tôi, tôi đã thay đổi dữ liệu trước đó trong một luồng bằng mRecyclerView.post (Runnable mới ...) và sau đó lại thay đổi dữ liệu trong luồng UI, điều này gây ra sự không nhất quán.


1
Tôi có hoàn cảnh giống như bạn, bạn đã giải quyết nó như thế nào? cảm ơn
baderkhane

2

Lỗi có thể do những thay đổi của bạn không phù hợp với những gì bạn đang thông báo. Trong trường hợp của tôi:

myList.set(position, newItem);
notifyItemInserted(position);

Tất nhiên những gì tôi phải làm:

myList.add(position, newItem);
notifyItemInserted(position);

2

Trong trường hợp của tôi, vấn đề là tôi đã sử dụng notifyDataSetChanged khi lượng dữ liệu mới được tải ít hơn dữ liệu ban đầu. Cách tiếp cận này đã giúp tôi:

adapter.notifyItemRangeChanged(0, newAmountOfData + 1);
adapter.notifyItemRangeRemoved(newAmountOfData + 1, previousAmountOfData);

Tại sao notifyDataSetChangedphụ thuộc vào dữ liệu mới? Tôi nghĩ rằng nó sẽ làm mới toàn bộ danh sách.
CoolMind

2

Tôi gặp vấn đề tương tự.

Ứng dụng của tôi sử dụng các thành phần Điều hướng với một đoạn chứa recyclerView của tôi. Danh sách của tôi hiển thị tốt khi lần đầu tiên đoạn được tải ... nhưng khi điều hướng đi và quay lại thì lỗi này xảy ra.

Khi điều hướng đi, vòng đời của đoạn chỉ đi qua onDestroyView và khi trở về, nó bắt đầu tại onCreateView. Tuy nhiên, bộ điều hợp của tôi đã được khởi tạo trong onCreate của đoạn và không khởi động lại khi quay lại.

Cách khắc phục là khởi tạo bộ điều hợp trong onCreateView.

Hy vọng điều này có thể giúp một ai đó.


0

Tôi đã gặp lỗi này vì tôi đã gọi nhầm một phương thức để xóa một hàng cụ thể khỏi recyclerview của tôi nhiều lần. Tôi đã có một phương pháp như:

void removeFriends() {
    final int loc = data.indexOf(friendsView);
    data.remove(friendsView);
    notifyItemRemoved(loc);
}

Tôi đã vô tình gọi phương thức này ba lần thay vì một lần, vì vậy lần thứ hai loclà -1 và lỗi được đưa ra khi nó cố gắng loại bỏ nó. Hai bản sửa lỗi là để đảm bảo phương thức chỉ được gọi một lần và cũng để thêm kiểm tra độ tỉnh táo như sau:

void removeFriends() {
    final int loc = data.indexOf(friendsView);
    if (loc > -1) {
        data.remove(friendsView);
        notifyItemRemoved(loc);
    }
}

0

Tôi gặp vấn đề tương tự và tôi đã đọc rằng điều này chỉ xảy ra trong điện thoại Samsung ... Nhưng thực tế cho thấy điều này xảy ra ở rất nhiều thương hiệu.

Sau khi kiểm tra tôi nhận ra rằng điều này chỉ xảy ra khi bạn cuộn nhanh RecyclerView và sau đó bạn quay lại bằng nút quay lại hoặc nút Lên. Vì vậy, tôi đặt nút bên trong và bật lại đoạn trích bên dưới:

someList = new ArrayList<>();
mainRecyclerViewAdapter = new MainRecyclerViewAdapter(this, someList, this);
recyclerViewMain.setAdapter(mainRecyclerViewAdapter);
finish();

Với giải pháp này, bạn chỉ cần tải một Arraylist mới vào bộ điều hợp và bộ điều hợp mới để recyclerView và sau đó bạn hoàn thành hoạt động.

Hy vọng nó sẽ giúp được ai đó


0

Tôi đã gặp lỗi này vì tôi đã gọi "notifyItemInserted" hai lần do nhầm lẫn.


0

Trong trường hợp của tôi, tôi đã có hơn 5000 mặt hàng trong danh sách. Vấn đề của tôi là khi cuộn chế độ xem của trình tái chế, đôi khi phương thức "onBindViewHolder" được gọi trong khi phương thức "myCustomAddItems" đang thay đổi danh sách.

Giải pháp của tôi là thêm "sync (syncObject) {}" vào tất cả các phương thức thay đổi danh sách dữ liệu. Cách này tại bất kỳ thời điểm nào chỉ có một phương pháp có thể đọc danh sách này.


0

Trong trường hợp của tôi, dữ liệu bộ chuyển đổi đã thay đổi. Và tôi đã sử dụng sai thông báo notifyItemInserted () cho những thay đổi này. Khi tôi sử dụng notifyItemChanged, lỗi đã biến mất.


0

Tôi gặp vấn đề tương tự khi tôi vừa gỡ bỏ và cập nhật các mục trong danh sách ... Sau nhiều ngày điều tra, tôi nghĩ rằng cuối cùng tôi đã tìm ra giải pháp.

Những gì bạn cần làm trước tiên là làm tất cả các notifyItemChangeddanh sách của bạn và chỉ sau đó làm tất cả notifyItemRemoved theo thứ tự giảm dần

Tôi hy vọng điều này sẽ giúp những người đang gặp phải vấn đề tương tự ...


0

Tôi đang sử dụng một Con trỏ nên tôi không thể sử dụng DiffUtils như được đề xuất trong các câu trả lời phổ biến. Để làm cho nó hoạt động với tôi, tôi vô hiệu hóa hình ảnh động khi danh sách không hoạt động. Đây là phần mở rộng khắc phục sự cố này:

 fun RecyclerView.executeSafely(func : () -> Unit) {
        if (scrollState != RecyclerView.SCROLL_STATE_IDLE) {
            val animator = itemAnimator
            itemAnimator = null
            func()
            itemAnimator = animator
        } else {
            func()
        }
    }

Sau đó, bạn có thể cập nhật bộ chuyển đổi của bạn như thế

list.executeSafely {
  adapter.updateICursor(newCursor)
}

0

Nếu sự cố xảy ra sau khi đa chạm, bạn có thể tắt đa chạm với

android:splitMotionEvents="false" 

trong tập tin bố trí.


-1

Nếu dữ liệu của bạn thay đổi nhiều, bạn có thể sử dụng

 mAdapter.notifyItemRangeChanged(0, yourData.size());

hoặc một số mục duy nhất trong tập dữ liệu của bạn thay đổi, bạn có thể sử dụng

 mAdapter.notifyItemChanged(pos);

Để sử dụng các phương pháp chi tiết, bạn có thể tham khảo tài liệu , theo một cách nào đó, cố gắng không sử dụng trực tiếp mAdapter.notifyDataSetChanged().


2
sử dụng notifyItemRangeChangedcũng tạo ra sự cố tương tự.
Lionelmessi

Điều đó phù hợp với một số tình huống. Có thể bạn đã cập nhật dữ liệu của mình cả trong luồng nền và luồng UI, điều này cũng sẽ gây ra sự không nhất quán. Nếu bạn chỉ cập nhật tập dữ liệu trong luồng UI, nó sẽ hoạt động.
Arron Cao
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.