Window 是什么
- Window 是一个相对概念上的载体,他并不是一个真正的容器,仅仅负责管理依赖 window 上面的 View 的一个类,充当管理者。
- Window 是一个抽象类,在源码实现中,只有一个具体的实现类,phoneWindow
- 创建 Window 需要通过 WindowManager 创建
- WindowManager 是外界访问 Window 的入口
- Window 具体实现位于 WindowManagerService 中
- WindowManager 和 WindowManagerService 的交互是通过 IPC 完成
Activity 和 Window 之间的关系
- Activity 只负责生命周期和事件处理
- Window 只控制视图
- 一个 Activity 包含一个 Window ,如果 Activity 没有 Window ,那就相当于 Service
- AMS 统一调度所有应用程序的 Activity
- WMS 控制所有 Window 的显示与隐藏以及要显示的位置
WindowManager
用来在应用与window之间的管理接口,管理窗口顺序,消息等。
WindowManagerService
简称 Wms,WindowManagerService 管理窗口的创建、更新和删除,显示顺序等,是 WindowManager 真正的实现类。它运行在 System_server 进程,作为服务端,客户端(应用程序)通过IPC调用和它进行交互。
Token
这里提到的Token主是指窗口令牌(Window Token),是一种特殊的Binder令牌,Wms用它唯一标识系统中的一个窗口。
Window 中的 Type
Window 类型 | 含义 | Window 层级 |
---|---|---|
应用 Window | 对应着一个 Activity | 1-99 |
子 Window | 不能单独存在,需要附属在父 Window 中(比如 Dialog 就是子 Window) | 1000-1999 |
系统 Window | 需要声明权限才能创建 Window ,比如 Toast 、状态栏、悬浮窗 | 2000-2999 |
应用窗口(1~99)
1 2 3 4 5 6 7 8 9 10
//第一个应用窗口 public static final int FIRST_APPLICATION_WINDOW = 1; //所有程序窗口的base窗口,其他应用程序窗口都显示在它上面 public static final int TYPE_BASE_APPLICATION = 1; //所有Activity的窗口,只能配合Activity在当前APP使用 public static final int TYPE_APPLICATION = 2; //目标应用窗口未启动之前的那个窗口 public static final int TYPE_APPLICATION_STARTING = 3; //最后一个应用窗口 public static final int LAST_APPLICATION_WINDOW = 99;
子窗口(1000~1999)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
//第一个子窗口 public static final int FIRST_SUB_WINDOW = 1000; // 面板窗口,显示于宿主窗口的上层,只能配合Activity在当前APP使用 public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; // 媒体窗口(例如视频),显示于宿主窗口下层 public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1; // 应用程序窗口的子面板,只能配合Activity在当前APP使用(PopupWindow默认就是这个Type) public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2; // 对话框窗口,只能配合Activity在当前APP使用 public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3; // 在媒体窗口上显示叠加的窗口 public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4; // 最后一个子窗口 public static final int LAST_SUB_WINDOW = 1999;
系统窗口(2000~2999)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
//系统窗口,非应用程序创建 public static final int FIRST_SYSTEM_WINDOW = 2000; //状态栏,只能有一个状态栏,位于屏幕顶端,其他窗口都位于它下方 public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; //搜索栏,只能有一个搜索栏,位于屏幕上方 public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; //电话窗口,它用于电话交互(特别是呼入),置于所有应用程序之上,状态栏之下,属于悬浮窗(并且给一个Activity的话按下HOME键会出现看不到桌面上的图标异常情况) public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; //系统警告提示窗口,出现在应用程序窗口之上,属于悬浮窗, 但是会被禁止 public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3; //信息窗口,用于显示Toast, 不属于悬浮窗, 但有悬浮窗的功能, 缺点是在Android2.3上无法接收点击事件 public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; // public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4; //锁屏窗口 public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4; //系统顶层窗口,显示在其他一切内容之上,此窗口不能获得输入焦点,否则影响锁屏 public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6; //电话优先,当锁屏时显示,此窗口不能获得输入焦点,否则影响锁屏 public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7; //系统对话框窗口 public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8; //锁屏时显示的对话框 public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9; //系统内部错误提示,显示在任何窗口之上 public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10; //内部输入法窗口,显示于普通UI之上,应用程序可重新布局以免被此窗口覆盖 public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11; //内部输入法对话框,显示于当前输入法窗口之上 public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12; //墙纸窗口 public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13; //状态栏的滑动面板 public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14; //安全系统覆盖窗口,这些窗户必须不带输入焦点,否则会干扰键盘 public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15; //最后一个系统窗口 public static final int LAST_SYSTEM_WINDOW = 2999; 2.窗口flags显示属性在WindowManager中也有定义: //窗口特征标记 public int flags; //当该window对用户可见的时候,允许锁屏 public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001; //窗口后面的所有内容都变暗 public static final int FLAG_DIM_BEHIND = 0x00000002; //Flag:窗口后面的所有内容都变模糊 public static final int FLAG_BLUR_BEHIND = 0x00000004; //窗口不能获得焦点 public static final int FLAG_NOT_FOCUSABLE = 0x00000008; //窗口不接受触摸屏事件 public static final int FLAG_NOT_TOUCHABLE = 0x00000010; //即使在该window在可获得焦点情况下,允许该窗口之外的点击事件传递到当前窗口后面的的窗口去 public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020; //当手机处于睡眠状态时,如果屏幕被按下,那么该window将第一个收到触摸事件 public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040; //当该window对用户可见时,屏幕处于常亮状态 public static final int FLAG_KEEP_SCREEN_ON = 0x00000080; //让window占满整个手机屏幕,不留任何边界 public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100; //允许窗口超出整个手机屏幕 public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200; //window全屏显示 public static final int FLAG_FULLSCREEN = 0x00000400; //恢复window非全屏显示 public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800; //开启窗口抖动 public static final int FLAG_DITHER = 0x00001000; //安全内容窗口,该窗口显示时不允许截屏 public static final int FLAG_SECURE = 0x00002000; //锁屏时显示该窗口 public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000; //系统的墙纸显示在该窗口之后 public static final int FLAG_SHOW_WALLPAPER = 0x00100000; //当window被显示的时候,系统将把它当做一个用户活动事件,以点亮手机屏幕 public static final int FLAG_TURN_SCREEN_ON = 0x00200000; //该窗口显示,消失键盘 public static final int FLAG_DISMISS_KEYGUARD = 0x00400000; //当该window在可以接受触摸屏情况下,让因在该window之外,而发送到后面的window的触摸屏可以支持split touch public static final int FLAG_SPLIT_TOUCH = 0x00800000; //对该window进行硬件加速,该flag必须在Activity或Dialog的Content View之前进行设置 public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000; //让window占满整个手机屏幕,不留任何边界 public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000; //透明状态栏 public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000; //透明导航栏 public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
悬浮窗案例实现
控制 Window 主要是通过 ViewManager 这个类来管理的,看看该类有什么方法
|
|
添加 View
可以看到该类只有这 3 个方法,添加 View 的视图,更新 View 视图,移除视图等方法。首先看看代码具体是如何实现的,从上面代码得知,添加 View 和更新视图都需要 ViewGroup.LayoutParams ,和一个 View,看看具体代码是如何实现的
|
|
- 其中有几种参数是必须要设置:layoutParams 、gravity、type、flags
- 这里最重要的就是 type 类型的参数,设置不同 type 类型相对应不一样的层级,比如可以设置全局的悬浮窗,悬浮在其他应用之上,Android 8.0 以上是 TYPE_APPLICATION_OVERLAY 类型,8.0 以下是 TYPE_SYSTEM_ALERT 类型,还需要申请对应的悬浮窗权限
更新视图
|
|
更新视图主要修改 x,y 的值,然后调用 updateViewLayout 即可
删除视图
|
|
完整代码
|
|