web-dev-qa-db-fra.com

Recycler View: Incohérence détectée. Adaptateur de titulaire de vue non valide positionViewHolder

Erreur d'incohérence de la vue recyclée détectée, erreur de défilement rapide ou défilement lors du chargement de plusieurs éléments.

FATAL EXCEPTION: main
Process: com.pratap.endlessrecyclerview, PID: 21997
Java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{56a082c position=40 id=-1, oldPos=39, pLpos:39 scrap [attachedScrap] tmpDetached no parent}
at Android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.Java:4251)
at Android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.Java:4382)
at Android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.Java:4363)
at Android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.Java:1961)
at Android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.Java:1370)
at Android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.Java:1333)
at Android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.Java:562)
at Android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.Java:2864)
at Android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.Java:1445)
at Android.support.v7.widget.RecyclerView.access$400(RecyclerView.Java:144)
at Android.support.v7.widget.RecyclerView$1.run(RecyclerView.Java:282)
at Android.view.Choreographer$CallbackRecord.run(Choreographer.Java:858)
at Android.view.Choreographer.doCallbacks(Choreographer.Java:670)
at Android.view.Choreographer.doFrame(Choreographer.Java:603)
at Android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.Java:844)
at Android.os.Handler.handleCallback(Handler.Java:746)
at Android.os.Handler.dispatchMessage(Handler.Java:95)
at Android.os.Looper.loop(Looper.Java:148)
at Android.app.ActivityThread.main(ActivityThread.Java:5443)
at Java.lang.reflect.Method.invoke(Native Method)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:728)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:618)

Adaptateur

public class DataAdapter extends RecyclerView.Adapter {
    private final int VIEW_ITEM = 1;
    private final int VIEW_PROG = 0;

    private List<Feed> mFeed;
    // The minimum amount of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 5;
    private int lastVisibleItem, totalItemCount;
    private boolean loading;
    private OnLoadMoreListener onLoadMoreListener;

    public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {

        mFeed = feeds;

        if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {

            final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
                .getLayoutManager();

            recyclerView
                .addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView,
                        int dx, int dy) {
                        super.onScrolled(recyclerView, dx, dy);

                        totalItemCount = linearLayoutManager.getItemCount();
                        lastVisibleItem = linearLayoutManager
                            .findLastVisibleItemPosition();
                        if (!loading
                            && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
                            // End has been reached
                            // Do something
                            if (onLoadMoreListener != null) {
                                onLoadMoreListener.onLoadMore();
                            }
                            loading = true;
                        }
                    }
                });
        }
    }

    @Override
    public int getItemViewType(int position) {
        return mFeed.get(position) == null ? VIEW_PROG : VIEW_ITEM;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
        int viewType) {
        RecyclerView.ViewHolder vh;
        if (viewType == VIEW_ITEM) {
            View v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.list_row, parent, false);

            vh = new StudentViewHolder(v);
        }
        else {
            View v = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.progress_item, parent, false);

            vh = new ProgressViewHolder(v);
        }
        return vh;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof StudentViewHolder) {

            Feed singleStudent= (Feed) mFeed.get(position);
            ((StudentViewHolder) holder).tvName.setText(singleStudent.getTitle());
            ((StudentViewHolder) holder).student= singleStudent;
        } else {
            ProgressViewHolder.PROGRESS_BAR.setIndeterminate(true);
        }
    }

    public void setLoaded() {
        loading = false;
    }

    public void  addFeed(Feed feed) {
        mFeed.add(feed);
        //mFeed.addAll(0, (Collection<? extends Feed>) feed);
        notifyItemInserted(mFeed.size());
        //notifyItemRangeInserted(0,mFeed.size());
        notifyDataSetChanged();
        //notifyItemInserted(mFeed.size());
        //setLoaded();
        //notifyItemInserted(mFeed.size());
    }

    public void removeAll(){
        mFeed.clear();
        notifyDataSetChanged();
    }

    @Override
    public int getItemCount() {
        return mFeed.size();
    }

    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
        this.onLoadMoreListener = onLoadMoreListener;
    }

    public static class StudentViewHolder extends RecyclerView.ViewHolder {
        public TextView tvName;

        public Feed student;
        public StudentViewHolder(View v) {
            super(v);
            tvName = (TextView) v.findViewById(R.id.tvName);

            //tvEmailId = (TextView) v.findViewById(R.id.tvEmailId);
        }
    }

    public static class ProgressViewHolder extends RecyclerView.ViewHolder {
        //public ProgressBar progressBar;
        public static ProgressBar PROGRESS_BAR;
        public ProgressViewHolder(View v) {
            super(v);
            PROGRESS_BAR = (ProgressBar) v.findViewById(R.id.progressBar1);
            //  progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
        }
    }
}

