RecyclerView และ java.lang.IndexOutOfBoundsException: ตรวจพบความไม่สอดคล้องกัน ตำแหน่งอะแดปเตอร์มุมมองที่ยึดไม่ถูกต้อง ViewHolder ในอุปกรณ์ Samsung


253

ฉันมีมุมมองรีไซเคิลที่ใช้งานได้ดีกับอุปกรณ์ทั้งหมดยกเว้น Samsung ที่ Samsung ฉันเข้าใจแล้ว

java.lang.IndexOutOfBoundsException: ตรวจพบความไม่สอดคล้องกัน ตำแหน่งของการ์ดเชื่อมต่อ view viewHolder ไม่ถูกต้อง

เมื่อฉันกลับไปที่ส่วนที่มีมุมมองรีไซเคิลจากกิจกรรมอื่น

รหัสอะแดปเตอร์:

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)

ฉันจะแก้ไขสิ่งนี้ได้อย่างไร


เมื่อคุณกลับมาเป็นข้อมูลของคุณเช่นเดียวกับเมื่อคุณออกจากหน้า?
khusrav

ฉัน Gatting ปัญหาเดียวกันว่ายูแก้ ....
Ashvin solanki

@ Владимирคุณพบคำตอบที่ชัดเจนหรือไม่?
Alireza Noorali

ในกรณีของฉันมันเป็นเพราะผมเริ่มงาน async และเมื่อครั้งหนึ่งของพวกเขาเสร็จสิ้นก่อนอื่นและผู้ใช้เลื่อนลงและในขณะเดียวกันอีกผู้ใช้ดำเนินการและการปรับปรุงอะแดปเตอร์จะได้รับการยกเว้นเช่นนี้เพราะงานที่สองกลับมาจำนวนน้อยของข้อมูล
Vasif

คำตอบ:


196

ปัญหานี้เกิดจากRecyclerViewข้อมูลที่แก้ไขในเธรดที่แตกต่างกัน วิธีที่ดีที่สุดคือตรวจสอบการเข้าถึงข้อมูลทั้งหมด LinearLayoutManagerและวิธีแก้ปัญหาคือการตัด

คำตอบก่อนหน้า

มีข้อผิดพลาดจริงใน RecyclerView และการสนับสนุน 23.1.1 ยังคงไม่ได้รับการแก้ไข

สำหรับวิธีแก้ปัญหาโปรดสังเกตว่าสแต็กหลัง backtrace ถ้าเราสามารถจับมันได้Exceptionในคลาสใดคลาสหนึ่งมันอาจจะข้ามความผิดพลาดนี้ได้ สำหรับฉันฉันสร้าง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("TAG", "meet a IOOBE in RecyclerView");
        }
    }
}

จากนั้นตั้งเป็นRecyclerView:

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

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

ที่จริงจับข้อยกเว้นนี้และดูเหมือนว่าไม่มีผลข้างเคียงใด ๆ

นอกจากนี้หากคุณใช้GridLayoutManagerหรือStaggeredGridLayoutManagerคุณต้องสร้างเสื้อคลุมสำหรับมัน

แจ้งให้ทราบRecyclerViewล่วงหน้า: อาจอยู่ในสถานะภายในที่ไม่ถูกต้อง


1
คุณใส่สิ่งนี้ไว้ตรงไหน? บนอะแดปเตอร์หรือกิจกรรม?
Steve Kamau

ขยายLinearLayoutManagerและแทนที่สิ่งนี้ ฉันจะเพิ่มคำตอบของฉัน
sakiM

14
code.google.com/p/android/issues/detail?id=158046คำตอบ # 12 บอกว่าอย่าทำอย่างนั้น
Robert

อืมคุณพูดถูก ดูเหมือนยากที่จะคลี่คลายความเป็นไปได้ทั้งหมดที่ไม่ใช่การปรับเปลี่ยนเธรด UI ในแอปของฉันฉันจะเก็บเรื่องนี้ไว้เป็นวิธีการแก้ปัญหาเท่านั้น
sakiM

1
สำหรับกรณีของฉันฉันทำในหัวข้อเดียวกัน . mDataHolder.get () removeAll (mHiddenGenre); mAdapter.notifyItemRangeRemoved (m ขยายButtonPosition, mHiddenGenre.size ());
JehandadK

73

