window固定一个进程显示在另外一个屏幕_直面底层:探索 Android 中的 Window

本文作者

作者:HiDhl

链接:

https://juejin.im/post/6844904180306411528

本文由作者授权发布。

直面底层系列文章上期:

直面底层:Android 渲染机制, Choreographer 必须得了解

1 引言
  • 这是 Android 10 源码分析系列

  • 分支:android-10.0.0_r14

  • 全文阅读大概 10 分钟

在之前的文章 0xA06 Android 10 源码分析:WindowManager 视图绑定以及体系结构 介绍了  Activity、Window、PhoneWindow、WindowManager 之间的关系,以及 Activity 和 Dialog 的视图绑定过程,而这篇文章主要两个目的:

  1. 对上一篇文章 0xA06 Android 10 源码分析:WindowManager 视图绑定以及体系结构 做深入的了解

  2. 为后面的篇文章「如何在 Andorid 系统里添加自定义 View」等等做好铺垫

通过这篇文章你将学习到以下内容,将在文末总结部分会给出相应的答案

  • Window 都有那些常用的参数?

  • Window 都那些类型?每个类型的意思?以及作用?

  • Window 那些过时的 API 以及处理方案?

  • Window 视图层级顺序是如何确定的?

  • Window 都那些 flag?每个 flag 的意思?以及作用?

  • Window 的软键盘模式?每个模式的意思?以及如何使用?

  • Kotlin 小技巧?

在开始分析之前,我们先来看一张图,熟悉一下几个基本概念,这些概念伴将随着整篇文章

3e44f2f995190c0904dd203dd7d18b83.gif
  • 我们在手机上看到的界面是二维的,但是实际上是一个三维,如上图所示

  • Window:是一个抽象类,它作为一个顶级视图添加到 WindowManager 中,View 是依附于 Window 而存在的,对 View 进行管理

  • WindowManager:它是一个接口,继承自接口 ViewManager,对 Window 进行管理

  • PhoneWindow:Window 唯一实现类,添加到 WindowManager 的根容器中

  • WindowManagerService:WindowManager 是 Window 的容器,管理着 Window,对 Window 进行添加和删除,最终具体的工作都是由 WindowManagerService 来处理的,WindowManager 和 WindowManagerService 通过 Binder 来进行跨进程通信,WindowManagerService 才是 Window 的最终管理者

这篇文章重要知识点是 Window 视图层级顺序是如何确定的,其他内容都是一些概念的东西,可以选择性的阅读,了解完基本概念之后,进入这篇文章的核心内容,我们先来了解一下Window 都有哪些常用的参数。

2 Window 都有哪些常用的参数

Window 的参数都被定义在 WindowManager 的静态内部类 LayoutParams 中

frameworks/base/core/java/android/view/WindowManager#LayoutParams.java

public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
    // window 左上角的 x 坐标
    public int x;

    // window 左上角的 y 坐标
    public int y;

    // Window 的类型
    public int type;

    // Window 的 flag 用于控制 Window 的显示
    public int flags;

    // window 软键盘输入区域的显示模式
    public int softInputMode;

    // window 的透明度,取值为0-1
    public float alpha = 1.0f;

    // window 在屏幕中的位置
    public int gravity;

    // window 的像素点格式,值定义在 PixelFormat 中
    public int format;
}

接下来我们我们主要来介绍一下 Window 的类型、Window 视图层级顺序、Window 的 flag、和 window 软键盘模式.

3 Window 都那些类型以及作用

Window 的类型大概可以分为三类:

  1. 应用程序 Window(Application Window)

  2. 子 Window(Sub Windwow)

  3. 系统 Window(System Window)

Window 的类型通过 type 值来表示,每个大类型又包含多个小类型,它们都定义在 WindowManager 的静态内部类 LayoutParams

frameworks/base/core/java/android/view/WindowManager#LayoutParams.java

