Toast显示和关闭自个控制的方法

来源:互联网 发布:七月算法 视频 百度云 编辑:程序博客网 时间:2024/05/16 06:02

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
 在CODE上查看代码片派生到我的代码片
  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.         Toast result = new Toast(context);  
  13.   
  14.         LayoutInflater inflate = (LayoutInflater)  
  15.                 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  16.         View v = inflate.inflate(com.android.internal.R.layout.transient_notification, 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
 在CODE上查看代码片派生到我的代码片
  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
 在CODE上查看代码片派生到我的代码片
  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
 在CODE上查看代码片派生到我的代码片
  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
 在CODE上查看代码片派生到我的代码片
  1. service.enqueueToast(pkg, tn, mDuration);  
首先看一下这个方法的参数是:pkg:包名,mDuration:显示的时间,tn:显示回调的包装类

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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  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.                 mNextView = 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.             params.setTitle("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.                     mParams.horizontalWeight = 1.0f;  
  84.                 }  
  85.                 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {  
  86.                     mParams.verticalWeight = 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.                 mView = null;  
  130.             }  
  131.         }  
  132.     }  
这个类也不复杂,我们看到他继承了一个类,这个类的形式不知道大家还熟悉吗?我们在前面介绍远程服务AIDL的时候看到过这种形式的类,所以我们可以看到他使用Binder机制,我们可以在源代码中搜索一下:ITransientNotification


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

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  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
 在CODE上查看代码片派生到我的代码片
  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
 在CODE上查看代码片派生到我的代码片
  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.                 mNextView = null;  
  14.             }  
  15.         };  
再看方法:handleShow
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  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.                     mParams.horizontalWeight = 1.0f;  
  20.                 }  
  21.                 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {  
  22.                     mParams.verticalWeight = 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
 在CODE上查看代码片派生到我的代码片
  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.             params.setTitle("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
 在CODE上查看代码片派生到我的代码片
  1. final TN mTN;  
擦,是包访问权限,不是public的,这时候就要借助强大的技术,反射了,我们只需要反射出这个变量,然后强暴她一次即可,得到这个变量我们可以得到这个TN类对象了,然后再使用反射获取他的show和hide方法即可,下面我们就来看一下实际的代码吧:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  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.         mToast = new Toast(mContext);  
  22.         mToast.setView(v);  
  23.   
  24.         reflectionTN();  
  25.     }  
  26.   
  27.     public void show() {  
  28.         try {  
  29.             showMethod.invoke(obj, null);  
  30.         } catch (Exception e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.     }  
  34.   
  35.     public void cancel() {  
  36.         try {  
  37.             hideMethod.invoke(obj, null);  
  38.         } catch (Exception e) {  
  39.             e.printStackTrace();  
  40.         }  
  41.     }  
  42.   
  43.     private void reflectionTN() {  
  44.         try {  
  45.             field = mToast.getClass().getDeclaredField("mTN");  
  46.             field.setAccessible(true);//强暴  
  47.             obj = field.get(mToast);  
  48.             showMethod = obj.getClass().getDeclaredMethod("show"null);  
  49.             hideMethod = obj.getClass().getDeclaredMethod("hide"null);  
  50.         } catch (Exception e) {  
  51.             e.printStackTrace();  
  52.         }  
  53.     }  
  54. }  
这里我们实例化一个Toast对象,但是没有调用showf方法,就是不让toast入系统显示队列中,这样就可以控制show方法和hide方法的执行了,下面是测试代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  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.         tView.setText("ReflectToast !!!");  
  19.         toast = new ReflectToast(this, tView);  
  20.           
  21.         findViewById(R.id.show_toast).setOnClickListener(new OnClickListener() {  
  22.             @Override  
  23.             public void onClick(View v) {  
  24.                if(isShown){  
  25.                    toast.cancel();  
  26.                    isShown = false;  
  27.                }else{   
  28.                    toast.show();  
  29.                    isShown = true;  
  30.                }  
  31.             }  
  32.         });  
  33.           
  34.     }  
  35. }  

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


0 0
原创粉丝点击