zoukankan      html  css  js  c++  java
  • WmS详解(二)之如何理解Window和窗口的关系?基于Android7.0源码

    上篇博客(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的窗口


    以上。




  • 相关阅读:
    Rotation Kinematics
    离职 mark
    PnP 问题方程怎么列?
    DSO windowed optimization 代码 (4)
    Adjoint of SE(3)
    IMU 预积分推导
    DSO windowed optimization 代码 (3)
    DSO windowed optimization 代码 (2)
    OKVIS 代码框架
    DSO windowed optimization 代码 (1)
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461650.html
Copyright © 2011-2022 走看看