Activité

public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {

    private Toolbar toolbar;

    private TextView tvEmptyView;
    private RecyclerView mRecyclerView;
    private DataAdapter mAdapter;
    private LinearLayoutManager mLayoutManager;
    private RestManager mManager;
    private List<Feed> mFeed;
    SwipeRefreshLayout mSwipeRefreshLayout;
    protected Handler handler;
    private int currentPage=1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        toolbar = (Toolbar) findViewById(R.id.toolbar);
        tvEmptyView = (TextView) findViewById(R.id.empty_view);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        mSwipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
        mSwipeRefreshLayout.setOnRefreshListener(this);
        //studentList = new ArrayList<Student>();
        mFeed = new ArrayList<Feed>();
        handler = new Handler();
        if (toolbar != null) {
            setSupportActionBar(toolbar);
            getSupportActionBar().setTitle("Android Students");

        }
        mManager = new RestManager();

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this);

        // use a linear layout manager
        mRecyclerView.setLayoutManager(mLayoutManager);

        // create an Object for Adapter
        mAdapter = new DataAdapter(mFeed,mRecyclerView);

        // set the adapter object to the Recyclerview
        mRecyclerView.setAdapter(mAdapter);
        //   mAdapter.notifyDataSetChanged();

        loadData(false);

        //        if (mFeed.isEmpty()) {
        //            mRecyclerView.setVisibility(View.GONE);
        //            tvEmptyView.setVisibility(View.VISIBLE);
        //
        //        } else {
        //            mRecyclerView.setVisibility(View.VISIBLE);
        //            tvEmptyView.setVisibility(View.GONE);
        //        }

        mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                //add null , so the adapter will check view_type and show progress bar at bottom
                mFeed.add(null);
                mAdapter.notifyItemInserted(mFeed.size() - 1);

                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //   remove progress item
                        mFeed.remove(mFeed.size() - 1);
                        // mAdapter.notifyItemRemoved(mFeed.size());
                        //add items one by one
                        int start = mFeed.size();
                        currentPage++;

                        Log.d("CurrentPage", String.valueOf(currentPage));
                        Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);

                        listCall.enqueue(new Callback<Results>() {

                            @Override
                            public void onResponse(Call<Results> call, Response<Results> response) {
                                mSwipeRefreshLayout.setRefreshing(false);
                                if (response.isSuccess()) {
                                    if (response.body() != null) {
                                        Results feedList = response.body();

                                        // List<Results> newUsers = response.body();

                                        Log.d("Retrofut", String.valueOf(feedList));

                                        for (int i = 0; i < feedList.results.size(); i++) {
                                            Feed feed = feedList.results.get(i);
                                            // mFeed.add(feed);
                                            mAdapter.addFeed(feed);
                                            //                                        mAdapter.notifyDataSetChanged();


                                            //mAdapter.notifyItemInserted(mFeed.size());

                                        }
                                        //    mAdapter.notifyDataSetChanged();
                                    }
                                }
                            }

                            @Override
                            public void onFailure(Call<Results> call, Throwable t) {
                                Log.d("Retrofut", "Error");
                                mFeed.remove(mFeed.size() - 1);
                                mAdapter.notifyItemRemoved(mFeed.size());

                                mAdapter.setLoaded();
                                mSwipeRefreshLayout.setRefreshing(false);
                            }
                        });

                        //        for (int i = 1; i <= 20; i++) {
                        //            studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
                        //
                        //        }

                        mAdapter.setLoaded();
                        //or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();

                    }
                }, 2000);
            }
        });
    }

    // load initial data
    private void loadData(final boolean removePreData) {

        Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);

        listCall.enqueue(new Callback<Results>() {

                             @Override
                             public void onResponse(Call<Results> call, Response<Results> response) {

                                 if (response.isSuccess()) {
                                     if (response.body() != null) {
                                         //  if(removePreData) mAdapter.removeAll();
                                         Results feedList = response.body();
                                         Log.d("Retrofut", String.valueOf(feedList));

                                         for (int i = 0; i < feedList.results.size(); i++) {
                                             Feed feed = feedList.results.get(i);
                                             // mFeed.add(feed);
                                             //mAdapter.notifyDataSetChanged();
                                             mAdapter.addFeed(feed);
                                         }

                                         mSwipeRefreshLayout.setRefreshing(false);
                                     }
                                 }
                             }

                             @Override
                             public void onFailure(Call<Results> call, Throwable t) {
                                 Log.d("Retrofut", String.valueOf(t));
                                 mFeed.remove(mFeed.size() - 1);
                                 mAdapter.notifyItemRemoved(mFeed.size());
                                 mAdapter.setLoaded();
                                 mSwipeRefreshLayout.setRefreshing(false);
                             }
                         }
        );

        //        for (int i = 1; i <= 20; i++) {
        //            studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
        //
        //        }

        mSwipeRefreshLayout.setRefreshing(true);
    }

    @Override
    public void onRefresh() {
        mFeed.clear();
        mAdapter.notifyDataSetChanged();
        loadData(true);
        currentPage=1;
    }
}
51
Prashanth

