Toast——多次点击只显示一次解决方案

本文介绍了解决Android中Toast在不同版本系统(特别是4.0版本)下显示异常的问题,提供了三种解决方案,包括适用于2.3版本以下的方案、摒弃系统Toast的方法以及推荐的一种通用方案。
 


        Android中的Toast是非常好用的一个信息提示控件,但是系统自带的Toast多次点击之后,会出现多次,如显示时间是2S,点击5次之后就会显示5X2=10S。

        针对以上问题,Android为我们提供了一个静态方法cancel(),但是此方法Android4.0版本有所区别,参考:http://www.eyeandroid.com/forum.php?mod=viewthread&tid=1894&page=1

原文如下:

在修复RUI桌面在4.0系统下的提示信息不完善的Bug过程的一些思路与大家分享一下。

Bug描述:

RUI在2.2的系统点击推荐图标下载后,就会进入下载队列中下载,如果再次点击相同的图标就会使用Toast提示“**已经在下载队列中”。但是在4.0的系统就会出现异常,第二次点击相同的推荐图标时没有出现Toast提示。

相关源码:

 

  1. publicstaticvoid showMessage(final Context act,finalint gravity,final String msg) { 
  2.         Toast.makeText(act, msg, Toast.LENGTH_SHORT).show(); 
  3.         new Thread(new Runnable() { 
  4.             public void run() { 
  5.                 handler.post(new Runnable() { 
  6.                     @Override 
  7.                     public void run() { 
  8.                         Log.v(TAG,"showMessage Runnable ThreadID: " + Thread.currentThread()); 
  9.                         synchronized (synLock) { 
  10.                             Log.v(TAG,"showMessage toast text: " + msg); 
  11.                             if (toast != null) { 
  12.                                 Log.v(TAG,"tosat != null"); 
  13.                                // 修复4.0中推荐下载信息不完善 
  14. //                              toast.cancel(); 
  15.                                 toast.setText(msg); 
  16.                                 toast.setDuration(Toast.LENGTH_SHORT); 
  17.                             }else
  18.                                 toast = Toast.makeText(act, msg, Toast.LENGTH_SHORT); 
  19.                             } 
  20. //                          toast.setGravity(gravity, 0, 0); 
  21.                             try
  22.                                 toast.show(); 
  23.                             }catch(Exception e) { 
  24.                                 Log.d(TAG,"Exception " + e.getMessage()); 
  25.                             } 
  26.                             Log.v(TAG,"toast: " + toast); 
  27.                         } 
  28.                     } 
  29.                 }); 
  30.             } 
  31.         }).start(); 
  32.     } 


分析Bug可能产生的原因:

1.      怀疑是context出现问题的原因。

因为Toast.makeText(Contextcontext, CharSequencetext, int duration)中有如下解释:

context The context to use. Usually yourandroid.app.Applicationorandroid.app.Activityobject.

而源码中传入的context是从NetworkService中传入的,所以怀疑这可能是出现Bug的原因。

我将context从Launcher中传入,Toast依然没有出现;直接Toast.makeText(act, msg, Toast.LENGTH_SHORT).show();结果出现Toast提示信息,所以不是context的原因。

2.      怀疑是互斥锁和handler.post()中的原因。

在代码中加入Log信息,从Log打印出来的信息可以得出已经运行到了互斥域内的代码。Log打印出来的运行线程是在主线程中,而且在run的代码中加入Toast.makeText(act, msg, Toast.LENGTH_SHORT),有Toast显示。所以排除了互斥锁和Handler.post()的原因。

经过上述分析后觉得很奇怪,就向其它人请教,让我慢慢调试。

后来看到cancel()的解释如下:

Closethe view if it's showing, or don't show it if it isn't showing yet. You do notnormally have to call this. Normally view will disappear on its own after theappropriate duration.

就怀疑问题出在这里。将代码toast.cancel()注释掉后运行,发现Toast提示信息出现,跟他人汇报后,提示“可以看看4.0中Toast的源码”。

下面是2.2和4.0中Toast源码的链接:

http://www.oschina.net/code/explore/android-2.2-froyo/android/widget/Toast.java

http://www.oschina.net/code/explore/android-4.0.1/core/java/android/widget/Toast.java

比较两者关于cancel()处理的差异发现了如下差异:

在4.0中如下:

final Runnable mHide = new Runnable() {

           public void run() {

                handleHide();

                // Don't do this in handleHide()because it is also invoked by handleShow()

                mNextView = null;

           }

       };

而2.2中没有mNextView = null;这一行代码。

再查看show()中有如下源码:

if (mNextView == null) {

           throw new RuntimeException("setView must have been called");

       }

所以判断产生的Bug的原因是2.2系统和4.0系统对cancel的处理的差异造成的,将cancel注释就可以正常显示了。

解决方案一(只适合2.3版本以下):


import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;

public class ToastUtil {
	private static Handler handler = new Handler(Looper.getMainLooper());

	private static Toast toast = null;

	private static Object synObj = new Object();

	public static void showMessage(final Context act, final String msg) {
		showMessage(act, msg, Toast.LENGTH_SHORT);
	}

	public static void showMessage(final Context act, final int msg) {
		showMessage(act, msg, Toast.LENGTH_SHORT);
	}

	public static void showMessage(final Context act, final String msg,
			final int len) {
		new Thread(new Runnable() {
			public void run() {
				handler.post(new Runnable() {
					@Override
					public void run() {
						synchronized (synObj) {
							if (toast != null) {
								toast.cancel();
								toast.setText(msg);
								toast.setDuration(len);
							} else {
								toast = Toast.makeText(act, msg, len);
							}
							toast.show();
						}
					}
				});
			}
		}).start();
	}

