Programing

RecyclerView 및 java.lang.IndexOutOfBoundsException : 불일치가 발견되었습니다.

lottogame 2020. 5. 5. 19:34
반응형

RecyclerView 및 java.lang.IndexOutOfBoundsException : 불일치가 발견되었습니다. Samsung 장치에서 잘못된 뷰 홀더 어댑터 위치


삼성을 제외한 모든 기기에서 완벽하게 작동하는 재활용 뷰가 있습니다. 삼성에서는

java.lang.IndexOutOfBoundsException : 불일치가 발견되었습니다. 잘못된 뷰 홀더 어댑터 위치

다른 활동에서 재활용보기로 조각으로 돌아갈 때.

어댑터 코드 :

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다른 스레드에서 수정 데이터 로 인해 발생 합니다. 가장 좋은 방법은 모든 데이터 액세스를 확인하는 것입니다. 해결 방법이 줄 바꿈 LinearLayoutManager됩니다.

이전 답변

실제로 RecyclerView에 버그가 있었고 지원 23.1.1은 여전히 ​​수정되지 않았습니다.

이 문제를 해결하려면 역 추적 스택을 확인하십시오. Exception일부 클래스 중 하나에서이 오류를 발견하면이 충돌을 건너 뛸 수 있습니다. 나를 위해, 나는를 생성 LinearLayoutManagerWrapper하고, 오버라이드 (override) 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내부 상태가 잘못되었을 수 있습니다.


완전히 새로운 내용으로 데이터를 새로 고치는 예입니다. 필요에 따라 쉽게 수정할 수 있습니다. 내 경우에는 다음을 호출하여이 문제를 해결했습니다.

notifyItemRangeRemoved(0, previousContentSize);

전에:

notifyItemRangeInserted(0, newContentSize);

이것은 올바른 해결책이며 AOSP 프로젝트 멤버 가이 게시물언급했습니다 .


나는이 문제에 대해 한 번 직면했으며 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);

새로운 답변 : 모든 RecyclerView 업데이트에 DiffUtil을 사용하십시오. 이것은 성능과 위의 버그 모두에 도움이됩니다. 여기를 봐

이전 답변 : 이것은 나를 위해 일했습니다. 열쇠는 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());
}

나는 비슷한 문제가 있었다.

아래 오류 코드 문제 :

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

이 문제 에 따르면 문제는 해결되어 2015 년 초반에 발표 될 가능성이 높습니다. 동일한 스레드에서 인용 한 내용은 다음과 같습니다.

notifyDataSetChanged 호출과 관련이 있습니다. [...]

Btw, 애니메이션과 성능을 죽이기 때문에 notifyDataSetChanged를 사용하지 않는 것이 좋습니다. 또한이 경우 특정 알림 이벤트를 사용하면 문제가 해결됩니다.

최신 버전의 지원 라이브러리에 여전히 문제가있는 경우 어댑터 내부의 호출 notifyXXX(특히, 사용 notifyDataSetChanged) 을 검토하여 (약간의 섬세한 / 불확실한) RecyclerView.Adapter계약을 준수하는지 확인하는 것이 좋습니다 . 또한 메인 스레드에서 해당 알림을 발행해야합니다.


나는 같은 문제가 있었다. 항목 삽입에 대한 어댑터의 알림이 지연되어 발생했습니다.

그러나 ViewHolder보기에서 일부 데이터를 다시 작성하려고 시도하고 RecyclerView측정 및 재 계산 어린이 수를 시작했습니다. 그 순간 충돌이 발생했습니다 (항목 목록과 크기가 이미 업데이트되었지만 어댑터에 아직 통보되지 않았습니다).


이 문제의 원인 은 다음과 같습니다.

  1. 항목 애니메이션을 사용할 때 Recycler의 내부 문제
  2. 다른 스레드에서 Recycler 데이터 수정
  3. 잘못된 방법으로 알림 메소드 호출

해결책:

