上篇博客(WmS详解(一)之token到底是什么?基于Android7.0源码)中我们简要介绍了token的作用,这里涉及到的概念非常多,其中出现频率最高的要数Window和窗口这一对搭档了,那么我们今天就来看看到底我们该如何理解Android系统中的Window和窗口。

窗口这个概念,从不同的角度来看它的含义不一样,如果我们从WmS(WindowManagerService)的角度来看窗口,那么这个窗口并不是一个Window类,而是一个View。用户发来的消息被WmS接收之后并不能直接发给各个View,真正接收用户消息的是IWindow类,IWindow类的实现类是ViewRootImpl.W类,每一个W类内部都有一个View变量,WmS在收到用户发来的消息之后,判断哪一个窗口处于活动状态,找到之后将用户消息交给W类,W类再把用户消息传递给内部的View变量,剩下的事情就是View对象来搞定了。总的来说,在WmS眼中,窗口就是一个View,本文后面提到窗口都是指一个View;Window则是对窗口行为的进一步提取和抽象,Window将窗口的一些公共行为抽取出来统一处理,相当于Window是窗口的一个子集。

OK,说完了窗口的定义,接下来我们来看看窗口的类型。

窗口分类

根据窗口的type属性,窗口可以分为三大类,分别是应用窗口、子窗口和系统窗口,View的添加都是通过WindowManagerImpl类中的addView方法来实现的,该方法最终调用了WindowManagerGlobal中的addView方法,我们来看看这个方法:

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException("view must not be null");
    }
    if (display == null) {
        throw new IllegalArgumentException("display must not be null");
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

......
......

      // If this is a panel window, then find the window it is being
      // attached to for future reference.
      if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            final int count = mViews.size();
            for (int i = 0; i < count; i++) {
                if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                    panelParentView = mViews.get(i);
                }
            }
        }

        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }

    ......
......

}

该方法接收四个参数,第一个参数表示要添加的View,第二个参数表示该View的参数,第三个参数表示要输出的显示设备,第四个参数表示该View的父布局,其中第二个参数params要求必须是WindowManager.LayoutParams的实例,这个参数params中有一个参数type就是用来标记每一个窗口的类型,这个类型实际上是一个int值,这个int值表示窗口的层,WmS在进行窗口叠加的时候,按照int常量大小分配不同的层,int值越大,View越靠近上层,int值越小,View越靠近下层。接下我们就来详细看一看这三种不同的窗口类型:

应用窗口

Activity对应的窗口就是应用窗口,但是由于Activity的加载是由AmS来完成的,因此,应用窗口的创建实际上是由AmS来完成的。所有的Activity默认的窗口类型都是TYPE_APPLICATION,这个从WindowManager.LayoutParams的构造方法中就可以看出:

public LayoutParams() {
            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            type = TYPE_APPLICATION;
            format = PixelFormat.OPAQUE;
        }

这一类中,系统预定义了几个变量,我们来看一下:

根据这张表小伙伴们可以发现,应用窗口的层值不会大于99,而系统在进行窗口叠加的时候会自动为窗口分配不同大小的层值。

子窗口

子窗口所以为子窗口是因为它有一个父窗口,父窗口可以是任意类型的其他窗口,我们在开发中常见的子窗口有PopupWindow、Dialog、ContextMenu、PopupMenu等。关于子窗口,系统也定义了几种类型,我们来看一下:

当我们创建子窗口时,我们可以指定窗口类型介于1000~1999之间,WmS在进行窗口叠加时,会动态调整层值。

系统窗口

常见的系统窗口有状态栏、导航栏(国内部分Android手机有)、发生ANR时的提示框、输入法窗口、Toast窗口、锁屏时显示的屏保以及各种管家自带的那种加速球。系统窗口不需要有对应的Activity窗口,它也不需要父窗口,大部分情况下我们见到的系统窗口都是由系统创建的,当然我们也可以自己来创建系统窗口,和另外两种类型的窗口一样,系统也帮我们创建了一部分系统窗口常量:

事实上,在Android7.0中,系统一共定义了三十多个系统窗口常量,但是有一部分目前并没有使用,所以我这里就没有列出来。当我们创建系统窗口时,我们可以指定系统窗口的层值在2000-2999之间,WmS在进行窗口叠加时,会动态调整该层值,但是该值会介于2000-2999之间。

窗口创建

看了很多理论知识,接下来我们来看看我们自己怎么样来创建窗口。

创建子窗口

假设我当前页面有一个Button,当我点击Button的时候,弹出一个子窗口,效果如下:

我们来看看代码:

    public void btnClick(View view) {
        IBinder token = view.getWindowToken();
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(200, 200, 0, 0, PixelFormat.TRANSPARENT);
        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
        lp.token = token;
        final TextView tv = new TextView(this);
        tv.setText("我是弹出子窗口");
        tv.setBackgroundColor(Color.BLUE);
        wm = getWindowManager();
        tv.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK) {
                    MainActivity.this.wm.removeView(tv);
                }
                return false;
            }
        });
        this.wm.addView(tv, lp);
    }

代码貌似没什么难度,这里我就不做过多解释了。

创建系统窗口

系统窗口可能是许多小伙伴使用较多的窗口创建方式,常见的360悬浮球就是用这种方式实现的。这里我也举一个小例子:

    public void addView(View view) {
        lp = new WindowManager.LayoutParams(80, 80, 0, 0, PixelFormat.TRANSPARENT);
        lp.flags =
                //Window不需要获取焦点,该属性会自动开启FLAG_NOT_TOUCH_MODAL
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                        //当前window区域内的事件自己处理,区域外的事件交给底层的window处理
                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                        //锁屏状态下亦能显示window出来
                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
        lp.gravity = Gravity.LEFT | Gravity.TOP;
        //注意参考点为ActionBar的左上角
        lp.x = 200;
        lp.y = 200;
        //type用来描述window的类型,window类型共分为三种:
        //应用级Window(1-99),子Window(1000-1999),系统Window(2000-2999)
        //层级大的Window会覆盖掉层级小的Window
        lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
        windowManager = getWindowManager();
        windowManager.addView(tv, lp);
    }

    public void removeView(View view) {
        windowManager.removeView(tv);
    }

这里两个方法,一个添加窗口,一个移除窗口。但是使用系统窗口一般需要我们添加权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

OK,关于Window和窗口的介绍就说这么多吧,有问题欢迎留言讨论。

参考资料:

1.浅析Android的窗口

以上。

WmS详解(二)之如何理解Window和窗口的关系?基于Android7.0源码的更多相关文章

  1. WmS简介(三)之Activity窗口是如何创建的?基于Android7.0源码

    OK,在前面两篇博客中我们分别介绍了WmS中的token,同时也向小伙伴们区分了Window和窗口的区别,并且按照type值的不同将Android系统中的窗口分为了三大类,那么本篇博客我们就来看看应用 ...

  2. WmS具体解释(二)之怎样理解Window和窗体的关系?基于Android7.0源代码

    上篇博客(WmS具体解释(一)之token究竟是什么?基于Android7.0源代码)中我们简要介绍了token的作用,这里涉及到的概念非常多,当中出现频率最高的要数Window和窗体这一对搭档了,那 ...

  3. WmS详解(一)之token到底是什么?基于Android7.0源码

    做Android有些年头了,Framework层三大核心View系统,WmS.AmS最近在研究中,这三大块,每一块都够写一个小册子来介绍,其中View系统的介绍,我之前有一个系列的博客(不过由于时间原 ...

  4. PopUpWindow使用详解(二)——进阶及答疑

      相关文章:1.<PopUpWindow使用详解(一)——基本使用>2.<PopUpWindow使用详解(二)——进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...

  5. Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)

    View 的绘制系列文章: Android View 的绘制流程之 Measure 过程详解 (一) Android View 绘制流程之 DecorView 与 ViewRootImpl 在上一篇  ...

  6. HTTPS详解二:SSL / TLS 工作原理和详细握手过程

    HTTPS 详解一:附带最精美详尽的 HTTPS 原理图 HTTPS详解二:SSL / TLS 工作原理和详细握手过程 在上篇文章HTTPS详解一中,我已经为大家介绍了 HTTPS 的详细原理和通信流 ...

  7. Linux dts 设备树详解(二) 动手编写设备树dts

    Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ...

  8. Shiro 安全框架详解二(概念+权限案例实现)

    Shiro 安全框架详解二 总结内容 一.登录认证 二.Shiro 授权 1. 概念 2. 授权流程图 三.基于 ini 的授权认证案例实现 1. 实现原理图 2. 实现代码 2.1 添加 maven ...

  9. .NET DLL 保护措施详解(二)关于性能的测试

    先说结果: 加了缓存的结果与C#原生代码差异不大了 我对三种方式进行了测试: 第一种,每次调用均动态编译 第二种,缓存编译好的对象 第三种,直接调用原生C#代码 .net dll保护系列 ------ ...