public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
    public int type;

    // 应用程序 Window 的开始值
    public static final int FIRST_APPLICATION_WINDOW = 1;
    // 应用程序 Window 的结束值
    public static final int LAST_APPLICATION_WINDOW = 99;

    // 子 Window 类型的开始值
    public static final int FIRST_SUB_WINDOW = 1000;
    // 子 Window 类型的结束值
    public static final int LAST_SUB_WINDOW = 1999;

    // 系统 Window 类型的开始值
    public static final int FIRST_SYSTEM_WINDOW     = 2000;
    // 系统 Window 类型的结束值
    public static final int LAST_SYSTEM_WINDOW      = 2999;
}
类型备注
FIRST_APPLICATION_WINDOW1应用程序 Window 的开始值
LAST_APPLICATION_WINDOW99应用程序 Window 的结束值
FIRST_SUB_WINDOW1000子 Window 的开始值
LAST_SUB_WINDOW1999子 Window 的结束值
FIRST_SYSTEM_WINDOW2000系统 Window 的开始值
LAST_SYSTEM_WINDOW2999系统 Window 的结束值

小技巧:如果是层级在 2000(FIRST_SYSTEM_WINDOW)以下的是不需要申请弹窗权限的。

应用程序 Window(Application Window):它的区间范围 [1,99],例如 Activity
frameworks/base/core/java/android/view/WindowManager#LayoutParams.java

// 应用程序 Window 的开始值
public static final int FIRST_APPLICATION_WINDOW = 1;

// 应用程序 Window 的基础值
public static final int TYPE_BASE_APPLICATION   = 1;

// 普通的应用程序
public static final int TYPE_APPLICATION        = 2;

// 特殊的应用程序窗口,当程序可以显示 Window 之前使用这个 Window 来显示一些东西
public static final int TYPE_APPLICATION_STARTING = 3;

// TYPE_APPLICATION 的变体,在应用程序显示之前,WindowManager 会等待这个 Window 绘制完毕
public static final int TYPE_DRAWN_APPLICATION = 4;

// 应用程序 Window 的结束值
public static final int LAST_APPLICATION_WINDOW = 99;

类型

备注
FIRST_APPLICATION_WINDOW应用程序 Window 的开始值
TYPE_BASE_APPLICATION应用程序 Window 的基础值
TYPE_APPLICATION普通的应用程序
TYPE_APPLICATION_STARTING特殊的应用程序窗口,当程序可以显示 Window 之前使用这个
Window 来显示一些东西
TYPE_DRAWN_APPLICATIONTYPE_APPLICATION 的变体 在应用程序显示之前,
WindowManager 会等待这个 Window 绘制完毕
LAST_APPLICATION_WINDOW应用程序 Window 的结束值

子 Window(Sub Windwow):它的区间范围 [1000,1999],这些 Window 按照 Z-order 顺序依附于父 Window 上(关于 Z-order 后文有介绍),并且他们的坐标空间相对于父 Window 的,例如:PopupWindow
frameworks/base/core/java/android/view/WindowManager#LayoutParams.java

// 子 Window 类型的开始值
public static final int FIRST_SUB_WINDOW = 1000;

// 应用程序 Window 顶部的面板。这些 Window 出现在其附加 Window 的顶部。
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

// 用于显示媒体(如视频)的 Window。这些 Window 出现在其附加 Window 的后面。
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

// 应用程序 Window 顶部的子面板。这些 Window 出现在其附加 Window 和任何Window的顶部
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

// 当前Window的布局和顶级Window布局相同时,不能作为子代的容器
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

// 用显示媒体 Window 覆盖顶部的 Window, 这是系统隐藏的 API
public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;

// 子面板在应用程序Window的顶部,这些Window显示在其附加Window的顶部, 这是系统隐藏的 API
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;

// 子 Window 类型的结束值
public static final int LAST_SUB_WINDOW = 1999;

类型

备注
FIRST_SUB_WINDOW子 Window 的开始值
TYPE_APPLICATION_PANEL应用程序 Window 顶部的面板,这些 Window 出现在其附加 Window 的顶部
TYPE_APPLICATION_MEDIA用于显示媒体(如视频)的 Window,这些 Window 出现在其附加 Window 的后面
TYPE_APPLICATION_SUB_PANEL应用程序 Window 顶部的子面板,这些 Window 出现在其附加 Window 和任何Window的顶部
TYPE_APPLICATION_ATTACHED_DIALOG当前Window的布局和顶级Window布局相同时,不能作为子代的容器
TYPE_APPLICATION_MEDIA_OVERLAY用显示媒体 Window 覆盖顶部的 Window, 这是系统隐藏的 API
TYPE_APPLICATION_ABOVE_SUB_PANEL子面板在应用程序Window的顶部,这些Window显示在其附加Window的顶部, 这是系统隐藏的 API
LAST_SUB_WINDOW子 Window 的结束值

