Android 7.1 GUI系统-窗口管理WMS-窗口属性(二)

谁借莪1个温暖的怀抱¢ 2022-06-04 08:46 621阅读 0赞

窗口类型及属性。

1),Android都有那些窗口类型,定义在WindowManager.java的内部类LayoutParams中。

  1. public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable @WindowManager.java{
  2. //代表通常的应用程序窗口的开始。
  3. public static final int FIRST_APPLICATION_WINDOW = 1;
  4. //应用窗口的基础值,其他应用窗口都在这之上。
  5. public static final int TYPE_BASE_APPLICATION = 1;
  6. //普通的应用窗口,它属于一个Activity token identifying。
  7. public static final int TYPE_APPLICATION = 2;
  8. //启动窗口,它不是被应用程序自身用的,而是被系统使用来显示某些东西,在应用程序能显示它自己的窗口之前。
  9. public static final int TYPE_APPLICATION_STARTING = 3;
  10. //应用窗口的结束值。
  11. public static final int LAST_APPLICATION_WINDOW = 99;
  12. //子窗口的起始值,这类必须设置它依附的窗口,在Z-order上,子窗口的类型紧邻它依附的窗口,子窗口的坐标空间也是相对于它的依附窗口。
  13. public static final int FIRST_SUB_WINDOW = 1000;
  14. //应用程序的panel窗口,比如hint窗口,pop窗口等,这类窗口在父窗口之上。
  15. public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
  16. //显示多媒体的窗口,如Video,这类窗口在父窗口之下。
  17. public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
  18. //应用的sub-panel窗口,显示在父窗口和TYPE_APPLICATION_PANEL之上。
  19. public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
  20. //类似与TYPE_APPLICATION_PANEL,但是这个窗口的layout发生像顶层窗口,而不是向容器的子窗口。
  21. public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
  22. //多媒体窗口的覆盖层,位于TYPE_APPLICATION_MEDIA 和应用窗口之间,通常是透明的才有意义。
  23. public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
  24. //子窗口的结束值。
  25. public static final int LAST_SUB_WINDOW = 1999;
  26. //系统窗口的起始值。
  27. public static final int FIRST_SYSTEM_WINDOW = 2000;
  28. //状态栏窗口。
  29. public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
  30. //搜索条窗口。
  31. public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
  32. //通话窗口,特别值来电,在所有应用窗口之上,在状态栏之下。
  33. public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
  34. //alert窗口,通常在应用窗口之上。
  35. public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
  36. //锁屏窗口。
  37. public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
  38. //短暂的通知窗口。
  39. public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
  40. //系统的overlay窗口,显示在一切之上,但是不能有input焦点,否则会妨碍keyguard。
  41. public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;
  42. //电话优先窗口,即使keyguard是active的,也会显示。
  43. public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
  44. //从状态栏下拉出的panel,recentpanel。
  45. public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
  46. //输入法窗口。
  47. public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
  48. //墙纸窗口,位于任何想在wallpaper之上的窗口后面。
  49. public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
  50. //音量条窗口。
  51. public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
  52. //屏保窗口,仅仅在keyguard之上。
  53. public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
  54. //系统窗口的结束值。
  55. public static final int LAST_SYSTEM_WINDOW = 2999;
  56. }

当进程想WMS申请一个窗口时,要指定窗口类型,WMS会根据申请的窗口类型及当前系统中已有窗口的情况,分配层级值,层级越大的窗口越靠近用户。层级的分配规则主要由assignLayersLocked实现。

  1. final void assignLayersLocked(WindowList windows)@WindowLayersController.java {
  2. int curBaseLayer = 0;
  3. int curLayer = 0;
  4. //windows保存了当前系统中的所有窗口。
  5. for (int i = 0, windowCount = windows.size(); i < windowCount; i++) {
  6. final WindowState w = windows.get(i);
  7. boolean layerChanged = false;
  8. int oldLayer = w.mLayer;
  9. if (w.mBaseLayer == curBaseLayer || w.mIsImWindow || (i > 0 && w.mIsWallpaper)) {
  10. //这个窗口的基础层级跟前一个窗口的基础层级一致,只要添加间隔WINDOW_LAYER_MULTIPLIER。这个间隔值是5。
  11. curLayer += WINDOW_LAYER_MULTIPLIER;
  12. }else{
  13. //如果不一致,把 curBaseLayer设置为这个窗口的值。
  14. curBaseLayer = curLayer = w.mBaseLayer;
  15. }
  16. }
  17. }

