programing

RecyclerView 및 java.lang.Index Out Of Bounds 예외:불일치가 탐지되었습니다.잘못된 뷰 홀더 어댑터 위치View삼성 장치의 홀더

javamemo 2023. 8. 13. 09:03
반응형

RecyclerView 및 java.lang.Index Out Of Bounds 예외:불일치가 탐지되었습니다.잘못된 뷰 홀더 어댑터 위치View삼성 장치의 홀더

저는 삼성을 제외한 모든 기기에서 완벽하게 작동하는 리사이클러 뷰를 가지고 있습니다.삼성에 대해, 나는 이해합니다.

자바.java.javaIndex Out Of Bounds 예외:불일치가 탐지되었습니다.잘못된 뷰 홀더 어댑터 위치View홀더

다른 활동의 재활용자 관점에서 파편으로 돌아갈 때.

어댑터 코드:

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어떤 클래스에서는 이 충돌을 건너뛸 수 있습니다.나를위해, 나는▁a를 .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이에 대한 래퍼를 생성해야 합니다.

The 사항공: TheRecyclerView잘못된 내부 상태일 수 있습니다.

이것은 완전히 새로운 콘텐츠로 데이터를 새로 고치는 예입니다.필요에 따라 쉽게 수정할 수 있습니다.이 문제는 다음과 같이 전화를 통해 해결했습니다.

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

문제의 원인:

  1. 항목 애니메이션이 활성화된 경우 Recycler의 내부 문제
  2. 다른 스레드의 Recycler 데이터 수정
  3. 잘못된 방법으로 통지 메서드 호출

솔루션:

-------------------------------------------------------------------

  • 예외 포착(특히 이유 #3에는 권장되지 않음)

다음과 같이 사용자 지정 선형 레이아웃 관리자를 생성하고 이를 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.setLayoutManager(new CustomLinearLayoutManager(activity));

-------------------------------------------------------

  • 항목 애니메이션 사용 안 함(이유 #1로 인한 문제 해결):

다시 다음과 같이 사용자 정의 선형 레이아웃 관리자를 만듭니다.

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

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

그런 다음 재활용기 설정레이아웃 관리자를 다음과 같이 봅니다.

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

---------------------------------------------------------------------

  • 이 솔루션은 3번 이유로 인해 발생한 문제를 해결합니다.통지 방법을 올바른 방법으로 사용하고 있는지 확인해야 합니다.또는 DiffUtil을 사용하여 현명하고 쉽고 원활한 방법으로 변경사항을 처리합니다. Android Recycler View에서 DiffUtil 사용

---------------------------------------------------------------------

  • 이유 #2에서는 재활용업체 목록에 대한 모든 데이터 액세스를 확인하고 다른 스레드에 수정 사항이 없는지 확인해야 합니다.

저도 비슷한 문제가 있었습니다.

아래 오류 코드에 문제가 있습니다.

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를 호출하는 것과 관련이 있습니다. [...]

그나저나, 애니메이션과 성능을 죽이기 때문에 notifyDataSetChanged를 사용하지 않는 것이 좋습니다.이 경우에도 특정 알림 이벤트를 사용하면 문제를 해결할 수 있습니다.

지원 라이브러리의 최신 버전과 관련하여 여전히 문제가 있는 경우 다음과 같은 문의 사항을 검토할 것을 제안합니다.notifyXXX (구적으로사은, 의용당신체사은▁(▁(용▁use)의 notifyDataSetChangedRecyclerView.Adapter계약 또한 .또한 메인 스레드에서 이러한 알림을 발행해야 합니다.

저도 같은 문제가 있었습니다.제가 아이템 삽입에 대한 어댑터 알림을 지연시켰기 때문에 발생했습니다.

그렇지만ViewHolder보기에서 일부 데이터를 다시 그리려고 시도했고 그것은 시작되었습니다.RecyclerView어린이 수 측정 및 재검증 - 해당 시점에서 충돌했습니다(추가 목록과 크기가 이미 업데이트되었지만 어댑터에 아직 알림이 표시되지 않음).

이 문제가 발생하는 또 다른 이유는 잘못된 인덱스(발생하지 않은 인덱스는 해당 메서드에 삽입 또는 제거)를 사용하여 메서드를 호출하는 경우입니다.

-항목 범위가 제거되었음을 알림

- 항목 제거 알림

-항목 범위 삽입 알림

- notify Item삽입됨

이러한 메서드에 대한 indexe 매개 변수를 확인하고 정확하고 정확한지 확인합니다.

저의 경우 서버(Firebase Firestore 사용 중)에서 데이터 업데이트를 받기 때문에 이 문제가 발생했는데 첫 번째 데이터 세트가 백그라운드에서 DiffUtil에서 처리되는 동안 다른 데이터 업데이트 세트가 와서 다른 DiffUtil을 시작하여 동시성 문제가 발생합니다.

즉, 백그라운드 스레드에서 DiffUtil을 사용한 후 메인 스레드로 돌아와 결과를 RecyclerView로 전송하는 경우, 짧은 시간에 여러 데이터 업데이트가 왔을 때 이 오류가 발생할 가능성이 있습니다.

는 이 멋진 설명의 조언에 따라 이 문제를 해결했습니다: https://medium.com/ @jonfhancock/get-diffitil-423378e126d2.

솔루션을 설명하기 위해서는 현재 업데이트가 Deque로 실행되는 동안 업데이트를 푸시하는 것입니다.그러면 디큐는 현재 업데이트가 완료되면 보류 중인 업데이트를 실행할 수 있으므로 이후의 모든 업데이트를 처리하지만 불일치 오류는 방지할 수 있습니다.

이것이 도움이 되기를 바랍니다. 왜냐하면 이것은 제 머리를 긁게 만들었기 때문입니다!

알림에 대해 잘못된 위치를 지정하면 이 문제가 발생합니다.ItemChanged,항목 범위 삽입 등나는:

이전 : (오류)

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 
 }

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

0),remitem때oved의▁notify할 Item Removed (0항목 제거됨(0). 충돌했습니다.내가 설정한 것으로 나타났습니다.setHasStableIds(true) 그고리로.getItemId저는 방금 물건 위치를 반송했습니다.나는 아이템의 아이템을 반환하기 위해 업데이트를 종료했습니다.hashCode()또는 자체 정의된 고유 ID로 문제를 해결했습니다.

이 문제는 다른 스레드에서 수정된 RecyclerViewData로 인해 발생합니다.

할 수 , 더 인기를 에: 이 스드화를확수있다습니인을 합니다. 문제가 발생하고 RxJava가 점점 더 인기를 끌고 있기 때문입니다. 사용 중인지 확인하십시오..observeOn(AndroidSchedulers.mainThread())notify[whatever changed]

어댑터의 코드 예제:

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

    [...]

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

저도 같은 문제에 부딪혔습니다.

내 앱은 내 재활용 보기가 포함된 조각과 함께 탐색 구성 요소를 사용합니다.처음에 조각을 로드했을 때 내 목록은 정상적으로 표시되었지만... 탐색하고 돌아오자 이 오류가 발생했습니다.

탐색 시 조각 라이프사이클은 DestroyView에서만 진행되었으며 반환 시 CreateView에서 시작되었습니다.그러나 내 어댑터가 Create의 fragment에서 초기화되어 반환 시 다시 초기화되지 않았습니다.

이 문제는 CreateView에서 어댑터를 초기화하는 것이었습니다.

이것이 누군가에게 도움이 되기를 바랍니다.

재활용업체 보기를 최신 버전으로 업데이트하여 해결되었습니다.

implementation "androidx.recyclerview:recyclerview:1.2.1"

때때로 그것은 사용하는 것의 실수 때문입니다.notifyItemRemoved그리고.notifyItemInserted다음을 사용하여 제거해야 합니다.

list.remove(position);
notifyItemRemoved(position);

항목을 추가하는 경우:

list.add(position, item);
notifyItemInserted(position);

그리고 마지막에는 다음과 같은 것을 잊지 마세요.

notifyItemRangeChanged(position, list.size());

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

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

솔루션:

나는 첫 번째 데이터 덩어리를 얻은 후에야 어댑터를 만들고 바로 초기화하여 이것을 해결했습니다.되고 그면다청삽입수할있고를크음러,있▁the▁then▁could고▁and수▁inserted,notifyItemRangeInserted문제없이 호출되었습니다.

이 오류는 변경 내용이 알림 내용과 일치하지 않기 때문에 발생할 수 있습니다.내 경우:

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

물론 제가 해야 할 일은:

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

제 문제는 재활용품 뷰에 대한 데이터 모델이 포함된 어레이 목록을 모두 삭제했음에도 불구하고 어댑터에 변경 사항을 알리지 않아 이전 모델의 오래된 데이터가 있다는 것입니다.뷰 홀더 위치에 대한 혼동을 야기했습니다.이 문제를 해결하려면 업데이트하기 전에 항상 어댑터에 데이터 집합이 변경되었음을 알립니다.

제 경우, 저는 이전에 mRecyclerView.post (새 Runnable...)를 사용하여 스레드 내의 데이터를 변경하다가 나중에 UI 스레드의 데이터를 다시 변경하여 불일치가 발생했습니다.

목록에서 항목을 제거하고 업데이트한 경우에도 동일한 문제가 발생했습니다.며칠 동안 조사한 끝에 마침내 해결책을 찾은 것 같습니다.

할 것을 하는 것입니다.notifyItemChanged 작업을 할 수 .notifyItemRemoved

이것이 같은 문제를 겪고 있는 사람들에게 도움이 되기를 바랍니다.

저의 경우 새로 로드된 데이터의 양이 초기 데이터보다 적을 때 notifyDataSetChanged를 사용한 것이 문제였습니다.이러한 접근 방식이 도움이 되었습니다.

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

@Bolling 아이디어 덕분에 목록에서 null 가능성을 피하기 위해 지원하기 위해 구현했습니다.

public void setList(ArrayList<ThisIsAdapterListObject> _newList) {
    //get the current items
    if (ThisIsAdapterList != null) {
        int currentSize = ThisIsAdapterList.size();
        ThisIsAdapterList.clear();
        //tell the recycler view that all the old items are gone
        notifyItemRangeRemoved(0, currentSize);
    }

    if (_newList != null) {
        if (ThisIsAdapterList == null) {
            ThisIsAdapterList = new ArrayList<ThisIsAdapterListObject>();
        }
        ThisIsAdapterList.addAll(_newList);
        //tell the recycler view how many new items we added
        notifyItemRangeInserted(0, _newList.size());
    }
}

저의 경우 목록에 5,000개 이상의 항목이 있습니다.문제는 재활용 장치 보기를 스크롤할 때 때때로 "BindView에서""내 사용자 지정 추가" 중에 "홀더"가 호출됩니다.Items" 메서드가 목록을 변경하는 중입니다.

제 해결책은 데이터 목록을 변경하는 모든 메서드에 "synchronized(syncObject){}"를 추가하는 것이었습니다.이렇게 하면 한 번에 한 가지 방법만 이 목록을 읽을 수 있습니다.

저는 커서를 사용하고 있기 때문에 인기 있는 답변에서 제안한 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)
}

나에게 일어난 일은ObservableList.removeIf.

removeIf(predicate)잘못 구현되었습니다. 제거 알림을 보내지 않습니다.따라서 RV가 모르는 사이에 요소가 제거되었기 때문에 지수는 분명히 틀렸습니다.

은 올른방법입니다.removeAll(predicate).

제가 실수로 재활용품 보기에서 특정 행을 제거하는 메서드를 여러 번 호출했기 때문에 이 오류가 발생했습니다.다음과 같은 방법이 있었습니다.

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

저도 같은 문제가 생겨서 삼성 폰에서만 이런 일이 발생했다고 읽었습니다...하지만 현실은 이것이 많은 브랜드에서 일어난다는 것을 보여주었습니다.

테스트 결과, 이 문제는 Recycler View를 빠르게 스크롤한 다음 뒤로 버튼이나 Up 버튼을 눌러야 발생한다는 것을 알게 되었습니다.그래서 저는 위쪽 버튼 안에 넣고 뒤로 다음 스니펫을 눌렀습니다.

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

이 솔루션을 사용하면 새 어레이 목록을 어댑터에 로드하고 새 어댑터를 recyclerView에 로드하면 작업이 완료됩니다.

누군가에게 도움이 되길 바랍니다.

"알림"을 호출하는 중이었기 때문에 이 오류가 발생했습니다.항목 삽입"을 두 번 실수했습니다.

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

반응형