RecyclerView ve java.lang.IndexOutOfBoundsException: Tutarsızlık algılandı. Samsung cihazlarda geçersiz görünüm tutucu adaptör konumu


253

Samsung dışındaki tüm cihazlarda mükemmel çalışan bir geri dönüşümcüsü görünümüm var. Samsung'da alıyorum

java.lang.IndexOutOfBoundsException: Tutarsızlık algılandı. Geçersiz görünüm tutucu bağdaştırıcısının konumu

Başka bir etkinlikten geri dönüşümcü görünümü ile parçaya geri döndüğümde.

Adaptör kodu:

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

İstisna:

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)

Bunu nasıl düzeltebilirim?


geri döndüğünüzde, verileriniz sayfadan ayrıldığınızla aynı mı?
khusrav

u çözmek nasıl aynı sorunu geçiyorum ....
Ashvin solanki 5:18

@ Владимир Kesin cevabı buldunuz mu?
Alireza Noorali

Benim durumumda, async görevine başladığım içindi, Ve biri diğerinden önce tamamlandığında ve kullanıcı aşağı kaydırdığında ve bu arada başka bir tamamlar ve güncellemeler adaptör kullanıcısı böyle bir istisna alabilir, çünkü ikinci görev daha az veri döndürdü
Vasif

Yanıtlar:


196

Bu soruna RecyclerViewfarklı iş parçacığında değiştirilen veriler neden olur . En iyi yol tüm veri erişimini kontrol etmektir. Ve geçici bir çözüm sardı LinearLayoutManager.

Önceki cevap

Aslında RecyclerView'da bir hata vardı ve 23.1.1 desteği hala düzeltilmedi.

Geçici bir çözüm için, backtrace yığınlarının, Exceptionbazı sınıflardan birinde bunu yakalayabilirsek, bu çökmeyi atlayabileceğini unutmayın. Benim için bir oluşturmak LinearLayoutManagerWrapperve geçersiz kılmak 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");
        }
    }
}

Ardından şu şekilde ayarlayın RecyclerView:

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

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

Aslında bu istisnayı yakalayın ve henüz bir yan etkisi yok gibi görünüyor.

Ayrıca, kullanırsanız GridLayoutManagerveya StaggeredGridLayoutManagerbunun için bir sarıcı oluşturmanız gerekir.

Uyarı: RecyclerViewYanlış bir dahili durumda olabilir.


1
bunu tam olarak nereye koydun? adaptör veya etkinlik?
Steve Kamau

a'yı genişletin LinearLayoutManagerve bunu geçersiz kılın. Cevabımı ekleyeceğim.
sakiM

14
code.google.com/p/android/issues/detail?id=158046 Yanıt # 12, bunu yapmadığını söyledi.
Robert

hmm, haklısın. Uygulamamdaki tüm UI olmayan iş parçacığı değişikliklerini etkisiz hale getirmek zor görünüyor, bunu yalnızca geçici bir yol olarak tutacağım.
sakiM

1
Benim durumum için aynı iş parçacığında yapıyorum. . mDataHolder.get () removeAll (mHiddenGenre); mAdapter.notifyItemRangeRemoved (mExpandButtonPosition, mHiddenGenre.size ());
JehandadK

73

Bu, verileri tamamen yeni içerikle yenilemek için bir örnektir. Gereksinimlerinize uyacak şekilde kolayca değiştirebilirsiniz. Bunu benim durumumda arayarak çözdüm:

notifyItemRangeRemoved(0, previousContentSize);

önce:

notifyItemRangeInserted(0, newContentSize);

Bu doğru çözümdür ve bu yazıda bir AOSP proje üyesi tarafından da belirtilmiştir .


2
Bu çözüm benim için çalışıyor Burada çok cevap denedim ama işe yaramıyor (ilk çözümü denemedim)
AndroLife

