10

I have read about this kind of problem here but the answers don't seem to be working.

I show a Toast when user clicks a button. When the user continously clicks the button the toast keeps on being displayed again and again even when the user exits the activity.

The length of the toast is short. Length of the toast cannot be changed as the text is long.

This is what i have tried as of now:

    Toast toast;
    toast=Toast.makeText(getApplicationContext(),"text",Toast.LENGTH_SHORT);
    if(toast.getView().isShown()==false){
         toast.show();
    }

This did not work.

I tried :

    if(toast.getView().isShown()==true){            
        toast.cancel();
    }

in the onStop(). For some reason the cancel method never works.

If i put the .cancel() before i show the app... then there would be another null check for that. But after doing that also it did not work. I can show a dialog box instead of a toast but that would not be a solution.

Is there any way to check whether a toast is being displayed or not?

For reference

Community
  • 1
  • 1
Ciff
  • 596
  • 3
  • 8
  • 22

4 Answers4

17

The trick is to keep track of the last Toast that was shown, and to cancel that one.

What I have done is to create a Toast wrapper, that contains a static reference to the last Toast displayed.

When I need to show a new one, I first cancel the static reference, before showing the new one (and saving it in the static).

Here's full code of the Boast wrapper I made - it mimics enough of the Toast methods for me to use it. By default the Boast will cancel the previous one, so you don't build up a queue of Toasts waiting to be displayed.

This code can be found in my Github gist:

If you just want to know how to cancel the notifications when exiting your app, you will find lots of help in there. If you have improvements or suggestions, please feel free to fork it and get in touch. This is a very old answer, but code has been stable in production on a few apps for some time.

BTW - this should be a direct drop-in replacement for Toast in most use cases.


package mobi.glowworm.lib.ui.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.Nullable;
import android.widget.Toast;

import java.lang.ref.WeakReference;

/**
 * {@link Toast} decorator allowing for easy cancellation of notifications. Use this class if you
 * want subsequent Toast notifications to overwrite current ones. </p>
 * <p/>
 * By default, a current {@link Boast} notification will be cancelled by a subsequent notification.
 * This default behaviour can be changed by calling certain methods like {@link #show(boolean)}.
 */
public class Boast {
    /**
     * Keeps track of certain Boast notifications that may need to be cancelled. This functionality
     * is only offered by some of the methods in this class.
     * <p>
     * Uses a {@link WeakReference} to avoid leaking the activity context used to show the original {@link Toast}.
     */
    @Nullable
    private volatile static WeakReference<Boast> weakBoast = null;

    @Nullable
    private static Boast getGlobalBoast() {
        if (weakBoast == null) {
            return null;
        }

        return weakBoast.get();
    }

    private static void setGlobalBoast(@Nullable Boast globalBoast) {
        Boast.weakBoast = new WeakReference<>(globalBoast);
    }


    // ////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Internal reference to the {@link Toast} object that will be displayed.
     */
    private Toast internalToast;

    // ////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Private constructor creates a new {@link Boast} from a given {@link Toast}.
     *
     * @throws NullPointerException if the parameter is <code>null</code>.
     */
    private Boast(Toast toast) {
        // null check
        if (toast == null) {
            throw new NullPointerException("Boast.Boast(Toast) requires a non-null parameter.");
        }

        internalToast = toast;
    }

