异常java.lang.IndexOutOfBoundsException:检测到不一致。视图持有者适配器positionViewHolder无效

时间:2017-03-28 08:34:44

标签: android

我正在开发Android应用程序,并在Play商店发布后,我很多时候都面临这个问题。

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)

我长时间搜索了这个问题并应用了很多解决方案,但异常仍然提出

我申请的解决方案之一是制作自定义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;
    }
}

在我的应用程序中依赖于RecyclerView,加载更多滑动以刷新功能

我使用Volley从网络服务获取数据加载更多滑动以刷新

在RecyclerView中加载更多项目,在Volley Response的回调中,我将新项目附加到ArrayList并通知适配器如下:

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

Adapter.UpdateItems的代码

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

如果是负载更多,我会执行以下操作;在OnRefresh

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

LatestEntry的代码是:

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

请注意以下内容:

  1. Recyclerview版本为:recyclerview-v7:25.3.0
  2. 我在ViewPager中有三个RecylerView
  3. 每个ViewPager包含片段和片段包含RecylerView的ListFragment。
  4. 所有RecylcrView都有一个适配器。
  5. 任何人都可以帮助解决这个问题吗?

4 个答案:

答案 0 :(得分:1)

我有类似的情况 - viewpagers作为回收者视图的项目。添加\删除项目时,这些“等待删除”项目有一些动画。因此,当我调用删除项目时,动画仍在表演我有:

Post.joins(:tags).having('count(tags.id) > 3').group('posts.id')

为了避免这种异常,我创建了一个延迟删除项目的处理程序。但!这是错的。如果您已经更改了适配器RV的数据,则应该立即没有任何处理程序或多线程通知这些更改。 因此,请考虑将此方式更改为更新不需要单独线程的内容。

答案 1 :(得分:0)

RecyclerView$Recycler.validateViewHolderForOffsetPosition抱怨未收到有效的ViewHolder项,而它似乎位于第11项的偏移位置。

假设您在向适配器添加更多项目时可能忘记给ViewHolder充气......处理方式似乎不对;普通的,只需要更新数据阵列,然后用例如通知适配器。 notifyDataChanged();在将项目直接添加到适配器时,可以考虑在内部触发这些通知。

swipeRefreshLayout.setRefreshing(false)可能应该在回调方法中发生,以便在加载完成时轮子动画消失。

也许检查这个example以了解框架触发事件的顺序(因此通常是问题,一个人创建了框架已经带来的功能)...踩到可能还提供了一个线索,为什么没有有效的ViewHolder为该项目充气。

答案 2 :(得分:0)

可能是您设置为适配器的列表已被更改,但notifyDataSetChanged尚未被调用。 就我而言,我延迟了对notifyDataSetChanged的调用。但是适配器的getItemCount()已更改为另一个值,因此崩溃。

答案 3 :(得分:-1)

致电notifyItemRangeChanged(positionStart,itemCount)

在致电notifyItemRangeInserted(positionStart,itemCount);

之前

为我工作。