Toast信息提示框之所以在显示一定时间后会自动关闭,是因为在系统中有一个Toast队列。系统会依次从队列中取(出队列)一个Toast,并显示 它。在显示一段时间后,再关闭,然后再显示下一个Toast信息提示框。直到Toast队列中所有Toast都显示完为止。那么有些时候需要这个 Toast信息提示框长时间显示,直到需要关闭它时通过代码来控制,而不是让系统自动来关闭Toast信息提示框。不过这个要求对于Toast本身来说有些过分,因为Toast类并没有提供这个功能。虽然如此,但方法总比问题多。通过一些特殊的处理还是可以实现这个功能的,而且并不复杂。

Toast信息提示框需要调用Toast.show方法来显示。下面来看一下show方法的源代码。
他有两个静态的常量Toast.SHORT和Toast.LONG,这个在后面我会在源码中看到这个两个时间其实是2.5s和3s。

public void show() {
    if (mNextView == null) {
        throw new RuntimeException("setView must have been called");
    }
    INotificationManager service = getService();
    String pkg = mContext.getPackageName();
    TN tn = mTN;
    try {
        //  将当前Toast加入到Toast队列
        service.enqueueToast(pkg, tn, mDuration);
    } catch (RemoteException e) {
        // Empty
    }
}

show方法的代码并不复杂,可以很容易找到如下的代码。

service.enqueueToast(pkg, tn, mDuration);



      从上面的代码可以很容易推断出它的功能是将当前的Toast加入到系统的Toast队列中。看到这里,各位读者应该想到。虽然show方法的表面功能是 显示Toast信息提示框,但其实际的功能是将Toast加入到队列中,再由系统根据Toast队列来显示Toast信息提示框。那么我们经过更进一步地 思考,可以大胆地做出一个初步的方案。既然系统的Toast队列可以显示Toast信息提示框,那么我们为什么不可以自己来显示它呢?这样不是可以自己来 控制Toast的信息提示框的显示和关闭了吗!当然,这就不能再调用show方法来显示Toast信息提示框了(因为show方法会将Toast加入队 列,这样我们就控制不了Toast了)。

既然初步方案已拟定,现在就来实施它。先在Toast类找一下还有没有其他的show方法。结果发现了一个TN类,该类是Toast的一个内嵌类。 在TN类中有一个show方法。TN是ITransientNotification.Stub的子类。从ITransientNotification 和TN类中的show方法初步推断(因为Transient的中文意思是“短暂的”)系统是从Toast队列中获得了Toast对象后,利用TN对象的 show方法显示Toast,再利用TN.hide方法来关闭Toast。首先声明,这只是假设,我们还不知道这么做是否可行!当然,这也是科学研究的一 般方法,先推断或假设,然后再证明推断或假设。

现在关键的一步是获得TN对象。遗憾的是TN被声明成private类型,外部无法访问。不过别着急。在Toast类中有一个mTN变量。虽然不是 public变量,但仍然可以通过反射技术访问该变量。mTN变量会在创建Toast对象时初始化。因此,只要获得mTN变量,就获得了TN对象。下面的 代码显示了一个永远不会自动关闭的Toast信息提示框。

//  先创建一个Toast对象
Toast toast = Toast.makeText(this, "永不消失的Toast", Toast.LENGTH_SHORT);
//  设置Toast信息提示框显示的位置(在屏幕顶部水平居中显示)
toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0);
try
{
    //  从Toast对象中获得mTN变量
    Field field = toast.getClass().getDeclaredField("mTN");
    field.setAccessible(true);
            Object obj = field.get(toast);
    //  TN对象中获得了show方法
            Method method =  obj.getClass().getDeclaredMethod("show", null);
    //  调用show方法来显示Toast信息提示框
            method.invoke(obj, null);
}
catch (Exception e)
{
}


     上面的代码中try{…}catch(…){…}语句中的代码是关键。先利用事先创建好的Toast对象获得了mTN变量。然后再利用反射技术获得了TN对象的show方法。