Sorun bu yöntemleri kullanarak aynı iş parçacığı üzerinde yapıldığında bile bu tutarsızlık oluşturmaktır.
JehandadK

notifyItemRangeInsertedBazı Samsung cihazlarında bu sorunu kullanmıyorum ve kullanmıyorum
user25

Ve burada oldukça konu dışı. Yazar kullanmadınotifyItemRangeInserted
user25

1
Teşekkür ederim! Bu bana yardımcı oldu.
DmitryKanunnikoff

35

Bu sorunla bir kez karşılaştım LayoutManagerve tahmin animasyonlarını sararak ve devre dışı bırakarak bunu çözdüm.

İşte bir örnek:

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

Ve şu şekilde ayarlayın RecyclerView:

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

bu benim için işe yarıyor gibi görünüyor, ama bunun neden işe yaradığını söyleyebilir misiniz?
Dennis Anderson

Benim için de düzeltildi. Bunun bu çöküşün nedeni olabileceğini nasıl tahmin ettiniz?
Rahul Rastogi

1
LinearLayoutManager yönteminin temel sınıfı supportPredictiveAnimations () yönteminin varsayılan olarak false değerini döndürür. Buradaki yöntemi geçersiz kılarak ne elde ediyoruz? public boolean supportsPredictiveItemAnimations() { return false; }
M. Hig

1
@ M.Hig için dokümantasyon LinearLayoutManagervarsayılanın yanlış olduğunu söylüyor, ancak bu ifade yanlış :-( için ayrıştırılan kodda şu LinearLayoutManagervar: public boolean supportsPredictiveItemAnimations () {return this.mPendingSavedState == null && this.mLastStackFromEnd == this.mStackFromEnd ;}
Clyde

Recycler görünüm bağdaştırıcımı güncellemek için diff araçlarını kullanıyorum ve bu yanıt bir kilitlenme düzeltildi. Çok teşekkürler, sevgili yazar!
Eugene P.

29

Yeni yanıt: Tüm RecyclerView güncellemeleri için DiffUtil kullanın. Bu, hem performansa hem de yukarıdaki hataya yardımcı olacaktır. Buraya Bakın

Önceki cevap: Bu benim için çalıştı. Anahtar, notifyDataSetChanged()doğru şeyleri doğru sırada kullanmamak ve yapmaktır:

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
Bu, iyi bir açıklama ile en kapsamlı çözümdür. Teşekkürler!
Sakiboy

notifydatasetchanged (), @Bolling yerine notifyitemrangeinserted öğesini kullanmanın amacı nedir?
Ankur_009

@FilipLuch Nedenini açıklayabilir misiniz?
Sreekanth Karumanaghat

3
@SreekanthKarumanaghat emin, neden açıklayamadım bilmiyorum. Temel olarak listedeki tüm öğeleri temizler ve yeniden oluşturur. Arama sonuçlarında olduğu gibi, çoğu zaman aynı öğeleri alırsınız veya yenileme tamamlandığında aynı öğeleri alırsınız ve ardından her şeyi yeniden yaratırsınız, bu da performans kaybıdır. Bunun yerine DiffUtils kullanın ve yalnızca tüm öğeler yerine değişiklikleri güncelleyin. Her seferinde A'dan Z'ye gitmek gibi, ama orada sadece F'yi değiştirdin.
Filip Luchianenco

2
DiffUtil gizli bir hazinedir. Paylaşım için teşekkürler!
Sileria

22

Bu soruna neden olan nedenler :

  1. Öğe animasyonları etkinleştirildiğinde Recycler'da dahili bir sorun
  2. Başka bir iş parçacığında Recycler verilerinde değişiklik
  3. Bildirim yöntemlerini yanlış bir şekilde çağırmak

ÇÖZÜM:

----------------- ÇÖZÜM 1 ---------------

  • İstisnayı yakalama (Özellikle 3. nedenden dolayı önerilmez)

Aşağıdaki gibi özel bir LinearLayoutManager oluşturun ve ReyclerView olarak ayarlayın

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

            }
        }

