45

I have several SeekBar and onSeekBarProgressStop(), I want to show a Toast message.

But if on SeekBar I perform the action rapidly then UI thread somehow blocks and Toast message waits till UI thread is free.

Now my concern is to avoid the new Toast message if the Toast message is already displaying. Or is their any condition by which we check that UI thread is currently free then I'll show the Toast message.

I tried it in both way, by using runOnUIThread() and also creating new Handler.

Sufian
  • 6,405
  • 16
  • 66
  • 120
Deepak Goel
  • 5,624
  • 6
  • 39
  • 53

11 Answers11

65

I've tried a variety of things to do this. At first I tried using the cancel(), which had no effect for me (see also this answer).

With setDuration(n) I wasn't coming to anywhere either. It turned out by logging getDuration() that it carries a value of 0 (if makeText()'s parameter was Toast.LENGTH_SHORT) or 1 (if makeText()'s parameter was Toast.LENGTH_LONG).

Finally I tried to check if the toast's view isShown(). Of course it isn't if no toast is shown, but even more, it returns a fatal error in this case. So I needed to try and catch the error. Now, isShown() returns true if a toast is displayed. Utilizing isShown() I came up with the method:

    /**
     * <strong>public void showAToast (String st)</strong></br>
     * this little method displays a toast on the screen.</br>
     * it checks if a toast is currently visible</br>
     * if so </br>
     * ... it "sets" the new text</br>
     * else</br>
     * ... it "makes" the new text</br>
     * and "shows" either or  
     * @param st the string to be toasted
     */

    public void showAToast (String st){ //"Toast toast" is declared in the class
        try{ toast.getView().isShown();     // true if visible
            toast.setText(st);
        } catch (Exception e) {         // invisible if exception
            toast = Toast.makeText(theContext, st, toastDuration);
            }
        toast.show();  //finally display it
    }
Community
  • 1
  • 1
Addi
  • 1,099
  • 1
  • 12
  • 17
  • 18
    Good answer. One remark: The reason isShown() "raises an exception" is that toast.getView() initially returns null. Simply test for null as opposed to using try...catch. – Paul-Jan Feb 22 '13 at 13:50
  • You may consider doing a straight check rather than try/catch as this is a expected and common situation. – ooolala Oct 14 '14 at 04:54
  • 1
    See my answer (http://stackoverflow.com/a/29573861/2457744) for how to do it without to try/catch. – J Wang Apr 11 '15 at 03:44
  • When i am using Toast.LENGTH_SHORT it`s not working, after using Toast.LENGTH_LONG working fine. – varotariya vajsi Jan 28 '19 at 11:06
  • 1
    Toast.getView() always returns null on Android 11+ (https://stackoverflow.com/questions/62884286/toast-getview-returns-null-on-android-11-api-30), so this solution is obsolete – Tad Oct 13 '21 at 11:59
46

The following is an alternative solution to the most popular answer, without the try/catch.

public void showAToast (String message){
        if (mToast != null) {
            mToast.cancel();
        }
        mToast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
        mToast.show();
}
Community
  • 1
  • 1
J Wang
  • 2,075
  • 1
  • 20
  • 26
4

A clean solution that works out of the box. Define this on your Activity:

private Toast toast;

/**
 * Use this to prevent multiple Toasts from spamming the UI for a long time.
 */
public void showToast(CharSequence text, int duration)
{
    if (toast == null)
        toast = Toast.makeText(this, text, duration);
    else
        toast.setText(text);
    toast.show();
}

public void showToast(int resId, int duration)
{
    showToast(getResources().getText(resId), duration);
}
wvdz
  • 16,251
  • 4
  • 53
  • 90
3

My solution is:

public class Utils {
    public static Toast showToast(Context context, Toast toast, String str) {
        if (toast != null)
            toast.cancel();
        Toast t = Toast.makeText(context, str, Toast.LENGTH_SHORT);
        t.show();
        return t;
    }
}

and the caller should have a Toast member for this method's parameter, or

class EasyToast {
    Toast toast;
    Context context;

    public EasyToast(Context context) {
        this.context = context;
    }

    public Toast show(String str) {
        if (toast != null)
            toast.cancel();
        Toast t = Toast.makeText(context, str, Toast.LENGTH_SHORT);
        t.show();
        return t;
    }

}

have a helper class like this.

fung
  • 441
  • 5
  • 5
  • I came to the same solution. If isShown() doesn't work as expected then just don't use it :) and cancel() the toast if it's not null. – ka3ak Jul 01 '20 at 09:10
2

keep track of the last time you showed the toast, and make re-showing it a no-op if it falls within some interval.

public class RepeatSafeToast {

    private static final int DURATION = 4000;

    private static final Map<Object, Long> lastShown = new HashMap<Object, Long>();

    private static boolean isRecent(Object obj) {
        Long last = lastShown.get(obj);
        if (last == null) {
            return false;
        }
        long now = System.currentTimeMillis();
        if (last + DURATION < now) {
            return false;
        }
        return true;
    }

    public static synchronized void show(Context context, int resId) {
        if (isRecent(resId)) {
            return;
        }
        Toast.makeText(context, resId, Toast.LENGTH_LONG).show();
        lastShown.put(resId, System.currentTimeMillis());
    }

    public static synchronized void show(Context context, String msg) {
        if (isRecent(msg)) {
            return;
        }
        Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
        lastShown.put(msg, System.currentTimeMillis());
    }
}

and then,

RepeatSafeToast.show(this, "Hello, toast.");
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown

this isn't perfect, since the length of LENGTH_SHORT and LENGTH_LONG are undefined, but it works well in practice. it has the advantage over other solutions that you don't need to hold on to the Toast object and the call syntax remains terse.

Jeffrey Blattman
  • 22,176
  • 9
  • 79
  • 134
  • Sweet, exactly what I was looking for. Works great. – Alin Jan 05 '15 at 21:48
  • Maybe good idea, but not implemented perfectly. You are putting objects in HashMap, but not removing them. – xmen May 02 '15 at 03:33
  • since the key is the string that's being toasted, the size of the map is limited by the number of strings being toasted. your point is valid only if you are toasting dynamic strings ("foo bar" + i). if you need better, replace HashMap with LruCache. – Jeffrey Blattman May 06 '15 at 23:37
2

The enhanced function from above thread, which will show toast only if not visible with same text message:

 public void showSingleToast(){
        try{
            if(!toast.getView().isShown()) {    
                toast.show();
            }
        } catch (Exception exception) {
            exception.printStackTrace();       
            Log.d(TAG,"Toast Exception is "+exception.getLocalizedMessage());
            toast = Toast.makeText(this.getActivity(),   getContext().getString(R.string.no_search_result_fou`enter code here`nd), Toast.LENGTH_SHORT);
            toast.show();
        }

    }
Sagar Jethva
  • 986
  • 12
  • 26
Sandeep Kharat
  • 500
  • 1
  • 4
  • 12
1

A combined solution

For my case, I needed to cancel the current toast if it shown and display another one.

This was to solve the scenario when the user asks for a service while it is still loading or not available I need to show a toast (might me different if the requested service is different). Otherwise, the toasts will keep showing in order and it will take a very long time to hide them automatically.

So basically I save the instance of the toast am creating and the following code is how to cancel it safly

synchronized public void cancel() {
    if(toast == null) {
        Log.d(TAG, "cancel: toast is null (occurs first time only)" );
        return;
    }
    final View view = toast.getView();
    if(view == null){
        Log.d(TAG, "cancel: view is null");
        return;
    }
    if (view.isShown()) {
        toast.cancel();
    }else{
        Log.d(TAG, "cancel: view is already dismissed");
    }
}

And to use it I can now not worry about cancelling as in:

if (toastSingleton != null ) {
    toastSingleton.cancel();
    toastSingleton.showToast(messageText);
}else{
    Log.e(TAG, "setMessageText: toastSingleton is null");
}

The showToast is up to you how to implement it as I needed a custom look for my toast.

hannunehg
  • 318
  • 2
  • 12
  • 1
    True logic and true check for null view before check if it is shown. And multiple calls for showing a toast is a long weird experience especially if the user is already out of the app. Must cancel current then invoke a new one as you mentioned. Thanks. – Eftekhari Mar 22 '17 at 23:52
0

Good for stopping stacking e.g. click driven toast. Based off @Addi's answer.

public Toast toast = null;
//....
public void favsDisplay(MenuItem item)
{
    if(toast == null) // first time around
    {
        Context context = getApplicationContext();
        CharSequence text = "Some text...";
        int duration = Toast.LENGTH_SHORT;
        toast = Toast.makeText(context, text, duration);
    }
    try
    {
        if(toast.getView().isShown() == false) // if false not showing anymore, then show it
            toast.show();
    }
    catch (Exception e)
    {}
}
0

Check for showing toast message on screen either it is displayed or not. For Showing a toast message Make a separate class. And use the method of this class which display the toast message after checking the visibility of the toast message. Use This Snippet of code:

public class AppToast {

private static Toast toast;

public static void showToast(Context context, String message) {
    try {
        if (!toast.getView().isShown()) {
            toast=Toast.makeText(context, message, Toast.LENGTH_SHORT);
            toast.show();
        }
    } catch (Exception ex) {
        toast=Toast.makeText(context,message,Toast.LENGTH_SHORT);
        toast.show();
    }
}

}

I hope this solution will help you.

Thanks

Aman Goyal
  • 409
  • 5
  • 8
0

added timer to remove the toast after 2 seconds.

private Toast toast;

public void showToast(String text){
        try {
            toast.getView().isShown();
            toast.setText(text);
        }catch (Exception e){
            toast = Toast.makeText(mContext, text, Toast.LENGTH_SHORT);
        }
        if(toast.getView().isShown()){
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    toast.cancel();
                }
            }, 2000);
        }else{
            toast.show();
        }
    }

showToast("Please wait");
0

Solution for Android 11+ (API level 30 and above)

Toast.getView() is deprecated since API level 30:

This method was deprecated in API level 30. Custom toast views are deprecated. Apps can create a standard text toast with the makeText(android.content.Context, java.lang.CharSequence, int) method, or use a Snackbar when in the foreground. Starting from Android Build.VERSION_CODES#R, apps targeting API level Build.VERSION_CODES#R or higher that are in the background will not have custom toast views displayed.

If you want to avoid Toast overlapping, you could save the time the last Toast was shown using System.currentTimeMillis().

Here's a an example of use case where Toast is instantly overlapped only if the text of the new one is different from the last one, otherwise, it waits a certain amount of time before overlapping it (i.e SAME_TOAST_DURATION_BEFORE_OVERLAP):

public class SingleToast {
    private static Toast _toast;
    private static String _text;
    private static long _lastToast;
    private static final int SAME_TOAST_DURATION_BEFORE_OVERLAP = 2000; // in ms

    public static void show(Context context, String text, int duration) {
        if (_toast == null) {
            _toast = Toast.makeText(context.getApplicationContext(), text, duration);
            _text = text;
        } else {
            if (_text.equals(text)) {
                if (System.currentTimeMillis() - _lastToast > SAME_TOAST_DURATION_BEFORE_OVERLAP) {
                    _toast.cancel();
                } else {
                    return;
                }
            } else {
                _text = text;
                _toast.cancel();
                _toast.setText(_text);
            }
        }
        _lastToast = System.currentTimeMillis();
        _toast.show();
    }
}
Torpedo
  • 69
  • 6