I've got a ListActivity that displays a list of items from a database using a CursorAdapter, which initially contains all items in the table. I also provide an EditText view where the user can enter search text, and as characters are entered, I requery the database using a "LIKE" or "MATCH" where clause to filter the results (IOW, what lots of apps do when searching).
Currently, I do this in an AsyncTask by creating a new Cursor from the query, creating a new instance of my CursorAdapter class, and then calling list.setAdapter from the UI thread when the task completes. This is all working, but is there a more elegant way of effectively requerying the database with a new WHERE clause from withing the existing adapter/cursor and avoiding having to create new object instances each time? Any examples of this technique?
Doug Gordon
GHCS Software
First of all, CursorAdapter has an changeCursor-Method, were you can change the Cursor without changing the adapter itself. When changing the Cursor the corresponding AdapterView will automatically be notified and update itself.
For further abstraction, you might provide a business object that wraps the query and optionally the execution of the query and provide it to both the Adapter and the TextView (or the Activity possessing the TextView). The TextView changes the business object, the object creates a new cursor and tells the Adapter (using the Observer Pattern, e.g. a Listener) that the cursor changed, and the Adapter retrieves the new Cursor and updates itself.
Related
I want to populate RecyclerView using database. As currently there is no inbuilt adapter for populating RecyclerView using database, I have used CursorRecyclerAdapter by Shywim. I have created a sample App to test it and it worked fine. The feature I didn't liked is having an _id column in the resultset and calling swapCursor() on each database operation, mostly insert and delete. This goes same with ListView when using SimpleCursorAdapter. My query is what if I use ArrayList as the dataset instead of directly using the Cursor.
Benefits of doing this(my assumption) :
No more a need of _id column in the resultset.
Can fetch the data from database, put it into ArrayList and close the cursor.
No need of calling swapCursor() on each database operation as I can add/remove specify elements from the ArrayList and call notifyDataSetChanged()
I don't know the exact logic behind swapCursor() and notifyDataSetChanged(). So, can't decide which one is light-weight and efficient.
If someone has experienced this or done this before, please clear my doubts. Any corrections and suggestions are most welcome.
Using array list and custom adapter is better way for this as per my understanding.
See some scenarios below :
1) Cursor will close after each transaction so database will work smoothly.
2) As you can close cursor on operation done so it will never generate cursor not closed exception.
3) You can modify view of each row easily and manage custom adapter as per your choice.
There are many other reasons, but in short custom adapter is better then cursor adapter as per my understanding.
I found a working cursorAdapter for RecyclerView gist. It works similarly as for listView. But I can't understand why there is no default cursor adapter. It is bad practice using cursor adapter and need manually get data from db convert to list of objects and then use it? Or what explanation for this?
But I can't understand why there is no default cursor adapter
Google elected not to create any concrete adapters for specific types of data collections.
You are certainly welcome to use a Cursor as the model data for a RecyclerView.Adapter. Just bear in mind that a Cursor treats the position as internal state. Make sure that your RecyclerView.ViewHolder pulls the data out of the Cursor and uses it, rather than holding onto the Cursor itself and assuming that it will always automatically be pointing to the correct row.
This sample app demonstrates a RecyclerView backed by a Cursor, in this case a Cursor obtained from querying the MediaStore ContentProvider.
I wrote my own CursorAdapter for RecyclerView like following link: https://gist.github.com/skyfishjy/443b7448f59be978bc59
Then I found whenever I change something in database and want to show it in RecyclerView, I need to create a new Cursor by db.query() and use CursorAdpater's changeCursor(). Since query() will scan all rows in database, the RecyclerView will refresh slowly when data amount is big even I insert only one row into database.
Besides, as we all know, RecyclerView provides notifyItemInserted/Removed(position) for developers so that the RecyclerView can refresh partly, which is useful and beneficial to memory/time. However, when I use CursorAdapter, I don't know when and how I can use these methods because changing cursor isn't adding something directly to dataset binding with RecyclerView but refreshing all items in fact.
So are there any better ways to show data from database in RecyclerView and use RecyclerView's improving method to show variety of database?
I can tell you what i've done...
A. Loaded a cursor using Loader.
B. Copied the cursor into arraylist that is attached to the adapter (the cursor isnt attached to the adapter directly), close the cursor. Works well if there isnt a lot of data - if there is a lot rows then i would have load some of it to the arraylist and then when the user would scroll down i would query again and load from the last row of the array.
C. When the user would like to delete or add something i would do the operation on arrayList first (UI thread) notifiyItemChanged and then change the db (Back thread)
Hope i helped.
i'm using Sqliteloader by commnsware , this looks pretty simple because without help of content provider i'm able to use the loader functionality just by using simple SQL querys.
Now, on tapping on a button, i'm filling a form and insert the item to database like this.
ContentValues values = new ContentValues();
values.put(Tbldata.ACCOUNT, getActiveAccount());
values.put(Tbldata.time, dateobj.getTime());
getWritableDatabase().insert('Tbldata', 'ACCOUNT', values);
After this call, i will be calling finish() on my activity and it returns to my listview. Here the listview item is not updated
What is the best way to update listview?, should i call requery like this?
mLoaderManager.initLoader(0, null, this);
is it correct way of doing?,
What is the best way to update listview?
Call insert() on the SQLiteCursorLoader, not on the SQLiteDatabase. Quoting the documentation:
If you use the insert(), update(), delete(), replace(), and
execSQL() methods on SQLiteCursorLoader, the loader
framework will automatically update you to reflect a new
Cursor with the changed data. These methods take
the same parameters as they do on SQLiteDatabase.
Note that I am no longer maintaining SQLiteCursorLoader.
Say I have a List<User>. Now I can wrap this list in an ArrayAdapter.
List<User> users = Users.getAll();
ArrayAdapter<User> = new ArrayAdapter<User>(this, android.R.layout.simple_list_item_1, users);
I then bind the adapter to a listview to display the list of Users.
Users.getAll() uses Sugar ORM to query the database and return a list of users. Items can be added to the user list from the activity that displays the user list. I am wondering how do I keep the listview updated.
Option 1
One way is to manually update the users as a I add to the database and then call adapter.notifyDataSetChanged(). This works, but it doesn't feel right because I am maintaining a "fake" list that represents what is in the database.
Option 2
I am wondering how bad is it if I just clear the items in users, update it with the results of a new database query and then call adapter.notifyDataSetChanged()?
Will all the child views be thrown away and be re-rendered? Or does it call the equals() method to see if the models bound to each child is the same and then update only what is new?
Other Info
Since I am using SugarORM, I don't think I can get access to the Cursor to do something more efficient. However if there is a better way to keep the list synced with SugarORM, I am happy to hear that as well.
In answer to your option 2: No, it doesnt call equals, because the adapter works in conjunction with the widget to re-use the views, it doens't create a new view foreach item in the list, it create a view foreach visible item and as you scroll re-uses view that left the screen.
The best option here is to create your own adapter, creating a class extending BaseAdapter and creating your own logic inside it requerying the database and notifying the change to the listview (or gridview)..
On the other hand doing what you said here:
I am wondering how bad is it if I just clear the items in users, update it with the results of a new database query and then call adapter.notifyDataSetChanged()?
isn't bad either.
Create a DAO class that extends Observable, then have your Adapter implement Observer. Now every time you add or remove a SugarRecord, do through the DAO class and whoever is register as the Observer will get notified through the following method:
#Override
public void update(Observable observable, Object o)
You can more about Observable/Observer pattern here. This is just one of the many examples and tutorials out there.