Ardından RecyclerVIew Layout Manager'ı aşağıdaki gibi ayarlayın:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- ÇÖZÜM 2 ---------------

  • Öğe animasyonlarını devre dışı bırak (1 numaralı nedenden kaynaklanıyorsa sorunu düzeltir):

Yine, aşağıdaki gibi özel bir Lineer Layout Manager oluşturun:

    public class CustomLinearLayoutManager extends LinearLayoutManager {

            //Generate constructors

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

Ardından RecyclerVIew Layout Manager'ı aşağıdaki gibi ayarlayın:

recyclerView.setLayoutManager(new CustomLinearLayoutManager(activity));

----------------- ÇÖZÜM 3 ---------------

  • Bu çözüm, 3 numaralı nedenden kaynaklanıyorsa sorunu giderir. Bildirim yöntemlerini doğru şekilde kullandığınızdan emin olmanız gerekir. Alternatif olarak, değişikliği akıllı, kolay ve sorunsuz bir şekilde ele almak için DiffUtil'i kullanın. Android RecyclerView'de DiffUtil kullanma

----------------- ÇÖZÜM 4 ---------------

  • 2. nedenden ötürü, geri dönüşümcü listesine tüm veri erişimini kontrol etmeniz ve başka bir iş parçacığında herhangi bir değişiklik olmadığından emin olmanız gerekir.

Bu benim senaryomda çalıştı, geri dönüştürücüler ve bağdaştırıcılar için özel bileşenler var çünkü DiffUtil kullanamıyorum ve hata tam olarak bilinen belirli senaryolarda oluşur, ben sadece öğe animatörleri kaldırmak için başvurmadan yama gerekiyordu, bu yüzden ben sadece denemek ve yakalamak sarılmış
RJFares

17

Benzer bir sorun yaşadım.

Aşağıdaki hata kodunda sorun:

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

Çözüm:

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

Bu benim için harika çalıştı! Neden sadece kullanamadığımızdan emin değilim newList.size() - 1.
waseefakhtar

15

Bu konuya göre , sorun çözüldü ve muhtemelen 2015'in başlarında bir süre yayınlandı. Aynı konudan bir alıntı :

Özellikle notifyDataSetChanged çağrısı ile ilgilidir. [...]

Btw, animasyonları ve performansı öldürdüğü için notifyDataSetChanged kullanmamanızı şiddetle tavsiye ederim. Ayrıca bu durumda, belirli bildirim etkinliklerinin kullanılması bu soruna geçici bir çözüm getirecektir.

Hala destek kitaplığının yeni bir sürümüyle ilgili sorun yaşıyorsanız , (biraz hassas / anlaşılmaz) sözleşmeye uyduğunuzdan emin olmak için bağdaştırıcınızdaki çağrılarınızı notifyXXX(özellikle, kullanımınızı notifyDataSetChanged) gözden geçirmenizi öneririm RecyclerView.Adapter. Ayrıca ana iş parçacığında bu bildirimleri yayınladığınızdan emin olun.


16
gerçekten değil, ben performans ile ilgili senin katılıyorum ama notifyDataSetChanged () animasyonları öldürmez, notifyDataSetChanged () kullanarak animasyon yapmak için a) RecyclerView.Adapter nesnesi setHasStableIds (true) çağırmak ve bir geri dönmek için Adaptör içinde getItemId geçersiz kılmak Her satır için benzersiz uzun bir değer ve göz
atın

@PirateApp Yorumunuzu bir cevap olarak yapmayı düşünmelisiniz. Denedim ve iyi çalışıyor.
mr5

Doğru değil! Yine de bu sorunla ilgili Google Konsolundan raporlar alın. Ve cihaz elbette Samsung -Samsung Galaxy J3(2017) (j3y17lte), Android 8.0
user25

10

