1

I'm learning Android/Java and I have a ListView attached to a database, and contains a simple_list_item_multiple_choice check box in each row. When the user checks 1 check box and hits a delete button, that row is deleted and the ListView updates accordingly. However, if the user checks multiple rows, my app breaks.

Why does my app break when selecting multiple rows in the ListView to delete from the attached database?

public class ListViewDisplayNumbers extends ListActivity {
public void DeleteNumber() {

    NumbersDataSource datasource = new NumbersDataSource(this);
    datasource.open();

    ListView listView = (ListView) findViewById(android.R.id.numberlist);
    SparseBooleanArray checked = listView.getCheckedItemPositions();

    List<Number> values = datasource.getAllNumbers();
    ArrayAdapter<Number> adapter = new ArrayAdapter<Number>(this, android.R.layout.simple_list_item_multiple_choice, values);

    for (int i = 0; i < checked.size(); i++) {
        int position = checked.keyAt(i);
        if (checked.valueAt(i))
            datasource.deleteNumber(adapter.getItem(position));
            adapter.remove(adapter.getItem(position)); //breaks on the last one of a multiple
    }

    adapter.notifyDataSetChanged();
    setListAdapter(adapter);
    datasource.close();
}

Log cat:

01-25 12:26:06.236: E/AndroidRuntime(7140): FATAL EXCEPTION: main
01-25 12:26:06.236: E/AndroidRuntime(7140): java.lang.IndexOutOfBoundsException: Invalid index 3, size is 2
01-25 12:26:06.236: E/AndroidRuntime(7140):     at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at java.util.ArrayList.get(ArrayList.java:304)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.widget.ArrayAdapter.getItem(ArrayAdapter.java:337)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.example.hw.ListViewDisplayNumbers.deleteNumber(ListViewDisplayNumbers.java:103)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.example.hw.ListViewDisplayNumbers.onOptionsItemSelected(ListViewDisplayNumbers.java:128)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.app.Activity.onMenuItemSelected(Activity.java:2507)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected(PhoneWindow.java:982)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:149)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.android.internal.view.menu.ListMenuPresenter.onItemClick(ListMenuPresenter.java:175)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.widget.AdapterView.performItemClick(AdapterView.java:292)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.widget.AbsListView.performItemClick(AbsListView.java:1393)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.widget.AbsListView$PerformClick.run(AbsListView.java:3022)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.widget.AbsListView$1.run(AbsListView.java:3817)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.os.Handler.handleCallback(Handler.java:605)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.os.Handler.dispatchMessage(Handler.java:92)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.os.Looper.loop(Looper.java:137)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at android.app.ActivityThread.main(ActivityThread.java:4517)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at java.lang.reflect.Method.invoke(Method.java:511)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
01-25 12:26:06.236: E/AndroidRuntime(7140):     at dalvik.system.NativeStart.main(Native Method)
thedeepfield
  • 6,138
  • 25
  • 72
  • 107

1 Answers1

1

Try doing this backwards

for (int i = checked.size()-1; i >=0;i--) {
    int position = checked.keyAt(i);
    if (checked.valueAt(i)){
        datasource.deleteNumber(adapter.getItem(position));
        adapter.remove(adapter.getItem(position)); //breaks on the last one of a multiple
     }
}

Rationale: Had a similar issue and came across this SO question which recommended to do it backwards. It would make sense though, since removing might mess up indexes if you go forwards, while if you go backwards, you shouldn't be affecting anything as you modify the list.

Oh, and I noticed you may have wanted to put both adapter statements in one if block.

Community
  • 1
  • 1
A--C
  • 36,351
  • 10
  • 106
  • 92
  • you sir are a genius! i don't fully understand what is going on. Could you please enlighten me? However now I'm noticing that if i check then unchecked the row, then press the delete button. the row disappears but it does not get deleted from the database. Why does the row disappears from the ListView if I check then uncheck it? – thedeepfield Jan 25 '13 at 20:46
  • @thedeepfield For doing it backwards - It is *probable* that `checked` keeps its size until `notifyDataSetChanged()` so `i` keeps increasing until it becomes out of bounds for the `List` that `adapter` is holding. As for your second issue, odd - I would pin it on the `SparseBooleanArray` & its adapter. Try debug statements everywhere in the method, tracing can be a headache. Trick is to be very careful when using `SparseBooleanArray` – A--C Jan 25 '13 at 21:02