Android Toast cancel问题、源码分析和解决方案
本文中部分内容摘自API 部分代码来自android.widget.Toast类源代码
解决方案中 ToastUtil代码 由http://www.linuxidc.com/Linux/2012-01/51925.htm 修改而来(基本没做什么修改 删除了一个方法 添加了一个方法)
Toast介绍:
A toast is a view containing a quick little message for the user. The toast class helps you create and show those.
When the view is shown to the user, appears as a floating view over the application. It will never receive focus. The user will probably be in the middle of typing something else. The idea is to be as unobtrusive as possible, while still showing the user the information you want them to see. Two examples are the volume control, and the brief message saying that your settings have been saved.
The easiest way to use this class is to call one of the static methods that constructs everything you need and returns a new Toast object.
问题:
问题主要出现在通过按钮或事件触发Toast时,若多次点击按钮(或某事件多次触发),会触发一系列Toast,它们会依次缓慢出现在屏幕上,无法消除,令用户看起来很难受。
当然 Toast类中存在消除Toast的方法
public void cancel ()
Since: API Level 1
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.
但是事实证明,API中对cancel类的说明完全是胡扯。
比如我在程序中存放一个ArrayList<Toast>对象用来存放所有的Toast对象,按照API的说法,即使我按了无数次的按钮触发了无数次Toast,但是只要遍历ArrayList 将全部Toast cancel掉不就一切都清净了?
遗憾的事,即使遍历ArrayList 对每一个Toast对象指向cancel方法,只有当前正在显示的Toast会被cancel掉,队列中的Toast的依然会依次展现。
查看下Toast类的源代码:
首先找到 cancel方法及相关调用链:
public void cancel() {
mTN.hide();
// TODO this still needs to cancel the inflight notification if any
}
/**
* schedule handleHide into the right thread
*/
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
final Runnable mHide = new Runnable() {
public void run() {
handleHide();
}
};
public void handleHide() {
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
// note: checking parent() just to make sure the view has
// been added... i have seen cases where we get here when
// the view isn't yet added, so let's try not to crash.
if (mView.getParent() != null) {
if (localLOGV) Log.v(
TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
mView = null;
}
}
看到了么,源代码中显示,执行cancel方法 只会把正在显示的Toast移除,还未被显示的Toast,根本就通不过上面的if条件判断。
API中所说的 Close the view if it's showing, or don't show it if it isn't showing yet.
只有前半段是对的,后半段纯属胡扯。
解决方案:
由于Toast中控制显示 隐藏的部分private class TN extends ITransientNotification.Stub 是个私有内部类,所以继承Toast修改代码是不成了,也听说有高手用反射动态修改这部分的,不知解决情况究竟如何。
这里这个解决办法很简单,既然加入Toast队列的Toast我无法控制,我自己在外部实现一个Toast队列,自己控制它就是了。
简单的实现:全局只有1个Toast对象 由此类中的静态方法控制显示。
每次显示时,cancel掉原来的Toast对象。即,Toast队列中,只有0或1个Toast。每次触发Toast动作,都会立即清除上一个Toast.
package XXXXXXXXXXXXXXXXXXXXXX;
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 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();
}
public static void cancelCurrentToast() {
if (toast != null) {
toast.cancel();
}
}
}
--------------------------
2月28日追加:
实测 在2.2 2.3中此方法工作良好。
在4.0系统中效果极差
多次触发Toast 无法正常显示
相关推荐
小米mimu系统,会对toast进行拦截,在用户提交的toast 消息体拼接一个前缀,由于该种会导致插件工程出现资源错乱,获取appLabel异常,现我们通过hook 动态代理,对消息发送做一个劫持,修改消息信息,还原原来的消息
Android Toast使用解析附代码,实现类似WEB开发中的弹出层效果,当用户点击一个链接或操作按钮时,会弹出一个操作层(浮动层)或浮动菜单,进而执行下一步操作。本源码是使用Android中的Toast对象来模拟实现网页中的...
android 解决Toast重复显示问题!
原因分析 ...问题解决 既然系统不允许我们调用Toast,那么我们就自立门户——自己写一个Toast出来。我们总体的思路是:在Activity的布局中添加View实现Toast的效果。 Toast背景shape定义 我们知道
Android Toast 自定义背景、图片 随心使用
Android Toast 工具类
主要为大家详细介绍了android自定义Toast设定显示时间,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
android Toast大全(五种情形)建立属于你自己的Toast
全局Toast工具类,作用:连续显示toast提示时取消上一个toast 通过ToastUtil.toast(Context(), "文本")调用
Android Toast各种使用方法及DEMO
自定义Toast,解决系统Toast存在的问题
React Native Toast 一个用于android和ios类似于android toast提示控件,android使用原生toast, ios使用UIView Toast
Android应用源码之自定义彩色Toast.zip
一个简单的自定义Toast资源,您可以根据自己的需要更改我的代码即可实现您想要的效果(主要是更改xml文件的布局),这是androidstudio的项目,但是不妨碍在eclipse中使用,可以直接在eclipse中新建android项目,然后将...
自定义彩色Toast源码,是一个很不错的Android源码,有兴趣的伙伴们抽时间可以看一下把。
Android Toast 大全 (五种形式) 建立属于自己的 Toast
android Toast对象的使用 自定义Toast
c# winform 类似android toast消息功能 超级强大,多个项目在用,可插拔,封装好了,拿去就可以用了
当点击某个控件会弹出Toast时,如果直接使用Toast.makeText()的方式...解决这个问题很简单,Toast.makeText()会不断的实例Toast这个对象,我们只要在Toast不为空的情况下不再去实例就行了。这里是一个简单的ToastDemo。