Window表示一个窗口的概念,在日常开发中直接接触Window的机会并不多,但是在某些特殊时候我们需要在桌面上显示一个类似悬浮窗的东西,那么这种效果就需要用到Window来实现。Window是一个抽象类,它的具体实现是PhoneWindow。创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和Window-ManagerService的交互是一个IPC过程。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的,因此Window实际是View的直接管理者。从第4章中所讲述的View的事件分发机制也可以知道,单击事件由Window传递给DecorView,然后再由DecorView传递给我们的View,就连Activity的设置视图的方法setContentView在底层也是通过Window来完成的。 下面代码是在window 400*400的位置上添加一个button,即使跳转Activity也依然再顶部展示按钮
val button = Button(this)
button.text = "button"
val layoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT
, WindowManager.LayoutParams.WRAP_CONTENT
, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
, 0
, PixelFormat.TRANSPARENT)
layoutParams.flags = (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
layoutParams.gravity = Gravity.LEFT or Gravity.TOP
layoutParams.x = 400
layoutParams.y = 400
val windowManager = windowManager
windowManager.addView(button, layoutParams)
WindowManager. LayoutParams 中的flags 和type 这两个参数比较重要 Flags参数表示Window的属性,它有很多选项,通过这些选项可以控制Window的显 示特性,这里主要介绍几个比较常用的选项,剩下的请查看官方文档。FLAG_NOT_FOCUSABLE 表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用 FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window。FLAG_NOT_TOUCH_MODAL 在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前 Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标 记,否则其他Window将无法收到单击事件。FLAG_SHOW_WHEN_LOCKED 开启此模式可以让Window显示在锁屏的界面上。Type参数表示Window的类型,Window有三种类型,分别是应用Window、子Window和系统Window。应用类Window对应着一Activity。子Window不能单独存在,它需要属在特定的父Window之中,比如常见的一些Dialog就是一个子Window。系统Window是需要声明权限在创建的Window,比如Toast和系统状态栏这些都是系统Window。- Window是分层的,每个Window都有对应的z-ordered,层级大的会覆盖在层级小的Window的上面,这和HTML中的z-index的概念是完全一致的。在三类Window中,应用Window的层级范围是1~99,子Window的层级范围是1000~1999,系统Window的层级范围是2000~2999,这些层级范围对应着WindowManager.LayoutParams的type参数。如果想要Window位于所有Window的最顶层,那么采用较大的层级即可。很显然系统Window的层级是最大的,而且系统层级有很多值,一般我们可以选用TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR,如果采用TYPE_SYSTEM_ERROR,只需要为type参数指定这个层级即可:mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;同时声明权限:uses-permissionandroid:name=“android.permission.SYSTEM_ALERT_WINDOW”/。因为系统类型的Window是需要检查权限的,如果不在AndroidManifest中使用相应的权限,那么创建Window的时候就会报错 WindowManager所提供的功能很简单,常用的只有三个方法,即添加View、更新View和删除View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManager。
public void addView(View view,ViewGroup.LayoutParams params);
public void updateViewLayout(View view,ViewGroup.LayoutParams params);
public void removeView(View view);
WindowManager常用的就只有这三个功能而已,但是这三个功能已经足够我们使用了。它可以创建一个Window并向其添加View,还可以更新Window中的View,另外如果想要删除一个Window,那么只需要删除它里面的View即可。由此来看,WindowManager操作Window的过程更像是在操作Window中的View。我们时常见到那种可以拖动的Window效果,其实是很好实现的,只需要根据手指的位置来设定LayoutParams中的x和y的值即可改变Window的位置。
public boolean onTouch(View v,MotionEvent event) {
int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
mLayoutParams.x = rawX;
mLayoutParams.y = rawY;
mWindowManager.updateViewLayout(mFloatingButton,mLayoutParams);
break;
}
default:
break;
}
return false;
}