关闭Toast和显示Toast的方法类似,只是需要获得hide方法,代码如下:

try
{
//  需要将前面代码中的obj变量变成类变量。这样在多个地方就都可以访问了
Method method =  obj.getClass().getDeclaredMethod("hide", null);
    method.invoke(obj, null);
}
catch (Exception e)
{
}


     上面的代码已经很完美地实现了通过代码控制Toast信息提示框显示和关闭的功能。但如果想实现得更完美,可以在Android SDK源代码中找一个叫ITransientNotification.aidl的文件(该文件是AIDL服务定义文件,将在后面详细介绍),并在 Android工程的src目录中建一个android.app包,将这个文件放到这个包中。然后ADT会自动在gen目录中生成了一个 android.app包,包中有一个ITransientNotification.java文件。由于Android SDK自带的ItransientNotification接口属于内部资源,外部程序无法访问,因此,只能将从Toast对象中获得的mTN变量转换成 刚才生成的ITransientNotification对象了。这样就不需要使反射技术获得show和hide方法了。经过改良的显示和关闭Toast 信息提示框的代码如下:

ITransientNotification  notification = (ITransientNotification) field.get(toast);
//  显示Toast信息提示框
notification.show();
//  关闭Toast信息提示框
notification.hide();

Toast的源代码:
我们平常使用的makeText方法:


[java]  view plain  copy

1. /**
2.      * Make a standard toast that just contains a text view.
3.      *
4.      * @param context  The context to use.  Usually your {@link android.app.Application}
5.      *                 or {@link android.app.Activity} object.
6.      * @param text     The text to show.  Can be formatted text.
7.      * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or
8.      *                 {@link #LENGTH_LONG}
9.      *
10.      */  
11. public static Toast makeText(Context context, CharSequence text, int duration) {  
12. new Toast(context);  
13.   
14.         LayoutInflater inflate = (LayoutInflater)  
15.                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
16. null);  
17.         TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);  
18.         tv.setText(text);  
19.           
20.         result.mNextView = v;  
21.         result.mDuration = duration;  
22.   
23. return result;  
24.     }

从这里面我们可以知道Toast显示的布局文件时transient_notification.xml,关于这个文件,我们可以在源码目录中搜索一下transient_notification.xml:




