2016-02-26 10 views
33

Recycler Visualizza Incoerenza errore rilevato, venendo durante lo scorrimento veloce o lo scorrimento durante il caricamento di più oggetti ..Vista Recycler: Rilevata incoerenza. Adattatore vista titolare valido 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) 

adattatore

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

Attività

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

Ho lo stesso errore che sta arrivando perché ho usato il riciclatore infinito mentre i miei dati arrivano all'ultima posizione non ottiene il conteggio degli oggetti come se lo si impostasse ottenere 27 conteggio di elementi in ogni scroll alla fine potrebbe essere tu non ottenere 27 elementi in modo che si verifichi poi dopo che ho capito che il problema ha appena creato la logica che se mod (%) list.size() per oggetto sto ottenendo se è uguale a 0 basta utilizzare notifyItemrangeInserted altrimenti notifyDataSetChanged (); –

+0

C'è un modo per riprodurre questo? Ho riscontrato questo arresto anomalo nell'app di produzione ma non riesco a riprodurlo localmente. – Ezio

risposta

23

I t sembra simile con nota Android bug

Ci sono abbastanza brutto, ma l'approccio

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

Per me funziona a lavorare senza alcun by-effetto.

+0

L'app non si arresta in modo anomalo dopo l'utilizzo ma l'errore è ancora presente. Qual è il motivo esatto di questo errore? –

+0

le cose funzionano .. ma potresti spiegare il motivo del crash? –

+3

questa è una pessima idea, approvare l'eccezione e metterla a tacere ... https://issuetracker.google.com/issues/37030377#comment9 @Angad Tiwari –

2

avevo problema simile, e anche questa soluzione mi ha aiutato, dopo ho aggiunto nuovo elemento al mio RV:

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

Questo problema è un bug sapere di vista riciclo .La soluzione migliore è

cancellare l'elenco ogni volta prima vista ricicli rinfreschi

Per risolvere questo problema basta chiamare notifyDataSetChanged() con la lista vuota prima di aggiornare vista riciclo.

Per esempio

//Method for refresh recycle view 

if (!yourList.isEmpty()) 

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

adapter.notifyDataSetChanged(); 
+0

Sto cadendo questo problema recyclerview LoadMore items –

+0

@AhamadullahSaikat hai avuto risposta? –

+0

Sì. la prima risposta di questa domanda è funzionante per me .. –

1

Utilizzare questa funzione per aggiornare 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 

`

0

Ho avuto questo problema quando si scorre veloce attraverso la mia infinita/paging RecyclerView. La radice del mio problema derivava dal fatto che avevo un elemento "header" all'inizio della lista, questa voce di intestazione non faceva parte dell'origine dati, era appena inserita all'inizio dell'elenco adapter. Quindi, quando si scorre velocemente e si aggiungono nuove pagine di articoli allo RecyclerViewAdapter e si notifica allo adapter che erano stati inseriti nuovi dati, non stavo tenendo conto dell'elemento di intestazione aggiuntivo, rendendo così errata la dimensione dell'elenco dell'adattatore ... e causando questa eccezione ...

Quindi, in breve, se si utilizza un'intestazione/piè di pagina nell'adattatore RecyclerView, assicurarsi di prenderlo in considerazione durante l'aggiornamento dei dati degli adattatori.

Esempio:

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: Credo che si potrebbe sempre e solo utilizzare il metodo adattatore getItemCount() per ottenere la dimensione, invece di ottenere la dimensione della “lista di dati” e l'aggiunta di esso. Il tuo metodo getItemCount() dovrebbe già tenere conto di eventuali intestazioni/piè di pagina/etc aggiuntivi che hai nel tuo elenco.

Problemi correlati