	public static void showMessage(final Context act, final int msg,
			final int len) {
		new Thread(new Runnable() {
			public void run() {
				handler.post(new Runnable() {
					@Override
					public void run() {
						synchronized (synObj) {
							if (toast != null) {
								toast.cancel();
								toast.setText(msg);
								toast.setDuration(len);
							} else {
								toast = Toast.makeText(act, msg, len);
							}
							toast.show();
						}
					}
				});
			}
		}).start();
	}

}


 

解决方案二(摒弃系统Toast):

import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

public class ToastUtil2 {

	public static TextView tv;

	public static WindowManager mWindowManager = null;

	public static void showMessage(final Context act, final String msg) {
		mWindowManager = (WindowManager)act.getSystemService(Context.WINDOW_SERVICE);
		showMessage(act, msg, Toast.LENGTH_SHORT);

	}

	public static void showMessage(final Context act, final int msg) {

		showMessage(act, act.getString(msg), Toast.LENGTH_SHORT);

	}

	public static void showMessage(Context context, String msg, int Length){

		if (tv == null){
			tv = new TextView(context);
			tv.setText(msg);
			tv.setTextSize(20);
//			tv.setBackgroundResource(R.drawable.bg_layoutinput_focused);
			tv.setPadding(0, 0, 0, 30);
		}else{
			tv.setText(msg);
			tv.setTextSize(20);
//			tv.setBackgroundResource(R.drawable.bg_layoutinput_focused);
			tv.setPadding(0, 0, 0, 30);
		}
		
		if (tv.getParent() == null){
			WindowManager.LayoutParams params = new WindowManager.LayoutParams();
			params.gravity = Gravity.BOTTOM;
			params.alpha = 0.65f;
			params.x = 0;
			params.height = WindowManager.LayoutParams.WRAP_CONTENT;
			params.width = WindowManager.LayoutParams.WRAP_CONTENT;
			params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

			| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE

			| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON

			| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

			params.format = PixelFormat.TRANSLUCENT;
			params.windowAnimations = 0;
			mWindowManager.addView(tv, params);
			handler.sendEmptyMessageDelayed(101, 1000);
		}else{
			tv.setText(msg);
			handler.removeMessages(101);
			handler.sendEmptyMessageDelayed(101, 1000);
		}

	}

	static Handler handler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);

			if (tv.getParent() != null)
				mWindowManager.removeView(tv);

		}

	};

	public static void cancelCurrentToast(){

		if (tv != null && tv.getParent() != null){

			mWindowManager.removeView(tv);

		}

	}

}


 解决方案三(推荐):

public class ToastUtil3 {

	private static String oldMsg;
	protected static Toast toast   = null;
	private static long oneTime=0;
	private static long twoTime=0;
	
	public static void showToast(Context context, String s){	
		if(toast==null){ 
			toast =Toast.makeText(context, s, Toast.LENGTH_SHORT);
			toast.show();
			oneTime=System.currentTimeMillis();
		}else{
			twoTime=System.currentTimeMillis();
			if(s.equals(oldMsg)){
				if(twoTime-oneTime>Toast.LENGTH_SHORT){
					toast.show();
				}
			}else{
				oldMsg = s;
				toast.setText(s);
				toast.show();
			}		
		}
		oneTime=twoTime;
	}
	
	
	public static void showToast(Context context, int resId){	
		showToast(context, context.getString(resId));
	}

}


 


 

### 如何在 Android 中对 Toast 进行非空判断 在 Android 开发中,`Toast` 是一种用于显示短暂消息的方式。然而,由于 `Toast` 的设计特性,在某些情况下可能会遇到重复创建或覆盖的情况。为了防止这种情况发生并确保 `Toast` 不为空或者不会多次弹出相同的消息,可以通过以下方式进行处理。 #### 方法一:通过全局变量管理 `Toast` 为了避免多个 `Toast` 同时存在,可以在应用中维护一个全局的 `Toast` 对象,并在其每次展示前先取消之前的实例再重新设置新内容。 以下是具体实现: ```java public class MainActivity extends AppCompatActivity { private Toast toast; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); button.setOnClickListener(v -> showCustomToast("This is a test message")); } public void showCustomToast(String message) { if (toast != null) { toast.cancel(); // 取消当前存在的 Toast 实例 } if (message != null && !message.isEmpty()) { // 非空判断 toast = Toast.makeText(this, message, Toast.LENGTH_SHORT); toast.show(); } else { Log.e("MainActivity", "Message cannot be empty"); } } } ``` 上述代码实现了对 `Toast` 的非空验证以及避免重复弹窗的功能[^4]。 #### 方法二:封装自定义工具类 如果项目中有大量地方需要用到类似的逻辑,则可以考虑将其抽象成一个通用方法供其他模块调用。 下面是基于静态方法的一个简单例子: ```java public class ToastUtils { private static Toast currentToast; /** * 显示带有非空校验功能的 Toast 提示框。 * * @param context 上下文对象 * @param text 待显示的文字信息 */ public static void showToast(Context context, String text) { if (context == null || TextUtils.isEmpty(text)) { return; } if (currentToast != null) { currentToast.cancel(); // 如果已有正在运行中的 Toast 则立即停止它 } currentToast = Toast.makeText(context.getApplicationContext(), text, Toast.LENGTH_SHORT); currentToast.show(); } } ``` 此版本不仅包含了基本的安全性检测还提供了更灵活的应用场景支持[^5]。 --- ### 总结 无论是采用直接操作还是借助辅助函数的形式都可以有效解决因缺乏必要检查而导致用户体验下降的问题。以上两种方案均能很好地满足需求——即只允许有效的字符串作为输入参数传入至最终呈现给用户的界面组件之中去执行相应的行为动作序列化过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值