Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recycler View: Inconsistency detected. Invalid view holder adapter positionViewHolder

Recycler View Inconsistency Detected error, coming while scrolling fast or scrolling while loading more items..

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) 

Adapter

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

Activity

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;     } } 
like image 832
Prashanth Avatar asked Feb 26 '16 13:02

Prashanth


2 Answers

This issue is a known bug of RecyclerView. The best solution is, clear the list every time before refresh RecyclerView.

For fix this issue just call notifyDataSetChanged() with empty list before updating recycle view.

For example

//Method for refresh recycle view  if (!yourList.isEmpty())  yourList.clear(); //The list for update recycle view  adapter.notifyDataSetChanged();  
like image 60
EKN Avatar answered Sep 28 '22 00:09

EKN


It looks similar with known android bug

There are quite ugly, but working approach

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

For me it works without any by-effect.

like image 44
paynd Avatar answered Sep 28 '22 00:09

paynd