系统 Window(System Window): 它区间范围 [2000,2999],例如:Toast,输入法窗口,系统音量条窗口,系统错误窗口
frameworks/base/core/java/android/view/WindowManager#LayoutParams.java

// 系统Window类型的开始值
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;

@Deprecated
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;

@Deprecated
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;

// 已经从系统中被移除,可以使用 TYPE_KEYGUARD_DIALOG 代替
public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;

@Deprecated
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;

@Deprecated
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;

@Deprecated
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;

// 系统对话框窗口
public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;

// 锁屏时显示的对话框
public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;

@Deprecated
// API 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;

// 输入法窗口,位于普通 UI 之上,应用程序可重新布局以免被此窗口覆盖
public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;

// 输入法对话框,显示于当前输入法窗口之上
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;

// 墙纸
public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;

// 状态栏的滑动面板
public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;

// 应用程序叠加窗口显示在所有窗口之上
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;

// 系统Window类型的结束值
public static final int LAST_SYSTEM_WINDOW      = 2999;

类型

备注
FIRST_SYSTEM_WINDOW系统 Window 类型的开始值
TYPE_STATUS_BAR系统状态栏,只能有一个状态栏,它被放置在屏幕的顶部,所有其他窗口都向下移动
TYPE_SEARCH_BAR系统搜索窗口,只能有一个搜索栏,它被放置在屏幕的顶部
TYPE_PHONEAPI 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
TYPE_SYSTEM_ALERTAPI 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
TYPE_KEYGUARD已经从系统中被移除,可以使用 TYPE_KEYGUARD_DIALOG 代替
TYPE_TOASTAPI 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
TYPE_SYSTEM_OVERLAYAPI 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
TYPE_PRIORITY_PHONEAPI 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
TYPE_SYSTEM_ERRORAPI 已经过时,用 TYPE_APPLICATION_OVERLAY 代替
TYPE_APPLICATION_OVERLAY应用程序叠加窗口显示在所有窗口之上
TYPE_SYSTEM_DIALOG系统对话框窗口
TYPE_KEYGUARD_DIALOG锁屏时显示的对话框
TYPE_INPUT_METHOD输入法窗口,位于普通 UI 之上,应用程序可重新布局以免被此窗口覆盖
TYPE_INPUT_METHOD_DIALOG输入法对话框,显示于当前输入法窗口之上
TYPE_WALLPAPER墙纸
TYPE_STATUS_BAR_PANEL状态栏的滑动面板
LAST_SYSTEM_WINDOW系统 Window 类型的结束值

需要注意的是:

  1. TYPE_PHONE、TYPE_SYSTEM_ALERT、TYPE_TOAST、TYPE_SYSTEM_OVERLAY、TYPE_PRIORITY_PHONE、TYPE_SYSTEM_ERROR 这些 type 在 API 26 中均已经过时,使用 TYPE_APPLICATION_OVERLAY 代替,需要申请 Manifest.permission.SYSTEM_ALERT_WINDOW 权限

  2. TYPE_KEYGUARD 已经被从系统中移除,可以使用 TYPE_KEYGUARD_DIALOG 来代替

4 Window 视图层级顺序

我们在手机上看的是二维的,但是实际上是三维的显示,如下图所示

3e44f2f995190c0904dd203dd7d18b83.gif

在文章开头介绍了参数类型包含了 Window 的 x 轴坐标、Window 的 y 轴坐标, 既然是一个三维坐标系,那么 z 轴坐标在哪里?

接下来就是我们要分析的非常重要的一个类 WindowManagerService,当添加 Window 的时候已经确定好了 Window 的层级,显示的时候才会根据当前的层级确定 Window 应该在哪一层显示。

