第八章 理解 Window 和 WindowManager
在桌面上需要展示一个类似悬浮窗的东西,需要使用到 Window 来实现。
Window 是一个抽象类,具体实现是 PhoneWindow。WindowManger 是外界访问 Window 的入口,Window的具体实现位于 WindowMangerService 中,WindowManager 和 WindowMangerService的交互是一个 IPC 过程。
Android 中的所有视图都是通过 Window 来呈现的,Activity、Dialog 和 Toast 的视图都是附加在 Window 上,因此,Window 也是 View 的直接管理者。
View 的事件分发机制:单击事件由 Window 传递给 DectorView,然后再由 DecorView 传递给我们的 View。连Activity 设置视图的方法 setContentView() 在底层也是通过 Window 完成。
8.1 Window 和 WindowManager
使用 WindowManager 添加一个 Window:
/**
* 当手机是锁屏状态的时候,此时运行代码,则会展现在锁屏界面上,可返回到锁屏界面
* 锁屏时点击不会弹出Toast,但是会走onClick方法。
*/
final Button button = new Button(getApplicationContext());
button.setText("text");
final WindowManager.LayoutParams layoutParams
= new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSPARENT);
layoutParams.flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
;
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
/**
* 不设置gravity或者设置为CENTER,则下面的x,y设置无效
*/
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
/**
* layoutParams.x/y:是组件的左上角坐标
* event.getRawX/Y:是手指相对于屏幕的左上角的距离
* layoutParams.y:不包括通知栏的高度,y = event.getRawX - event.getX - 通知栏的高度
*/
layoutParams.x = 0;
layoutParams.y = 0;
final WindowManager windowManager = getWindowManager();
windowManager.addView(button, layoutParams);
Flags:表示 Window 的属性。
FLAG_SHOW_WHEN_LOCKED:开启此模式可以让 Window 出现在锁屏上。
FLAG_SHOW_NOT_FOCUSABLE:表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会启用FLAG_NOT_TOUCH_MODAL,最终事件会传递给下层的具有焦点的Window。
FLAG_NOT_TOUCH_MODAL:系统会将当前 Window 区域以外的单击事件传递给底层的Window,当前的Window区域以内的单击事件则自己处理。一般都需要开启此标记,否则其他 Window 无法收到单击事件
Type:表示 Window 的类型,Window 有三种类型,应用 Window、子 Window 和系统 Window,Window 是分层的,每隔 Window 都有对应的 z-ordered,层级打的会覆盖层级小的 Window的上面。
应用类 Window:对应一个 Activity,层级范围是 1 ~ 99。
子 Window:不能单独存在,需要附属在特定的父 Window 之中,比如常见的 Dialog;层级范围是1000 ~ 1999
系统 Window:需要声明权限在能创建的 Window,比如 Toast 和系统状态栏;层级范围是2000 ~ 2999。
WindowManager 继承 ViewManager,常用方法:添加 View(addView),更新 View(updateViewLayout),删除 View(removeView).这三个方法定义在 ViewManager 中。
WindowManager 操作 Window 的过程更像是在操作 Window 中的 View。
8.2 Window 的内部机制
每一个 Window 都对应着一个 View 和一个 ViewRootImpl,Window 和 View 通过 ViewRootImpl 来建立联系。实际使用中无法直接访问 Window,对 Window 的访问必须通过 WindowManager。
8.2.1 Window 的添加过程
Session的个人理解:
在WindowManagerGlobal
中新建一个 ViewRootImpl 对象,而 ViewRootImpl 会调用WindowManagerGlobal.getWindowSession()
,在该方法中首先会获取WMS对象,通过调用windowManager.openSession
获取 Session 对象,之后WindowManagerGlobal
便通过 ViewRootImpl 调用 Session 的 add、remove 方法,在 add、remove 方法中会调用 WMS 的 addWindow、removeWindow
。
8.2.2 Window 的删除过程
8.2.3 Window 的更新过程
8.3 Window 的创建过程
View 必须附着在 Window 的上面,有视图的地方就有Window,因此,Activity、Dialog、Toast等视图都对应着一个Window。
8.3.1 Acticity 的 Window 创建过程
创建Window:
setContentView():
8.3.2 Dialog 的 Window 创建过程
new Dialog(this).setContentView(view/resourceId)show();
1. 创建 Window
和Activity非常类似
Dialog(Context context, int theme, boolean createContextThemeWrapper) {
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
/**
* Window的创建也是通过PolicyManager.makeNewWindow来完成,创建后的对象实际上就是PhoneWindow。
*/
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
}
2. 初始化DecorView并将Dialog的视图添加到DecorView中
通过window添加指定的文件。
public void setContentView(int layoutResID) {
mWindow.setContentView(layoutResID);
}
3. 将DecorView添加到Window中显示
public void show() {
/**
* 通过WindowManager将DecorView添加到Window中
*/
mWindowManager.addView(mDecor, l);
}
- 当 Dialog 被关闭时,会通过 WindowManager 来移除DecorView:
/**
* 移除DecorView
*/
mWindowManager.removeViewImmediate(mDecor);
上一篇: 使用RMAN备份时应如何处置归档日志文件
下一篇: 切钢管