Exception java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid ViewViewer Holder Holder Adapter

I am developing an android app and after publishing it on play store, I face this problem many times.

Exception java.lang.IndexOutOfBoundsException: Inconsistency detected.Invalid view holder adapter positionViewHolder{2f9d83c position=11 id=-1, oldPos=-1, pLpos:-1 no parent}
android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition (RecyclerView.java:5297)
android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline (RecyclerView.java:5479)
android.support.v7.widget.GapWorker.prefetchPositionWithDeadline (GapWorker.java:282)
android.support.v7.widget.GapWorker.flushTaskWithDeadline (GapWorker.java:336)
android.support.v7.widget.GapWorker.flushTasksWithDeadline (GapWorker.java:349)
android.support.v7.widget.GapWorker.prefetch (GapWorker.java:356)
android.support.v7.widget.GapWorker.run (GapWorker.java:387)
android.os.Handler.handleCallback (Handler.java:815)
android.os.Handler.dispatchMessage (Handler.java:104)
android.os.Looper.loop (Looper.java:207)
android.app.ActivityThread.main (ActivityThread.java:5737)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:789)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:679)

      

I searched for this issue for a long time and applied many solutions but the exception was still throwing

One of the solutions I have applied to is creating a custom LinearLayoutManager:

public class WrapContentLinearLayoutManager  extends LinearLayoutManager {

    public WrapContentLinearLayoutManager(Context context) {
        super(context);
    }

    //... constructor
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
            setAutoMeasureEnabled(false);
        } catch (IndexOutOfBoundsException e) {
            Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
        }
    }

    public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        setAutoMeasureEnabled(false);
    }

    public WrapContentLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        setAutoMeasureEnabled(false);
    }

    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }
}

      

My app depends on a RecyclerView with Load More and Expand to update .

I am using Volley to get data from a web service for Load More and Carry out to update

To load an additional item into the RecyclerView, inside the Volley response callback, I add new items to the ArrayList and notify the adapter Like this:

    final int positionStart = list.size() - 1;
    final int itemCount = response.length();
    for (int i=0;i<response.length();i++)
    {
        try 
        {
            obj = response.getJSONObject(i);
            list.add(new ItemEntry(obj.getInt("Id"),obj.getString("Title").trim()obj.getBoolean("isFavorite"));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        if(index == 0)
        { 
            // for first Initialize for adapter 
            adapter = new MyAdapter(latestEntryList,getActivity());
            adapter.setOnItemClickCallBack(mainFragment);
            recyclerView.setAdapter(adapter);
        }
        else
        {
            // for load more items
            recyclerView.post(new Runnable() 
            {
                @Override
                public void run() 
                {
                   adapter.updateItems(positionStart,itemCount);
                }
            });
        }
    }

      

Code for adapter .UpdateItems

  public void updateItems(int positionStart,int itemCount) {
        itemsCount = getItemCount();
        this.notifyItemRangeInserted(positionStart,itemCount);
    }

      

In case of loading More, I do the following: in OnRefresh

   public void onRefresh() {
        listFragment.LatestEntry(0,false);
// 0 the index and False isAppend to list of not
    } 

      

Code for LastEntry:

   private void LatestEntry(final int index, final boolean isAppend)
        {
        String customerId = "123";
        String uri = String.format(BASE_URL+"/api/Entry/LatestEntry?customerId=%s&index=%s",customerId,index);

            JsonArrayRequest LatestEntryJsonArrayRequest = new JsonArrayRequest(Request.Method.GET, uri,
                null, new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) 
                   {
                        JSONObject obj = new JSONObject();
                        if(response.length() > 0 && !isAppend) 
                        {
                            int startPosition = 0;
                            int preSize;

                            if(adapter != null)
                                preSize = adapter.entries.size();
                            else
                                preSize = 0;
                            if(preSize > 0 && EntryList.size() > 0) {
                                EntryList.clear();
                                adapter.entries.clear();
                                adapter.notifyItemRangeRemoved(startPosition, preSize);
                            }
                        }

                        final int positionStart = EntryList.size() - 1;
                        final int itemCount = response.length();
                        for (int i=0;i<response.length();i++)
                        {
                            try {
                                obj = response.getJSONObject(i);
                                list.add(new ItemEntry
                               (obj.getInt("Id"),obj.getString("Title").trim()
                                  obj.getBoolean("isFavorite"));

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }

                        if(index == 0) 
                        { // for first Initialize for adapter 
                            adapter = new MyAdapter(latestEntryList,getActivity());
                            adapter.setOnItemClickCallBack(mainFragment);
                            recyclerView.setAdapter(adapter);
                        }
                        else
                        {
                            // for load more items
                            recyclerView.post(new Runnable() 
                            {
                                @Override
                                public void run() {
                                   adapter.updateItems(positionStart,itemCount);
                                }
                            });
                        }
                    }
                    setListShownNoAnimation(true);
                    if(mainFragment.swipeRefreshLayout.isRefreshing())
                      mainFragment.swipeRefreshLayout.setRefreshing(false);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Toast.makeText(MyApplication.getAppContext(),error.getMessage(),Toast.LENGTH_LONG);
                }
            }
            );

            LatestEntryJsonArrayRequest.setTag("LatestEntry");
            KidsSingleton.getInstance().addToRequestQueue(LatestEntryJsonArrayRequest);
        }

      

Please note the following:

  • Recyclerview version: recyclerview-v7: 25.3.0
  • I have three RecylerViews in a ViewPager
  • Each ViewPager contains a Fragment and a Fragment contains a list of RecylerView fragments.
  • There is one adapter for all RecylcrViews.

Can anyone help overcome this problem?

+3


source to share


3 answers


RecyclerView$Recycler.validateViewHolderForOffsetPosition

complains about not getting a valid element ViewHolder

even though it appears to be at the offset position of the 11th element.

suggested that you might have forgotten to bloat ViewHolder

when adding more items to the adapter ... the way of handling seems to be wrong; while normal, you only need to update the dataset and then notify the Adapter for example. notifyDataChanged()

; think by adding items directly to the adapter, these notifications are triggered from within.



swipeRefreshLayout.setRefreshing(false)

should probably happen in the callback method so that the wheel animation will disappear when the download completes.

maybe check this example to get an idea of ​​the order in which the framework fires events (hence this is often a problem that the functionality that the framework is already associated with) ... getting into it may also be a clue to understanding why there is no valid one for this element ViewHolder

.

0


source


I have a similar situation - viewers as recyclers viewers. When adding / removing elements, there was some kind of animation for these "pending deletion" elements. So when I called to delete an element and the animation was still in effect, I got:

IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

      



To avoid this exception, I created a handler to delay deletion of items. BUT! This is not true. If you have changed the adapter details, the RV should be immediately without any handler or threading notified of these changes. So consider changing this path to something where updates won't need separate threads.

0


source


Call notifyItemRangeChanged(positionStart,itemCount)

Before calling notifyItemRangeInserted(positionStart,itemCount);

Worked for me.

-1


source







All Articles