------------------ 솔루션 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로 인해 문제가 해결 된 경우) :

다음과 같이 사용자 정의 선형 레이아웃 관리자를 작성하십시오.

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

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

그런 다음 RecyclerVIew Layout Manager를 다음과 같이 설정하십시오.

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

------------------ 솔루션 3 ---------------

  • 이 솔루션은 이유 3으로 인한 문제를 해결합니다. 올바른 방법으로 알림 메소드를 사용하고 있는지 확인해야합니다. 또는 DiffUtil을 사용하여 스마트하고 쉽고 매끄러운 방식으로 변경 사항을 처리하십시오. Android RecyclerView에서 DiffUtil 사용

------------------ 솔루션 4 ---------------

  • 이유 # 2의 경우, 리사이클 러 목록에 대한 모든 데이터 액세스를 확인하고 다른 스레드에서 수정 사항이 없는지 확인해야합니다.

이것은 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 
 }

이 문제가 발생하는 또 다른 이유는 잘못된 색인 (삽입 또는 제거가 발생하지 않은 색인)으로 이러한 메소드를 호출 할 때입니다.

-notifyItemRangeRemoved

-notifyItemRemoved

-notifyItemRangeInserted

-notifyItemInserted

이 메소드에 대한 indexe 매개 변수를 점검하고 정확하고 정확한지 확인하십시오.


이 버그는 23.1.1에서 수정되지 않았지만 일반적인 해결 방법은 예외를 포착하는 것입니다.


이 문제는 다른 스레드에서 수정 된 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]
    }
});

내 경우 notifyItemRemoved (0)을 호출 할 때마다 충돌이 발생했습니다. 내가 설정 setHasStableIds(true)하고 getItemId항목 위치를 방금 반환했습니다. 항목 hashCode()또는 자체 정의 된 고유 ID 를 반환하도록 업데이트 하여 문제를 해결했습니다.


필자의 경우 서버에서 데이터 업데이트 (Firebase Firestore를 사용하고 있음)로 인해이 문제가 발생했으며 백그라운드에서 첫 번째 데이터 세트가 DiffUtil에 의해 처리되는 동안 다른 데이터 업데이트 세트가 발생하여 동시성 문제가 발생합니다. 다른 DiffUtil을 시작하여

즉, 백그라운드 스레드에서 DiffUtil을 사용하고 결과를 RecylerView에 디스패치하기 위해 기본 스레드로 돌아온 경우 여러 데이터 업데이트가 짧은 시간에 발생하면이 오류가 발생할 가능성이 있습니다.

나는이 훌륭한 설명에서 조언을 따라이 문제를 해결했습니다 : https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

해결책을 설명하는 것은 현재 업데이트가 Deque로 실행되는 동안 업데이트를 푸시하는 것입니다. 그런 다음 큐는 현재 업데이트가 완료되면 보류중인 업데이트를 실행할 수 있으므로 모든 후속 업데이트를 처리하지만 불일치 오류는 피할 수 있습니다!

이것이 나를 도와 머리를 긁었기 때문에 이것이 도움이되기를 바랍니다!


다음과 같은 경우에만 문제가 발생했습니다.

빈 목록으로 어댑터를 만들었습니다 . 그런 다음 항목을 삽입하고을 호출했습니다 notifyItemRangeInserted.

해결책:

나는 첫 번째 데이터 덩어리가 있고 어댑터를 즉시 초기화 한 후에 만 ​​어댑터를 작성 하여이 문제를 해결했습니다. 그런 다음 다음 청크를 notifyItemRangeInserted문제없이 삽입하고 호출 할 수 있습니다 .


내 문제는 리사이클 뷰의 데이터 모델이 포함 된 배열 목록을 모두 지우더라도 어댑터에 해당 변경 사항을 알리지 않았으므로 이전 모델의 오래된 데이터가 있다는 것입니다. 뷰 홀더 위치에 대한 혼란을 야기했습니다. 이를 수정하려면 다시 업데이트하기 전에 항상 데이터 세트가 변경되었음을 어댑터에 알리십시오.


