`

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  无法正常显示

分享到:
评论
1 楼 qlraishui 2012-08-04  
4.0确实效果极差

相关推荐

Global site tag (gtag.js) - Google Analytics