2016-02-26 46 views
33

回收站視圖不一致檢測到的錯誤,而未來快速滾動或滾動,同時加載多個項目..Recycler View:檢測到不一致。無效觀看的轉接器positionViewHolder

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) 

適配器

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

活動

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

我有同樣的錯誤,因爲我使用無盡的回收,因爲我的數據到達最後的位置,它沒有得到物品計數​​,就像你設置它在每次滾動最後可能會得到27項目計數可能是你沒有得到27項,所以它發生之後,因爲我得到什麼問題剛剛創建的邏輯,如果我mod(%)list.size()的項目我得到,如果它等於0只是使用notifyItemrangeInserted否則notifyDataSetChanged( ); –

+0

有什麼方法可以重現此?我在我的生產應用程序中遇到了這個崩潰,但無法在本地重現它 – Ezio

回答

23

我牛逼看起來與已知的Android bug

類似有相當難看,但工作方法

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

對我來說,工作沒有任何副作用。

+0

使用此應用程序後不會崩潰,但錯誤仍然存​​在,此錯誤的確切原因是什麼? –

+0

事情工作..但你能解釋崩潰的原因? –

+3

這是一個非常糟糕的主意,引發了這個例外並使其沉默...... https://issuetracker.google.com/issues/37030377#comment9 @Angad Tiwari –

2

我similiar問題,也該解決方案幫助了我,我已經添加了新的項目,以我的RV之後:

recyclerView.getRecycledViewPool().clear(); 
adapter.notifyDataSetChanged(); 
8

這個問題是回收視圖的知道是不是BUG。最好的解決辦法是

清除列表刷新之前的循環圖

每次爲了解決這個問題,剛剛更新的循環圖前用空列表調用notifyDataSetChanged()。

例如

//Method for refresh recycle view 

if (!yourList.isEmpty()) 

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

adapter.notifyDataSetChanged(); 
+0

我陷入了這個問題recyclerview LoadMore項目 –

+0

@AhamadullahSaikat你有沒有得到它的答案? –

+0

是的。這個問題的第一個答案是適合我的.. –

1

使用此刷新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 

`

0

通過我無限/分頁RecyclerView滾動快,當我有這個問題。我的問題的根源在於我在列表的開頭有一個「標題」項目,這個標題項目不是數據源的一部分,它只是插在adapter列表的開頭。因此,當快速滾動並向RecyclerViewAdapter添加新的項目頁面並通知adapter已插入新數據時,我沒有考慮額外的標題項目,從而導致適配器列表的大小錯誤並導致這個例外...

因此,簡而言之,如果您在我們的RecyclerView適配器中使用頁眉/頁腳,請確保在更新適配器數據時將其考慮在內。

例子:

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

編輯: 我想你總是可以只使用適配器方法getItemCount()得到的大小,而不是從「數據表」獲取規模和增加它。你的getItemCount()方法應該已經考慮到你在列表中的任何額外的頁眉/頁腳/等。

相關問題