필자의 경우 이전에 mRecyclerView.post (new Runnable ...)를 사용하여 스레드 내부의 데이터를 변경 한 다음 나중에 UI 스레드의 데이터를 변경하여 불일치가 발생했습니다.


변경 사항이 알림 내용과 일치하지 않아서 오류가 발생할 수 있습니다. 나의 경우에는:

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

물론해야 할 일 :

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

필자의 경우 문제는 새로로드 된 데이터의 양이 초기 데이터보다 적을 때 notifyDataSetChanged를 사용했다는 것입니다. 이 접근법은 나를 도왔습니다 :

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

나는 같은 문제에 부딪쳤다.

내 앱은 내 recyclerView가 포함 된 조각으로 Navigation 구성 요소를 사용합니다. 프래그먼트가 처음로드 될 때 내 목록이 잘 표시되었지만 탐색하고 돌아올 때이 오류가 발생했습니다.

프래그먼트 라이프 사이클을 탐색 할 때 onDestroyView 만 통과하고 돌아 오면 onCreateView에서 시작되었습니다. 그러나 내 어댑터는 조각의 onCreate에서 초기화되었으며 반환 할 때 다시 초기화되지 않았습니다.

수정은 onCreateView에서 어댑터를 초기화하는 것이 었습니다.

이것이 누군가를 도울 수 있기를 바랍니다.


실수로 내 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);
    }
}

나는 같은 문제를 겪었고 이것이 삼성 전화에서만 일어난다는 것을 읽었습니다 ... 그러나 현실은 이것이 많은 브랜드에서 발생한다는 것을 보여주었습니다.

테스트 후 나는 RecyclerView를 빠르게 스크롤 한 다음 뒤로 버튼 또는 위로 버튼으로 되돌아 갈 때만 발생한다는 것을 깨달았습니다. 그래서 위로 버튼을 넣고 아래 스 니펫을 뒤로 밀었습니다.

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

이 솔루션을 사용하면 새 Arraylist를 어댑터에로드하고 새 어댑터를 recyclerView에로드 한 다음 작업을 마칩니다.

그것이 누군가를 돕기를 바랍니다.


실수로 "notifyItemInserted"를 두 번 호출했기 때문에이 오류가 발생했습니다.


내 경우에는 목록에 5000 개 이상의 항목이 있습니다. 내 문제는 리사이클 뷰를 스크롤 할 때 때때로 "myCustomAddItems"메소드가 목록을 변경하는 동안 "onBindViewHolder"가 호출된다는 것입니다.

내 솔루션은 데이터 목록을 변경하는 모든 메소드에 "동기화 (syncObject) {}"를 추가하는 것이 었습니다. 이 방법으로 언제라도 한 가지 방법 만이 목록을 읽을 수 있습니다.


필자의 경우 어댑터 데이터가 변경되었습니다. 그리고 이러한 변경 사항에 대해 notifyItemInserted ()를 잘못 사용했습니다. notifyItemChanged를 사용하면 오류가 사라졌습니다.


데이터가 많이 바뀌면

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

또는 데이터 세트의 일부 단일 항목 변경 사항은

 mAdapter.notifyItemChanged(pos);

자세한 메소드 사용법은 doc을 참조하여 직접 사용하지 않도록하십시오 mAdapter.notifyDataSetChanged().


나는 같은 문제가 있었다. 이와 같이 새 목록을 다시 작성하면 문제가 완벽하게 해결되었습니다.

// 어댑터 클래스에서

public void updateItems(List<ComponentEntity> newItems) {
    List<ComponentEntity> updatedItems = new ArrayList<>();
    updatedItems = newItems;
    items = updatedItems;
    notifyDataSetChanged();
}

참고 URL : https://stackoverflow.com/questions/31759171/recyclerview-and-java-lang-indexoutofboundsexception-inconsistency-detected-in

반응형