bauerca / drag-sort-listview

Android ListView with drag and drop reordering.
3.2k stars 1.44k forks source link

Original state after end of drag #44

Open tprochazka opened 12 years ago

tprochazka commented 12 years ago

Current version do one very bad think. After i put off dragged item to new place it restore original stare of ListView, so moved item is on the original place. It is visible only if store&update operation initialized from DropListener take more time (db operation)

It is possible place display dragged item directly to the new position? So it will not change after refresh. On iPhone also great that when I drop off item it slide to target place with animation.

bauerca commented 11 years ago

You should implement your DropListener such that immediately subsequent calls to getView(position, convert, parent) in your adapter return the correct, reordered items. Also, a call to notifyDataSetChanged() should be invoked in the DropListener.

If using a CursorAdapter, this means you need to create a mapping from cursor position to listview position that gets updated in the DropListener. You can then update the db in the background or something, and when it's finished redo your mapping.

tprochazka commented 11 years ago

Its too much complicated, it should work automatically. Some older version don't do that, it keep empty gap in the list which was not so disruptive.

bauerca commented 11 years ago

Unfortunately, I have to disagree. Reordering should be done in the Adapter (or model, in MVC terms). I do have plans to make reordering easier by providing DragSortCursorAdapters, etc. So you can stay tuned for that. Until then, you'll have to get your hands dirty! :)

tprochazka commented 11 years ago

Yes. I agree, that reordering must be complete in the adapter (DAO in my case and then propagated to the adapter), but it can take some time and is asynchronous (for example 200ms). But DragSortView should not restore original state at this time. You can leave empty space at the drop target (as before). I think that this is much better that handle change twice.

bauerca commented 11 years ago

First of all, let me say that performing a time-consuming db operation on every drag-sort drop is probably not a good design strategy (considering that there exists a way to make it responsive; e.g. by remapping your cursor positions to reordered listview positions, then handling the db changes asyncly or later). I expect your users would rather have a more responsive interaction. I understand the hesitation resulting from the inconvenience; and who knows, maybe you have other reasons that require the immediate db update, in which case you can tell me to can it :). But, okay, that is not the ultimate point here.

Here is my concern. It is subtle. Let's say I go with your suggestion. A user does a drag and drop. Upon drop, the list state freezes with a blank spot at the drop position. It is important to mention here that the blank spot represents a lot of DSLV internal state which relies on the presence of the floating View, which in this case, is now gone. Okay, with some work, that can be managed. Now what? How does DSLV know when to remove that blank spot state in favor of the reordered normal state? How does it distinguish your reorder update from any other adapter update (I'm not saying your app will do such other updates, but this has to be considered for others). This implies another callback mechanism and adds complication to the API. This is no good.

tprochazka commented 11 years ago

I do this operate async and it don't take long time. I perform only two UPDATE SQL. It is very fast, but DSLV meantime for very short time refresh layout to it's original state and only small part after that it is refreshed again, now it good state. So you telling me that I should do swap operation twice. Directly in the onDrop method (it mean in the UI thread) in the adapter. And how to swap data in loaded Cursor? I think that it mean copy Cursor data to another structure (collection) and work with it. It require twice time memory and I lost possibility to use simple CursorAdapter.

Here is my concern. It is subtle. Let's say I go with your suggestion. A user does a drag and drop. Upon drop, the list state freezes with a blank spot at the drop position. It is important to mention here that the blank spot represents a lot of DSLV internal state which relies on the presence of the floating View, which in this case, is now gone. Okay, with some work, that can be managed. Now what? How does DSLV know when to remove that blank spot state in favor of the reordered normal state? How does it distinguish your reorder update from any other adapter update (I'm not saying your app will do such other updates, but this has to be considered for others). This implies another callback mechanism and adds complication to the API. This is no good.

I don't understand where is problem. Any notifyDatasedChanged() will remove empty gap and render normal ListView layout with already reordered items. This empty gap will exist only from onDrop() to notifyDatasedChanged().

bauerca commented 11 years ago

I do this operate async and it don't take long time. I perform only two UPDATE SQL. It is very fast, but DSLV meantime for very short time refresh layout to it's original state and only small part after that it is refreshed again, now it good state. So you telling me that I should do swap operation twice. Directly in the onDrop method (it mean in the UI thread) in the adapter. And how to swap data in loaded Cursor? I think that it mean copy Cursor data to another structure (collection) and work with it. It require twice time memory and I lost possibility to use simple CursorAdapter.

It doesn't have to be that complicated. Take a look at Issue #20, the solution is a simple subclass of CursorAdapter.

I don't understand where is problem. Any notifyDatasedChanged() will remove empty gap and render normal ListView layout with already reordered items. This empty gap will exist only from onDrop() to notifyDatasedChanged().

What if notifyDataSetChanged() is not called? See issue #40. What if someone wants to cancel a drag and notify is not called? What then? For the majority of DSLV users I think there is no problem because they always call notifyDataSetChanged() from within their DropListener.drop() method. I think your approach may have unforeseen issues, though. What if a user wants to start another drag between onDrop and notifyDataSetChanged (200ms is plenty of time for that)? How will you define that behavior in DSLV if the blank spot is to remain? I imagine there are other issues.

I like the following definition of drag-sort termination:

When the drag-sort ends and the floating View is dropped into a new position, DSLV immediately returns to ListView functionality (until another drag-sort operation is initiated). That is, it displays the data given to it by the current ListAdapter. This means your implementation of ListAdapter should propagate drag-sort reorders as soon as the item is dropped. This allows immediate initiation of further drag-sorts.

bauerca commented 11 years ago

@tprochazka Have a look at DragSortCursorAdapter and the associated subclasses. These are in the most recent commit and should take the work out of that 2nd data-handling layer you were mentioning. Hope this helps. Cheers. -Carl

tprochazka commented 11 years ago

A little bit complicated, but very easy to use, only replace cursor class parent. Thank you very much!

But it has one side effect. Now is not possible to use setDropListener, setDragListener or setRemoveListener on the DragSortListView if I use DragSortCursorAdapter, because it is automatically delegated to the adapter in setAdapter() method. I know that I can move all executive code to the Adapter, but I think that should be still possible to setDropListener() for callback outside of adapter. Current behavior is unpredictable. And I think that adapter should be used only to provide data for the View not for update data.