在添加项目后,Recyclerview会将索引排除在列表大小之外

时间:2017-04-21 15:29:23

标签: android scroll android-recyclerview indexoutofboundsexception

我有一个带有LinearLinearManager的RecyclerView。我的方法addItems()从互联网上下载数据,然后依次添加10个项目:

itemIds.add(id);
listAdapter.notifyItemInserted(itemIds.size() - 1);

我在活动开始时以及当用户滚动到(size-3)时调用此方法:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
        if (!pending && !loadingMoreItems &&
            listAdapter.getItemCount() - layoutManager.findLastVisibleItemPosition() <= 3) {
            loadingMoreItems = true;
            Log.d(LOG_TAG, "new data!");
            addItems();
        }
    }
});

当我启动应用程序并滚动到位置7时,将加载新数据。但是在添加这些项目后(新项目大小为20,我检查了),我收到以下错误:

java.lang.IndexOutOfBoundsException:
Inconsistency detected. Invalid view holder adapter position
ViewHolder{9cff95d position=27 id=-1, oldPos=7, pLpos:5
scrap [attachedScrap] tmpDetached no parent}

因此,RecyclerView希望将旧位置7的ViewHolder重新用于新位置27 - 但当然,该位置不存在。

为了进行调试,我添加了一个扩展LinearLayoutManager的自定义类,它可以防止由于IndexOutOfBoundsException而崩溃:

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    try {
        super.onLayoutChildren(recycler, state);
    } catch (IndexOutOfBoundsException e) {
        e.printStackTrace();
    }
}

然后我的列表完美运行 - 新项目可以顺利添加到列表中,甚至多次,因此它会产生无限列表。 Logcat中只有这个小堆栈跟踪。

我觉得只使用try / catch忽略这个IndexOutOfBoundsException是不对的,它实际上对catch没有任何作用。有没有人知道如何解决这个奇怪的位置27?

编辑:在测试了不同的商品尺寸后,我发现Recyclerview总是希望在此位置加载商品:

<position when reload request was started> + 2 * <reloaded item size>

当然,这总是超出总列表大小。

3 个答案:

答案 0 :(得分:2)

这种情况正在发生,因为您正在向列表中添加数据,同时您在通知适配器之前尝试滚动Recyclerview。

所以当您在第7个位置后加载数据时执行此操作

mData.add(yourdata);
mAdapter.notifyDataSetChanged();

如果您将继续添加数据并继续滚动它。它会给你&#34;检测到不一致。&#34;错误。因为适配器认为只有7个项目,当你滚动它已经变成了10个没有通知adaper。

答案 1 :(得分:1)

您似乎必须使用此方法listAdapter.notifyItemRangeInserted(startPosition, itemCount);

notifyItemInserted(position)用于仅插入一个项目。如果要插入多个项目,则必须使用notifyItemRangeInserted(position, itemCount)

您是否会使用此方法插入所有10个项目,而不是逐个添加循环并检查是否获得相同的异常?

答案 2 :(得分:0)

除了Dinesh Bob所说的,我也不认为你的代码有任何问题。我将发布如何在RecyclerView中实现分页:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            int visibleItemCount=linearLayoutManager.getChildCount();
            int totalItemCount=linearLayoutManager.getItemCount();
            int firstVisibleItemPosition=linearLayoutManager.findFirstVisibleItemPosition();

            if(!isPageLoading && !isLastPage)
            {
                if((visibleItemCount + firstVisibleItemPosition)>=totalItemCount)
                {
                    offset+=limit;
                    loadMoreItems(offset);
                }
            }
        }
    });

loadMoreItems继续进行API调用,然后将项添加到适配器,如下所示:

public void addAllItems(List<Item> itemList)
{
    for(Item itemRow: itemList)
    {
        this.itemList.add(itemRow);
        notifyItemInserted(this.itemList.size() - 1);
    }
}

由于其中的错误,在使用ListView时实现分页时需要捕获异常的块。但是Recyclerview并不需要它。

还会给你的代码一个机会,看看它到底出了什么问题。