WindowManager 是 Window 的容器,管理着 Window,对 Window 进行添加和删除,具体的工作都是由 WMS 来处理的,WindowManager 和 WMS 通过 Binder 来进行跨进程通信,WMS 才是 Window 的最终管理者,我先来看一下 WMS 的 addWindow 方法

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
        LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
        InsetsState outInsetsState) {

            final WindowState win = new WindowState(this, session, client, token, parentWindow,
            appOp[0], seq, attrs, viewVisibility, session.mUid,
            session.mCanAddInternalSystemWindow);
            ......

            win.mToken.addWindow(win);
            ......

            win.getParent().assignChildLayers();
            ......

}
  • WindowState 计算当前 Window 层级

  • win.mToken.addWindow 这个方法将当前的 win 放入 WindowList 中,WindowList 是一个 ArrayList

  • displayContent.assignWindowLayers 方法 计算 z-order 值, z-order 值越大越靠前,就越靠近用户

Window 视图层级顺序 用 Z-order 来表示,Z-order 对应着 WindowManager.LayoutParams 的 type 值,Z-order 可以理解为 Android 视图的层级概念,值越大越靠前,就越靠近用户。

WindowState 就是 windowManager 中的窗口,一个 WindowState 表示一个 window

那么 Z-order 的值的计算逻辑在 WindowState 类中,WindowState 构造的时候初始化当前的 mBaseLayer 和 mSubLayer,这两个参数应该是决定 z-order 的两个因素
frameworks/base/services/core/java/com/android/server/wm/WindowState.java

static final int TYPE_LAYER_MULTIPLIER = 10000;
static final int TYPE_LAYER_OFFSET = 1000;

WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
        WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
        int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
        PowerManagerWrapper powerManagerWrapper) {

        // 判断该是否在子 Window 的类型范围内[1000,1999]
        if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {

            // 调用 getWindowLayerLw 方法返回值在[1,33]之间,根据不同类型的 Window 在屏幕上进行排序
            mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;

            // mSubLayer 子窗口的顺序
            // 调用 getSubWindowLayerFromTypeLw 方法返回值在[-2.3]之间 ,返回子 Window 相对于父 Window 的位置
            mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
            ......

        } else {

            mBaseLayer = mPolicy.getWindowLayerLw(this)
                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
            mSubLayer = 0;
            ......

        }  
}
  • mBaseLayer 是基础序,对应的区间范围 [1,33]

  • mSubLayer 相同分组下的子 Window 的序,对应的区间范围 [-2.3]

  • 判断该是否在子 Window 的类型范围内[1000,1999]

  • 如果是子 Window,调用 getWindowLayerLw 方法,计算 mBaseLayer 的值,返回一个用来对 Window 进行排序的任意整数,调用 getSubWindowLayerFromTypeLw 方法,计算 mSubLayer 的值,返回子 Window 相对于父 Window 的位置

  • 如果不是子 Window,调用 getWindowLayerLw 方法,计算 mBaseLayer 的值,返回一个用来对 Window 进行排序的任意整数,mSubLayer 值为 0

计算 mBaseLayer 的值

调用 WindowManagerPolicy 的 getWindowLayerLw 方法,计算 mBaseLayer 的值
frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java

int APPLICATION_LAYER = 2;
int APPLICATION_MEDIA_SUBLAYER = -2;
int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
int APPLICATION_PANEL_SUBLAYER = 1;
int APPLICATION_SUB_PANEL_SUBLAYER = 2;
int APPLICATION_ABOVE_SUB_PANEL_SUBLAYER = 3;

/**
* 根据不同类型的 Window 在屏幕上进行排序
* 返回一个用来对窗口进行排序的任意整数,数字越小,表示的值越小
*/   
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
    // 判断是否在应用程序 Window 类型的取值范围内 [1,99]
    if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
        return APPLICATION_LAYER;
    }


    switch (type) {
        case TYPE_WALLPAPER: // 壁纸,通过 window manager 删除它
            return  1;
        case TYPE_PHONE: // 电话
            return  3;
        case TYPE_SEARCH_BAR: // 搜索栏
            return  6;
        case TYPE_SYSTEM_DIALOG: // 系统的 dialog
            return  7;
        case TYPE_TOAST: // 系统 toast
            return  8;
        case TYPE_INPUT_METHOD: // 输入法
            return  15;
        case TYPE_STATUS_BAR: // 状态栏
            return  17;
        case TYPE_KEYGUARD_DIALOG: //锁屏
            return  20;
        ......

        case TYPE_POINTER:
            // the (mouse) pointer layer
            return  33;
        default:
            return APPLICATION_LAYER;
    }
}

