1. Window和WindowManager
Window
有三种类型,层级大的会覆盖在层级小的上面
- 应用
Window
,对应Activity
,层级范围是 1~99 - 子
Window
,对应Dialog
和PopupWindow
,层级范围是 1000~1999 - 系统
Window
,对应Toast
层级范围是 2000~2999
WindowManager
继承ViewManager
,可以显示类似悬浮窗效果,主要实现下面三个方法
public interface ViewManager {
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
2. 创建悬浮窗
悬浮窗对应系统Window
,通过WindowManager.addView(View, ViewGroup.LayoutParams)
加入界面
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; // 窗口位置
lp.format = PixelFormat.TRANSPARENT; // 位图格式
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; // 窗口的层级关系
lp.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 窗口的模式
wm.addView(view, lp);
WindowManager.LayoutParams
的type
属性取值
常量 | 含义 |
FIRST_APPLICATION_WINDOW | 应用程序窗口 |
TYPE_BASE_APPLICATION | 所有程序窗口的“基地”窗口 |
TYPE_APPLICATION | 普通应用功能程序窗口 |
TYPE_APPLICATION_STARTING | 用于应用程序启动时所显示的窗口 |
LAST_APPLICATION_WINDOW | 应用程序窗口结束 |
FIRST_SUB_WINDOW | 子窗口 |
TYPE_APPLICATION_PANE | 面板窗口 |
TYPE_APPLICATION_MEDIA | 媒体窗口 |
TYPE_APPLICATION_SUB_PANEL | 应用程序窗口的子面板 |
TYPE_APPLICATION_ATTACHED_DIALOG | 对话框 |
TYPE_APPLICATION_MEDIA_OVERLAY | 媒体信息 |
LAST_SUB_WINDOW | 子窗口结束 |
FIRST_SYSTEM_WINDOW | 系统窗口 |
TYPE_STATUS_BAR | 状态栏 |
TYPE_SEARCH_BAR | 搜索栏 |
TYPE_PHONE | 电话窗口 |
TYPE_SYSTEM_ALERT | 系统提示 |
TYPE_KEYGUARD | 锁屏窗口 |
TYPE_TOAST | 信息窗口 |
TYPE_SYSTEM_OVERLAY | 系统顶层窗口 |
TYPE_PRIORITY_PHONE | 电话优先,当锁屏时显示 |
TYPE_SYSTEM_DIALOG | 系统对话框(例如音量调节框) |
TYPE_KEYGUARD_DIALOG | 锁屏时显示的对话框 |
TYPE_SYSTEM_ERROR | 系统内部错误提示,显示于所有内容之上 |
TYPE_INPUT_METHOD | 内部输入法窗口,显示于普通UI之上 |
TYPE_INPUT_METHOD_DIALOG | 内部输入法对话框,显示于当前输入法窗口之上 |
TYPE_WALLPAPER | 墙纸窗口 |
TYPE_STATUS_BAR_PANEL | 状态栏的滑动面板 |
LAST_SYSTEM_WINDOW | 系统窗口结束 |
WindowManager.LayoutParams
的flags
属性取值
常量 | 含义 |
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | 允许在屏幕开启的时候锁定屏幕 |
FLAG_DIM_BEHIND | window会变暗,使用dimAmount属性来控制变暗的程度 |
FLAG_NOT_FOCUSABLE | window永远不会获取焦点 |
FLAG_NOT_TOUCHABLE | window永远无法获取点击事件 |
FLAG_NOT_TOUCH_MODAL | 允许window之外点击事件传递给其他在其之后的window |
FLAG_TOUCHABLE_WHEN_WAKING | device休眠的时候,当触摸屏被点击,window会收到首次点击事件 |
FLAG_KEEP_SCREEN_ON | 当这个window对用户是可见状态,保持设备屏幕不关闭且不变暗 |
FLAG_LAYOUT_IN_SCREEN | 将window放置在整个屏幕之内,无视其他的装饰 |
FLAG_LAYOUT_NO_LIMITS | 允许window扩展至屏幕之外 |
FLAG_FULLSCREEN | 当这个window显示的时候,隐藏所有的装饰物 |
FLAG_FORCE_NOT_FULLSCREEN | 覆盖FLAG_FULLSCREEN效果,并强制显示屏幕上的一些装饰 |
FLAG_SECURE | 把这个window中的内容看作需要保护的内容,防止被截屏 |
FLAG_SCALED | 一种特殊模式,布局参数用于指示显示比例 |
FLAG_IGNORE_CHEEK_PRESSES | 会过滤不需要的点击事件 |
FLAG_LAYOUT_INSET_DECOR | 这个flag只能配合 FLAG_LAYOUT_IN_SCREEN 一起使用 |
FLAG_ALT_FOCUSABLE_IM | 反转FLAG_NOT_FOCUSABLE选项 |
FLAG_WATCH_OUTSIDE_TOUCH | 一个点击事件如果发生在你的window之外的范围,你就会接收到一个特殊的MotionEvent,MotionEvent.ACTION_OUTSIDE |
FLAG_SHOW_WHEN_LOCKED | 使得window可以在锁屏状态下显示 |
FLAG_SHOW_WALLPAPER | 如果你的window有透明的区域,墙纸会显示在那 |
FLAG_TURN_SCREEN_ON | 当window被添加或者显示,系统会点亮屏幕 |
FLAG_DISMISS_KEYGUARD | 当使用的是无密码的锁屏界面,显示此window会使锁屏界面被自动解锁 |
FLAG_SPLIT_TOUCH | window会接收来自window边界之外发送给其他window的点击事件 |
FLAG_HARDWARE_ACCELERATED | window是否启动硬件加速,请求硬件加速但不能保证硬件加速生效 |
FLAG_LOCAL_FOCUS_MODE | 在这种模式下,window不会通过windowmanager获取到event |
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | window负责绘制状态栏的背景 |
创建悬浮窗需要系统权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
3. 悬浮窗禁用
在Android6.0以后,悬浮窗权限默认是禁用的,需要用户确定打开,在返回时确认权限。
public void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !Settings.canDrawOverlays(this)) {
Toast.makeText(this, "请授权!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION);
} else {
showSuspensionWindow(mSuspensionView);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_OVERLAY_PERMISSION) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
showSuspensionWindow(mSuspensionView);
}
}
}