一个窗口的mBaseLayer的值是怎么来的呢?一个窗口有关的信息都是在WindowState中保存的。

  1. WindowState(...WindowState attachedWindow,WindowManager.LayoutParams a,...)@WindowState.java{
  2. if ((mAttrs.type >= FIRST_SUB_WINDOW &&mAttrs.type <= LAST_SUB_WINDOW)) {
  3. //如果子窗口,层级值取决于父窗口的类型,每种类型在窗口管理策略中有一个对照表,来确定基础层级值。
  4. mBaseLayer = mPolicy.windowTypeToLayerLw(attachedWindow.mAttrs.type)
  5. *WindowManagerService.TYPE_LAYER_MULTIPLIER
  6. + WindowManagerService.TYPE_LAYER_OFFSET;
  7. //子窗口,还有一个相对父窗口的偏移值,这个偏移值决定了子窗口可能在父窗口之上,也可能在父窗口之下。
  8. mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
  9. }else{
  10. //基础层级值还要乘上TYPE_LAYER_MULTIPLIER(10000),加上偏移 TYPE_LAYER_OFFSET(1000),因为系统中可能出现同类型的多个窗口,所以要有一个较大的间隔。
  11. mBaseLayer = mPolicy.windowTypeToLayerLw(a.type)
  12. * WindowManagerService.TYPE_LAYER_MULTIPLIER
  13. + WindowManagerService.TYPE_LAYER_OFFSET;
  14. }
  15. }

上面计算层级值时,用到mPolicy,就是窗口管理策略类的实例。Android手机系统,默认的窗口管理策略类是PhoneWindowManager,在WindowManagerService.java中直接实例化了相应对象。

finalWindowManagerPolicy mPolicy = new PhoneWindowManager();

所以WindowState构造函数中用到的两个函数windowTypeToLayerLw,subWindowTypeToLayerLw都是PhoneWindowManager中的方法。

  1. public int windowTypeToLayerLw(int type)@PhoneWindowManager.java {
  2. if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
  3. return 2;
  4. }
  5. switch (type) {
  6. case TYPE_PRIVATE_PRESENTATION:
  7. return 2;
  8. case TYPE_WALLPAPER:
  9. // wallpaper is at the bottom, though the window manager may move it.
  10. return 2;
  11. case TYPE_DOCK_DIVIDER:
  12. return 2;
  13. case TYPE_QS_DIALOG:
  14. return 2;
  15. case TYPE_PHONE:
  16. return 3;
  17. case TYPE_SEARCH_BAR:
  18. case TYPE_VOICE_INTERACTION_STARTING:
  19. return 4;
  20. case TYPE_VOICE_INTERACTION:
  21. // voice interaction layer is almost immediately above apps.
  22. return 5;
  23. case TYPE_INPUT_CONSUMER:
  24. return 6;
  25. case TYPE_SYSTEM_DIALOG:
  26. return 7;
  27. case TYPE_TOAST:
  28. // toasts and the plugged-in battery thing
  29. return 8;
  30. case TYPE_PRIORITY_PHONE:
  31. // SIM errors and unlock. Not sure if this really should be in a high layer.
  32. return 9;
  33. case TYPE_DREAM:
  34. // used for Dreams (screensavers with TYPE_DREAM windows)
  35. return 10;
  36. case TYPE_SYSTEM_ALERT:
  37. // like the ANR / app crashed dialogs
  38. return 11;
  39. case TYPE_INPUT_METHOD:
  40. // on-screen keyboards and other such input method user interfaces go here.
  41. return 12;
  42. case TYPE_INPUT_METHOD_DIALOG:
  43. // on-screen keyboards and other such input method user interfaces go here.
  44. return 13;
  45. case TYPE_KEYGUARD_SCRIM:
  46. // the safety window that shows behind keyguard while keyguard is starting
  47. return 14;
  48. case TYPE_STATUS_BAR_SUB_PANEL:
  49. return 15;
  50. case TYPE_STATUS_BAR:
  51. return 16;
  52. case TYPE_STATUS_BAR_PANEL:
  53. return 17;
  54. case TYPE_KEYGUARD_DIALOG:
  55. return 18;
  56. case TYPE_VOLUME_OVERLAY:
  57. // the on-screen volume indicator and controller shown when the user
  58. // changes the device volume
  59. return 19;
  60. case TYPE_SYSTEM_OVERLAY:
  61. // the on-screen volume indicator and controller shown when the user
  62. // changes the device volume
  63. return 20;
  64. case TYPE_NAVIGATION_BAR:
  65. // the navigation bar, if available, shows atop most things
  66. return 21;
  67. case TYPE_NAVIGATION_BAR_PANEL:
  68. // some panels (e.g. search) need to show on top of the navigation bar
  69. return 22;
  70. case TYPE_SCREENSHOT:
  71. // screenshot selection layer shouldn't go above system error, but it should cover
  72. // navigation bars at the very least.
  73. return 23;
  74. case TYPE_SYSTEM_ERROR:
  75. // system-level error dialogs
  76. return 24;
  77. case TYPE_MAGNIFICATION_OVERLAY:
  78. // used to highlight the magnified portion of a display
  79. return 25;
  80. case TYPE_DISPLAY_OVERLAY:
  81. // used to simulate secondary display devices
  82. return 26;
  83. case TYPE_DRAG:
  84. // the drag layer: input for drag-and-drop is associated with this window,
  85. // which sits above all other focusable windows
  86. return 27;
  87. case TYPE_ACCESSIBILITY_OVERLAY:
  88. // overlay put by accessibility services to intercept user interaction
  89. return 28;
  90. case TYPE_SECURE_SYSTEM_OVERLAY:
  91. return 29;
  92. case TYPE_BOOT_PROGRESS:
  93. return 30;
  94. case TYPE_POINTER:
  95. // the (mouse) pointer layer
  96. return 31;
  97. }
  98. Log.e(TAG, "Unknown window type: " + type);
  99. return 2;
  100. }
  101. public int subWindowTypeToLayerLw(int type) @PhoneWindowManager.java{
  102. switch (type) {
  103. case TYPE_APPLICATION_PANEL:
  104. case TYPE_APPLICATION_ATTACHED_DIALOG:
  105. return APPLICATION_PANEL_SUBLAYER;
  106. case TYPE_APPLICATION_MEDIA:
  107. return APPLICATION_MEDIA_SUBLAYER;
  108. case TYPE_APPLICATION_MEDIA_OVERLAY:
  109. return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
  110. case TYPE_APPLICATION_SUB_PANEL:
  111. return APPLICATION_SUB_PANEL_SUBLAYER;
  112. case TYPE_APPLICATION_ABOVE_SUB_PANEL:
  113. return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
  114. }
  115. Log.e(TAG, "Unknown sub-window type: " + type);
  116. return 0;
  117. }