นี่คือตัวอย่างสำหรับการรีเฟรชข้อมูลด้วยเนื้อหาใหม่ที่สมบูรณ์ คุณสามารถปรับเปลี่ยนได้อย่างง่ายดายเพื่อให้เหมาะกับความต้องการของคุณ ฉันแก้ไขสิ่งนี้ในกรณีของฉันโดยโทร:

notifyItemRangeRemoved(0, previousContentSize);

ก่อน:

notifyItemRangeInserted(0, newContentSize);

นี่คือทางออกที่ถูกต้องและยังกล่าวถึงในโพสต์นี้โดยสมาชิกโครงการ AOSP


2
วิธีนี้ใช้ได้ผลสำหรับฉันฉันลองคำตอบมากมายที่นี่ แต่พวกเขาไม่ได้ผล (ฉันไม่ได้ทดสอบวิธีแก้ปัญหาแรกที่ลูกสาว)
AndroLife

ปัญหาคือการใช้วิธีการเหล่านี้สร้างความไม่สอดคล้องนั้นแม้ว่าจะทำบนเธรดเดียวกันก็ตาม
JehandadK

ฉันไม่ได้ใช้notifyItemRangeInsertedและมีปัญหานี้กับอุปกรณ์ Samsung บางตัว
25

และค่อนข้างปิดหัวข้อที่นี่ ผู้เขียนไม่ได้ใช้notifyItemRangeInserted
25

1
ขอบคุณ! นั่นช่วยฉัน
DmitryKanunnikoff

35

ฉันประสบกับปัญหานี้หนึ่งครั้งและฉันแก้ไขได้ด้วยการห่อLayoutManagerและปิดใช้งานภาพเคลื่อนไหวที่คาดเดาได้

นี่คือตัวอย่าง:

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

ดูเหมือนว่าจะใช้งานได้สำหรับฉัน แต่คุณสามารถบอกได้ไหมว่าทำไมจึงใช้งานได้
Dennis Anderson

แก้ไขให้ฉันด้วย คุณคาดการณ์ได้อย่างไรว่านี่อาจเป็นสาเหตุของความผิดพลาดนี้
ราหุล Rastogi

1
คลาสพื้นฐานของวิธีการ LinearLayoutManager สนับสนุนPredictiveAnimations () ส่งกลับค่าเท็จตามค่าเริ่มต้น เราได้อะไรจากการเอาชนะวิธีที่นี่? public boolean supportsPredictiveItemAnimations() { return false; }
M. Hig

1
@ M.Hig เอกสารสำหรับLinearLayoutManagerบอกว่าค่าเริ่มต้นเป็นเท็จ แต่คำสั่งนั้นเป็นเท็จ :-( รหัสที่แปลแล้วสำหรับLinearLayoutManagerสิ่งนี้มี: บูลีสาธารณะ publicPredictiveItemAnimations บูลสาธารณะ () {ส่งกลับนี้ ;}
ไคลด์

ฉันใช้ diff utils เพื่ออัปเดตอะแดปเตอร์มุมมอง recycler ของฉันและคำตอบนี้แก้ไขข้อขัดข้อง ขอบคุณมากผู้เขียนที่รัก!
Eugene P.

29

คำตอบใหม่: ใช้ DiffUtil สำหรับการอัพเดท RecyclerView ทั้งหมด สิ่งนี้จะช่วยทั้งประสิทธิภาพและข้อผิดพลาดด้านบน ดูที่นี่

คำตอบก่อนหน้านี้ใช้ได้กับฉัน กุญแจสำคัญคือการไม่ใช้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());
}

1
นี่เป็นคำตอบที่ละเอียดที่สุดพร้อมคำอธิบายที่ดี ขอบคุณ!
Sakiboy

ดังนั้นอะไรคือจุดประสงค์ของการใช้การแจ้งเตือนการจัดวางแทนการแจ้งเตือนชุดข้อมูล (), @Bolling
Ankur_009

@FilipLuch คุณอธิบายได้ไหม
Sreekanth Karumanaghat

