I have a RecyclerView with a LinearLinearManager. My method addItems()
downloads data from the Internet and then adds 10 items one after the other like this:
itemIds.add(id);
listAdapter.notifyItemInserted(itemIds.size() - 1);
I call this method when the activity is started 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 start the application and scroll to position 7, new data is loaded. But after these items have been added (new item size is 20, I checked that), 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 old position 7 for the new position 27 – but that position doesn't exist, of course.
For debugging, I added a custom class extending the LinearLayoutManager that prevents crashing because of 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 perfectly – new items are smoothly added to the list, even multiple times, so it results in an infinite list. There's only this little stack trace in the Logcat.
It doesn't feel right to me just ignoring this IndexOutOfBoundsException using a try/catch that actually does nothing on catch. Does anyone have an idea on how to fix that weird position 27?
Edit: After testing different item sizes, I discovered that the Recyclerview always wants to load the item at this position:
<position when reload request was started> + 2 * <reloaded item size>
Which is of course always out of the total list size.