Recycler View:检测到不一致。视图持有者适配器positionViewHolder无效

时间:2016-02-26 13:49:32

标签: android-recyclerview recycler-adapter

Recycler View Inconsistency检测到错误,在加载更多项目时快速滚动或滚动时出现..

   var selection3 = reeks
    .Where(n => n > 1000 && n < 10)
    .Take(100); // First 100 items in case the sequence is too long

适配器

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

13 个答案:

答案 0 :(得分:33)

它与已知的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));

对我而言,没有任何副作用。

答案 1 :(得分:15)

此问题是RecyclerView的已知错误。最好的解决方案是,每次刷新RecyclerView之前清除列表。

要修复此问题,请在更新循环视图之前使用空列表调用notifyDataSetChanged()。

例如

//Method for refresh recycle view

if (!yourList.isEmpty())

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

adapter.notifyDataSetChanged(); 

答案 2 :(得分:4)

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

`

答案 3 :(得分:1)

在我的无尽/分页RecyclerView中快速滚动时遇到了这个问题。我的问题的根源来自于我在列表的开头有一个“标题”项,这个标题项不是数据源的一部分,它只是插入adapter的开头名单。因此,当快速滚动并向RecyclerView Adapter添加新的项目页面并通知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()方法应该已经考虑了列表中的任何其他页眉/页脚/等。

答案 4 :(得分:1)

在我的情况下,我这样做notifyItemInserted(position); 这引起了我这个问题然后我用它并且它完美地工作。notifyItemRangeInserted(startIndex,endIndex);

答案 5 :(得分:1)

也许您可以在刷新适配器之前尝试以下操作:

dataList.clear(); 
patrolListAdapter.notifyDataSetChanged();

答案 6 :(得分:1)

将此行与设置recyclerView一起放入。通过将RecyclerView的ItemAnimator设置为null可以解决此问题。

in kotlin
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.itemAnimator = null

答案 7 :(得分:0)

我有类似的问题。从RecyclerView中删除所有视图帮助了我:

RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();

答案 8 :(得分:0)

我遇到了同样的问题。 像这样重新创建新列表可以很好地解决问题:

适配器

中的

public void updateItems(List<ComponentEntity> newItems) {
        List<ComponentEntity> updatedItems = new ArrayList<>();
        updatedItems = newItems;
        items = updatedItems;
        notifyDataSetChanged();
}

答案 9 :(得分:0)

尝试将private List<Feed> mFeed;更改为private ArrayList<Feed> mFeed;

然后在您的构造函数中将mFeed = feeds更改为

public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {
    mFeed.addAll(feeds);
    ...

然后您可以像这样正常添加:

public void  addFeed(Feed feed) {
    mFeed.add(feed);
    notifyItemInserted(mFeed.size());
}

因此将List更改为ArrayList解决了我的问题。我认为是因为List是一个抽象类,而addremoveclear等是方法,实际上并未在那里实现。因此,当您定义一个列表(private ArrayList<Feed> mFeed;)并用另一个列表(mFeed = feeds;来初始化它时,调用mFeed.add(feed)不会执行任何操作,并导致recyclerView引发异常。

答案 10 :(得分:0)

对我来说,问题是在实施增量搜索时数据集发生更改时我没有发布notifyDatasetChanged。

我有一个根据用户在搜索小部件中搜索的内容进行过滤的列表。对于列表中的每个项目,我都在发出远程请求,当我返回结果时,我正在更新该特定单元格。

为了使回收者视图正常工作,我必须同时进行通知

过滤原始数据集,然后发布数据集更改

this.searchResultTable?.post {
    this.searchResultTable?.adapter?.notifyDataSetChanged()
}

收到回复后,再次发布通知

this.searchResultTable?.post {
    this.searchResultTable?.adapter?.notifyItemChanged(index, updateDataHashMap)
}

您必须发布更新,而不是直接发送通知消息,以防止在布局布局之前更新出现时回收器视图崩溃。

另一个重要的陷阱是,当您在远程响应后发布各个更新时,必须确保用户当前看到的列表是发送请求时存在的列表。

答案 11 :(得分:0)

问题出在这行代码中:

 mFeed = feeds;

您正在将mFeed分配给调用者的实例feeds,因此只要调用者对其变量进行更改(可能是添加,清除或删除项目),本地mFeed就会更改

尝试更改为

mFeed.addAll(feeds);

别忘了将mFeed初始化为符合您需求的列表mFeed = new ArrayList<>();

答案 12 :(得分:0)

  

将此行与设置recyclerView一起放入。问题已由解决   将RecyclerView的ItemAnimator设置为null。

in kotlin
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.itemAnimator = null

在Java中

recyclerView.setItemAnimator(null);