3
@ กรีก Karumanaghat แน่ใจว่าไม่เป็นเหตุผลว่าทำไมฉันไม่ได้อธิบายเหตุผล โดยทั่วไปเขาล้างแล้วสร้างรายการทั้งหมดในรายการ เช่นเดียวกับในผลการค้นหาบ่อยครั้งที่คุณได้รับไอเท็มเดียวกันหรือเมื่อรีเฟรชเสร็จแล้วคุณจะได้ไอเท็มเดียวกันจากนั้นคุณก็จะสร้างทุกสิ่งขึ้นมาใหม่ซึ่งเป็นการเสียประสิทธิภาพ ใช้ DiffUtils แทนและอัพเดตเฉพาะการเปลี่ยนแปลงมากกว่าไอเท็มทั้งหมด มันเหมือนกับการไปจาก A ถึง Z ทุกครั้ง แต่คุณเปลี่ยนค่า F ในนั้น
Filip Luchianenco

2
DiffUtil เป็นสมบัติที่ซ่อนอยู่ ขอบคุณสำหรับการแบ่งปัน!
Sileria

22

สาเหตุที่ทำให้เกิดปัญหานี้:

  1. ปัญหาภายในใน Recycler เมื่อเปิดใช้งานภาพเคลื่อนไหวของรายการ
  2. การดัดแปลงข้อมูล Recycler ในเธรดอื่น
  3. การโทรแจ้งวิธีการในทางที่ผิด

สารละลาย:

----------------- การแก้ไข 1 1 ---------------

  • การจับข้อยกเว้น (ไม่แนะนำโดยเฉพาะอย่างยิ่งสำหรับเหตุผล # 3)

สร้าง LinearLayoutManager ที่กำหนดเองดังต่อไปนี้และตั้งเป็น 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");
                }

            }
        }

จากนั้นตั้งค่า RecyclerVIew Layout Manager ดังต่อไปนี้:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- โซลูชัน 2 ---------------

  • ปิดใช้งานภาพเคลื่อนไหวรายการ (แก้ไขปัญหาหากเกิดจากเหตุผลที่ # 1):

อีกครั้งสร้าง Linear Layout Manager ที่กำหนดเองดังต่อไปนี้:

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

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

จากนั้นตั้งค่า RecyclerVIew Layout Manager ดังต่อไปนี้:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- โซลูชัน 3 ---------------

  • การแก้ไขปัญหานี้แก้ไขปัญหาหากเกิดจากสาเหตุ # 3 คุณต้องตรวจสอบให้แน่ใจว่าคุณใช้วิธีการแจ้งเตือนในวิธีที่ถูกต้อง หรือใช้ DiffUtil เพื่อจัดการกับการเปลี่ยนแปลงในวิธีที่ชาญฉลาดง่ายและราบรื่น ใช้ DiffUtil ใน Android RecyclerView

----------------- การแก้ไข 4 ---------------

  • สำหรับเหตุผลที่ # 2 คุณต้องตรวจสอบการเข้าถึงข้อมูลทั้งหมดในรายการรีไซเคิลและตรวจสอบให้แน่ใจว่าไม่มีการแก้ไขในเธรดอื่น

สิ่งนี้ใช้งานได้ในสถานการณ์ของฉันฉันไม่สามารถใช้ DiffUtil ได้เพราะฉันมีส่วนประกอบที่กำหนดเองสำหรับผู้รีไซเคิลและอะแดปเตอร์และข้อผิดพลาดเกิดขึ้นอย่างแน่นอนในสถานการณ์ที่เฉพาะเจาะจงซึ่งเป็นที่รู้จักฉันแค่ต้องแก้ไขมัน พันไว้ในลองและจับ
RJFares

17

ฉันมีปัญหาที่คล้ายกัน

ปัญหาในรหัสข้อผิดพลาดด้านล่าง:

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

มันใช้งานได้ดีสำหรับฉัน! ไม่แน่ใจว่าทำไมเราnewList.size() - 1ถึงใช้ไม่ได้
waseefakhtar

15

ตามปัญหานี้ปัญหาได้รับการแก้ไขแล้วและมีแนวโน้มว่าจะมีการเปิดตัวในช่วงใกล้ต้นปี 2558 ข้อความอ้างอิงจากหัวข้อเดียวกัน :

มันมีความเกี่ยวข้องโดยเฉพาะกับการเรียก informDataSetChanged [ ... ]

Btw ฉันขอแนะนำอย่างยิ่งว่าไม่ควรใช้ informDataSetChanged เพราะจะฆ่าภาพเคลื่อนไหวและประสิทธิภาพ นอกจากนี้สำหรับกรณีนี้การใช้เหตุการณ์แจ้งเตือนเฉพาะจะช่วยแก้ไขปัญหาได้

หากคุณยังคงมีปัญหาเกี่ยวกับรุ่นล่าสุดของห้องสมุดสนับสนุนผมจะแนะนำให้ตรวจสอบการโทรของคุณไปnotifyXXX(โดยเฉพาะการใช้งานของnotifyDataSetChanged) ภายในอะแดปเตอร์ของคุณเพื่อให้แน่ใจว่าคุณจะยึดมั่นใน (ค่อนข้างละเอียดอ่อน / ปิดบัง) RecyclerView.Adapterสัญญา นอกจากนี้ต้องแน่ใจว่าได้ออกการแจ้งเตือนเหล่านั้นในหัวข้อหลัก


16
ไม่จริง ๆ ฉันเห็นด้วยกับส่วนของคุณเกี่ยวกับประสิทธิภาพการทำงาน แต่ NOTEDataSetChanged () ไม่ฆ่าภาพเคลื่อนไหวเพื่อเคลื่อนไหวโดยใช้ informDataSetChanged (), a) โทร setHasStableIds (จริง) บน RecyclerView.Adapter วัตถุของคุณและ b) แทนที่ getItemId ภายในอะแดปเตอร์ของคุณ ค่ายาวที่ไม่ซ้ำกันสำหรับแต่ละแถวและตรวจสอบภาพเคลื่อนไหวทำงานได้ดี
PirateApp