根据不同类型的 Window 在屏幕上进行排序,返回一个用来对 Window 进行排序的任意整数,数字越小,表示的值越小,通过以下公式来计算它的基础序 ,基础序越大,Z-order 值越大越靠前,就越靠近用户,我们以 Activity 为例:

Activity 属于应用层 Window,它的取值范围在 [1,99] 内,调用 getWindowLayerLw 方法返回 APPLICATION_LAYER,APPLICATION_LAYER 值为 2,通过下面方法进行计算:

static final int TYPE_LAYER_MULTIPLIER = 10000;
static final int TYPE_LAYER_OFFSET = 1000;

mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;

那么最终 Activity 的 mBaseLayer 值是 21000。

计算 mSubLayer 的值

调用 getSubWindowLayerFromTypeLw 方法 ,传入  WindowManager.LayoutParams 的实例 a 的 type 值,计算 mSubLayer 的值
frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java

int APPLICATION_LAYER = 2;
int APPLICATION_MEDIA_SUBLAYER = -2;
int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
int APPLICATION_PANEL_SUBLAYER = 1;
int APPLICATION_SUB_PANEL_SUBLAYER = 2;
int APPLICATION_ABOVE_SUB_PANEL_SUBLAYER = 3;

/**
* 计算 Window 相对于父 Window 的位置
* 返回 一个整数,正值在前面,表示在父 Window 上面,负值在后面,表示在父 Window 的下面
*/
default int getSubWindowLayerFromTypeLw(int type) {
    switch (type) {
        case TYPE_APPLICATION_PANEL: // 1000
        case TYPE_APPLICATION_ATTACHED_DIALOG: // 1003
            return APPLICATION_PANEL_SUBLAYER; // return 1
        case TYPE_APPLICATION_MEDIA:// 1001
            return APPLICATION_MEDIA_SUBLAYER;// return -2
        case TYPE_APPLICATION_MEDIA_OVERLAY:
            return APPLICATION_MEDIA_OVERLAY_SUBLAYER; // return -1
        case TYPE_APPLICATION_SUB_PANEL:// 1002
            return APPLICATION_SUB_PANEL_SUBLAYER;// return 2
        case TYPE_APPLICATION_ABOVE_SUB_PANEL:
            return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;// return 3
    }
    return 0;
}

计算子 Window 相对于父 Window 的位置,返回一个整数,正值表示在父 Window 上面,负值表示在父 Window 的下面。

5 Window 的 flag

Window 的 flag 用于控制 Window 的显示,它们的值也是定义在 WindowManager 的内部类 LayoutParams 中
frameworks/base/core/java/android/view/WindowManager#LayoutParams.java

// 当 Window 可见时允许锁屏
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;

// Window 后面的内容都变暗
public static final int FLAG_DIM_BEHIND        = 0x00000002;

@Deprecated
// API 已经过时,Window 后面的内容都变模糊
public static final int FLAG_BLUR_BEHIND        = 0x00000004;

// Window 不能获得输入焦点,即不接受任何按键或按钮事件,例如该 Window 上 有 EditView,点击 EditView 是 不会弹出软键盘的
// Window 范围外的事件依旧为原窗口处理;例如点击该窗口外的view,依然会有响应。另外只要设置了此Flag,都将会启用FLAG_NOT_TOUCH_MODAL
public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;

// 设置了该 Flag,将 Window 之外的按键事件发送给后面的 Window 处理, 而自己只会处理 Window 区域内的触摸事件
// Window 之外的 view 也是可以响应 touch 事件。
public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;

// 设置了该Flag,表示该 Window 将不会接受任何 touch 事件,例如点击该 Window 不会有响应,只会传给下面有聚焦的窗口。
public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;

