Search code examples
androidsqlitelistviewandroid-cursoradapter

Cannot edit ListView items while using CursorAdapter


I'm building an app that uses SQLite for persistence and also incorporates a CursorAdapter. I am able to add items into the ListView and the SQLite table and can view those items on app restart. I'm also able to delete items and view the changes once I restart again. I've added an edit function with an onClickListener where once I click on an item, it is supposed to take me to the edit screen where I can edit that specific item. This is what I have:

Mainactivity.java

public class MainActivity extends AppCompatActivity {
    DatabaseHelper databaseHelper;
    private final int REQUEST_CODE = 10;
    ArrayList <String> toDoItems;
    ArrayAdapter<String> adapter;
    SimpleCursorAdapter altAdapter;
    Cursor itemListCursor;
    ListView listItems;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listItems = (ListView) findViewById(R.id.listViewItems);
        toDoItems = new ArrayList<>();
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, toDoItems);
        databaseHelper = new DatabaseHelper(this);
        getItemListAsCursor();
        altAdapter = new SimpleCursorAdapter(
                this,
                android.R.layout.simple_list_item_1,
                itemListCursor,
                new String[]{TO_DO},
                new int[]{android.R.id.text1},
                0);

        listItems.setAdapter(altAdapter);

        listItems.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> adapter, View item, int position, long id) {
                itemDeleteFromDatabase(id);
                getItemListAsCursor();
                altAdapter.swapCursor(itemListCursor);
                return true;
            }
        });

        listItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapter, View item, int pos, long id) {
                Intent intent = new Intent(MainActivity.this, EditItemActivity.class);
                intent.putExtra("itemName", toDoItems.get(pos));
                intent.putExtra("itemPos", id);
                startActivityForResult(intent, REQUEST_CODE);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        itemListCursor.close();
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
            String item = data.getStringExtra("item");
            int itemPosition = Integer.parseInt(data.getStringExtra("itemPos"));
            toDoItems.add(itemPosition, item);
            databaseHelper.insertData(item);
            toDoItems.remove(itemPosition + 1);
            itemDeleteFromDatabase((long) (itemPosition + 1));
            adapter.notifyDataSetChanged();
        }
    }

    public void addItem(View v) {
        EditText newItem = (EditText) findViewById(R.id.itemInputEditText);

        if (newItem.getText().length() == 0) {
            Toast.makeText(this, "You need to enter a to do.", Toast.LENGTH_SHORT).show();
        } else {
            String item = newItem.getText().toString();
            databaseHelper.insertData(item);
            getItemListAsCursor();
            altAdapter.swapCursor(itemListCursor);
            adapter.notifyDataSetChanged();
            newItem.setText("");
        }
    }

    public  void getToDos(){
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
        Cursor cursor = database.rawQuery("select * from student",null);
        if (cursor.moveToFirst()) {
            while (!cursor.isAfterLast()) {
                String name = cursor.getString(cursor.getColumnIndex("todo"));
                adapter.add(name);
                adapter.notifyDataSetChanged();
                cursor.moveToNext();
            }
        }
    }

    public void getItemListAsCursor() {
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
        itemListCursor = database.query(TABLE_NAME,null,null,null,null,null,null);
    }

    public boolean itemDeleteFromDatabase(Long id) {
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
        boolean databaseDelete = database.delete(TABLE_NAME, ID + "=?", new String[]{Long.toString(id)}) > 0;
        return databaseDelete;
    }
}

EditItemActivity.java

public class EditItemActivity extends AppCompatActivity {
    EditText editToDo;
    Long itemPos;
    Button saveChanges;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_item);

        editToDo = (EditText) findViewById(R.id.edit_todo_item);
        saveChanges = (Button)findViewById(R.id.save_changes);

        String itemName = getIntent().getStringExtra("itemName");
        editToDo.setText(itemName);
        itemPos = getIntent().getLongExtra("itemPos", 0);

        saveChanges.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onSubmit();
            }
        });
    }

    public void onSubmit() {
        Intent item = new Intent();
        item.putExtra("itemName", editToDo.getText().toString());
        item.putExtra("itemPos", itemPos);
        setResult(RESULT_OK, item);
        finish();
    }
}

but when I click on an item, I get the infamous indexOutOfBounds exception. This is the stacktrace:

FATAL EXCEPTION: main
                                                                         Process: ca.ozbek.preworktodoapp, PID: 13721
                                                                         java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
                                                                             at java.util.ArrayList.get(ArrayList.java:411)
                                                                             at ca.ozbek.preworktodoapp.MainActivity$2.onItemClick(MainActivity.java:65)

I know that because I am using a CursorAdapter that I should be using id in my intent.putExtra("item", toDoItems.get(pos)); and intent.putExtra("itemPos", String.valueOf(pos)); lines of code instead of pos but I can't do that because id is a long value.


Solution

  • If you create a DBHelper Class life with be very easy to handle all CRUD functions So your list has a position and your record has an ID when you click the btnDelete in what ever Activity you are on use code like this

    // Calls the Method deleteDBRow in DatabaseHelper
    // which acts on the TABLE_INFO to remove a record by getting the record ID
                helper.deleteDBRow(String.valueOf(dbList.get(position).getID()));
                ListActivity.removeListRow(position);
    

    // Code line above calls Method in ListActivity to notify recycler view of changes // NOTICE the List keeps items by position not ID <== READ

    OK now over in your DBHelper Class you implement this method code below

    /* Delete a record from database table named "TABLE_INFO" */
    /* based on the selected records id in Col_ID*/
    public void deleteDBRow(String rowid){
    
        db = this.getWritableDatabase();
        db.delete(TABLE_INFO, Col_ID + " = ?", new String[] { rowid });
        db.close();
        return;
    }