@PirateApp คุณควรพิจารณาที่จะแสดงความคิดเห็นของคุณเป็นคำตอบ ฉันได้ลองแล้วมันใช้งานได้ดี
mr5

ไม่จริง! ยังคงได้รับรายงานจาก Google Console เกี่ยวกับปัญหานี้ และอุปกรณ์แน่นอนคือ Samsung -Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
25

10

ผมมีปัญหาเหมือนกัน. มันเกิดเพราะฉันล่าช้าแจ้งเตือนสำหรับอะแดปเตอร์เกี่ยวกับการแทรกรายการ

แต่ViewHolderพยายามที่จะวาดข้อมูลบางอย่างในมุมมองของมันและมันก็เริ่มนับการRecyclerViewวัดและนับเด็ก ๆ - ในขณะนั้นมันล้มเหลว (รายการรายการและขนาดของมันได้รับการปรับปรุงแล้ว แต่อะแดปเตอร์ยังไม่ได้รับการแจ้งเตือน)


8

สิ่งนี้จะเกิดขึ้นเมื่อคุณระบุตำแหน่งที่ไม่ถูกต้องสำหรับ alertItemChanged, alertItemRangeInserted ฯลฯ สำหรับฉัน:

ก่อนหน้านี้: (ผิดพลาด)

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 
 }

1
ทำไมnotifyItemRangeInserted(initialSize, mChannelItemList.size()-1);ไม่notifyItemRangeInserted(initialSize, list.size());?
CoolMind

Undestood คุณผสมกันinitialSizeและlistมีขนาด ดังนั้นตัวแปรทั้งสองของคุณผิด
CoolMind

สำหรับฉันมันใช้งานได้notifyItemRangeInserted(initialSize, list.size()-1);แต่ฉันไม่เข้าใจ ทำไมฉันต้องลดขนาดที่แทรกลงหนึ่งรายการสำหรับ itemCount
plexus

7

อีกเหตุผลหนึ่งที่ทำให้เกิดปัญหานี้คือเมื่อคุณเรียกวิธีการเหล่านี้ด้วยดัชนีที่ไม่ถูกต้อง (ดัชนีที่ไม่มีการแทรกหรือลบออก)

-notifyItemRangeRemoved

-notifyItemRemoved

-notifyItemRangeInserted

-notifyItemInserted

ตรวจสอบพารามิเตอร์ indexe กับวิธีการเหล่านี้และตรวจสอบให้แน่ใจว่าถูกต้องและถูกต้อง


2
นี่เป็นปัญหาของฉัน มีข้อยกเว้นเกิดขึ้นเมื่อไม่มีการเพิ่มอะไรเข้าไปในรายการ
Rasel

6

ข้อผิดพลาดนี้ยังคงไม่ได้รับการแก้ไขใน 23.1.1 แต่วิธีแก้ไขปัญหาทั่วไปจะต้องได้รับการยกเว้น