Ben de aynı problemi yaşadım. Adaptör için öğe ekleme hakkında bildirimi ertelediğimden kaynaklandı.

Ancak ViewHoldergörünümünde bazı verileri yeniden çizmeye çalıştı ve RecyclerViewölçüm ve yeniden sayma çocuk sayısını başlattı - o anda çöktü (öğe listesi ve boyutu zaten güncellendi, ancak adaptör henüz bildirilmedi).


8

Bu, notifyItemChanged, notifyItemRangeInserted vb. İçin yanlış konum belirttiğinizde olur.

Önce: (Hatalı)

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

Sonra: (Doğru)

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

1
Neden notifyItemRangeInserted(initialSize, mChannelItemList.size()-1);olmasın notifyItemRangeInserted(initialSize, list.size());?
CoolMind

Undestood. Karışık initialSizeve listboyut. Yani, her iki değişkeniniz de yanlış.
CoolMind

Benim için çalışıyor notifyItemRangeInserted(initialSize, list.size()-1);ama anlamıyorum. İtemCount için eklenen boyutu neden bir azaltmalıyım?
pleksus

7

Bu sorunun oluşmasının başka bir nedeni, bu yöntemleri yanlış dizinlerle çağırdığınızdadır (orada OLMAYAN dizinler ekleme veya kaldırma)

-notifyItemRangeRemoved

-notifyItemRemoved

-notifyItemRangeInserted

-notifyItemInserted

endeks parametrelerini bu yöntemlere göre kontrol edin ve kesin ve doğru olduklarından emin olun.


2
Bu benim sorunumdu. Listeye hiçbir şey eklenmediğinde istisna oluşur.
Rasel

6

Bu hata 23.1.1'de hala düzeltilmemiştir, ancak genel bir çözüm istisnayı yakalamak olacaktır.


18
Tam olarak nerede yakaladın? Yığın izlemedeki tek kod yerel Android kodudur.
howettl

1
@Saki_M yanıtı gibi yakalayın.
Renan Bandeira

Bu aslında Renan için olsa işe yarıyor mu? Düzeltmeyi bir süredir test ettiniz mi? Hata sadece zaman zaman oluşur, bu yüzden sadece bunun zaman içinde çalışıp çalışmadığını göreceğim.
Simon

Bu gerçekten işe yarıyor, ancak benimkine ilişkin bazı çocuk görüşleri tutarsız kalıyor.
david

@david Tutarsız bir şekilde bunun sonucu nedir?
Sreekanth Karumanaghat

4

Bu soruna, farklı iş parçacığında değiştirilen RecyclerView Verileri neden olur

Bir sorun olarak iş parçacığı teyit edebilir ve ben sorunu karşılaştım ve RxJava giderek daha popüler hale geliyor: .observeOn(AndroidSchedulers.mainThread())ne zaman aradığınızdan emin olunnotify[whatever changed]

Adaptörden kod örneği:

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

    [...]

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

DiffUtil.calculateDiff (diffUtilForecastItemChangesAnlayser (this.mWeatherForecatsItemWithMainAndWeathers, weatherForecastItems)) çağrılırken ana iş parçacığındayım. Konu: Konu [main, 5, main]
Mathias Seguy Android2ee

4

Benim durumumda her zaman notifyItemRemoved (0) çağırdığımda çöktü. Ayarladığım setHasStableIds(true)ve getItemIdiçeriğin yeni pozisyonunu döndüğüm ortaya çıktı. hashCode()Sorunu çözen öğenin veya kendi tanımladığı benzersiz kimliği döndürmek için güncellemeyi bitirdim.


4

Benim durumumda, sunucudan veri güncellemeleri almak nedeniyle bu sorunu alıyordum (Firebase Firestore kullanıyorum) ve ilk veri kümesi arka planda DiffUtil tarafından işlenirken, başka bir veri güncellemesi kümesi geliyor ve bir eşzamanlılık sorununa neden oluyor başka bir DiffUtil başlatarak.

