Android - Custom ListView Adapter - Multi Selection remove - Indexoutofbounds - why?

前端 未结 6 493
终归单人心
终归单人心 2021-01-18 11:01

I have a custom Listview using a adapter class to extend ArrayAdapter of a Item class. I have the ability to change between choice modes of NONE,Single and Multi. This all

6条回答
  •  醉话见心
    2021-01-18 11:04

    There appears to be a bug in AbsListView that causes this issue. It can happen with any subclass of AbsListView, including ListView and GridView.

    In single- and multi-choice mode, the ListView responds to a notifyDataSetChanged() call on its adapter by verifying the set of checked items in confirmCheckedPositionsById(). Since the selected item(s) have already been deleted from the dataset at that point, the adapter will throw an exception. Notably, this issue only occurs if the adapter's hasStableIds() method returns true.

    Loosely speaking, this is the relevant path:

    1. You select one or more items in the ListView
    2. The ListView updates its list of selected items
    3. You click the delete button
    4. The item(s) are removed from your dataset
    5. You call notifyDataSetChanged() on your adapter, and it notifies its observers that the dataset has changed. The ListView is one of those observers.
    6. Next time the ListView is redrawn, it sees the adapter's notification and calls handleDataChanged(). At this point, the ListView still thinks that our now-deleted items are selected and in the dataset.
    7. The handleDataChanged() method calls confirmCheckedPositionsById(), which in turn tries to call getItemId() on the adapter using a stale position. If the deleted item happens to be near the end of the list, this position is likely to be out of bounds for the array, and the adapter will throw IndexOutOfBoundsException.

    Two possible workarounds are as follows:

    • Create a new adapter every time the dataset changes, as noted in other answers. This has the unfortunate effect of losing the current scroll position unless it's saved and restored manually.

    • Clear the selected items by calling clearChoices() on the ListView (or GridView) before you call notifyDataSetChanged() on the adapter. The selected items will be deleted anyhow, so losing the current selection state is unlikely to be a problem. This method will preserve the scroll position and should prevent flickering while the list is being updated.

提交回复
热议问题