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;
}
}
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.
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();
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
`
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();
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 RecyclerView
Adapter
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.
Peut-être que vous pouvez essayer ceci avant d'actualiser l'adaptateur:
dataList.clear();
patrolListAdapter.notifyDataSetChanged();
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);
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.
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.
J'ai eu le même problème. Supprimer toutes les vues de RecyclerView m'a aidé à:
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();
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<>();