2),窗口都有那些属性。

窗口属性除了前面的窗口类型外,还有窗口标记,都是放置在WindowManager.LayoutParams中。

  1. public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable @WindowManager.java{
  2. //只要这个窗口是可见的,即使屏幕是开启的也允许锁屏,可以单独使用,也可以结合FLAG_KEEP_SCREEN_ON,或者
  3. FLAG_SHOW_WHEN_LOCKED
  4. public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
  5. //在这个窗口背后的所有东西都会变暗,使用dimAmount控制变暗的程度。
  6. public static final int FLAG_DIM_BEHIND = 0x00000002;
  7. //这个窗口不获取按键输入焦点,所以不能把key或别的button事件给它,这些事件会发给它后面的窗口,这个标记会enable FLAG_NOT_TOUCH_MODAL。
  8. public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
  9. //这个窗口不接收touch 事件。
  10. public static final int FLAG_NOT_TOUCHABLE = 0x00000010;
  11. //如果FLAG_NOT_FOCUSABLE没有设置,这个窗口是focusable的,允许这个窗口外的pointer事件发给它后面的窗口,如果这个窗口是not focusable的,它自己会处理所有的pointer事件。
  12. public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
  13. //设置这个flag,如果设备睡眠了,你可以接收到第一次touch 事件,通常第一次的touch事件是被系统消费,因为用户看不到他们按到的是什么。
  14. public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
  15. //只要窗口是可见的,就要设备保持屏幕是亮的。
  16. public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
  17. //让窗口在整个屏幕范围内,忽略边上的装饰条(如状态栏)。
  18. public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;
  19. //允许窗口超过屏幕区域。
  20. public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200;
  21. //隐藏所有的屏幕装饰条,全屏的窗口会忽略SOFT_INPUT_ADJUST_RESIZE整个值,窗口依然保持全屏不会resize。
  22. 这个flag可以在主题中控制,如:android.R.attr#windowFullscreen,Theme_Black_NoTitleBar_Fullscreen。。。
  23. public static final int FLAG_FULLSCREEN = 0x00000400;
  24. //会覆盖掉FLAG_FULLSCREEN,强制显示屏幕装饰条。
  25. public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
  26. //认为窗口的内容是受保护的,禁止出现在屏幕截图中,或者不安全的显示中。
  27. public static final int FLAG_SECURE = 0x00002000;
  28. //有时跟屏幕贴的很近,如打电话时,这种情况下的某些事件可能是无意的,不应该响应。
  29. public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;
  30. //窗口可以显示在锁屏窗口之上,结合FLAG_KEEP_SCREEN_ON,点亮屏幕时在显示锁屏窗口前,可以直接显示这个窗口;结合FLAG_DISMISS_KEYGUARD,可以自动退出非安全的锁屏。
  31. public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
  32. //系统墙纸会显示在窗口后面,只有窗口surface是透明的区域才能看到后面的墙纸。可以通过主题控制,android.R.attr#windowShowWallpaper,android.R.style#Theme_Wallpaper_NoTitleBar...
  33. public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
  34. //窗口显示时,点亮屏幕。
  35. public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
  36. //窗口显示时,退出非安全锁屏。
  37. public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
  38. //设置这个flag的窗口会接收它范围之外的touch事件,发送给支持多点触控的其他窗口。
  39. public static final int FLAG_SPLIT_TOUCH = 0x00800000;
  40. //窗口是否需要硬件加速。可以通过编程的方式控制,比如:
  41. Window w = activity.getWindow();
  42. in Activity's onCreate();
  43. w.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
  44. WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
  45. 这个flag的设置要在setContentView之前。
  46. 如果在manifest中用android.R.attr#hardwareAccelerated打开了硬件加速,上面的方法不能disable。
  47. 这个flag在activity和应用的xml属性中,是被系统默认enable的,在manifest把它disable,就可以在Activity或者dialog中通过上面的方法enable或者disable。
  48. public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000;
  49. //允许窗口的内容超出屏幕的overscan区域,窗口应该正确的调整它的内容来适应overscan区域。
  50. 这个flag也可以在主题中控制,android.R.attr#windowOverscan,android.R.style#Theme_Holo_NoActionBar_Overscan…
  51. 设置这个flag的窗口,正常的内容出现在overscan区域可能会有一定程度的模糊,为了确保内容的关键部分可以对用户可见,可以使用View.setFitsSystemWindows(boolean),设置view层次中一些点有适当的偏移量。
  52. 类似的方法属性有:android.R.attr#fitsSystemWindows,View.fitSystemWindows(Rect),View.setSystemUiVisibility(int)...
  53. public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000;
  54. //设置透明的状态栏,可以在主题中通过属性控制:android.R.attr#windowTranslucentStatus,android.R.style#Theme_Holo_NoActionBar_TranslucentDecor…
  55. 设置这个flag的窗口,会自动设置View#SYSTEM_UI_FLAG_LAYOUT_STABLE,View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
  56. public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
  57. //使窗口置于本地焦点模式,这个模式下的窗口可以使用Window#setLocalFocus(boolean, boolean),控制焦点独立于windowmanager,通常这个模式下的窗口不会从WindowManager得到touch,key事件,而是仅仅通过本地injection(Window#injectInputEvent(InputEvent)).
  58. public static final int FLAG_LOCAL_FOCUS_MODE = 0x10000000;
  59. }