    // ////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Make a standard {@link Boast} that just contains a text view.
     *
     * @param context  The context to use. Usually your {@link android.app.Application} or
     *                 {@link android.app.Activity} object.
     * @param text     The text to show. Can be formatted text.
     * @param duration How long to display the message. Either {@link Toast#LENGTH_SHORT} or
     *                 {@link Toast#LENGTH_LONG}
     */
    @SuppressLint("ShowToast")
    public static Boast makeText(Context context, CharSequence text, int duration) {
        return new Boast(Toast.makeText(context, text, duration));
    }

    /**
     * Make a standard {@link Boast} that just contains a text view with the text from a resource.
     *
     * @param context  The context to use. Usually your {@link android.app.Application} or
     *                 {@link android.app.Activity} object.
     * @param resId    The resource id of the string resource to use. Can be formatted text.
     * @param duration How long to display the message. Either {@link Toast#LENGTH_SHORT} or
     *                 {@link Toast#LENGTH_LONG}
     * @throws Resources.NotFoundException if the resource can't be found.
     */
    @SuppressLint("ShowToast")
    public static Boast makeText(Context context, int resId, int duration)
            throws Resources.NotFoundException {
        return new Boast(Toast.makeText(context, resId, duration));
    }

    /**
     * Make a standard {@link Boast} that just contains a text view. Duration defaults to
     * {@link Toast#LENGTH_SHORT}.
     *
     * @param context The context to use. Usually your {@link android.app.Application} or
     *                {@link android.app.Activity} object.
     * @param text    The text to show. Can be formatted text.
     */
    @SuppressLint("ShowToast")
    public static Boast makeText(Context context, CharSequence text) {
        return new Boast(Toast.makeText(context, text, Toast.LENGTH_SHORT));
    }

    /**
     * Make a standard {@link Boast} that just contains a text view with the text from a resource.
     * Duration defaults to {@link Toast#LENGTH_SHORT}.
     *
     * @param context The context to use. Usually your {@link android.app.Application} or
     *                {@link android.app.Activity} object.
     * @param resId   The resource id of the string resource to use. Can be formatted text.
     * @throws Resources.NotFoundException if the resource can't be found.
     */
    @SuppressLint("ShowToast")
    public static Boast makeText(Context context, int resId) throws Resources.NotFoundException {
        return new Boast(Toast.makeText(context, resId, Toast.LENGTH_SHORT));
    }

    // ////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Show a standard {@link Boast} that just contains a text view.
     *
     * @param context  The context to use. Usually your {@link android.app.Application} or
     *                 {@link android.app.Activity} object.
     * @param text     The text to show. Can be formatted text.
     * @param duration How long to display the message. Either {@link Toast#LENGTH_SHORT} or
     *                 {@link Toast#LENGTH_LONG}
     */
    public static void showText(Context context, CharSequence text, int duration) {
        Boast.makeText(context, text, duration).show();
    }

    /**
     * Show a standard {@link Boast} that just contains a text view with the text from a resource.
     *
     * @param context  The context to use. Usually your {@link android.app.Application} or
     *                 {@link android.app.Activity} object.
     * @param resId    The resource id of the string resource to use. Can be formatted text.
     * @param duration How long to display the message. Either {@link Toast#LENGTH_SHORT} or
     *                 {@link Toast#LENGTH_LONG}
     * @throws Resources.NotFoundException if the resource can't be found.
     */
    public static void showText(Context context, int resId, int duration)
            throws Resources.NotFoundException {
        Boast.makeText(context, resId, duration).show();
    }

    /**
     * Show a standard {@link Boast} that just contains a text view. Duration defaults to
     * {@link Toast#LENGTH_SHORT}.
     *
     * @param context The context to use. Usually your {@link android.app.Application} or
     *                {@link android.app.Activity} object.
     * @param text    The text to show. Can be formatted text.
     */
    public static void showText(Context context, CharSequence text) {
        Boast.makeText(context, text, Toast.LENGTH_SHORT).show();
    }

    /**
     * Show a standard {@link Boast} that just contains a text view with the text from a resource.
     * Duration defaults to {@link Toast#LENGTH_SHORT}.
     *
     * @param context The context to use. Usually your {@link android.app.Application} or
     *                {@link android.app.Activity} object.
     * @param resId   The resource id of the string resource to use. Can be formatted text.
     * @throws Resources.NotFoundException if the resource can't be found.
     */
    public static void showText(Context context, int resId) throws Resources.NotFoundException {
        Boast.makeText(context, resId, Toast.LENGTH_SHORT).show();
    }

    // ////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Close the view if it's showing, or don't show it if it isn't showing yet. You do not normally
     * have to call this. Normally view will disappear on its own after the appropriate duration.
     */
    public void cancel() {
        internalToast.cancel();
    }

    /**
     * Show the view for the specified duration. By default, this method cancels any current
     * notification to immediately display the new one. For conventional {@link Toast#show()}
     * queueing behaviour, use method {@link #show(boolean)}.
     *
     * @see #show(boolean)
     */
    public void show() {
        show(true);
    }

    /**
     * Show the view for the specified duration. This method can be used to cancel the current
     * notification, or to queue up notifications.
     *
     * @param cancelCurrent <code>true</code> to cancel any current notification and replace it with this new
     *                      one
     * @see #show()
     */
    public void show(boolean cancelCurrent) {
        // cancel current
        if (cancelCurrent) {
            final Boast cachedGlobalBoast = getGlobalBoast();
            if ((cachedGlobalBoast != null)) {
                cachedGlobalBoast.cancel();
            }
        }

        // save an instance of this current notification
        setGlobalBoast(this);

        internalToast.show();
    }

}
Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
  • thanx.but there must be some other alternative to cancel a toast. If we could just know id the toast is being displayed... – Ciff Apr 19 '13 at 08:38
  • @Ciff There might be - but you are trying to reference a new object, which is why it doesn't work. This code is **one way** to do what you need to do - keep a reference to the `Toast` you are trying to cancel, and call `Toast.cancel()` on it. – Richard Le Mesurier Apr 19 '13 at 09:02
  • @Ciff looking at the Android source code, you could try calling `Toast.getView()` - but you still need a reference to that object i.e. you need to keep track of the last toast you displayed. Looking at the source code might help you a lot in this instance, as it is quite simple code - it also shows that a lot of the toast functionality is handled by the system itself, and cannot be accessed by us. – Richard Le Mesurier Apr 19 '13 at 09:08
  • @Ciff I missed that you already call `Toast.getView()` but you are calling it on the **wrong object**. – Richard Le Mesurier Apr 19 '13 at 09:12
  • Wouldn't it be shorter and/or nicer to subclass `Toast`? – acrespo Jan 16 '14 at 15:39
  • perhaps it would? ymmv – Richard Le Mesurier Jan 16 '14 at 16:32
  • I also firstly ended up with a similar solution. And the previous `Toast` is getting cancelled indeed. But here are a couple of problems: 1) When you try to tap a button really fast it seems the system is not able to cancel `Toast` immediately - they are still being queued up; 2) In my case I used `Toast` with custom view and saving it in a static var creates a memory leak, to solve this I created a base activity with private `Toast` field. – Stan Mots Nov 03 '16 at 15:05
  • 1
    Not working, I have three toast on screen and all shown one after other. However it cancels the previous one but it feels like all toast has been show. – Geek Aug 31 '17 at 12:56
5

Instead of cancelling the toast. change the text. For Example

        Toast t;

        t = Toast.makeText(this, "hi", 3000);
        t.show();

when you need a different toast then use

        t.setText("bye");
        t.show();

And If you want to dismiss the toast simply call t.cancel()

stinepike
  • 54,068
  • 14
  • 92
  • 112
1

You can cancel individual Toasts by calling cancel() on the Toast object. AFAIK, there is no way for you to cancel all outstanding Toasts, though.

Imran Ali
  • 539
  • 4
  • 17
  • Is there any way to check wether a taost is being displayed or not? – Ciff Apr 19 '13 at 06:27
  • This answer has the exact same wording as CommonsWare's answer to this duplicate question: https://stackoverflow.com/questions/2755277/android-hide-all-shown-toast-messages – stevehs17 Sep 29 '17 at 02:18
1

Try keeping the timestamp of the last toast, and don't allow any new toasts until a timeout period has elapsed.

Something like:

private static final long TOAST_TIMEOUT_MS = 2000; // tweak this constant

private static long lastToastTime = 0;

public void onButtonClicked() {
  long now = System.currentTimeMillis();
  if (lastToastTime + TOAST_TIMEOUT_MS < now) {
    Toast.makeText(...).show();
    lastToastTime = now;
  }
}

I wouldn't worry about a single toast sticking around for a second after the user exits the app -- this is a pretty standard behavior.

Jschools
  • 2,698
  • 1
  • 17
  • 18