30

I've been using a common "myToast" which I use "myToast.cancel() prior to issuing a new toast. For Android v2.3 and older, this works great. When a new toast needs to be sent, the old one, if still on-screen, is canceled (and disappears immediately) to be replaced with the new toast. This avoids stacking up a bunch of toasts if the user presses a key multiple times that needs the alert (and other conditions). My actual case is one toast appears when a wrong key is pressed, and another appears if the Clear key is not pressed.

For Android 4.0 and 4.1, issuing a myToast.cancel() before the next toast kills both the current and the next toast. The current cancel() API does indicate it cancels the current AND the next toast (which seems rather stupid). Why cancel a toast you want to put up?

Any ideas on making cancel work consistently across Android versions (and the way it works in v2.3 and older)?

I'll try some inelegant dual toast system with tracking for which toast is in use, but it seems such a pain work around this bad behavior in 4.x to get what works perfectly and logically in older Android versions.


Ok, I solved it, but it's not nearly as clean as I would have liked. I implemented a dual toast approach, where it alternates between two toasts. First we define the toasts for the activity prior to the OnCreate:

Toast toast0;
Toast toast1;
private static boolean lastToast0 = true;

In the OnCreate:

toast0 = new Toast(getApplicationContext());
toast0.cancel();
toast1 = new Toast(getApplicationContext());
toast1.cancel();

And finally, when I need to display the toast and cancel the prior toast at the same time I use something similar to:

if (lastToast0) {
    toast0.cancel();
    toast1.setDuration(Toast.LENGTH_LONG);
    toast1.setText("new message");
    toast1.show();
    lastToast0 = false;
} else {
    toast1.cancel();
    toast0.setDuration(Toast.LENGTH_LONG);
    toast0.setText("new message");
    toast0.show();
    lastToast0 = true;
}

If you need to just cancel an existing toast (before it times out) use:

toast0.cancel();
toast1.cancel();

Tested on Nexus 7 (4.1), Emulator 4.0, and several devices with Android 2.2, 2.3.

Dinith Rukshan Kumara
  • 638
  • 2
  • 10
  • 19
Frank
  • 605
  • 1
  • 8
  • 14
  • How are you cancelling the old toast? Before or after making the new one? – TheZ Oct 16 '12 at 20:00
  • Can you post your code where you're doing the toasting? – Khantahr Oct 16 '12 at 20:07
  • I was cancelling it before the new one - that's why it's so strange that Android 4.x hides the new one (but the API for toast cancel sort of explains that will happen). I edited in a solution I created that works. Just not very nice looking. – Frank Oct 17 '12 at 17:11
  • Your solution is indeed not clean, but it seems to work just fine. And @nandeesh solution below does not work in my Android 11 (activity crash after a `Toast already killed` warning in the logcat). – Luis A. Florit Apr 20 '21 at 15:35

9 Answers9

52

Instead of calling cancel(). Try resetting the text and call show(). This should cancel the last toast by itself

myToast.setText("wrong key")
myToast.show();

If you keep using the same myToast instead of creating one every time I guess they won't stack up.

Neeraj Sewani
  • 3,952
  • 6
  • 38
  • 55
nandeesh
  • 24,740
  • 6
  • 69
  • 79
3

Did nandeesh's solution not work for you? His solution would be cleaner than using two different toasts.

For example, (expanding on his/her answer) prior to onCreate we'd declare the toast:

private Toast myToast;

and in onCreate we'd have to initialize it using makeToast (otherwise we'd get an error):

myToast = Toast.makeText(getApplicationContext(), null, Toast.LENGTH_SHORT);

and whenever we want a toast to be shown we'd simply call:

myToast.setText("some text");
myToast.show();

and this would replace the previous toast properly.