Kısacası, sonuçları bir RecylerView'e göndermek için ana iş parçacığına geri gelen bir arka plan iş parçacığında DiffUtil kullanıyorsanız, birden çok veri güncelleştirmesi kısa sürede geldiğinde bu hatayı alma şansını çalıştırın.

Bunu bu harika açıklamadaki tavsiyeleri izleyerek çözdüm: https://medium.com/@jonfhancock/get-threading-right-with-diffutil-423378e126d2

Sadece çözümü açıklamak için güncel bir Deque çalışırken güncellemeleri itmek olduğunu. Daha sonra deque, güncelleştirme bittiğinde bekleyen güncellemeleri çalıştırabilir, böylece sonraki tüm güncellemeleri işleyebilir, ancak tutarsızlık hatalarını da önleyebilir!

Umarım bu yardımcı olur çünkü bu beni kafamı çizdi!


Bağlantı için teşekkürler!
CoolMind

3

Sorun sadece benim için şu durumlarda ortaya çıktı:

Bağdaştırıcıyı boş bir listeyle oluşturdum . Sonra öğeleri ekledim ve aradım notifyItemRangeInserted.

Çözüm:

Bunu sadece ilk veri yığınına sahip olduktan ve hemen onunla başladıktan sonra Bağdaştırıcı oluşturarak çözdüm. Bir sonraki yığın eklenebilir ve notifyItemRangeInsertedsorunsuz bir şekilde çağrılabilir.


Bunun sebebi olduğunu sanmıyorum. Boş listelerle birçok bağdaştırıcım var, sonra öğeleri ekledim notifyItemRangeInserted, ancak orada bu istisna yoktu.
CoolMind

3

Benim sorunum, geri dönüşümcü görünümü için veri modeli içeren her iki dizi listesini temiz olsa bile, ben önceki modelden eski verileri vardı, bu değişiklik bağdaştırıcı bildirmek değildi. Hangi görünüm tutucu pozisyonu ile ilgili karışıklığa neden oldu. Bunu düzeltmek için, tekrar güncellemeden önce veri setinin değiştirildiği adaptöre her zaman haber verin.


ya da öğenin kaldırılması durumunda haber
ver

benim model konteyner referansını kullanır bu yüzden
Remario

3

Benim durumumda daha önce mRecyclerView.post (yeni Runnable ...) ile bir iş parçacığı içinde veri değiştiriyordu ve daha sonra tekrar UI iş parçacığı, tutarsızlık neden veri değiştirdi.


1
seninle aynı duruma sahibim, nasıl çözdün? teşekkürler
baderkhane

2

Hata, değişikliklerinizin bildirdiğiniz şeyle tutarsız olmasından kaynaklanabilir. Benim durumumda:

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

Elbette ne yapmam gerekiyordu:

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

2

Benim durumumda sorun, yeni yüklenen veri miktarı ilk verilerden daha az olduğunda notifyDataSetChanged kullanmasıydı. Bu yaklaşım bana yardımcı oldu:

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

Neden notifyDataSetChangedyeni verilere bağlıdır? Tüm listeyi yenileyeceğini düşündüm.
CoolMind

2

Ben de aynı problemle karşılaştım.

Uygulamam, recyclerView öğemi içeren bir parçayla Gezinme bileşenlerini kullanıyor. Parçam ilk kez yüklendiğinde listem iyi görünüyordu ... ancak uzaklaşıp geri geldikten sonra bu hata oluştu.

Uzaklaşırken parça yaşam döngüsü sadece onDestroyView'da gerçekleşti ve geri döndüğünde onCreateView'da başladı. Ancak, bağdaştırıcım parçanın onCreate öğesinde başlatıldı ve geri dönerken yeniden başlatmadı.

Düzeltme onCreateView içinde bağdaştırıcı başlatmak oldu.

Umarım bu birine yardımcı olabilir.


0

