Window和WindowManager(一)

window,我们都知道所有的视图其实都是通过window来呈现的,不管是activity还是dialog等,window是所有view的实际管理者。我们在学习View的事件分发机制的时候也会提到window,view的所有事件都是通过window传递给DecerVIew,然后DecerView再传递给我们的View等等等等。但是我们在实际开发的过程中对window的接触确实不是很多,只有当需要做一些类似于悬浮窗之类的效果的时候才会使用到window的相关概念,所以我们今天来梳理一下有关window的知识点。

Window是一个抽象类,他的具体实现是PhoneWindow,创建window很简单,借助WindowManager几行代码即可实现:

    Button button = new Button(mContext);
    WindowManager.LayoutParams params = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT,0,0,PixelFormat.TRANSPARENT);
    params.flags = LayoutParams.FLAG_NOT_TOUCH_MODEL|LayoutParams.FLAG_NOT_FOCUSABLE|LayoutParams.FLAG_SHOW_WHEN_LOCKED;
    params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
    parms.gravity = Gravity.LEFT|Gravity.TOP;
    params.x = 100;
    params.y = 300;
    WindowManager manager = (WindowManger)getSystemService(Context.WINDOW_SERVICE);
    manager.addView(button,params);

上述代码可以将一个Button添加到屏幕坐标为100,300的位置上,代码比较简单,flags和type参数我们来介绍一下:

  1. type参数,表示Window的类型,有三种,分别是应用Window、系统Window和子Window,应用Window对应着一个Activity,系统Window通常需要声明权限,比如我们在桌面的悬浮球,而子Window不能独立存在,通常依赖于应用Window存在,如dialog和popupWindow等。

    Window的分层中,层级大的会覆盖在层级小的上边,其中应用Window的层级范围是1-99,子Window的层级范围是1000-1999,而系统Window的层级范围是2000-2999,Window层级的指定就需要用到WindowManager.LayoutParams中的type参数了,我们可以自己指定数字,也可以使用系统已经内置的一些值,例如,如果我们要做可以显示在桌面上的悬浮球,只需要使用系统Window中的TYPE_SYSTEM_ERROR即可,同时在manifest中声明权限:< uses-permission android:name=”android,permission.SYSTEM_ALERT_WINDOW”/>,没有权限的话,系统会报错。

  2. flags,表示window的属性,常用的有一下几种:

    FLAG_NOT_FOCUSABLE

    表示Window不需要获取焦点,此时不会接受任何输入事件,开启这个标记会自动开启FLAG_NOT_TOUCH_MODEL这个标识,事件会传递给下层具有焦点的Window去处理
    

    FLAG_NOT_TOUCH_MODEL

    在这个模式下,系统会将当前Window区域内的事件交给他自己去处理,而区域外的事件则交给下层的Window去处理,这个标记一般都是开启的。   
    

    FLAG_SHOW_WHEN_LOCKED

    开启这个模式可以让Window显示在锁屏界面上
    

Window的内部机制

  1. Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,可以说Window并不是客观存在的,而是以View的形式来展现,所以我们并不能直接访问到Window,所有对于Window的操作都是由WindowManager来进行的,而WindowManager是一个接口,具体实现是WindowManagerImpl类。Window的主要操作有三个,addView,updateView,removeView。

    1。Window的添加过程,我们来看一下WindowMnagerImpl中的源码:

        @override
        public void addView(View view,ViewGrounp.LayoutParams params){
            mGlobal.addView(view,params,mDisplay,mParentWindow);
        }
    
        @override
        public void updateViewLayout(View view,ViewGroup.LayoutParams params){
            mGlobal.updateViewLayout(view,params);
        }
    
        @override
        public void removeView(View view){
            mGlobal.removeView(view,false);
        }

    阅读源码我们可以看到,WindowManagerImpl并没有直接实现Window的操作,而是交给了WindowManagerGlobal来处理,WindowManagerGlobal以工厂的形式对外提供自己的实例。WindowManagerImpl将所有的操作全部委托给了WindowManagerGlobal来实现。下面我们来看一下WindowManagerGlobal的addView的具体实现:

    1。检查参数是否合法,如果是子Window的话,还需要调整一些布局参数
    
    if(view == null){
        throw new IllegalArgumentException("view must not be null);
    }
    if(display ==null){
        throw new IllegalArgumentException("diaplay must not be null);
    }
    if(!(params instanceof WindowManager.LayoutParams)){
        throw new IllegalArgumentException("params must be WindlwManager.LayoutParams");
    }
    
    final WindowManager.LayoutParams wparams = (WindowMnager.LayoutParams) params;
    if(parentWindow !=null){
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    }
    2.创建ViewRootImpl并将View添加到列表中
    
    private final ArrayList<View> mViews  = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots  = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams  = new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews= new ArraySet<View>();
    

    上边代码中,mViews存储的是所有Window对应的view,mRoots存储的则是所有Window对应的ViewRootImpl,mParams存储的是多有Window对应的布局参数,mDyingViews则存储的是所有的正在被删除的View对象,或者那些已经调用remove方法 但是还没有进行删除的view对象。

    addView方法通过如下方式将Window的一系列对象添加到列表中:

    root = new ViewRootImpl(view.getContext(),display);
    view.setLayoutParams(wparams);
    
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    3.通过ViewRootImpl来更新界面并完成Window 的添加过程,
    调用ViewRootImpl的setView方法,
    setView内部会调用requestLayout来进行一步刷新界面。
    

    2.Window的remove操作,remove操作与add操作类似,但需要注意的是WindowMnager提供了两个方法来删除,removeView和removeViewImmediate,分别表示移除删除和同步删除,一般来说使用异步删除即可,删除的具体操作是有ViewRootImpl的die方法来完成的,异步删除时,die方法只是发送了一个删除请求,将View添加到mDyingViews中,而并没有实际删除View,等待handle处理该请求,调用doDie方法,而同步删除的时候,不会发送消息,而是直接调用doDie方法删除View。

    3.Window的更新过程就比较简单了,它首先会更新View的LayoutParams替换掉老的LayoutParams,接着更新ViewRootImpl中的LayoutParams,通过ViewRootImpl的setLayoutParams方法来实现,ViewRootImpl会调用方法重新绘制Window的视图。

以上的操作,最终都会由WindowManagerService来进行操作,都是IPC过程。

参考资料:Android开发艺术探索

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值