[html]  view plain  copy

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <!--  
    3. /* //device/apps/common/res/layout/transient_notification.xml  
    4. **  
    5. ** Copyright 2006, The Android Open Source Project  
    6. **  
    7. ** Licensed under the Apache License, Version 2.0 (the "License");  
    8. ** you may not use this file except in compliance with the License.  
    9. ** You may obtain a copy of the License at  
    10. **  
    11. **     http://www.apache.org/licenses/LICENSE-2.0  
    12. **  
    13. ** Unless required by applicable law or agreed to in writing, software  
    14. ** distributed under the License is distributed on an "AS IS" BASIS,  
    15. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
    16. ** See the License for the specific language governing permissions and  
    17. ** limitations under the License.  
    18. */  
    19. -->  
    20.   
    21. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    22. android:layout_width="match_parent"  
    23. android:layout_height="match_parent"  
    24. android:orientation="vertical"  
    25. android:background="?android:attr/toastFrameBackground">  
    26.   
    27. <TextView  
    28. android:id="@android:id/message"  
    29. android:layout_width="wrap_content"  
    30. android:layout_height="wrap_content"  
    31. android:layout_weight="1"  
    32. android:layout_gravity="center_horizontal"  
    33. android:textAppearance="@style/TextAppearance.Toast"  
    34. android:textColor="@color/bright_foreground_dark"  
    35. android:shadowColor="#BB000000"  
    36. android:shadowRadius="2.75"  
    37. />  
    38.   
    39. </LinearLayout>


    看到了这个布局是如此的简单,里面显示的内容就是使用TextView来操作的,当然我们也可以修改这个布局的,他提供了一个setView方法,我们可以自定义样式来进行显示的:



    [java]  view plain  copy

    1. Toast toast = new Toast(this);  
    2. View v = LayoutInflater.from(this).inflate(R.layout.activity_main, null);  
    3. toast.setView(v);  
    4. toast.show();

    R.layout.activity_main是我们自己的布局文件

    同时我们也可以看到Toast.makeText方法也会返回一个Toast,在这个方法里我们看到他是使用系统的布局文件,然后在哪个TextView中进行显示内容,同时返回这个Toast,所以如果我们想得到这个系统的显示View可以使用这个方法得到一个Toast,然后再调用getView方法就可以得到了,同时我们也是可以在这个view上继续加一下我们相加的控件,但是这样做是没必要的,这里只是说一下。

    下面接着来看一下显示的show方法吧:


    [java]  view plain  copy

    1. /**
    2.  * Show the view for the specified duration.
    3.  */  
    4. public void show() {  
    5. if (mNextView == null) {  
    6. throw new RuntimeException("setView must have been called");  
    7.     }  
    8.   
    9.     INotificationManager service = getService();  
    10.     String pkg = mContext.getPackageName();  
    11.     TN tn = mTN;  
    12.     tn.mNextView = mNextView;  
    13.   
    14. try {  
    15.         service.enqueueToast(pkg, tn, mDuration);  
    16. catch (RemoteException e) {  
    17. // Empty  
    18.     }  
    19. }


    这个方法很简单的,首先获取一个服务,然后将我们需要显示的toast放到这个服务的队列中进行显示,那么这里最主要的方法就是:


    [java]  view plain  copy

    1. service.enqueueToast(pkg, tn, mDuration);

    首先看一下这个方法的参数是:pkg:包名,mDuration:显示的时间,tn:显示回调的包装类



    这里我们可以看到其实最重要的参数是tn了,因为显示的逻辑可能就在这个类里面,找到源代码:


    [java]  view plain  copy

    1. private static class TN extends ITransientNotification.Stub {  
    2. final Runnable mShow = new Runnable() {  
    3. @Override  
    4. public void run() {  
    5.                 handleShow();  
    6.             }  
    7.         };  
    8.   
    9. final Runnable mHide = new Runnable() {  
    10. @Override  
    11. public void run() {  
    12.                 handleHide();  
    13. // Don't do this in handleHide() because it is also invoked by handleShow()  
    14. null;  
    15.             }  
    16.         };  
    17.   
    18. private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();  
    19. final Handler mHandler = new Handler();      
    20.   
    21. int mGravity;  
    22. int mX, mY;  
    23. float mHorizontalMargin;  
    24. float mVerticalMargin;  
    25.   
    26.   
    27.         View mView;  
    28.         View mNextView;  
    29.   
    30.         WindowManager mWM;  
    31.   
    32.         TN() {  
    33. // XXX This should be changed to use a Dialog, with a Theme.Toast  
    34. // defined that sets up the layout params appropriately.  
    35. final WindowManager.LayoutParams params = mParams;  
    36.             params.height = WindowManager.LayoutParams.WRAP_CONTENT;  
    37.             params.width = WindowManager.LayoutParams.WRAP_CONTENT;  
    38.             params.format = PixelFormat.TRANSLUCENT;  
    39.             params.windowAnimations = com.android.internal.R.style.Animation_Toast;  
    40.             params.type = WindowManager.LayoutParams.TYPE_TOAST;  
    41. "Toast");  
    42.             params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
    43.                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
    44.                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;  
    45.         }  
    46.   
    47. /**
    48.          * schedule handleShow into the right thread
    49.          */  
    50. @Override  
    51. public void show() {  
    52. if (localLOGV) Log.v(TAG, "SHOW: " + this);  
    53.             mHandler.post(mShow);  
    54.         }  
    55.   
    56. /**
    57.          * schedule handleHide into the right thread
    58.          */  
    59. @Override  
    60. public void hide() {  
    61. if (localLOGV) Log.v(TAG, "HIDE: " + this);  
    62.             mHandler.post(mHide);  
    63.         }  
    64.   
    65. public void handleShow() {  
    66. if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView  
    67. " mNextView=" + mNextView);  
    68. if (mView != mNextView) {  
    69. // remove the old view if necessary  
    70.                 handleHide();  
    71.                 mView = mNextView;  
    72.                 Context context = mView.getContext().getApplicationContext();  
    73. if (context == null) {  
    74.                     context = mView.getContext();  
    75.                 }  
    76.                 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);  
    77. // We can resolve the Gravity here by using the Locale for getting  
    78. // the layout direction  
    79. final Configuration config = mView.getContext().getResources().getConfiguration();  
    80. final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());  
    81.                 mParams.gravity = gravity;  
    82. if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {  
    83. 1.0f;  
    84.                 }  
    85. if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {  
    86. 1.0f;  
    87.                 }  
    88.                 mParams.x = mX;  
    89.                 mParams.y = mY;  
    90.                 mParams.verticalMargin = mVerticalMargin;  
    91.                 mParams.horizontalMargin = mHorizontalMargin;  
    92. if (mView.getParent() != null) {  
    93. if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);  
    94.                     mWM.removeView(mView);  
    95.                 }  
    96. if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);  
    97.                 mWM.addView(mView, mParams);  
    98.                 trySendAccessibilityEvent();  
    99.             }  
    100.         }  
    101.   
    102. private void trySendAccessibilityEvent() {  
    103.             AccessibilityManager accessibilityManager =  
    104.                     AccessibilityManager.getInstance(mView.getContext());  
    105. if (!accessibilityManager.isEnabled()) {  
    106. return;  
    107.             }  
    108. // treat toasts as notifications since they are used to  
    109. // announce a transient piece of information to the user  
    110.             AccessibilityEvent event = AccessibilityEvent.obtain(  
    111.                     AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);  
    112.             event.setClassName(getClass().getName());  
    113.             event.setPackageName(mView.getContext().getPackageName());  
    114.             mView.dispatchPopulateAccessibilityEvent(event);  
    115.             accessibilityManager.sendAccessibilityEvent(event);  
    116.         }          
    117.   
    118. public void handleHide() {  
    119. if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);  
    120. if (mView != null) {  
    121. // note: checking parent() just to make sure the view has  
    122. // been added...  i have seen cases where we get here when  
    123. // the view isn't yet added, so let's try not to crash.  
    124. if (mView.getParent() != null) {  
    125. if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);  
    126.                     mWM.removeView(mView);  
    127.                 }  
    128.   
    129. null;  
    130.             }  
    131.         }  
    132.     }


    这个类也不复杂,我们看到他继承了一个类,这个类的形式不知道大家还熟悉吗?我们在前面介绍远程服务AIDL的时候看到过这种形式的类,所以我们可以看到他使用Binder机制,我们可以在源代码中搜索一下:ITransientNotification


    android 13 Toast 带图标 怎么去掉 toast弹窗去除_源代码

    看到了,果然是个aidl文件,我们打开看一下:


    [java]  view plain  copy

    1. /* //device/java/android/android/app/ITransientNotification.aidl
    2. **
    3. ** Copyright 2007, The Android Open Source Project
    4. **
    5. ** Licensed under the Apache License, Version 2.0 (the "License"); 
    6. ** you may not use this file except in compliance with the License. 
    7. ** You may obtain a copy of the License at 
    8. **
    9. **     http://www.apache.org/licenses/LICENSE-2.0 
    10. **
    11. ** Unless required by applicable law or agreed to in writing, software 
    12. ** distributed under the License is distributed on an "AS IS" BASIS, 
    13. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    14. ** See the License for the specific language governing permissions and 
    15. ** limitations under the License.
    16. */  
    17.   
    18. package android.app;  
    19.   
    20. /** @hide */  
    21. oneway interface ITransientNotification {  
    22. void show();  
    23. void hide();  
    24. }

    好吧,我们看到就是两个方法,一个是show显示,一个是隐藏hide,那就看他的实现了,回到上面的代码中:


    [java]  view plain  copy

    1. /**
    2.  * schedule handleShow into the right thread
    3.  */  
    4. @Override  
    5. public void show() {  
    6. if (localLOGV) Log.v(TAG, "SHOW: " + this);  
    7.     mHandler.post(mShow);  
    8. }  
    9.   
    10. /**
    11.  * schedule handleHide into the right thread
    12.  */  
    13. @Override  
    14. public void hide() {  
    15. if (localLOGV) Log.v(TAG, "HIDE: " + this);  
    16.     mHandler.post(mHide);  
    17. }


    TN类中的实现这两个方法,内部使用Handler机制:post一个mShow和mHide:


    [java]  view plain  copy

    1.  final Runnable mShow = new Runnable() {  
    2. @Override  
    3. public void run() {  
    4.                 handleShow();  
    5.             }  
    6.         };  
    7.   
    8. final Runnable mHide = new Runnable() {  
    9. @Override  
    10. public void run() {  
    11.                 handleHide();  
    12. // Don't do this in handleHide() because it is also invoked by handleShow()  
    13. null;  
    14.             }  
    15.         };


    再看方法:handleShow

    [java]  view plain  copy

    1. public void handleShow() {  
    2. if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView  
    3. " mNextView=" + mNextView);  
    4. if (mView != mNextView) {  
    5. // remove the old view if necessary  
    6.                 handleHide();  
    7.                 mView = mNextView;  
    8.                 Context context = mView.getContext().getApplicationContext();  
    9. if (context == null) {  
    10.                     context = mView.getContext();  
    11.                 }  
    12.                 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);  
    13. // We can resolve the Gravity here by using the Locale for getting  
    14. // the layout direction  
    15. final Configuration config = mView.getContext().getResources().getConfiguration();  
    16. final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());  
    17.                 mParams.gravity = gravity;  
    18. if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {  
    19. 1.0f;  
    20.                 }  
    21. if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {  
    22. 1.0f;  
    23.                 }  
    24.                 mParams.x = mX;  
    25.                 mParams.y = mY;  
    26.                 mParams.verticalMargin = mVerticalMargin;  
    27.                 mParams.horizontalMargin = mHorizontalMargin;  
    28. if (mView.getParent() != null) {  
    29. if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);  
    30.                     mWM.removeView(mView);  
    31.                 }  
    32. if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);  
    33.                 mWM.addView(mView, mParams);  
    34.                 trySendAccessibilityEvent();  
    35.             }  
    36.         }



    看一下TN的构造方法:

    这个方法主要是来调节toast的显示位置,同时我们可以看到这个显示使用的是WindowManager控件,将我们toast的显示的视图view放到WindowManger中的。


    [java]  view plain  copy

    1. TN() {  
    2. // XXX This should be changed to use a Dialog, with a Theme.Toast  
    3. // defined that sets up the layout params appropriately.  
    4. final WindowManager.LayoutParams params = mParams;  
    5.             params.height = WindowManager.LayoutParams.WRAP_CONTENT;  
    6.             params.width = WindowManager.LayoutParams.WRAP_CONTENT;  
    7.             params.format = PixelFormat.TRANSLUCENT;  
    8.             params.windowAnimations = com.android.internal.R.style.Animation_Toast;  
    9.             params.type = WindowManager.LayoutParams.TYPE_TOAST;  
    10. "Toast");  
    11.             params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON  
    12.                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
    13.                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;  
    14.         }


    之所以用WindowManger,我猜原因很简单,因为WindowManager是可以独立于Activity来显示的,我们知道toast在我们推出Activity的时候都还可以进行显示的。这个WindowManger用途也很广泛的,那个360桌面清理小工具就是使用这个控件显示的(后台开启一个service就可以了,不需要借助Activity)。同时toast也提供了setGravity或者setMargin方法进行设置toast的显示位置,其实这些设置就是在设置显示view在WindowManager中的位置

    通过上面的知识我们或许稍微理清了思路,就是首先借助TN类,所有的显示逻辑在这个类中的show方法中,然后再实例一个TN类变量,将传递到一个队列中进行显示,所以我们要向解决这个显示的时间问题,那就从入队列这部给截断,因为一旦toast入队列了,我们就控制不了,因为这个队列是系统维护的,所以我们现在的解决思路是:

    1、不让toast入队列

    2、然后我们自己调用TN类中的show和hide方法

    第一个简单,我们不调用toast方法就可以了,但是第二个有点问题了,因为我们看到TN这个类是私有的,所以我们也不能实例化他的对象,但是toast类中有一个实例化对象:tn


    [java]  view plain  copy

    1. final TN mTN;

    是包访问权限,不是public的,这时候就要借助强大的技术,反射了,我们只需要反射出这个变量,一次即可,得到这个变量我们可以得到这个TN类对象了,然后再使用反射获取他的show和hide方法即可,下面我们就来看一下实际的代码吧:

    [java]  view plain  copy

    1. package com.weijia.toast;  
    2.   
    3. import java.lang.reflect.Field;  
    4. import java.lang.reflect.Method;  
    5.   
    6. import android.content.Context;  
    7. import android.view.View;  
    8. import android.widget.Toast;  
    9.   
    10. public class ReflectToast {  
    11.       
    12.     Context mContext;  
    13.   
    14. private Toast mToast;  
    15. private Field field;  
    16. private Object obj;  
    17. private Method showMethod, hideMethod;  
    18.   
    19. public ReflectToast(Context c, View v) {  
    20. this.mContext = c;  
    21. new Toast(mContext);  
    22.         mToast.setView(v);  
    23.   
    24.         reflectionTN();  
    25.     }  
    26.   
    27. public void show() {  
    28. try {  
    29. null);  
    30. catch (Exception e) {  
    31.             e.printStackTrace();  
    32.         }  
    33.     }  
    34.   
    35. public void cancel() {  
    36. try {  
    37. null);  
    38. catch (Exception e) {  
    39.             e.printStackTrace();  
    40.         }  
    41.     }  
    42.   
    43. private void reflectionTN() {  
    44. try {  
    45. "mTN");  
    46. true);//
    47.             obj = field.get(mToast);  
    48. "show", null);  
    49. "hide", null);  
    50. catch (Exception e) {  
    51.             e.printStackTrace();  
    52.         }  
    53.     }  
    54. }

    这里我们实例化一个Toast对象,但是没有调用showf方法,就是不让toast入系统显示队列中,这样就可以控制show方法和hide方法的执行了,下面是测试代码:



    [java]  view plain  copy

    1. package com.weijia.toast;  
    2.   
    3. import android.app.Activity;  
    4. import android.os.Bundle;  
    5. import android.view.View;  
    6. import android.view.View.OnClickListener;  
    7. import android.widget.TextView;  
    8.   
    9. public class MainActivity extends Activity {  
    10.     ReflectToast toast;  
    11. boolean isShown = false;  
    12.       
    13. @Override  
    14. public void onCreate(Bundle savedInstanceState) {  
    15. super.onCreate(savedInstanceState);  
    16.         setContentView(R.layout.activity_main);  
    17. final TextView tView = new TextView(this);  
    18. "ReflectToast !!!");  
    19. new ReflectToast(this, tView);  
    20.           
    21. new OnClickListener() {  
    22. @Override  
    23. public void onClick(View v) {  
    24. if(isShown){  
    25.                    toast.cancel();  
    26. false;  
    27. else{   
    28.                    toast.show();  
    29. true;  
    30.                }  
    31.             }  
    32.         });  
    33.           
    34.     }  
    35. }

    通过一个按钮可以控制toast的显示了,想显示多长时间就显示多长时间