8.创建窗口的过程

从WMS的角度来看,一个窗口类并不是Window类,而是一个View类.WMS接收到用户消息后,需要把消息派发到窗口,View类本身并不能直接接收WMS传递过来的消息,真正接受用户消息的必须是IWindow类,而实现IWindow类的是ViewRootImpl.W类,每个W内部都包含了一个ViewRootImpl变量.WMS并不介意该窗口是属于哪个应用程序的,WMS会按一定的规则判断哪个窗口处于活动状态,然后把用户消息给W类,W类再把用户消息传递给内部的ViewRootImpl变量,剩下的消息处理就由View对象完成.

Framework定义了三种窗口类型,三种类型的定义在WindowManager类中.

  • 第一种为应用窗口.所谓的应用窗口一般是指该窗口对应一个Activity,由于加载Activity是由AMS完成的,因此,对于应用程序来讲,要创建一个应用类窗口,只能在Activity内部完成.
  • 第二种是子窗口.所谓的子窗口是指,该窗口必须有一个父窗口,父窗口可以是一个应用类型窗口,也可以是任何其他类型的窗口.
  • 第三类是系统窗口.系统窗口不需要对应任何Activity,也不需要父窗口,对于应用程序而言,理论上是无法创建系统窗口的,因为所有的应用程序都没有这个权限,然而系统进程可以创建系统窗口.

WindowManager对这三种类型进行了细化,把每一种类型都用一个int常量表示,这些实际上代表了窗口对应的层(Layer).WMS在进行窗口叠加时,会按照该int常量的大小分配不同层,int值越大,代表层的位置越靠上面,即所谓z-order.

所有Activity默认的窗口类型都是TYPE_APPLICATION,WMS再进行窗口叠加时,会动态改变应用窗口的层值,但层值不会大于99.

创建子窗口时,客户端可以指定窗口类型介于1000~1999之间,WMS在进行窗口叠加时,会动态调整层值.

创建系统窗口时,可以指定层值在2000~2999之间,WMS在进行窗口叠加时,会动态调整该层值.

位置 定义
Activity IBinder mToken
Window IBinder mAppToken
WindowManager.LayoutParams IBinder token
ViewRootImpl View.AttachInfo mAttachInfo
View View.AttachInfo mAttachInfo
View.AttachInfo IBinder mWindowToken;IBinder mPanelParentWindowToken;IWindow mWindow;

WindowMangerImpl委托WindowManagerGlobal进行addView,WindowManagerGlobal中维护了三个数组,用于保存该应用程序中所拥有的窗口的状态,它们分别是:

  • ArrayList<View> mViews这里的每一个View对象都将成为WMS所认为的一个窗口
  • ArrayList<ViewRootImpl> mRoots所有的ViewRootImpl对象,mViews中每个View对象都对应的ViewRootImpl对象.
  • ArrayList<WindowManager.LayoutParams> mParams当把mViews中的View对象当做一个窗口添加进WMS中,WMS要求每个被添加的窗口都要对应一个LayoutParams对象,mParams正是保存了每个窗口对应的参数对象.

系统窗口的含义有两个方面:

  • 系统窗口不依赖于应用,而应用类窗口都必须有一个应用Activity与之对应.
  • 系统窗口是由系统创建的,应用程序没有权限创建.但有三个系统窗口除外,这三个系统窗口分别为TYPE_TOAST,TYPE_INPUT_METHOD,TYPE_WALLPAPER.
IBinder token = view.getWindowToken();
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
params.token = token;
View popup = LayoutInflater.from(context).inflate(layoutId, null);
mWm = (WindowManager)mContext.getSystemService("window");
mWm.addView(popup, params);
OnKeyListener mKeyListener = new OnKeyListener(){
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK) {
            mWm.removeView(popup);
        }
        return false;
    }
};
popup.setOnKeyListener(mKeyListener);

Activity/Dialog setContentView的时候会创建PhoneWindow.PopupWindow是framework提供的一种UI控件,PopupWindow并不继承于Window类,所以,该类本身并不是一个窗口类.PopupWindow并没有创建任何Window对象,换句话说,它添加的是一个纯粹的窗口,而不添加任何"杂质".窗口是指真正看得见的东西,而Window类仅仅是一个针对窗口交互的抽象而已,比如Window类中有菜单的概念,还可以处理系统按键,比如"HOME","BACK"等,而纯粹的窗口是没有这些的,纯粹的窗口会把所有处理用户消息的工作交给所包含的View/ViewGroup去处理,即不包含任何"杂质".在WMS眼里,所有的窗口都是纯粹的窗口.

PhoneWindow有一个重要的内部类PanelFeatureState,该类包含了PhoneWindow中所有的子窗口.选项菜单窗口只是一种特色feature而已,事实上,程序员可以为Activity中Window类添加任意多个不同的特色窗口,而所有这些特色窗口的信息保存在PanelFeatureState[] mPanels变量中.

在WMS中,当进行窗口重新排序时,就算一个系统窗口,如果其token值是其父窗口的token值,那么当其父窗口隐藏后,该系统窗口也会被隐藏.在Activity中添加系统窗口时,使用父窗口的token值,例如view.getWindowToken()作为系统窗口的token,这样做的结果是,当该Activity没有停止时,系统窗口会处于屏幕最上方,甚至可以覆盖掉系统状态栏窗口,而一旦该Activity处于隐藏状态时,新加的系统窗口也会被隐藏起来.在WMS内部,当判断系统窗口的token没有父窗口时,才会把该窗口的层值调整到屏幕上方,使之显示出来.

results matching ""

    No results matching ""