// 只要 Window 可见时屏幕就会一直亮着
public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;

// 允许 Window 占满整个屏幕
public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;

// 允许 Window 超过屏幕之外
public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;

// 全屏显示,隐藏所有的 Window 装饰,比如在游戏、播放器中的全屏显示
public static final int FLAG_FULLSCREEN      = 0x00000400;

// 表示比FLAG_FULLSCREEN低一级,会显示状态栏
public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;

// 当用户的脸贴近屏幕时(比如打电话),不会去响应此事件
public static final int FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000;

// 则当按键动作发生在 Window 之外时,将接收到一个MotionEvent.ACTION_OUTSIDE事件。
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;

@Deprecated
// 窗口可以在锁屏的 Window 之上显示, 使用 Activity#setShowWhenLocked(boolean) 方法代替
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;

// 表示负责绘制系统栏背景。如果设置,系统栏将以透明背景绘制,
// 此 Window 中的相应区域将填充 Window#getStatusBarColor()和 Window#getNavigationBarColor()中指定的颜色。
public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;

// 表示要求系统壁纸显示在该 Window 后面,Window 表面必须是半透明的,才能真正看到它背后的壁纸
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;

flag

备注
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON当 Window 可见时允许锁屏
FLAG_DIM_BEHINDWindow 后面的内容都变暗
FLAG_BLUR_BEHINDAPI 已经过时,Window 后面的内容都变模糊
FLAG_NOT_FOCUSABLEWindow 不能获得输入焦点,即不接受任何按键或按钮事件,例如该 Window 上 有 EditView,点击 EditView 是 不会弹出软键盘的,Window 范围外的事件依旧为原窗口处理;例如点击该窗口外的view,依然会有响应。另外只要设置了此Flag,都将会启用FLAG_NOT_TOUCH_MODAL
FLAG_NOT_TOUCH_MODAL设置了该 Flag,将 Window 之外的按键事件发送给后面的 Window 处理, 而自己只会处理 Window 区域内的触摸事件,Window 之外的 view 也是可以响应 touch 事件
FLAG_NOT_TOUCHABLE设置了该Flag,表示该 Window 将不会接受任何 touch 事件,例如点击该 Window 不会有响应,只会传给下面有聚焦的窗口
FLAG_KEEP_SCREEN_ON只要 Window 可见时屏幕就会一直亮着
FLAG_LAYOUT_IN_SCREEN允许 Window 占满整个屏幕
FLAG_LAYOUT_NO_LIMITS允许 Window 超过屏幕之外
FLAG_FULLSCREEN全屏显示,隐藏所有的 Window 装饰,比如在游戏、播放器中的全屏显示
FLAG_FORCE_NOT_FULLSCREEN表示比FLAG_FULLSCREEN低一级,会显示状态栏
FLAG_IGNORE_CHEEK_PRESSES当用户的脸贴近屏幕时(比如打电话),不会去响应此事件
FLAG_WATCH_OUTSIDE_TOUCH则当按键动作发生在 Window 之外时,将接收到一个MotionEvent.ACTION_OUTSIDE事件
FLAG_SHOW_WHEN_LOCKED已经过时,窗口可以在锁屏的 Window 之上显示, 使用 Activity#setShowWhenLocked(boolean) 方法代替
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS表示负责绘制系统栏背景。如果设置,系统栏将以透明背景绘制,
此 Window 中的相应区域将填充 Window#getStatusBarColor()和 Window#getNavigationBarColor()中指定的颜色
FLAG_SHOW_WALLPAPER表示要求系统壁纸显示在该 Window 后面,Window 表面必须是半透明的,才能真正看到它背后的壁纸
6 Window 的 SoftInputMode

表示 window 软键盘输入区域的显示模式,常见的情况 Window 的软键盘打开会占据整个屏幕,遮挡了后面的视图,例如看直播的时候底部有个输入框点击的时候,输入框随着键盘一起上来,而有的时候,希望键盘覆盖在所有的 View 之上,界面保持不动等等。