18
จับมันที่ไหนกันแน่? รหัสเดียวในการติดตามสแต็กคือรหัส Android ดั้งเดิม
howettl

1
จับมันเหมือนคำตอบ @saki_M
Renan Bandeira

สิ่งนี้ใช้งานได้จริงสำหรับคุณหรือไม่แม้ว่า Renan? คุณได้ทดสอบการแก้ไขไประยะหนึ่งแล้วหรือยัง? ข้อผิดพลาดเกิดขึ้นเป็นครั้งคราวเท่านั้นดังนั้นฉันจะเห็นว่าการทำงานในช่วงเวลา
Simon

ใช้งานได้จริง แต่มุมมองของเด็กในเหมืองยังคงไม่คงเส้นคงวา
david

@david ไม่คงเส้นคงวายังคงเป็นสิ่งที่เป็นผลมาจากสิ่งนี้?
Sreekanth Karumanaghat

4

ปัญหานี้เกิดจากข้อมูล RecyclerView แก้ไขในหัวข้อที่แตกต่างกัน

สามารถยืนยันเธรดเป็นปัญหาเดียวและเนื่องจากฉันพบปัญหาและ RxJava กำลังเป็นที่นิยมมากขึ้น: ตรวจสอบให้แน่ใจว่าคุณกำลังใช้อยู่.observeOn(AndroidSchedulers.mainThread())ทุกครั้งที่คุณโทรnotify[whatever changed]

ตัวอย่างรหัสจากอะแดปเตอร์:

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

    [...]

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

ฉันอยู่บนเธรดหลักขณะเรียก DiffUtil.calculateDiff (diffUtilForecastItemChangesAnlayser (this.mWeatherForecatsItemWithMainAndWeathers, weatherForecastItems)) บันทึกมีความชัดเจนบนเธรด: Thread [main, 5, main]
Mathias Seguy Android2ee

4

ในกรณีของฉันทุกครั้งที่ฉันโทรแจ้งเตือนItemRemoved (0) มันล้มเหลว ปรากฎว่าฉันตั้งค่าsetHasStableIds(true)และในgetItemIdฉันเพิ่งกลับตำแหน่งรายการ ฉันสิ้นสุดการอัปเดตเพื่อคืนhashCode()รหัสที่ไม่ซ้ำของรายการหรือกำหนดเองซึ่งแก้ไขปัญหาได้


4

ในกรณีของฉันฉันได้รับปัญหานี้เนื่องจากได้รับการอัปเดตข้อมูลจากเซิร์ฟเวอร์ (ฉันใช้ Firebase Firestore) และในขณะที่ DiffUtil กำลังประมวลผลชุดแรกอยู่ในพื้นหลังชุดอัปเดตข้อมูลอีกชุดหนึ่งและทำให้เกิดปัญหาการทำงานพร้อมกัน โดยเริ่มต้น DiffUtil อื่น

กล่าวโดยย่อถ้าคุณใช้ DiffUtil ในเธรดพื้นหลังซึ่งกลับมาที่เธรดหลักเพื่อส่งผลลัพธ์ไปยัง RecylerView คุณจะมีโอกาสได้รับข้อผิดพลาดนี้เมื่อการอัปเดตข้อมูลหลายรายการเกิดขึ้นในเวลาอันสั้น

ฉันแก้ไขได้โดยทำตามคำแนะนำในคำอธิบายที่ยอดเยี่ยมนี้: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

เพียงเพื่ออธิบายวิธีแก้ปัญหาคือการผลักดันการอัปเดตในขณะที่ปัจจุบันกำลังวิ่งไปที่ Deque deque สามารถเรียกใช้การอัปเดตที่รอดำเนินการได้เมื่อการอัปเดตปัจจุบันเสร็จสิ้นดังนั้นจึงจัดการการอัปเดตที่ตามมาทั้งหมด แต่หลีกเลี่ยงข้อผิดพลาดที่ไม่สอดคล้องเช่นกัน!

หวังว่านี่จะช่วยได้เพราะอันนี้ทำให้ฉันเกาหัว!


ขอบคุณสำหรับลิงค์!
CoolMind

3

ปัญหาเกิดขึ้นสำหรับฉันเมื่อ:

ฉันสร้างอะแดปเตอร์กับรายการที่ว่างเปล่า notifyItemRangeInsertedแล้วฉันจะแทรกรายการและเรียกว่า

สารละลาย:

ฉันแก้ไขได้โดยการสร้างอะแดปเตอร์เฉพาะหลังจากที่ฉันมีข้อมูลชิ้นแรกและเริ่มต้นมันทันที ชิ้นต่อไปสามารถแทรกและnotifyItemRangeInsertedเรียกโดยไม่มีปัญหา


ฉันไม่คิดว่ามันเป็นเหตุผล ฉันมีอะแดปเตอร์จำนวนมากที่มีรายการว่างจากนั้นเพิ่มรายการด้วยnotifyItemRangeInsertedแต่ไม่เคยมีข้อยกเว้นนี้
CoolMind

3

ปัญหาของฉันคือแม้ว่าฉันจะล้างรายการอาร์เรย์ทั้งสองที่มีโมเดลข้อมูลสำหรับมุมมองรีไซเคิล แต่ฉันไม่ได้แจ้งอะแดปเตอร์ของการเปลี่ยนแปลงนั้นดังนั้นจึงมีข้อมูลเก่าจากรุ่นก่อนหน้า ซึ่งก่อให้เกิดความสับสนเกี่ยวกับตำแหน่งของมุมมอง ในการแก้ไขปัญหานี้จะแจ้งให้อแด็ปเตอร์ทราบว่าชุดข้อมูลเปลี่ยนแปลงก่อนที่จะอัปเดตอีกครั้ง


หรือเพียงแค่แจ้งว่ามีการลบรายการแทน
Remario

แบบจำลองของฉันใช้การอ้างอิงของคอนเทนเนอร์นั่นคือเหตุผล
Remario

3

ในกรณีของฉันฉันเปลี่ยนข้อมูลก่อนหน้านี้ภายในเธรดด้วย mRecyclerView.post (Runnable ใหม่ ... ) จากนั้นเปลี่ยนข้อมูลในเธรด UI อีกครั้งในภายหลังซึ่งทำให้เกิดความไม่สอดคล้องกัน


1
ฉันมีสถานการณ์เช่นเดียวกับคุณคุณแก้ไขมันได้อย่างไร ขอบคุณ
baderkhane

2

ข้อผิดพลาดอาจเกิดจากการเปลี่ยนแปลงของคุณไม่สอดคล้องกับสิ่งที่คุณแจ้ง ในกรณีของฉัน:

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

ฉันต้องทำอะไรแน่นอน:

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

2

ในกรณีของฉันปัญหาคือฉันใช้ informDataSetChanged เมื่อจำนวนข้อมูลที่โหลดใหม่มีค่าน้อยกว่าข้อมูลเริ่มต้น วิธีนี้ช่วยให้ฉัน:

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

ทำไมต้องnotifyDataSetChangedขึ้นอยู่กับข้อมูลใหม่ ฉันคิดว่ามันจะรีเฟรชรายการทั้งหมด
CoolMind

2

ฉันพบปัญหาเดียวกัน

แอพของฉันใช้ส่วนประกอบการนำทางด้วยส่วนที่มี recyclerView ของฉัน รายการของฉันแสดงผลได้ดีในครั้งแรกที่มีการโหลดชิ้นส่วน ... แต่เมื่อออกไปและกลับมาเกิดข้อผิดพลาดนี้ขึ้น

เมื่อไปที่วงจรชีวิตของแฟรกเมนต์ดำเนินการผ่าน onDestroyView เท่านั้นและเมื่อส่งคืนจะเริ่มต้นที่ onCreateView อย่างไรก็ตามอะแดปเตอร์ของฉันถูกเตรียมใช้งานใน onCreate ของแฟรกเมนต์และไม่ได้กำหนดค่าเริ่มต้นใหม่เมื่อส่งคืน

การแก้ไขคือการเริ่มต้นอะแดปเตอร์ใน onCreateView

หวังว่านี่อาจช่วยใครซักคน


0

ฉันได้รับข้อผิดพลาดนี้เนื่องจากฉันเรียกวิธีผิดพลาดเพื่อลบแถวที่ระบุออกจาก 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);
    }
}

0

ฉันมีปัญหาเดียวกันและฉันได้อ่านว่าสิ่งนี้เกิดขึ้นในโทรศัพท์ซัมซุงเท่านั้น ... แต่ความจริงแสดงให้เห็นว่าสิ่งนี้เกิดขึ้นในหลายยี่ห้อ