Il semble similaire avec connu Android bug

Il y a assez moche, mais l'approche de travail

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("Error", "IndexOutOfBoundsException in RecyclerView happens");
        }
    }
}


mRecyclerView.setLayoutManager(new WrapContentGridLayoutManager(getContext(), spanCount));

Pour moi, cela fonctionne sans aucun effet secondaire.

36
paynd

Ce problème est un bogue connu de RecyclerView. La meilleure solution consiste à vider la liste à chaque fois avant d'actualiser RecyclerView.

Pour résoudre ce problème, appelez notifyDataSetChanged () avec une liste vide avant de mettre à jour la vue Recyclage.

Par exemple

//Method for refresh recycle view

if (!yourList.isEmpty())

yourList.clear(); //The list for update recycle view

adapter.notifyDataSetChanged(); 
18
EKN

Utilisez ceci pour actualiser un RecyclerView

items.clear(); //here items is an ArrayList populating the RecyclerView
adapter.notifyDataSetChanged();
items.addAll(list);// add new data 
adapter.notifyItemRangeInserted(0, items.size);// notify adapter of new data

`

6
Nikhil Bansal

J'ai eu un problème similaire, et aussi cette solution m'a aidé, après avoir ajouté un nouvel élément à mon VR:

recyclerView.getRecycledViewPool().clear();
adapter.notifyDataSetChanged();
5
K.Os

J'ai eu ce problème en faisant défiler rapidement mon sans fin/paging RecyclerView. La racine de mon problème vient du fait que j'avais un élément "en-tête" au début de la liste, cet élément en-tête ne faisant pas partie de la source de données, il était simplement inséré au début du adapter liste. Ainsi, lors du défilement rapide et de l'ajout de nouvelles pages d'éléments au RecyclerViewAdapter et d'informer le adapter que de nouvelles données avaient été insérées, je ne prenais pas en compte l'élément d'en-tête supplémentaire , rendant ainsi la taille de la liste de l'adaptateur erronée ... et provoquant cette exception ...

En bref, si vous utilisez un en-tête/pied de page dans notre adaptateur RecyclerView, assurez-vous de le prendre en compte lors de la mise à jour des données des adaptateurs.

Exemple:

public void addNewPageToList(List<MyData> list)
{   //
    // Make sure you account for any header/footer in your list!
    //
    // Add one to the currentSize to account for the header item.
    //
    int currentSize = this.adapterList.size() + 1;
    this.adapterList.addAll(list);
    notifyItemRangeInserted(currentSize, this.adapterList.size());
}

Edit: J'imagine que vous pouvez toujours utiliser la méthode de l'adaptateur getItemCount() pour obtenir la taille, au lieu d'obtenir la taille de la “liste de données” et de l'ajouter. Votre méthode getItemCount() devrait déjà prendre en compte tout en-tête/pied de page/etc supplémentaire figurant dans votre liste.

1
Sakiboy

Peut-être que vous pouvez essayer ceci avant d'actualiser l'adaptateur:

dataList.clear(); 
patrolListAdapter.notifyDataSetChanged();
1
BLiYing

Dans mon cas, je le faisais en tant que notifyItemInserted(position);. Cela m'a causé ce problème, puis je l'ai utilisé et cela a parfaitement fonctionné .notifyItemRangeInserted(startIndex,endIndex);

1
Ali Akram

En ce qui me concerne, le problème était que je ne publiais pas notifyDatasetChanged lorsque l'ensemble de données a été modifié lors de la mise en œuvre de la recherche incrémentielle.

J'avais une liste filtrée en fonction des recherches de l'utilisateur dans le widget de recherche. Pour chaque élément de la liste, je faisais une demande à distance et, lorsque j'ai obtenu le résultat, je mettais à jour cette cellule.

Je devais faire les deux notifications pour que la vue du recycleur fonctionne

Filtrez le jeu de données d'origine puis publiez le changement de jeu de données

this.searchResultTable?.post {
    this.searchResultTable?.adapter?.notifyDataSetChanged()
}

Après avoir reçu une réponse, envoyez à nouveau des notifications

this.searchResultTable?.post {
    this.searchResultTable?.adapter?.notifyItemChanged(index, updateDataHashMap)
}

Vous devez publier des mises à jour plutôt que d'envoyer des messages de notification directement afin d'éviter que la vue du recycleur ne se bloque lorsque la mise à jour arrive avant que la vue ne soit affichée.

Un autre point important à prendre en compte est que lorsque vous publiez les mises à jour individuelles après la réponse à distance, vous devez vous assurer que la liste actuellement affichée par l'utilisateur correspond à celle qui existait au moment de l'envoi des demandes.

0
ykonda

Essayez de changer private List<Feed> mFeed; En private ArrayList<Feed> mFeed;

Et dans votre constructeur, remplacez mFeed = feeds Par

public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {
    mFeed.addAll(feeds);
    ...

Ensuite, vous pouvez ajouter normalement comme ceci:

public void  addFeed(Feed feed) {
    mFeed.add(feed);
    notifyItemInserted(mFeed.size());
}

Ainsi, changer List en ArrayList a résolu mon problème. Je pense que c’est parce que List est une classe abstraite et que les méthodes add, remove, clear etc. ne sont pas réellement implémentées ici. Ainsi, lorsque vous définissez une liste (private ArrayList<Feed> mFeed;) Et l'initialisez avec une autre liste (mFeed = feeds;), Appeler mFeed.add(feed) ne fait rien et force recyclerView à lancer une exception.

0
Hafez Divandari

J'ai eu le même problème. Supprimer toutes les vues de RecyclerView m'a aidé à:

RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();
0
Andrew Churilo

Le problème est dans cette ligne de code:

 mFeed = feeds;

vous affectez mFeed à l'instance de l'appelant feeds de sorte que chaque fois que l'appelant modifie sa variable (il peut s'agir d'ajouter, d'effacer ou de supprimer des éléments), votre mFeed local changera

essayer de changer pour

mFeed.addAll(feeds);

n'oubliez pas d'initialiser mFeed à n'importe quelle liste, comme mFeed = new ArrayList<>();

0
Jorge Arimany