软键盘模式(SoftInputMode) 值,与 AndroidManifest 中 Activity 的属性 android:windowSoftInputMode 是对应的,因此可以在 AndroidManifest 文件中为 Activity 设置android:windowSoftInputMode

 <activity android:windowSoftInputMode="adjustNothing" />

也可以在 Java 代码中为 Window 设置 SoftInputMode

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);

SoftInputMode 常用的有以下几个值

// 不会改变软键盘的状态
public static final int SOFT_INPUT_STATE_UNCHANGED = 1;

// 当用户进入该窗口时,隐藏软键盘
public static final int SOFT_INPUT_STATE_HIDDEN = 2;

// 当窗口获取焦点时,隐藏软键盘
public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;

// 当用户进入窗口时,显示软键盘
public static final int SOFT_INPUT_STATE_VISIBLE = 4;

// 当窗口获取焦点时,显示软键盘
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;

// window会调整大小以适应软键盘窗口
public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;

// 没有指定状态,系统会选择一个合适的状态或依赖于主题的设置
public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;

// 当软键盘弹出时,窗口会调整大小,例如点击一个EditView,整个layout都将平移可见且处于软件盘的上方
// 同样的该模式不能与SOFT_INPUT_ADJUST_PAN结合使用;
// 如果窗口的布局参数标志包含FLAG_FULLSCREEN,则将忽略这个值,窗口不会调整大小,但会保持全屏。
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;

// 当软键盘弹出时,窗口不需要调整大小, 要确保输入焦点是可见的,
// 例如有两个EditView的输入框,一个为Ev1,一个为Ev2,当你点击Ev1想要输入数据时,当前的Ev1的输入框会移到软键盘上方
// 该模式不能与SOFT_INPUT_ADJUST_RESIZE结合使用
public static final int SOFT_INPUT_ADJUST_PAN = 0x20;

// 将不会调整大小,直接覆盖在window上
public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;

model

备注
SOFT_INPUT_STATE_UNCHANGED不会改变软键盘的状态
SOFT_INPUT_STATE_VISIBLE当用户进入窗口时,显示软键盘
SOFT_INPUT_STATE_HIDDEN当用户进入该窗口时,隐藏软键盘
SOFT_INPUT_STATE_ALWAYS_HIDDEN当窗口获取焦点时,隐藏软键盘
SOFT_INPUT_STATE_ALWAYS_VISIBLE当窗口获取焦点时,显示软键盘
SOFT_INPUT_MASK_ADJUSTwindow 会调整大小以适应软键盘窗口
SOFT_INPUT_ADJUST_UNSPECIFIED没有指定状态,系统会选择一个合适的状态或依赖于主题的设置
SOFT_INPUT_ADJUST_RESIZE1. 当软键盘弹出时,窗口会调整大小,例如点击一个EditView,整个layout都将平移可见且处于软件盘的上方
2. 同样的该模式不能与SOFT_INPUT_ADJUST_PAN结合使用
3. 如果窗口的布局参数标志包含FLAG_FULLSCREEN,则将忽略这个值,窗口不会调整大小,但会保持全屏
SOFT_INPUT_ADJUST_PAN1. 当软键盘弹出时,窗口不需要调整大小, 要确保输入焦点是可见的
2. 例如有两个EditView的输入框,一个为Ev1,一个为Ev2,当你点击Ev1想要输入数据时,当前的Ev1的输入框会移到软键盘上方
3. 该模式不能与SOFT_INPUT_ADJUST_RESIZE结合使用
SOFT_INPUT_ADJUST_NOTHING将不会调整大小,直接覆盖在window上
参考文献 https://developer.android.google.cn/reference/android/view/WindowManager.LayoutParams.html?hl=en#summary https://www.jianshu.com/p/3528255475a2

最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!

历史系列文章:

直面底层:说 Java 反射效率低,你知道原因吗?

直面底层: Android 之 VSYNC、 Choreographer 起源!

直面底层:WindowManager 视图绑定以及体系结构

直面底层:你真的了解 View.post() 原理吗?

直面底层:“吹上天”的协程,带你深入源码分析

直面底层:经常用的ViewTreeObserver 背后的原理

b0a5c2dc369819b64a5af0ab76e8b2ef.png

扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~

┏(^0^)┛明天见!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值