Yanlışlıkla benim recyclerview birden çok kez belirli bir satırı kaldırmak için bir yöntem çağırıyordu çünkü bu hatayı aldım. Gibi bir yöntem vardı:

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

Yanlışlıkla bu yöntemi bir kez yerine üç kez çağırıyordum, bu yüzden ikinci kez loc-1 idi ve kaldırmaya çalıştığında hata verildi. İki düzeltme, yöntemin yalnızca bir kez çağrıldığından emin olmak ve ayrıca böyle bir sağlık kontrolü eklemekti:

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

0

Aynı sorunu yaşadım ve bunun sadece Samsung telefonlarda olduğunu okudum ... Ama gerçekte bunun birçok markada gerçekleştiğini gösterdi.

Test ettikten sonra bunun sadece RecyclerView aygıtını hızlı kaydırdığınızda ve sonra geri düğmesi veya Yukarı düğmesiyle geri döndüğünüzde olduğunu fark ettim. Bu yüzden Yukarı düğmesine koydum ve aşağıdaki snippet'e basıldım:

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

Bu çözümle adaptöre yeni bir Arraylist ve recyclerView için yeni adaptör yüklediniz ve daha sonra aktiviteyi bitiriyorsunuz.

Umarım birine yardımcı olur


0

Ben yanlışlıkla iki kez "notifyItemInserted" çağırıyordu çünkü bu hatayı aldım.


0

Benim durumumda listede 5000'den fazla ürün vardı. Benim sorunum geri dönüşüm görünümünde kaydırma yaparken, bazen "onBindViewHolder" "myCustomAddItems" yöntemi listeyi değiştirirken çağrılması oldu.

Benim çözüm veri listesini değiştiren tüm yöntemlere "senkronize (syncObject) {}" eklemek oldu. Bu şekilde herhangi bir zamanda sadece bir yöntem bu listeyi okuyabilir.


0

Benim durumumda, adaptör verileri değişti. Ve bu değişiklikler için yanlış notifyItemInserted () kullanıyordum. NotifyItemChanged kullandığımda, hata gitti.


0

Listedeki öğeleri kaldırdığımda ve güncellediğimde de aynı sorunla karşılaştım ... Araştırma günlerinden sonra nihayet bir çözüm bulduğumu düşünüyorum.

Yapmanız gereken ilk önce tüm notifyItemChangedlistenizi yapmak ve sadece o zaman notifyItemRemoved azalan bir sırayla yapmak

Umarım bu aynı sorunla karşılaşan insanlara yardımcı olur ...


0

Bir imleç kullanıyorum, bu yüzden popüler cevaplarda önerilen DiffUtils kullanamıyorum. Benim için çalışması için liste boş olmadığında animasyonları devre dışı bırakıyorum. Bu sorunu gideren uzantıdır:

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

Ardından adaptörünüzü bu şekilde güncelleyebilirsiniz

list.executeSafely {
  adapter.updateICursor(newCursor)
}

0

Çoklu dokunmadan sonra sorun oluşursa, çoklu dokunmayı devre dışı bırakabilirsiniz.

android:splitMotionEvents="false" 

düzen dosyasında.


-1

Verileriniz çok değişiyorsa,

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

veya veri kümenizdeki bazı tek öğeler değişirse,

 mAdapter.notifyItemChanged(pos);

Ayrıntılı yöntemlerin kullanımı için dokümana başvurabilir, bir şekilde doğrudan kullanmamaya çalışabilirsiniz mAdapter.notifyDataSetChanged().


2
kullanmak notifyItemRangeChangedda aynı çökmeyi üretir.
lionelmessi

Bu bazı durumlara uygundur. Veri kümenizi hem arka plan iş parçacığında hem de UI iş parçacığında güncellediniz, bu da tutarsızlığa neden olacaktır. Veri kümesini yalnızca UI iş parçacığında güncelleştirirseniz çalışır.
Arron Cao
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.