package com.hjq.toast; import android.app.Activity; import android.app.Application; import android.content.Context; import android.graphics.PixelFormat; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.WindowManager; import android.widget.Toast; /** * author : Android 轮子哥 * github : https://github.com/getActivity/ToastUtils * time : 2018/11/02 * desc : 自定义 Toast 辅助类 */ final class ToastHelper extends Handler { /** 当前的吐司对象 */ private final Toast mToast; /** WindowManager 辅助类 */ private final WindowHelper mWindowHelper; /** 当前应用的包名 */ private final String mPackageName; /** 当前是否已经显示 */ private boolean mShow; ToastHelper(Toast toast, Application application) { super(Looper.getMainLooper()); mToast = toast; mPackageName = application.getPackageName(); mWindowHelper = WindowHelper.register(this, application); } @Override public void handleMessage(Message msg) { // 收到取消显示的消息 cancel(); } boolean isShow() { return mShow; } void setShow(boolean show) { mShow = show; } /*** * 显示吐司弹窗 */ void show() { if (!isShow()) { /* 这里解释一下,为什么不复用 WindowManager.LayoutParams 这个对象 因为如果复用了,不同 Activity 之间不能共用一个,第一个 Activity 调用显示方法可以显示出来,但是会导致后面的 Activity 都显示不出来 又或者说,非第一次调用显示方法的 Activity 都会把这个显示请求推送给之前第一个调用显示的 Activity 上面,如果第一个 Activity 已经销毁,还会报以下异常 android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@ef1ccb6 is not valid; is your activity running? */ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); /* // 为什么不能加 TYPE_TOAST,因为通知权限在关闭后设置显示的类型为 Toast 会报错 // android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? params.type = WindowManager.LayoutParams.TYPE_TOAST; */ /* // 这个是旧版本的写法,新版本已经废弃,因为 Activity onPause 方法被调用后这里把 Toast 取消显示了,这样做的原因:防止内存泄露 // 判断是否为 Android 6.0 及以上系统并且有悬浮窗权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(mToast.getView().getContext())) { // 解决使用 WindowManager 创建的 Toast 只能显示在当前 Activity 的问题 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { params.type = WindowManager.LayoutParams.TYPE_PHONE; } } */ params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = android.R.style.Animation_Toast; params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; params.packageName = mPackageName; // 重新初始化位置 params.gravity = mToast.getGravity(); params.x = mToast.getXOffset(); params.y = mToast.getYOffset(); try { Activity topActivity = mWindowHelper.getTopActivity(); if (topActivity != null && !topActivity.isFinishing()) { WindowManager windowManager = (WindowManager) topActivity.getSystemService(Context.WINDOW_SERVICE); if (windowManager != null) { windowManager.addView(mToast.getView(), params); } } // 添加一个移除吐司的任务 sendEmptyMessageDelayed(hashCode(), mToast.getDuration() == Toast.LENGTH_LONG ? IToastStrategy.LONG_DURATION_TIMEOUT : IToastStrategy.SHORT_DURATION_TIMEOUT); // 当前已经显示 setShow(true); } catch (IllegalStateException | WindowManager.BadTokenException ignored) { // 如果这个 View 对象被重复添加到 WindowManager 则会抛出异常 // java.lang.IllegalStateException: View android.widget.TextView has already been added to the window manager. // 如果 WindowManager 绑定的 Activity 已经销毁,则会抛出异常 // android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@ef1ccb6 is not valid; is your activity running? } } } /** * 取消吐司弹窗 */ void cancel() { // 移除之前移除吐司的任务 removeMessages(hashCode()); if (isShow()) { try { Activity topActivity = mWindowHelper.getTopActivity(); if (topActivity != null) { WindowManager windowManager = (WindowManager) topActivity.getSystemService(Context.WINDOW_SERVICE); if (windowManager != null) { windowManager.removeViewImmediate(mToast.getView()); } } } catch (IllegalArgumentException ignored) { // 如果当前 WindowManager 没有附加这个 View 则会抛出异常 // java.lang.IllegalArgumentException: View=android.widget.TextView not attached to window manager } // 当前没有显示 setShow(false); } } }