随机推荐

  1. poj 1046 ——Color Me Less

    提交地址:http://poj.org/problem?id=1046 Color Me Less Time Limit: 1000MS   Memory Limit: 10000K Total Su ...

  2. STL rope

    rope的部分简单操作 函数 功能 push_back(x) 在末尾添加x insert(pos,x) 在pos插入x erase(pos,x) 从pos开始删除x个 replace(pos,x) 从 ...

  3. 洛谷P1593 因子和

    题目描述 输入两个正整数a和b,求a^b的因子和.结果太大,只要输出它对9901的余数. 输入输出格式 输入格式: 仅一行,为两个正整数a和b(0≤a,b≤50000000). 输出格式: a^b的因 ...

  4. 删数方案数(regex)

    [题目描述] 给出一个正整数序列 a,长度为 n,cyb 不喜欢完美,他要删掉一些数(也可以不删,即删掉0个),但是他不会乱删,他希望删去以后,能将 a 分成 2 个集合,使得两个非空集合的数的和相同 ...

  5. ●Codevs 4158 残缺的字符串

    题链: http://codevs.cn/problem/4158/ 题解: FFT. 定义两个相同长度的字符串s1,s2的距离为 $$dis(s1,s2)=\sum_{i=0}^{len-1}(s1 ...

  6. PHP Laravel框架入门心得 | How to study PHP Laravel Framework

    PHP有不少开发框架,其中比较出名的有Symfony和Laravel. 我说说我最近入门Laravel的感受和学习方法吧. 1.第一个感受是Laravel的社区讨论和学习资源真的是太棒了,中文化也做得 ...

  7. spring boot新建项目问题总结

    1.启动报:Unregistering JMX-exposed beans on shutdown 原因:以来的Tomcat没有启动 解决办法:在pom.xml加入依赖 <dependency& ...

  8. django rest-framework 3.类 实现restful

    上节提到过,REST框架分别提供了对函数和类的装饰器,之前已经都是通过函数来写视图函数的,现在来尝试使用class 类来实现视图函数 使用基于类编写API视图,允许重用常用的功能,减少代码重复. 一. ...

  9. cocos2d-x-3.x 学习总结(一)

    这周学习了<cocos2d-x 3.x 游戏开发之旅>的第三章,做如下总结: 1.关于创建标签对象 书中是 Label* label = Label::create(); 可是总是提示出错 ...

  10. C语言程序设计第一次作业(2017.10.10完成)

    一:程序框图以及正确运行结果: (1)给出圆半径,得出圆面积: ①程序框图如下: ②测试图如下: 经过测试 ,输入半径2能得出正确结果.多次测试,输入不同值,均得出正确结果,证明稳定性. ③实验分析: ...