TSL
  • 161
  • 1
  • 9
  • I'm not sure where you read that Nandeesh suggest using two toasts, he explicitly said `If you keep using the same myToast instead of creating one everytime i guess they wont stack up.` At that point, I see no difference between your answer and his. Also, I would suggest that you write your answers without comparing yours to other people answer. That sounds rude to me, especially when you are saying things about their answer that are wrong. – ForceMagic Jan 02 '14 at 15:42
  • Where did I say nandeesh was using two toasts? Additionally, my answer was simply to expand upon his and give a longer example. – TSL Jan 02 '14 at 19:26
  • Sorry, this still confuse me `Did nandeesh's solution not work for you? His solution would be cleaner than using two different toasts.` I'm not sure why you are pointing out his answer and then post a similar one, I mean the part that was missing from his answer doesn't make it "not working" IMO. – ForceMagic Jan 02 '14 at 20:43
  • 1
    This should answer should probably be a comment instead – Jacob R Aug 28 '17 at 19:05
  • This sometimes forces activity crash in my Android 11 after a `Toast already killed` warning in the `logcat`. – Luis A. Florit Apr 20 '21 at 15:12
3

Here is my answer copied from another similar question here:

The Boast class accomplishes exactly what you need.


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.

If you just want to know how to cancel the notifications when exiting your app, you will find lots of help in there.


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();
    }

}
Community
  • 1
  • 1
Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
  • I love your solution, and it is working fine on 4.x but do you have any idea why it is not working on 2.x? (I haven't tried 3.x) – Naphtali Gilead May 24 '13 at 19:23
  • Sorry no idea. It is production code and I thought I'd tested it for backwards compatibility. Will repost if o find out anything in the future. – Richard Le Mesurier May 24 '13 at 21:49
3

Make a java class as ShowToast.java like below

    public class ShowToast {

        private static Toast toast;

        public static void show(Context mcontext, String text) {
            if (toast != null) 
               toast.cancel();
            toast = Toast.makeText(mcontext, text, Toast.LENGTH_SHORT);
            toast.show();
        }
    }

Then call it as

    ShowToast.show(getApplicationContext(),"YOUR_TOAST_TEXT");
Avirup Ghosh
  • 171
  • 1
  • 5
2

cancel() doesn't do anything I'm afraid.

I would suggest using Crouton https://github.com/keyboardsurfer/Crouton

Joe Simpson
  • 2,546
  • 4
  • 30
  • 46
  • using toast.cancel() prior to a new toast partly works - the existing toast if present disappears. What changed is in Android 4.x (maybe 3.x) it also hides the next toast that I want to replace the first with! – Frank Oct 17 '12 at 16:46
1

This my solution works perfect both for 4.* and 2.3 Android versions

static Toast toast;
.....

if (toast != null)
    toast.cancel();

boolean condition = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
if ((toast == null && condition) || !condition)
    toast = Toast.makeText(context, text, Toast.LENGTH_LONG);
if ((toast != null && condition))
    toast.setText(text);
toast.show();
Alex Halevin
  • 49
  • 1
  • 3
1

Create an Toast Object:

Toast toastobject=null;

Now use the below code to display the toast. This will work find for me

    int index = clickCounter-1;


    if(toastobject!= null)
            {
                toastobject.cancel();
            }
            toastobject = Toast.makeText(this,"Toast Text" , Toast.LENGTH_SHORT);
            listItems.remove(index);
            toastobject.show();
Vignesh KM
  • 1,979
  • 1
  • 18
  • 24
0

create new function and call this.

ImageButton ABtn = (ImageButton) findViewById(R.id.Btn);
ABtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{       
SETToast("mytext");
}
});

    private Toast toast = null;

public void SETToast( String text)
{
  if(toast==null)
  {
     toast = Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT); 
     toast.show();
     final Handler handler = new Handler(); 
     handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            toast=null;
        }
     }, 2000);
  }
  else
  {
      toast.setText(text);
  }   
}
Ehsan Jelodar
  • 1,544
  • 19
  • 25
0

Kotlin approach:

class MyActivity: Activity {        
     private var toast: Toast? = null

     fun yourFunction() {
       toast?.cancel()

       toast = if(documentWasSaved) {
            makeText(this, "Document was saved"), Toast.LENGTH_LONG)
       } else {
            makeText(this, "Failed to save your document", Toast.LENGTH_LONG)
       }

       toast?.show()
    }
 }
Edhar Khimich
  • 1,468
  • 1
  • 17
  • 20