窗口标记的设置方法:

Windoww = activity.getWindow();

w.setFlags(W,indowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,

WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

3),除了WindowManager.LayoutParams中的属性外,View.java中的一些Flag也会影响窗口的显示,如SystemUI相关的。

  1. public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource @View.java{
  2. //setSystemUiVisibility(int),view请求systemUI可见。
  3. public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
  4. //View请求systemUI进入低调模式,通常用于games,book readers,video plahers,或者其他身临其境的应用,这个模式下,状态栏或者导航图标会变暗。
  5. public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;
  6. //View请求进入普通的全屏模式,以便在用户跟应用交互时,它的内容可以占据整个屏幕。
  7. 这个flagWindowManager.LayoutParams.FLAG_FULLSCREEN有相同的视觉效果,在这中模式下的view 窗口,非关键性的屏幕装饰都会被隐藏,如果通过Window.FEATURE_ACTION_BAR_OVERLAYActionBar处于overlay模式,SYSTEM_UI_FLAG_FULLSCREEN这个flag也会隐藏actionbar
  8. 如果需要长时间的全屏,使用WindowManager.LayoutParams#FLAG_FULLSCREEN是较好的选择,如果是短暂的全屏可以使用SYSTEM_UI_FLAG_FULLSCREEN。
  9. public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
  10. public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
  11. public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
  12. public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
  13. public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
  14. public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
  15. ...
  16. }

可以通过setSystemUiVisibility(int)设置一个View的属性,如果要设置整个Viewtree的属性,可以在Activity的onCreate中,setContentView之前,通过以下代码设置:

intviewFlag = View.SYSTEM_UI_FLAG_FULLSCREEN |SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

|SYSTEM_UI_FLAG_LAYOUT_STABLE;

getWindow(),getDecorView().setSystemUiVisibility(viewFlag);

发表评论

表情:
评论列表 (有 0 条评论,621人围观)

还没有评论,来说两句吧...

相关阅读