Type
TYPE_PHONE
:电话窗口,这是非应用窗口,用于来电的界面,该窗口通常置于所有应用之上,但在状态栏下。TYPE_SYSTEM_ALERT
:系统窗口,例如低电量警告弹窗,在应用窗口之上。TYPE_TOAST
:透明通知,不会拦截触摸事件,可以向下透传。
优先级
TYPE_SYSTEM_ALERT
> TYPE_PHONE
> TYPE_TOAST
Android版本
19
以下TYPE_TOAST
不能响应点击事件,不能获取焦点TYPE_PHONE
权限拉下通知栏不可见,权限比TYPE_SYSTEM_ALERT
低TYPE_PHONE和TYPE_SYSTEM_ALERT需要权限
<uses-permission android:name=".SYSTEM_ALERT_WINDOW"/>
,且在23以上版本增加了动态权限管理特性,DrawOverOtherApps选项下可以动态修改悬浮窗权限。
系统源码
- Windows Type的相关逻辑都在
PhoneWindowManager
中
@Override
public boolean checkShowToOwnerOnly( attrs) {
switch () {
default:
break;
...
// These are the windows that by default are shown to all users. However, to
// protect against spoofing, check permissions below.
case TYPE_PHONE:
...
break;
// Check if third party app has set window to system window type.
return (
.INTERNAL_SYSTEM_WINDOW)
!= PackageManager.PERMISSION_GRANTED;
}
- 可以看到TYPE_PHONE是展示给所有用户的,因此需要检测权限。
- TYPE_SYSTEM_ALERT只展示给持有用户。
@Override
public int checkAddPermission( attrs, int[] outAppOp) {
int type = ;
switch (type) {
case TYPE_TOAST:
// XXX right now the app process has complete control over
// this... should introduce a token to let the system
// monitor/control what they are doing.
outAppOp[0] = AppOpsManager.OP_TOAST_WINDOW;
break;
...
case TYPE_PHONE:
case TYPE_SYSTEM_ALERT:
case TYPE_SYSTEM_ERROR:
case TYPE_SYSTEM_OVERLAY:
...
permission = .SYSTEM_ALERT_WINDOW;
outAppOp[0] = AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
break;
default:
permission = .INTERNAL_SYSTEM_WINDOW;
}
if (permission != null) {
if ((permission)
!= PackageManager.PERMISSION_GRANTED) {
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
}
return WindowManagerGlobal.ADD_OKAY;
}
-
TYPE_TOAST
不进行权限验证,直接返回WindowManagerGlobal.ADD_OKAY
;TYPE_PHONE
与TYPE_SYSTEM_ALERT
需要验证.SYSTEM_ALERT_WINDOW
权限。 - 另外,其他Window Type需要申请
.INTERNAL_SYSTEM_WINDOW
权限,该权限只有系统应用才能获取,所以不能用于悬浮窗使用。
Android 19以下版本Toast无法获取焦点和触发触摸事件
//Android 2.0 - 2.3.7 PhoneWindowManager
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_SYSTEM_OVERLAY:
case TYPE_SECURE_SYSTEM_OVERLAY:
case TYPE_TOAST:
// These types of windows can't receive input events.
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
break;
}
}
//Android 4.0.1 - 4.3.1 PhoneWindowManager
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_SYSTEM_OVERLAY:
case TYPE_SECURE_SYSTEM_OVERLAY:
case TYPE_TOAST:
// These types of windows can't receive input events.
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
break;
}
}
//Android 4.4 PhoneWindowManager
@Override
public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
switch (attrs.type) {
case TYPE_SYSTEM_OVERLAY:
case TYPE_SECURE_SYSTEM_OVERLAY:
// These types of windows can't receive input events.
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
break;
}
}
- 在4.0.1以前,当使用TYPE_TOAST时,Android会给我们加上FLAG_NOT_FOCUSABLE和FLAG_NOT_TOUCHABLE。
- 从4.0.1开始,会额外再去掉FLAG_WATCH_OUTSIDE_TOUCH,这样真的是什么事件都没了。
- 从版本19开始,Case中的TYPE_TOAST被移除了。
**所以从4.4开始, 使用TYPE_TOAST的同时还可以接收触摸事件和按键事件了, 而4.4以前只能显示出来, 不能交互.
**API level 18及以下使用TYPE_TOAST无法接收触摸事件的原因也找到了。