23

I am using Snackbar in android and I have implemented an action so that user can undo the action (the action is clearing all the items in the listview).Removing and adding the items back to the listview has already been done and working fine.

My issue is that, items are stored in sqlite database and how can I delete the items from tables? (How can I know that the user has not clicked the undo button, so that I can completely remove the data from database).

This is the code inside OnOptionsItemSelcted()

case R.id.action_clear:
        final List<Word> temp = new ArrayList<Word>(data);
        data.clear();
        adapter.notifyDataSetChanged();
        View view = findViewById(R.id.layoutFavWords);
        Snackbar.make(view,"Deleted Saved Selection.", Snackbar.LENGTH_LONG).
        setAction("Undo", new OnClickListener() {

            @Override
            public void onClick(View v) {
                for(Word word:temp)
                    data.add(word);
                adapter.notifyDataSetChanged(); 
            }

        }).show();
        break;

So if the user has not clicked the undo button during the visible period of the snackbar, then I need to permanently delete the data from database.

Any solutions for this?

Mahozad
  • 18,032
  • 13
  • 118
  • 133
chathura
  • 3,362
  • 6
  • 41
  • 68
  • delete them immediately and insert them again if the press on undo ? – Blackbelt Jun 25 '15 at 17:33
  • yes, its another option. But is there any way to achieve what I want to do?? delete only if the user hasn't clicked the undo btn – chathura Jun 25 '15 at 17:34
  • 1
    imo its good enough. You assume that the user knows what he wants but he can change his mind (undo). Or you probably want a `confirm-like` snackbar. Looking at the documentation it doesn't look much more than a Toast – Blackbelt Jun 25 '15 at 17:35
  • I have to do that if there is no way to achieve it like that. Thanks for the reply – chathura Jun 25 '15 at 17:40
  • you would like to have some sort of dismiss event, which is not available, at least in the documentation, – Blackbelt Jun 25 '15 at 17:42
  • 2
    What will happen if user delete data and then kill your app? The records will stay in db, this is not right, delete button should remove records immediately and you need to save temp of this records for UNDO, on UNDO selected you need to insert it in db again and reload list. – Vasil Valchev Mar 21 '16 at 21:01
  • You should change your selected answer to Mahozad's answer since his post explains exactly what you need to do to solve what you wrote in bold. – Xam Feb 18 '21 at 23:11

5 Answers5

32

As far as I know, it is by design. You should:

  • Delete the item as soon as the user taps the delete button;
  • Store it temporarily in a class variable;
  • If the user taps Undo, add the item again to the database.

This approach is safer and more robust; you shouldn't wait for the snackbar to be dismissed, because that action could not even happen. Just think of user force-quitting the app while the snackbar is still on: should the item be deleted or not? It should.

A more trustworthy source is g+ post by Ian Lake (deleted because of G+ deprecation). In the comments you can read:

you want your UI to react immediately (not wait for the snackbar to disappear) - most systems (particularly those that sync to an external server) have the concept of a 'soft delete' where things are marked as deleted. In those cases, an undo action would just be unmarking the record as deleted. This system works even if the user were to leave the app before the snackbar finishes (you can't assume the snackbar will always complete its animation!).

The easiest way to do that is to temporarily save the record elsewhere (even a local variable), then re-insert it if they happen to hit the undo button.

Ewoks
  • 12,285
  • 8
  • 58
  • 67
natario
  • 24,954
  • 17
  • 88
  • 158
  • 5
    In case of force-quit, what if the user wanted to Undo? Better to have the data in case of failure, for the user deleting again is just a tap away, but creating the data again will not be comparatively easy, and in some cases impossible e.g. in the case of the data having some sensor reading. Sure the UI should react immediately, as in my case, I have two collections, one for the items and one for trash. So the item is moved to trash. DB updates are made at a later point in time, and the UI works quiet fast as compared to DB operation on each change. – jasxir Jul 23 '15 at 06:26
  • Flag for deletion, run an extra cleanup task on app startup to clean up any lingering records that might've slipped through the cracks (i.e. didn't get cleaned up when Snackbar was dismissed)..or even reset their deletion flag. That way you also have the ability to, say, provide the User with another opportunity to undo...nothing worse than accidentally deleting something and missing the undo button before the app gets killed/phone dies/screen cracks. – straya Aug 04 '15 at 23:45
11

Android Support library v23 added Snackbar.Callback which you can use to listen if the snackbar was dismissed by user or timeout.

Example borrowed from astinxs post:

Snackbar.make(getView(), "Hi there!", Snackbar.LENGTH_LONG).setCallback( new Snackbar.Callback() {
            @Override
            public void onDismissed(Snackbar snackbar, int event) {
                switch(event) {
                    case Snackbar.Callback.DISMISS_EVENT_ACTION:
                        Toast.makeText(getActivity(), "Clicked the action", Toast.LENGTH_LONG).show();
                        break;
                    case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
                        Toast.makeText(getActivity(), "Time out", Toast.LENGTH_LONG).show();
                        break;
                }
            }

            @Override
            public void onShown(Snackbar snackbar) {
                Toast.makeText(getActivity(), "This is my annoying step-brother", Toast.LENGTH_LONG).show();
            }
        }).setAction("Go away!", new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        }).show();
Community
  • 1
  • 1
Irritator
  • 322
  • 3
  • 11
6

Example:

final java.util.Timer timer = new Timer();
Snackbar snackbar = Snackbar.make(...).setAction("Undo", new OnClickListener() {
        @Override
        public void onClick(View v) {
            timer.cancel();
            for(Word word:temp)
                data.add(word);
            adapter.notifyDataSetChanged(); 
        }
    }).show();
timer.schedule(new TimerTask() {
    public void run() {
        // delete from db
    }
}, snackbar.getDuration());

It may be a good idea to add a little to the snackbar.getDuration() time (100-200ms?) as timers are not very exact in terms of timing and this way they may get called just before the snackbar is about to close, althought the possibility is rather small in this case.

wfranczyk
  • 420
  • 3
  • 12
3

If you don't want to delete the record from database immediately, try this:

// Backup the item for undo
int itemIndex = viewHolder.getAdapterPosition();
Item item = adapter.getItem(itemIndex);

// Delete only from the adapter
adapter.removeItem(itemIndex);

Snackbar.make(getView(), "Item deleted", LENGTH_LONG)
        .addCallback(new BaseCallback<Snackbar>() {
            public void onDismissed(Snackbar transientBottomBar, int event) {
                if (event != DISMISS_EVENT_ACTION) {
                    // Dismiss wasn't because of tapping "UNDO"
                    // so here delete the item from databse
                }
            }
        })
        .setAction("UNDO", v -> adapter.addItem(item, itemIndex))
        .show();
Mahozad
  • 18,032
  • 13
  • 118
  • 133
0

My way is to hv "deleted" column that is boolean, just change the stat to be true if deleted then undo to change back stat to be false, also u maybe want trash controller or scheduler to delete all the false values on every week.

Vampire
  • 109
  • 1
  • 4