Android(安卓)Toast cancel问题、源码分析和解决方案
AndroidToastcancel问题、源码分析和解决方案
本文中部分内容摘自API 部分代码来自android.widget.Toast类源代码
解决方案中 ToastUtil代码 由http://www.linuxidc.com/Linux/2012-01/51925.htm 修改而来(基本没做什么修改 删除了一个方法 添加了一个方法)
Toast介绍:
Atoastisaviewcontainingaquicklittlemessagefortheuser.Thetoastclasshelpsyoucreateandshowthose.
Whentheviewisshowntotheuser,appearsasafloatingviewovertheapplication.Itwillneverreceivefocus.Theuserwillprobablybeinthemiddleoftypingsomethingelse.Theideaistobeasunobtrusiveaspossible,whilestillshowingtheusertheinformationyouwantthemtosee.Twoexamplesarethevolumecontrol,andthebriefmessagesayingthatyoursettingshavebeensaved.
TheeasiestwaytousethisclassistocalloneofthestaticmethodsthatconstructseverythingyouneedandreturnsanewToastobject.
问题:
问题主要出现在通过按钮或事件触发Toast时,若多次点击按钮(或某事件多次触发),会触发一系列Toast,它们会依次缓慢出现在屏幕上,无法消除,令用户看起来很难受。
当然Toast类中存在消除Toast的方法
publicvoidcancel()
Since:APILevel1
Closetheviewifit'sshowing,ordon'tshowitifitisn'tshowingyet.Youdonotnormallyhavetocallthis.Normallyviewwilldisappearonitsownaftertheappropriateduration.
但是事实证明,API中对cancel类的说明完全是胡扯。
比如我在程序中存放一个ArrayList<Toast>对象用来存放所有的Toast对象,按照API的说法,即使我按了无数次的按钮触发了无数次Toast,但是只要遍历ArrayList将全部Toastcancel掉不就一切都清净了?
遗憾的事,即使遍历ArrayList对每一个Toast对象指向cancel方法,只有当前正在显示的Toast会被cancel掉,队列中的Toast的依然会依次展现。
查看下Toast类的源代码:
首先找到cancel方法及相关调用链:
publicvoidcancel(){
mTN.hide();
//TODOthisstillneedstocanceltheinflightnotificationifany
}
/**
*schedulehandleHideintotherightthread
*/
publicvoidhide(){
if(localLOGV)Log.v(TAG,"HIDE:"+this);
mHandler.post(mHide);
}
finalRunnablemHide=newRunnable(){
publicvoidrun(){
handleHide();
}
};
publicvoidhandleHide(){
if(localLOGV)Log.v(TAG,"HANDLEHIDE:"+this+"mView="+mView);
if(mView!=null){
//note:checkingparent()justtomakesuretheviewhas
//beenadded...ihaveseencaseswherewegetherewhen
//theviewisn'tyetadded,solet'strynottocrash.
if(mView.getParent()!=null){
if(localLOGV)Log.v(
TAG,"REMOVE!"+mView+"in"+this);
mWM.removeView(mView);
}
mView=null;
}
}
看到了么,源代码中显示,执行cancel方法只会把正在显示的Toast移除,还未被显示的Toast,根本就通不过上面的if条件判断。
API中所说的Closetheviewifit'sshowing,ordon'tshowitifitisn'tshowingyet.
只有前半段是对的,后半段纯属胡扯。
解决方案:
由于Toast中控制显示隐藏的部分privateclassTNextendsITransientNotification.Stub是个私有内部类,所以继承Toast修改代码是不成了,也听说有高手用反射动态修改这部分的,不知解决情况究竟如何。
这里这个解决办法很简单,既然加入Toast队列的Toast我无法控制,我自己在外部实现一个Toast队列,自己控制它就是了。
简单的实现:全局只有1个Toast对象由此类中的静态方法控制显示。
每次显示时,cancel掉原来的Toast对象。即,Toast队列中,只有0或1个Toast。每次触发Toast动作,都会立即清除上一个Toast.
packageXXXXXXXXXXXXXXXXXXXXXX;
importandroid.content.Context;
importandroid.os.Handler;
importandroid.os.Looper;
importandroid.widget.Toast;
publicclassToastUtil{
privatestaticHandlerhandler=newHandler(Looper.getMainLooper());
privatestaticToasttoast=null;
privatestaticObjectsynObj=newObject();
publicstaticvoidshowMessage(finalContextact,finalStringmsg){
showMessage(act,msg,Toast.LENGTH_SHORT);
}
publicstaticvoidshowMessage(finalContextact,finalintmsg){
showMessage(act,msg,Toast.LENGTH_SHORT);
}
publicstaticvoidshowMessage(finalContextact,finalintmsg,
finalintlen){
newThread(newRunnable(){
publicvoidrun(){
handler.post(newRunnable(){
@Override
publicvoidrun(){
synchronized(synObj){
if(toast!=null){
toast.cancel();
toast.setText(msg);
toast.setDuration(len);
}else{
toast=Toast.makeText(act,msg,len);
}
toast.show();
}
}
});
}
}).start();
}
publicstaticvoidcancelCurrentToast(){
if(toast!=null){
toast.cancel();
}
}
}
--------------------------
2月28日追加:
实测 在2.2 2.3中此方法工作良好。
在4.0系统中效果极差
多次触发Toast 无法正常显示
更多相关文章
- Android(安卓)Okhttp + Android提交post表单乱码问题
- [Android(安卓)相机]Android(安卓)相机开发的基本流程
- Android(安卓)知识点积累(一)
- android判断是否联网
- Java/Android(安卓)Annotation processor实践:greendaoannotation
- android 在listview上的 gallery 禁止上下滑动
- Android中更详细的log获取方法
- RatingBar的使用方法
- Android(安卓)input处理机制(一)InputReader