หลังจากการทดสอบฉันรู้ว่าสิ่งนี้จะเกิดขึ้นเฉพาะเมื่อคุณเลื่อน RecyclerView อย่างรวดเร็วและจากนั้นคุณย้อนกลับด้วยปุ่มย้อนกลับหรือปุ่มขึ้น ดังนั้นฉันจึงใส่ปุ่ม Up และบนบีบตัวอย่างข้อมูลด้านล่าง:

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

ด้วยโซลูชันนี้คุณเพียงแค่โหลด Arraylist ใหม่ไปยังอะแดปเตอร์และอะแดปเตอร์ใหม่เพื่อ recyclerView จากนั้นทำกิจกรรมให้เสร็จ

หวังว่าจะช่วยใครซักคน


0

ฉันได้รับข้อผิดพลาดนี้เพราะฉันกำลังเรียก "แจ้งเตือน" แจ้งเตือนสองครั้งโดยไม่ได้ตั้งใจ


0

ในกรณีของฉันฉันมีมากกว่า 5,000 รายการในรายการ ปัญหาของฉันคือเมื่อเลื่อนมุมมองผู้รีไซเคิลบางครั้งวิธี "onBindViewHolder" ถูกเรียกในขณะที่วิธีการ "myCustomAddItems" กำลังแก้ไขรายการ

โซลูชันของฉันคือการเพิ่ม "ทำข้อมูลให้ตรงกัน (syncObject) {}" ให้กับวิธีการทั้งหมดที่เปลี่ยนแปลงรายการข้อมูล วิธีนี้ ณ จุดใดก็ได้ในเวลาหนึ่งวิธีเท่านั้นที่สามารถอ่านรายการนี้


0

ในกรณีของฉันข้อมูลอะแดปเตอร์เปลี่ยนไป และฉันใช้การแจ้งเตือนของItItemInserted () อย่างไม่ถูกต้อง เมื่อฉันใช้ alertItemChanged ข้อผิดพลาดได้หายไป


0

ฉันพบปัญหาเดียวกันเมื่อฉันลบและอัปเดตรายการในรายการ ... หลังจากผ่านการตรวจสอบหลายวันฉันคิดว่าในที่สุดฉันก็พบวิธีแก้ปัญหา

สิ่งที่คุณต้องทำคือทำnotifyItemChangedรายการทั้งหมดก่อนแล้วจึงทำตามnotifyItemRemoved ลำดับจากมากไปน้อย

ฉันหวังว่าสิ่งนี้จะช่วยให้ผู้คนที่กำลังประสบปัญหาเดียวกัน ...


0

ฉันใช้เคอร์เซอร์ดังนั้นฉันจึงไม่สามารถใช้ DiffUtils ตามที่เสนอในคำตอบยอดนิยม เพื่อให้มันใช้งานได้สำหรับฉันฉันจะปิดการใช้งานภาพเคลื่อนไหวเมื่อรายการไม่ได้ใช้งาน นี่คือส่วนขยายที่แก้ไขปัญหานี้:

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

จากนั้นคุณสามารถอัปเดตอะแดปเตอร์ของคุณได้

list.executeSafely {
  adapter.updateICursor(newCursor)
}

0

หากปัญหาเกิดขึ้นหลังจากมัลติทัชคุณสามารถปิดการใช้งานมัลติทัชได้

android:splitMotionEvents="false" 

ในรูปแบบไฟล์


-1

หากข้อมูลของคุณเปลี่ยนแปลงมากคุณสามารถใช้

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

หรือบางรายการในการเปลี่ยนแปลงชุดข้อมูลของคุณคุณสามารถใช้

 mAdapter.notifyItemChanged(pos);

สำหรับรายละเอียดการใช้งานวิธีการที่คุณสามารถอ้างเอกสารmAdapter.notifyDataSetChanged()ในทางที่พยายามที่จะไม่ใช้โดยตรง


2
การใช้notifyItemRangeChangedยังสร้างความผิดพลาดเดียวกัน
lionelmessi

เหมาะกับบางสถานการณ์ บางทีคุณอาจอัปเดตชุดข้อมูลของคุณทั้งในเธรดพื้นหลังและเธรด UI ซึ่งจะทำให้เกิดความไม่สอดคล้องกัน หากคุณอัปเดตชุดข้อมูลในเธรด UI ก็จะทำงานได้
Arron Cao
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.