需求
在项目中需要用到不依赖Activity Context启动的Dialog,并且Dialog的显示层级要在最顶部(没错,就是流氓软件!)
方案
在网络上搜索“Android AlertDialog 置顶”、“Android AlertDialog level”都找不到我想要的,最后搜索“Android AlertDialog Priority”找到了一篇文章,提示了我。
我重新整理了一下,Dialog是在WindowManager显示的,而WindowManager分很多不同的层,使用AlertDialog的alertDialog.getWindow().setType(WindowManager.LayoutParams.***)
方法可以设置这个层,这里就是关键了。从源码(WindowManager.java)里面看,type有很多,我们只关注系统等级且没有被隐藏的,目前有如下类型:
/**
* Start of system-specific window types. These are not normally
* created by applications.
*window类型:第一个系统窗口。这些弹窗不是通过application正常创建的。
*/
public static final int FIRST_SYSTEM_WINDOW = 2000;
/**
* Window type: the status bar. There can be only one status bar
* window; it is placed at the top of the screen, and all other
* windows are shifted down so they are below it.
* In multiuser systems shows on all users' windows.
* window类型:状态栏窗口。因为只能有个一个状态栏window,所以它被放在屏幕最上面,
* 且其他window在它下面。在多用户系统,可以显示在所有用户的window之上。
*/
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
/**
* Window type: the search bar. There can be only one search bar
* window; it is placed at the top of the screen.
* In multiuser systems shows on all users' windows.
* window类型:搜索条窗口。(后面同上,略)
*/
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
/**
* Window type: phone. These are non-application windows providing
* user interaction with the phone (in particular incoming calls).
* These windows are normally placed above all applications, but behind
* the status bar.
* In multiuser systems shows on all users' windows.
* window类型:通话窗口。这些非应用window为用户提供通话的交互,特别是来电的时候
* 在多用户系统,可以显示在所有用户的window之上。
*/
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
/**
* Window type: system window, such as low power alert. These windows
* are always on top of application windows.
* In multiuser systems shows only on the owning user's window.
* window类型:系统警告窗口,例如低电弹窗。这些window总在应用之上的window层。
* 在多用户系统,只显示在自己的window之上。
*/
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
/**
* Window type: keyguard window.
* In multiuser systems shows on all users' windows.
* @removed
* window类型:屏保(guard为守卫的意思)。
* 在多用户系统,可以显示在所有用户的window之上。
*/
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
/**
* Window type: transient notifications.
* In multiuser systems shows only on the owning user's window.
* window类型:Toast对应的窗口。
* 在多用户系统,只显示在自己的window之上。
*/
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
/**
* Window type: system overlay windows, which need to be displayed
* on top of everything else. These windows must not take input
* focus, or they will interfere with the keyguard.
* In multiuser systems shows only on the owning user's window.
* window类型:系统覆盖窗口,用来在顶部展示。
* 这类window需要释放输入焦点,不然会中断屏幕保护。
* 在多用户系统,只显示在自己的window之上。
*/
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;
/**
* Window type: priority phone UI, which needs to be displayed even if
* the keyguard is active. These windows must not take input
* focus, or they will interfere with the keyguard.
* In multiuser systems shows on all users' windows.
* window类型:在屏幕保护下的通话窗口。
* 这类window需要释放输入焦点,不然会中断屏幕保护。
* 在多用户系统,可以显示在所有用户的window之上。
*/
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
/**
* Window type: panel that slides out from the status bar
* In multiuser systems shows on all users' windows.
* window类型:从状态栏下拉下来的面板。(变量名又是系统对话框,估计就是和它同级吧)
* 在多用户系统,可以显示在所有用户的window之上。
*/
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
/**
* Window type: dialogs that the keyguard shows
* In multiuser systems shows on all users' windows.
* window类型:屏保弹出的对话框
* 在多用户系统,可以显示在所有用户的window之上。
*/
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
/**
* Window type: internal system error windows, appear on top of
* everything they can.
* In multiuser systems shows only on the owning user's window.
* window类型:系统错误窗口
* 在多用户系统,只显示在自己的window之上。
*/
public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;
/**
* Window type: internal input methods windows, which appear above
* the normal UI. Application windows may be resized or panned to keep
* the input focus visible while this window is displayed.
* In multiuser systems shows only on the owning user's window.
* window类型:输入法窗口,出现在正常UI之上。
* 应用可能会重置大小
* 在多用户系统,只显示在自己的window之上。
*/
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
/**
* Window type: internal input methods dialog windows, which appear above
* the current input method window.
* In multiuser systems shows only on the owning user's window.
* window类型:输入法中备选窗口,出现在当前输入法窗口之上。
* 在多用户系统,只显示在自己的window之上。
*/
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
/**
* Window type: wallpaper window, placed behind any window that wants
* to sit on top of the wallpaper.
* In multiuser systems shows only on the owning user's window.
* window类型:墙纸对应的窗口,放置在任何想在其之上的窗口之后。
* 在多用户系统,只显示在自己的window之上。
*/
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
/**
* Window type: panel that slides out from over the status bar
* In multiuser systems shows on all users' windows.
* window类型:从状态栏下拉下来的面板。
* 在多用户系统,可以显示在所有用户的window之上。
*/
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
PS:我测试了其中部分类型,文首说提醒我的那篇文章说系统弹窗不需要依赖Activity,其实部分还是需要的,我以 TYPE_SYSTEM_ERROR为准测试各个类型弹窗的层级其结果为:
类型 | 直接调用结果 |
---|---|
TYPE_INPUT_METHOD | 报错需要Activity |
TYPE_STATUS_BAR_PANEL | 相当于 TYPE_SYSTEM_ERROR |
TYPE_INPUT_METHOD_DIALOG | 在 TYPE_SYSTEM_ERROR之下 |
TYPE_WALLPAPER | 报错需要Activity |
TYPE_SYSTEM_DIALOG | 在 TYPE_SYSTEM_ERROR之下 |
TYPE_PRIORITY_PHONE | 在 TYPE_SYSTEM_ERROR之下 |
TYPE_TOAST | 在 TYPE_SYSTEM_ERROR之下 |
TYPE_STATUS_BAR | 报错需要Activity |
TYPE_SYSTEM_OVERLAY | 相当于 TYPE_SYSTEM_ERROR, 但是能点击背景 |
注意:我只是根据表象判断,大家可以各取所需。知道如何再深入源码查找窗口层级变化的大神可以指教指教。
代码
//权限
//代码
AlertDialog alertDialog;
// 创建构建器
AlertDialog.Builder builder = new AlertDialog.Builder(mContext,AlertDialog.THEME_HOLO_LIGHT);
// 设置参数
builder.setTitle("提示")
.setNeutralButton("忽略", new DialogInterface.OnClickListener() {// 中那个按钮
@Override
public void onClick(DialogInterface dialog,int which) {
dismissDialog();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dismissDialog();
}
});
alertDialog= builder.create();
//设置层级
Window window = mAlertDialog.getWindow();
if (window == null) {
Log.w("system dialog","dialog window is null");
return;
}
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);//这里我选了最高级
注意:弹窗可以在Service中弹出,也可以在BroadcastReceiver中弹出,当然你选择的等级应该不需要Activity才行
注意事项
- 其实,并不建议使用系统级弹窗,会在其他应用之上,影响用户体验。
- 当窗口的层级高于屏幕保护(锁屏)的层级时,连续按开机键,弹窗会驻留在锁屏界面,有可能遮挡用户的锁屏密码界面,甚至无法解锁,注意在锁屏时关闭弹窗。
- 如上第二条,即使及时关闭弹窗,在5.0以上的系统也会闪一下;而在4.2.2上暂时没有发现。