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

窗口类型及属性。

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

public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable @WindowManager.java{
//代表通常的应用程序窗口的开始。
	public static final int FIRST_APPLICATION_WINDOW = 1;
//应用窗口的基础值,其他应用窗口都在这之上。
	public static final int TYPE_BASE_APPLICATION   = 1;
//普通的应用窗口,它属于一个Activity token identifying。
	public static final int TYPE_APPLICATION        = 2;
//启动窗口,它不是被应用程序自身用的,而是被系统使用来显示某些东西,在应用程序能显示它自己的窗口之前。
	public static final int TYPE_APPLICATION_STARTING = 3;
//应用窗口的结束值。
	 public static final int LAST_APPLICATION_WINDOW = 99;

//子窗口的起始值,这类必须设置它依附的窗口,在Z-order上,子窗口的类型紧邻它依附的窗口,子窗口的坐标空间也是相对于它的依附窗口。
	public static final int FIRST_SUB_WINDOW = 1000;
//应用程序的panel窗口,比如hint窗口,pop窗口等,这类窗口在父窗口之上。
	public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
//显示多媒体的窗口,如Video,这类窗口在父窗口之下。
	public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
//应用的sub-panel窗口,显示在父窗口和TYPE_APPLICATION_PANEL之上。
	public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
//类似与TYPE_APPLICATION_PANEL,但是这个窗口的layout发生像顶层窗口,而不是向容器的子窗口。
	public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
//多媒体窗口的覆盖层,位于TYPE_APPLICATION_MEDIA 和应用窗口之间,通常是透明的才有意义。
	public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;
//子窗口的结束值。
	public static final int LAST_SUB_WINDOW = 1999;

//系统窗口的起始值。
	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;
//通话窗口,特别值来电,在所有应用窗口之上,在状态栏之下。
	public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
//alert窗口,通常在应用窗口之上。
	public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
//锁屏窗口。
	public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
//短暂的通知窗口。
	public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
//系统的overlay窗口,显示在一切之上,但是不能有input焦点,否则会妨碍keyguard。
	public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
//电话优先窗口,即使keyguard是active的,也会显示。
	public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
//从状态栏下拉出的panel,recentpanel。
	public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
//输入法窗口。
	public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
//墙纸窗口,位于任何想在wallpaper之上的窗口后面。
	public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
//音量条窗口。
	public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
//屏保窗口,仅仅在keyguard之上。
	public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
//系统窗口的结束值。
	public static final int LAST_SYSTEM_WINDOW      = 2999;
}

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


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

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


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

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

finalWindowManagerPolicy mPolicy = new PhoneWindowManager();

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


public int windowTypeToLayerLw(int type)@PhoneWindowManager.java {
        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
            return 2;
        }
        switch (type) {
        case TYPE_PRIVATE_PRESENTATION:
            return 2;
        case TYPE_WALLPAPER:
            // wallpaper is at the bottom, though the window manager may move it.
            return 2;
        case TYPE_DOCK_DIVIDER:
            return 2;
        case TYPE_QS_DIALOG:
            return 2;
        case TYPE_PHONE:
            return 3;
        case TYPE_SEARCH_BAR:
        case TYPE_VOICE_INTERACTION_STARTING:
            return 4;
        case TYPE_VOICE_INTERACTION:
            // voice interaction layer is almost immediately above apps.
            return 5;
        case TYPE_INPUT_CONSUMER:
            return 6;
        case TYPE_SYSTEM_DIALOG:
            return 7;
        case TYPE_TOAST:
            // toasts and the plugged-in battery thing
            return 8;
        case TYPE_PRIORITY_PHONE:
            // SIM errors and unlock.  Not sure if this really should be in a high layer.
            return 9;
        case TYPE_DREAM:
            // used for Dreams (screensavers with TYPE_DREAM windows)
            return 10;
        case TYPE_SYSTEM_ALERT:
            // like the ANR / app crashed dialogs
            return 11;
        case TYPE_INPUT_METHOD:
            // on-screen keyboards and other such input method user interfaces go here.
            return 12;
        case TYPE_INPUT_METHOD_DIALOG:
            // on-screen keyboards and other such input method user interfaces go here.
            return 13;
        case TYPE_KEYGUARD_SCRIM:
            // the safety window that shows behind keyguard while keyguard is starting
            return 14;
        case TYPE_STATUS_BAR_SUB_PANEL:
            return 15;
        case TYPE_STATUS_BAR:
            return 16;
        case TYPE_STATUS_BAR_PANEL:
            return 17;
        case TYPE_KEYGUARD_DIALOG:
            return 18;
        case TYPE_VOLUME_OVERLAY:
            // the on-screen volume indicator and controller shown when the user
            // changes the device volume
            return 19;
        case TYPE_SYSTEM_OVERLAY:
            // the on-screen volume indicator and controller shown when the user
            // changes the device volume
            return 20;
        case TYPE_NAVIGATION_BAR:
            // the navigation bar, if available, shows atop most things
            return 21;
        case TYPE_NAVIGATION_BAR_PANEL:
            // some panels (e.g. search) need to show on top of the navigation bar
            return 22;
        case TYPE_SCREENSHOT:
            // screenshot selection layer shouldn't go above system error, but it should cover
            // navigation bars at the very least.
            return 23;
        case TYPE_SYSTEM_ERROR:
            // system-level error dialogs
            return 24;
        case TYPE_MAGNIFICATION_OVERLAY:
            // used to highlight the magnified portion of a display
            return 25;
        case TYPE_DISPLAY_OVERLAY:
            // used to simulate secondary display devices
            return 26;
        case TYPE_DRAG:
            // the drag layer: input for drag-and-drop is associated with this window,
            // which sits above all other focusable windows
            return 27;
        case TYPE_ACCESSIBILITY_OVERLAY:
            // overlay put by accessibility services to intercept user interaction
            return 28;
        case TYPE_SECURE_SYSTEM_OVERLAY:
            return 29;
        case TYPE_BOOT_PROGRESS:
            return 30;
        case TYPE_POINTER:
            // the (mouse) pointer layer
            return 31;
        }
        Log.e(TAG, "Unknown window type: " + type);
        return 2;
}

public int subWindowTypeToLayerLw(int type) @PhoneWindowManager.java{
        switch (type) {
        case TYPE_APPLICATION_PANEL:
        case TYPE_APPLICATION_ATTACHED_DIALOG:
            return APPLICATION_PANEL_SUBLAYER;
        case TYPE_APPLICATION_MEDIA:
            return APPLICATION_MEDIA_SUBLAYER;
        case TYPE_APPLICATION_MEDIA_OVERLAY:
            return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
        case TYPE_APPLICATION_SUB_PANEL:
            return APPLICATION_SUB_PANEL_SUBLAYER;
        case TYPE_APPLICATION_ABOVE_SUB_PANEL:
            return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
        }
        Log.e(TAG, "Unknown sub-window type: " + type);
        return 0;
    }


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

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


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

窗口标记的设置方法:

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相关的。


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

	public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
	public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
	public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
	public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
	public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
...
}

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

intviewFlag = View.SYSTEM_UI_FLAG_FULLSCREEN |SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

|SYSTEM_UI_FLAG_LAYOUT_STABLE;

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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值