Recyclerview binds index to list size after adding items

I have a RecyclerView with LinearLinearManager. My method addItems()

downloads data from the internet and then adds 10 items one by one like this:

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

      

I call this method when the activity is triggered and when the user scrolls to (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();
        }
    }
});

      

When I launch the app and scroll to position 7, new data is loaded. But after these elements have been added (new element size is 20, I checked this), I get the following error:

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}

      

So the RecyclerView wants to reuse the ViewHolder of the old position 7 for the new position 27, but that position doesn't exist of course.

For debugging purposes, I added a custom class that extends LinearLayoutManager that prevents a crash due to an IndexOutOfBoundsException:

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

      

And then my list works great - new items are smoothly added to the list, even multiple times, so it results in an infinite list. There's just this little stack trace in Logcat.

It doesn't feel like I'm just ignoring this IndexOutOfBoundsException using try / catch, which doesn't actually do anything for the catch. Does anyone have any idea how to fix this weird position 27?

Edit: After testing different sizes of items, I found that the Recyclerview always wants to load the item at that position:

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

This is, of course, always out of the overall size of the list.

+3


source to share


3 answers


This is because you are adding data to the list and at the same time you are trying to scroll the recyclerview before the adapter receives a notification.

so when you load data after 7th position do this



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

      

if you keep adding data and keep scrolling through it. it will give you "mismatch detected". mistake. Since the adapter thinks thare is only 7 items and when you scroll it becomes 10 which is not notified of it.

+1


source


It looks like you should use this method listAdapter.notifyItemRangeInserted(startPosition, itemCount);

notifyItemInserted(position)

used to insert only one item. If you want to insert more than one element, you must usenotifyItemRangeInserted(position, itemCount)



Are you using this method to insert all 10 elements instead of adding one at a time in a loop and checking if you are getting the same exception?

0


source


Other than what Dinesh Bob said, I don't think there is anything wrong with your code. I'll just post how I implement pagination in the 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 goes ahead and makes an API call and then adds items to the adapter as shown below:

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

      

The block to throw the exception was needed when implementing pagination when using the ListView due to a bug in it. But recyclerview doesn't need this.

It will also give your code a chance to see exactly what is wrong with it.

0


source







All Articles