0

i built a RecyclerView Adapter which reads data out of a SQLite database. I have trouble updating the Adapter after the database were edited.

I know there are a few examples which show how to implement a swapCursor method into a RecyclerView Adapter, but they all contain a lot of additional code which i dont really know how to fit in. I feel like there is too little information about this out there.

In the following method i delete a database row depending on its position in the Adapter. When i call adapter.notifyItemRemoved(position) afterwards, it re-adds the item at the bottom. When i close and re-open the Activity, the database is correct. The problem is, that i dont swap the Cursor and i dont know how.

@Override
public void onDeleteClick(int position) {
    SQLiteDatabase database = new ExampleSQLiteHelper(this).getWritableDatabase();
    Cursor cursor = database.rawQuery("select * from " + ExampleContract.ExampleEntry.TABLE_NAME, null);
    cursor.moveToPosition(position);
    database.delete(ExampleContract.ExampleEntry.TABLE_NAME, ExampleContract.ExampleEntry._ID + "=?", new String[] {cursor.getString(cursor.getColumnIndexOrThrow(ExampleContract.ExampleEntry._ID))});
    //Here i have to swap the Cursor and notify the Adapter
}
  1. How do i swap the Cursor properly and close the old one?
  2. What Cursor do i have to provide? The one that queries the database for deletion is not up-to-date. So i would have to create a 2nd Cursor after deleting the row. That seems like a lot of Cursor creating and closing to me.
  3. Is it ok to delete a row depending on its position in the Adapter, like i do in the above example? Since my ViewHolder Class is static, i cant access the Adapter's Cursor in there and can only get the Adapter position into the onClick method.

Edit:

Here is my approach for change the Cursor. It does work, but i dont know if its correct. Can anyone confirm this and are the Cursors closed prperly?

@Override
public void onDeleteClick(int position) {
    SQLiteDatabase database = new ExampleSQLiteHelper(this).getWritableDatabase();
    Cursor cursor = database.rawQuery("select * from " + ExampleContract.ExampleEntry.TABLE_NAME, null);
    cursor.moveToPosition(position);
    database.delete(ExampleContract.ExampleEntry.TABLE_NAME, ExampleContract.ExampleEntry._ID + "=?", new String[] {cursor.getString(cursor.getColumnIndexOrThrow(ExampleContract.ExampleEntry._ID))});
    cursor.close();

    Cursor newCursor = database.rawQuery("select * from " + ExampleContract.ExampleEntry.TABLE_NAME, null);
    mAdapter.changeCursor(newCursor);
    mAdapter.notifyItemRemoved(position);
}

Adapter:

public void changeCursor(Cursor newCursor) {
    Cursor oldCursor = mCursor;
    mCursor = newCursor;
    oldCursor.close();
}
Florian Walther
  • 6,237
  • 5
  • 46
  • 104
  • The delete cursor doesn't return you to the table data... You need a select cursor. You can return it after the deletion is done – OneCricketeer Oct 29 '17 at 15:21

1 Answers1

0

That seems like a lot of Cursor creating and closing to me.

Well, without storing any extra data associated with the item you're deleting in the ViewHolder or adapter, you will need to perform a selection and extract the column. I would like to think adding a WHERE condition would benefit you, but moving to the necessary position is fine, in my opinion.

Since my ViewHolder Class is static, i cant access the Adapter's Cursor in there

I don't think you want the adapter cursor since it's already iterating the rows, but if your click method is defined onto the adapter class, you can get the cursor there. Otherwise adapter.this.cursor might work.

Regarding swapping the cursor, most examples you dismissed have that logic. Checkout , Using the recyclerview with a database

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • You mean i just create a new Cursor inside my ViewHolder? Do i understand that correctly? I didnt think about that. But it makes sense. – Florian Walther Oct 29 '17 at 15:42
  • Not quite. You can extract `ExampleContract.ExampleEntry._ID` while binding the cursor to the View. Then, you won't need a select cursor first, and can delete right away. – OneCricketeer Oct 29 '17 at 15:51
  • Ok i will try that, thank you! Can you also tell me where i have to close the Cursor which i provide to the Adapter? It shows me a warning but no matter where i close it (inside or outside of the Adapter), i get an Exception. – Florian Walther Oct 29 '17 at 16:03
  • Do you also know how to update my Cursor? In other forums im mostly recommended to store my data inside an ArrayList instead of reading it directly out of the database. – Florian Walther Oct 29 '17 at 18:41
  • I don't know what your exception is, so I can't suggest that. You should definitely use a Cursor unless you don't care about the performance penalty for using ArrayLists and needlessly storing all the database records in memory. You update by swapping the Cursor object reference... – OneCricketeer Oct 29 '17 at 18:45
  • The exception is only when closing the Cursor in the wrong place. I already got it to work by just creating a new Cursor after deleting the row, passing it to the Adapter which then just does mCursor = newCursor. Then i call adapter.notifyItemInserted. I just dont know how a proper swapCursor or changeCursor method has to look, because the examples i found online contain a lot of additional code i dont understand. – Florian Walther Oct 29 '17 at 18:47
  • I'm going to add my new approach in a minute, maybe you can look at it. It DOES work, but i dont know if its correct. – Florian Walther Oct 29 '17 at 18:50
  • Seems pretty straightforward in the link I shared. https://gist.github.com/skyfishjy/443b7448f59be978bc59#file-cursorrecyclerviewadapter-java-L92-L95 – OneCricketeer Oct 29 '17 at 18:51
  • I saw this example before but i dont understand it. The swapCursor method is very big and i dont know what 80% in there is for. There must be a way for a beginner to learn how to do that right. At the moment there isnt. – Florian Walther Oct 29 '17 at 18:55
  • That class isn't meant to be copied. You import it, then you **extend** it. https://gist.github.com/skyfishjy/443b7448f59be978bc59#file-mylistcursoradapter-java-L14 If you want to concern yourself with learning what it is actually doing, then you will need to do some extra learning about the [**`DataSetObserver`** class](https://developer.android.com/reference/android/database/DataSetObserver.html). – OneCricketeer Oct 29 '17 at 18:59
  • But i want to build it myself and dont use an Adapter from GitHub. Anyways, thanks for your help so far. – Florian Walther Oct 29 '17 at 18:59
  • Alright, well. Consider this: would you rather develop cool features in your own app, or spend your time learning patterns someone else has implemented again and again? Up to you, but best of luck – OneCricketeer Oct 29 '17 at 19:05
  • Alright, i think im gonna use this Adapter, since there was a prebuilt CursorAdapter for ListViews too. Allow me one more stupid question: How do i use this Gist in my Code? Do i just copy it or is there a way to download and implement it? I only used libraries before and they had dependencies. – Florian Walther Oct 29 '17 at 19:10
  • Btw the example you posted stored the Data also in another Object and reads it from there into the Adapter – Florian Walther Oct 29 '17 at 19:17
  • I never said anything about not mapping `Object <==> Cursor`. Only that ArrayList isn't needed. I would encourage you use a POJO if you needed custom logic within the model more than only extracting columns and displaying them. – OneCricketeer Oct 29 '17 at 19:21
  • But, yes. Copy the class into some `util` or `lib` package that you make yourself. That's really all that any other Gradle dependency does. – OneCricketeer Oct 29 '17 at 19:22
  • And here is a simpler version for you. https://gist.github.com/cbeyls/e5d0950b4f742cb2bbe1c226137d4e3f#file-recyclerviewcursoradapter-java-L29 – OneCricketeer Oct 29 '17 at 19:24