I've been attempting to implement drag and drop features into my notes app using ItemTouchHelper however I am struggling to figure out how to get it to work with cursors. I am currently retrieving stored note data from the database using a cursor loader and a content provider, and the adapter receives it's data from the returned cursor as shown.
public void onBindViewHolder(RecyclerViewAdapter.MyViewHolder holder, int position) {
mCursor.moveToPosition(position);
String title = mCursor.getString(mCursor.getColumnIndex(NotesContract.COL_TITLE));
String body = mCursor.getString(mCursor.getColumnIndex(NotesContract.COL_BODY));
holder.titleText.setText(title);
holder.bodyText.setText(body);
}
The problem I am having is that I don't know how I would keep track of the position of the moved items in the list when I drag and drop them. I can't change the order of the rows in the cursor. I have only seen examples where the data source for the adapter is a List. I have considered copying my cursor data into a list of Note objects, so that I could rearrange them to reflect the changes made by dragging and dropping, but this seems to defeat all you gain by using cursors and cursor loader. Also when cursor loader finishes loading it will return a new cursor with all the results in the original order again and populate the adapter accordingly, so any changes made to the order if my new List would be lost.
Does anyone know how to do this or am I going about this entirely the wrong way using cursors and a loader? Any help would be much appreciated. Thanks!
I have done this by adding a column named sort_order in the table to keep track of the sort order. When you drag and drop you update the sort_order in the onMove method in ItemTouchHelper.SimpleCallback.
A good tutorial how to implement drag and drop and swipe to dismiss you can find here
What exactly do you need that cursor for? If you have any specific question just add a comment. I worked a lot with this features!
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 have a ListFragment which displays a list of items obtained using a CursorLoader and a CursorAdapter as per the standard way of handling views of lists. This in itself is working fine,however I'm unsure as to how to use the same pattern to display the detailed view of a selected item.
A previous StackOverflow question discussed this but neither of the solutions suggested seem to me to be optimal. Using a ListView with a single item seems like overkill and wasteful of resources, whereas carrying out the work directly in the activity seems to be overloading the activity with data access and UI functionality. It also loses the ability to keep data in sync between the activity and the underlying storage.
Is there some middle ground that retains the split between loader, activity and adapter but for a single item rather than a list?
You can do without the Adapter and the ListView.
The CursorLoader returns you a Cursor. You can access the cursor without passing it to an adapter.
Just make sure the Cursor contains at least your one row.
if (cursor != null && cursor.moveToFirst()) {
// do whatever, ie String myString=cursor.getString(0)
// in case you fetch a string as the first element of your projection
}
I'm very surprised there is such a small amount of info on drag and drop sorting with a cursor adapter and list adapter.
The closest post I have found on stackoverflow is this one:
https://stackoverflow.com/a/5047618/317889
But, it's not clear to me how to implement what CommonsWare suggests - clarification would be very helpful.
So far I am binding the cursor data to a list adapter and setting this as follows:
mMyCursorAdapter = new MyCursorAdapter(getActivity(), null);
setListAdapter(mMyCursorAdapter);
getLoaderManager().initLoader(0, null, this);
The list is generated but I now wish to add drag and drop functionality to the list items.
I would like to know the best way to go about this from an architectural point of view and any pointers as to how to go about the development of the core functionality would also be useful.
This blog post by Jason McReynolds (including a sample project) helped me a whole lot.
It explains how to use Carl A. Bauer's Library Drag-Sort-ListView with a CursorAdapter and SqLite. It shows how to save the the ListView's newly ordered state in the database as well.
This can definitely be achieved and lucky for you most of the work has already been taken care of, but you will need to modify a class slightly to meet your specifications.
The default Android music app has all of the classes you'll need.
First, you'll need to grab their custom ListView that allows for dragging and dropping.
That can be found here - TouchInterceptor.java.
You'll also need to grab their custom Cursor that's used to actually move the items in your ListView. It's an inner class called NowPlayingCursor.
That can be found here - TrackBrowserActivity.java
NowPlayingCursor extends AbstractCursor and its used to return the queue. The method makeNowPlayingCursor() is specifcally where you'll write most of your own code. Instead of returning the queue, you'll need to return the items you interested in moving, whatever they may be.
In order to use the TouchInterceptor, you'll need to implement TouchInterceptor.DropListener.
private TouchInterceptor.DropListener mDropListener =
new TouchInterceptor.DropListener() {
public void drop(int from, int to) {
final NowPlayingCursor mNowPlayingCursor = (NowPlayingCursor) YOUR_CURSOR;
mNowPlayingCursor.moveItem(from, to);
// Call `notifyDataSetChanged` here.
}
};
You should also look their moveQueueItem method used to move an item from one index to another. This method is used in the NowPlayingCursor when onMove and moveItem are called.
That can be found here - MediaPlaybackService.java
So, there's some work to be done on your part, but this definitely possible.
Here is a library that hopefully will solve your problem, it enables drag and drop reordering of list items.
Has an excellent demo that includes use cases for Fragments and Cursors
https://github.com/bauerca/drag-sort-listview
Key features:
Clean drag and drop (no visual glitches; I hope!).
Intuitive and smooth scrolling while dragging.
Support for heterogeneous item heights.
Public startDrag() and stopDrag() methods.
Public interface for customizing the floating View.