110

I'm using a Snackbar from the com.android.support:design:22.2.0 library. I'm using it to undo deletions. To make my life easier, I'm going to make the UI look like things are actually deleted from the data source, and if the undo button in the snack bar is not pressed, actually perform the deletions from the data source. So, I want to know when the Snackbar is no longer visible, so it's safe to delete the items.

I can call getView() on the Snackbar, but I'm not sure what listener I should be using. I tried setOnSystemUiVisibilityChangeListener() but that didn't work, I believe it is only for the system status bar.

Additionally, Snackbar can not be extended, as it has a private constructor.

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Tyler Pfaff
  • 4,900
  • 9
  • 47
  • 62

13 Answers13

218

Google design library supports Snackbar callbacks in version 23. See Snackbar docs and Callback docs. You will then get notified when the Snackbar gets dismissed (and also when shown) and also the type of dismissal if this is useful for you:

snackbar.addCallback(new Snackbar.Callback() {

    @Override
    public void onDismissed(Snackbar snackbar, int event) {
      //see Snackbar.Callback docs for event details
      ...  
    }

    @Override
    public void onShown(Snackbar snackbar) {
       ...
    }
  });
Donal Rafferty
  • 19,707
  • 39
  • 114
  • 191
and_dev
  • 3,723
  • 1
  • 20
  • 28
58
snackbar.addCallback(new Snackbar.Callback() {

    @Override
    public void onDismissed(Snackbar snackbar, int event) {
        if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT) {
            // Snackbar closed on its own
        }
    }

    @Override
    public void onShown(Snackbar snackbar) {
        ...
    }
});
Saurabh Thorat
  • 18,131
  • 5
  • 53
  • 70
Ananth
  • 1,065
  • 1
  • 12
  • 20
  • 5
    nice mentioning the event, because the onDismissed is also called when the actionText is clicked – Amr El Aswar Nov 12 '16 at 17:11
  • 4
    For most cases it's probably best to check `if (event != Snackbar.Callback.DISMISS_EVENT_ACTION)` instead otherwise your dismiss logic won't run if the user manually dismisses or a snackbar is dismissed by another consecutive snackbar. – shyamal Oct 29 '18 at 16:42
  • 1
    Thanks for this line `event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT` – Pratik Butani Mar 05 '19 at 11:39
31

Snackbar.addCallback in kotlin

val snackBar = Snackbar
                .make(view, "Text Snackbar", Snackbar.LENGTH_LONG)
                .addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
                    override fun onShown(transientBottomBar: Snackbar?) {
                        super.onShown(transientBottomBar)
                    }

                    override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
                        super.onDismissed(transientBottomBar, event)
                    }
                })

        val snackBarView = snackBar.view
        snackBarView.setBackgroundColor(Color.RED)
        snackBar.show()
Sergey Milakov
  • 321
  • 3
  • 3
17

Recently I stumbled upon this matter myself when for scrolling and showing Snackback, too many were shown before the first even disappeared. I had to find a way to know if the app should have laid down the Snackbar.

I personally found this solution.

It is true Snackbar itself does not offer any kind of listener for it's state/visibility, but you can still get the View Object out of Snackbar ( getView(); ). From the View Object you have the chance to use a wide variety of methods to add listeners.

To implement it you have to go out of the common "all-in-one-line" Toast/Snackbar usage, because adding listeners returns void .

I personally found OnAttachStateChangeListener to fulfill my needs.

Dropping a snipper with my code in case it might turn useful for you.

Snackbar snack = Snackbar.make(getView(), "My Placeholder Text", Snackbar.LENGTH_LONG);

snack.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
    @Override
        public void onViewAttachedToWindow(View v) {
            canDisplaySnackbar = false;
        }

    @Override
    public void onViewDetachedFromWindow(View v) {

        Handler h = new Handler();
        h.postDelayed(new Runnable() {
            @Override
            public void run() {
                canDisplaySnackbar = true;

                }
        }, 1000);
    }
});
snack.show();

Please note that this is just my implementation for my own problem, Handler with a postDelayed Runnable might not even fit your case. It was just to give a general idea of the implementation I suggested using a snippet I already own.

FrancescoC
  • 1,058
  • 10
  • 19
13

To be notified when a snackbar has been shown or dismissed, you can provide a Snackbar.Callback via setCallback(Callback).

fab
  • 790
  • 6
  • 10
7

In Kotlin, this worked for me:

Snackbar.make(
    binding.root,
    getString(R.string.ticket_posted),
    LENGTH_LONG)
    .setAction(getString(R.string.undo)) {
        Log.d(TAG, "Try to undo ticket $key")
    }
    .addCallback(object : BaseCallback<Snackbar>() {
        override fun onDismissed(
            transientBottomBar: Snackbar?,
            event: Int
        ) {
            super.onDismissed(transientBottomBar, event)
            if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT) {
                Log.d(TAG, "Try to reset")
            }
        }
    })
    .show()
}
Charles Libicki
  • 211
  • 3
  • 6
6

onDismissed is also called when the action Text is clicked for that reason need to put one condition like

event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT

And now new code is look like below.

final Snackbar snackBar = Snackbar.make(findViewById(R.id.root_layout), result, Snackbar.LENGTH_LONG);

snackbar.addCallback(new Snackbar.Callback() {

@Override
public void onDismissed(Snackbar snackbar, int event) {
 if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT) {
        // Snackbar closed on its own
    }  
}

@Override
public void onShown(Snackbar snackbar) {
...
}
});
snackBar.show();
Vishal G. Gohel
  • 1,008
  • 1
  • 16
  • 31
  • Code only answers arent encouraged as they dont provide much information for future readers please provide some explanation to what you have written – WhatsThePoint Feb 01 '18 at 11:03
  • 1
    For most cases it's probably best to check `if (event != Snackbar.Callback.DISMISS_EVENT_ACTION)` instead otherwise your dismiss logic won't run if the user manually dismisses or a snackbar is dismissed by another consecutive snackbar. – shyamal Oct 29 '18 at 16:41
3

There is currently no way to get notification when the Snackbar has finished displaying.

In this thread a workaround is discussed based on a timer for the duration of the Snackbar display. Snackbar in Support Library doesn't include OnDismissListener()?

One issue to consider with this workaround is it is possible that the Snackbar duration is restarted. The material design specification for Snackbar says this will happen if an unrelated dialog or popup is displayed.

Community
  • 1
  • 1
BrentM
  • 5,671
  • 3
  • 31
  • 38
2

Thanks to @SergeyMilakov in Kotlin it is:

@SuppressLint("WrongConstant") // Suppress an error when set duration.
private fun showSnackbar() {
    val DURATION = 5000

    Snackbar.make(view!!, "Remove item?"), DURATION).apply {
        setAction("Undo") {
            // Your action when a button is clicked.
        }
        addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
            /* override fun onShown(transientBottomBar: Snackbar?) {
                super.onShown(transientBottomBar)
                Timber.d("*** onShown")
            }*/

            override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
                super.onDismissed(transientBottomBar, event)
                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {
                    // Your action when the Snackbar is closed and the button was not clicked.
                }

            }
        })
        view.setBackgroundColor(ContextCompat.getColor(context, R.color.black))
        setActionTextColor(ContextCompat.getColor(context, R.color.yellow))
        show()
    }
}

Note that Snackbar shows, even if you move to other screens (for instance, back). So, don't forget to check whether you make actions on the right screen.

Also Snackbar doesn't restore after screen rotation.

CoolMind
  • 26,736
  • 15
  • 188
  • 224
1

Currently you can't achieve it.

There isn't a listener called when the snackbar is dimissed.

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.

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
1
    snackbar.addCallback(new Snackbar.Callback() {
        public void onShown(Snackbar snackbar) {
           //  on show  
        }
 public void onDismissed(Snackbar snackbar, int event) {
          //  on dismiss  
        }
      });
dileep krishnan
  • 326
  • 4
  • 7
1

I have exactly the same problem as yours. My solution is...

              final Snackbar povrati_obrisani_unos = Snackbar.make (coordinatorLayout, "Ponisti brisanje", Snackbar.LENGTH_INDEFINITE)
                    .addCallback (new Snackbar.Callback (){
                        @Override
                        public void onDismissed(Snackbar transientBottomBar, int event) {
                            super.onDismissed (transientBottomBar, event);
                            if(event==DISMISS_EVENT_SWIPE){//here we detect if snackbar is swipped away and not cliked (which is undo in my case)
                                Uri uriDelete = Uri.parse (obrisano.getImageuri ());
                                ContentResolver contentResolver = getContentResolver();
                                contentResolver.delete (uriDelete, null, null);
                                Toast.makeText (MainActivity.this,
                                        "Ocitavanje i slika su trajno obrisani", Toast.LENGTH_SHORT).show ();
                            }

That is all, do not want to copy-paste snackbar.action which is undo. Want to be clear as I possibly can here.

Regards Nenad

Nenad Štrbić
  • 367
  • 4
  • 6