当前位置: 代码迷 >> Android >> Android Toast cancel有关问题、源码分析和解决方案
  详细解决方案

Android Toast cancel有关问题、源码分析和解决方案

热度:168   发布时间:2016-05-01 19:51:13.0
Android Toast cancel问题、源码分析和解决方案

?

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队列,自己控制它就是了。

简单的实现:全局只有1Toast对象?由此类中的静态方法控制显示。

每次显示时,cancel掉原来的Toast对象。即,Toast队列中,只有01Toast。每次触发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? 无法正常显示

  相关解决方案