相关文章:
Android 知识点总结(目录) https://blog.csdn.net/a136447572/article/details/81027701
理解Window和WindowManager
Window相关的主要有以下几个类、接口:
Window抽象类、Window.Callback接口,WindowManager接口、ViewManager接口、WindowManagerImpl实现类、WindowManagerGlobal类、ViewRootImpl类。
先把这几个类的作用、特性、工作流程总结一下,捋一捋思路,然后再去分析每个类,这样思路会更清晰。
1、Window表示一个窗口的概念,只有一个唯一实现类PhoneWindow,所有的能让用户看到的组件都是通过Window来展现的,Window规定了UI的展现方式、接收用户的触摸等交互,然后传给各个组件;这些组件通过实现Window.Callback接口,就可以接受到Window的通知了;
2、Window持有一个WindowManager对象,该对象的主要作用就是帮助Window完成部分功能实现,比如添加View、删除View(ViewManager定义了添加、删除View,WindowManager继承自ViewManager);
3、WindowManagerImpl就是WindowManager的具体实现(非唯一实现),大部分功能都在这里完成。除了完成自己的本职工作外,WindowManagerImpl还要将Window和View的显示通知给系统,所以这个类持有一个WindowManagerGlobal对象,该对象是单例、全局存在,的主要作用就是对Window和View的全局管理;
4、WindowManagerGlobal做两件事,一件是添加/删除/更新View(真正的执行者是ViewRootImpl),另一件就是通过List管理着所有View、ViewRootImpl、LayoutParamas等。而ViewRootImpl则通过performTraversals()发起View的绘制流程。
5、Activity通过new PhoneWindow()得到Window,通过getSystemService()得到WindowManager,通过mWindow.setWindowManager()方法使Window和WindowManager绑定,通过实现Window.Callback接口接收Window的通知。
通过WindowManager来创建一个Window
newBtn.setText("新建button");
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams layout = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,0,0, PixelFormat.TRANSLUCENT);
layout.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
layout.gravity = Gravity.CENTER;
layout.type = WindowManager.LayoutParams.TYPE_PHONE;
layout.x = 500;
layout.y = 500;
wm.addView(newBtn,layout);
FLAG_NOT_FOCUSABLE
表示窗口不需要获取焦点,也不需要接收各种事件,这属性会同时启动FLAG_NOT_TOUCH_MODAL,最终的事件会传递给下层的具体焦点的window
FLAG_NOT_TOUCH_MODAL,
在此模式下,系统会将当前window区域以外的单击事件传递给底层的Window,此前的Window区域以内的单机事件自己处理,这个标记很重要,一般来说都需要开启,否则其他window将无法获取单击事件
FLAG_SHOW_WHEN_LOCKED
开启这个属性可以让window显示在锁屏上
Window是分层的,每个Window对应着z-ordered,层级大的会覆盖在层级小的Window上面,这和HTML中的z-index的概念是一致的,在这三类中,应用是层级范围是1-99,子window的层级是1000-1999,系统的层级是2000-2999。这些范围对应着type参数,如果想要window在最顶层,那么层级范围设置大一点就好了,很显然系统的值要大一些,系统的值很多,我们一般会选择TYPE_SYSTEM_OVERLAY和TYPE_SYSTEM_ERROR,记得要设置权限哦;
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
WindowManager.LayoutParams的各种flag含义翻译了官方文档的flag部分
1
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
只要这个window对用户是可见的,则允许在屏幕开启的时候锁定屏幕
这个flag可以单独的使用,也可以配合FLAG_KEEP_SCREEN_ON和(或者) FLAG_SHOW_WHEN_LOCKED使用
2
FLAG_DIM_BEHIND
所有在这个window之后的会变暗,
使用dimAmount属性来控制变暗的程度(1.0不透明,0.0完全透明)
3
FLAG_NOT_FOCUSABLE
设置之后window永远不会获取焦点,所以用户不能给此window发送点击事件
焦点会传递给在其下面的可获取焦点的window
这个flag同时会启用 FLAG_NOT_TOUCH_MODAL flag , 不管你有没有手动设置
设置这个flag同时表明了这个window不会和软键盘交互,
(这句话的翻译我不知道对不对)所以window会独立于激活的软键盘之上(这句话的意思就是window会在Z轴上置于输入法之上,所以window可以全屏使用来覆盖住输入法,你可以使用 FLAG_ALT_FOCUSABLE_IM 来修改这个行为)
4
FLAG_NOT_TOUCHABLE
这个window永远无法获取点击事件
5
FLAG_NOT_TOUCH_MODAL
即使这个window是可获取焦点的,
也允许window之外点击事件传递给其他在其之后的window
如果不设置这个值,则window消费掉所有点击事件,不管这些点击事件是不是在window的范围之内
//如果要做悬浮框,我想这个flag肯定得设置,但api>=23就别想了
这个flag简而言之就是说,当前window区域以外的点击事件传递给下层window,当前window区域以内的点击事件自己处理
6
FLAG_TOUCHABLE_WHEN_WAKING
//This constant was deprecated in API level 20.等于说没什么吊用了
//This flag has no effect.
当设置了这个值,则device休眠的时候,当触摸屏被点击,window会收到首次点击事件,通常当用户看不见东西时,首次点击事件会被系统消费
7
FLAG_KEEP_SCREEN_ON
当这个window对用户是可见状态,则保持设备屏幕不关闭且不变暗
8
FLAG_LAYOUT_IN_SCREEN
将window放置在整个屏幕之内,无视其他的装饰(比如状态栏)
window要在考虑到屏幕的其他装饰来定位其中的内容
这个flag通常使用Window类的setFlags(int, int)方法来设置
9
FLAG_LAYOUT_NO_LIMITS
允许window扩展值屏幕之外
10
FLAG_FULLSCREEN
当这个window显示的时候,隐藏所有的装饰物(比如状态栏)
这个flag允许window使用整个屏幕区域
当设置这个flag的window处于顶层的时候,状态栏会被隐藏
全屏的时候会忽略 softInputMode 变量设置的SOFT_INPUT_ADJUST_RESIZE 效果,屏幕会不保持全屏且不会resize
这个flag可以在主题属性(theme)中来控制
通过 windowFullscreen 属性来控制
在一些常用的全屏主题中这个属性已经被设置好了
常用全屏主题: Theme_NoTitleBar_Fullscreen,
Theme_Black_NoTitleBar_Fullscreen,
Theme_Light_NoTitleBar_Fullscreen,
Theme_Holo_NoActionBar_Fullscreen,
Theme_Holo_Light_NoActionBar_Fullscreen,
Theme_DeviceDefault_NoActionBar_Fullscreen,
Theme_DeviceDefault_Light_NoActionBar_Fullscreen.
11
FLAG_FORCE_NOT_FULLSCREEN
覆盖FLAG_FULLSCREEN效果,并强制显示屏幕上的一些装饰(如状态栏)
12
FLAG_SECURE
把这个window中的内容看作需要保护的内容,
防止被截屏,或防止内容显示在一些不安全的屏幕上
see https://developer.android.google.cn/reference/android/view/Display.html#FLAG_SECURE for more details about secure surfaces and secure displays
13
FLAG_IGNORE_CHEEK_PRESSES
这个flag一般用于,当用户把脸贴在屏幕上,它会过滤不需要的点击事件
当检测到一个事件流,这个程序会直接接收到一个CANCEL事件.
这样程序可以正确的处理这种情况,
直到手指离开屏幕
14
FLAG_LAYOUT_INSET_DECOR
这个flag只能配合 FLAG_LAYOUT_IN_SCREEN 一起使用.
当在屏幕中请求layout时,window可能在一些装饰物(如状态栏)之上或者之后
当使用这个flag时,window manager会报告插入window的矩形大小,
来确保你的内容不会被装饰物(如状态栏)掩盖.
这个flag一般用Window类的 setFlags(int, int)方法来设置
15
FLAG_ALT_FOCUSABLE_IM
转变 FLAG_NOT_FOCUSABLE 设置的状态,关于这个window是怎么和当前的输入法交互
如果设置了FLAG_NOT_FOCUSABLE且FLAG_ALT_FOCUSABLE_IM也设置了,那么当这个window和input method交互的时候会被放置在input method后面或者旁边.
如果FLAG_NOT_FOCUSABLE没有设置而设置了FLAG_ALT_FOCUSABLE_IM,那么window不需要和input method交互,可以放置在输入法上面
//这个实际上是我强行理解的,翻译得可能不准确,如果要使用的话,还是要自己测试下
16
FLAG_WATCH_OUTSIDE_TOUCH
如果你已经设置了FLAG_NOT_TOUCH_MODAL,那么你可以设置FLAG_WATCH_OUTSIDE_TOUCH这个flag,
这样一个点击事件如果发生在你的window之外的范围,你就会接收到一个特殊的MotionEvent,MotionEvent.ACTION_OUTSIDE
注意,你只会接收到点击事件的第一下,而之后的DOWN/MOVE/UP等手势全都不会接收到
17
FLAG_SHOW_WHEN_LOCKED
一个特殊的flag,使得window可以在锁屏状态下显示
这个flag会使得window比keyguard或其他锁屏界面具有更高的层级
可以配合FLAG_KEEP_SCREEN_ON使用,点亮屏幕,在显示keyguard window之前显示你的window.
可以配合FLAG_DISMISS_KEYGUARD使用来自动解锁没密码的keyguards
这个flag只能应用在最顶层的全屏window上
用人话说就是可以让window显示在锁屏界面上
18
FLAG_SHOW_WALLPAPER //实测没吊用,也有可能我理解得不对
//Constant Value: 1048576 (0x00100000)
请求系统将壁纸显示在window后面,这个window表面需要是透明的才能显示出墙纸.
这个flag只能保证如果你的window有透明的区域,墙纸会显示在那
这个flag也可以在theme中使用 windowShowWallpaper 这个属性来设置.
有些主题已经设置好: Theme_Wallpaper,
Theme_Wallpaper_NoTitleBar,
Theme_Wallpaper_NoTitleBar_Fullscreen,
Theme_Holo_Wallpaper, Theme_Holo_Wallpaper_NoTitleBar,
Theme_DeviceDefault_Wallpaper,
Theme_DeviceDefault_Wallpaper_NoTitleBar.
19
FLAG_TURN_SCREEN_ON
当window被添加或者显示,系统会点亮屏幕,就好像用户唤醒屏幕一样
20
FLAG_DISMISS_KEYGUARD
当使用的是无密码的锁屏界面,显示此window会使锁屏界面被自动解锁,其他废话就不翻译了
21
FLAG_SPLIT_TOUCH
当window设置这个flag,window会接收来自window边界之外发送给其他window的点击事件,支持多点触控.
当这个flag没有设置的时候,第一下点击则决定了哪个window会接收整个点击事件,直到手指拿开.
当设置了这个flag,这每一个点击事件(不一定是第一个)都决定了那个window来接收剩下的点击事件,
直到手指拿开.点击事件会被分开传递给多个window.
貌似是关于多点触控的,我TM自己也没看明白
原文:Window flag: when set the window will accept for touch events outside of its bounds to be sent to other windows that also support split touch. When this flag is not set, the first pointer that goes down determines the window to which all subsequent touches go until all pointers go up. When this flag is set, each pointer (not necessarily the first) that goes down determines the window to which all subsequent touches of that pointer will go until that pointer goes up thereby enabling touches with multiple pointers to be split across multiple windows.
22
FLAG_HARDWARE_ACCELERATED
表示这个window是否启动硬件加速,请求硬件加速但不能保证硬件加速生效
如果仅是用来启动硬件加速,可以在代码中控制,使用下面的代码给指定window启动硬件加速:
Window w = activity.getWindow(); // in Activity's onCreate() for instance
w.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
有一件很重要的事需要记住,这个flag需要在Activity和dialog添加view之前来配置
当你在manifest中设置了硬件加速后,这个flag不能用来取消硬件加速
当在Activity标签或者application标签中设置了 hardwareAccelerated = true属性这个flag就自动设置上了
23
FLAG_LOCAL_FOCUS_MODE
设置flag启用local focus模式(我也不知道local focus mode是什么),
在这种模式下window可以不依赖windowmanager独立的控制焦点,
使用window的 setLocalFocus(boolean, boolean) 方法即可
通常在这种模式下,window不会通过windowmanager获取到touch/key event,
但是会通过local injection(我不知道这是什么)得到event,
by using injectInputEvent(InputEvent) 方法(貌似是手动的强行向一个window插入一个input event)
24
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
这个flag表示window负责绘制状态栏的北京
当设置了这个flag,系统状态栏会变透明,同时这个相应的区域会被填满 getStatusBarColor() and getNavigationBarColor()的颜色,
WindowManager所提供的功能很简单,常用的有三个方法,添加View,更新View,删除View,这三个方法定义在ViewManager中,而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);
}
参考文章
Android开发艺术探索——第八章:理解Window和WindowManager