《Android开发艺术探索》学习笔记-Window

WindowManager

获取方式:

context.getSystemService(Context.WINDOW_SERVICE)

自由添加一个window层的控件:

val layoutParams = WindowManager.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT, TYPE_APPLICATION_OVERLAY, 0,
            PixelFormat.TRANSPARENT
        )
        layoutParams.flags =
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
        layoutParams.gravity = Gravity.LEFT or Gravity.TOP
        layoutParams.x = 100
        layoutParams.x = 300

        val view = TextView(context)
        view.text = "WindowManager test"
        val windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        windowManager.addView(view,layoutParams)

window实际是以View的形式存在的,WindowManager添加View时会创建对应的window被View依附

  • WindowManager继承自ViewManager
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

是一组抽象方法,ViewGroup也实现自这个接口

  • WindowManager的实现类是WindowManagerImpl
  • WindowManagerImpl的方法实现实际上是操作WindowManagerGlobal,WindowManagerGlobal是单例
  • addView时,创建对应的ViewRootImpl,WindowManagerImpl中维护:
    mViews存放View对象;
    mRoots存放View对应的ViewRootImpl对象;
    addView的mParentWindow来源于createLocalWindowManager,通过createLocalWindowManager设置parentWindow(后面会说到设置所有依附于Activity的window的parentWindow都是Activity的window),Activity中获取的WindowManage都是绑定了PhoneWindow的
  if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

mParams存放添加这个View时的WindowManager.LayoutParams

  • 最后交给了ViewRootImpl.setView方法去添加
  • setView方法去添加
 requestLayout();
 ...
  res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
                    setFrame(mTmpFrame);

IWindowSession mWindowSession的实现是Session类,这里是个IPC,是通过WindowManagerService的openSession创建出来的,最后将任务交给了WindowManagerService的addWindow方法

  • mWindow是ViewRootImpl构造方法创建的,是个IWindow.Stub,也是用了IPC

Activity的Window

在Activity的attach方法中创建:

 mWindow = new PhoneWindow(this, window, activityConfigCallback);
 mWindow.setWindowControllerCallback(this);

所以Activity中的window就是PhoneWindow,并且设置了WindowControllerCallback就是Activity自己,Activity中的一些回调就来自这个window

Activity的setContentView方法

调用了window的setContentView

 getWindow().setContentView(layoutResID);

然后看PhoneWindow:

   public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

       。。。
       mLayoutInflater.inflate(layoutResID, mContentParent);
        。。。
    }
  1. 首先检查是否有mContentParent(setContentView方法可能会重复调用覆盖原有布局,所以这里需要判断),没有则:通过installDecor()先创建DecorView(#generateDecor(),同时会绑定当前Window),然后根据主题设置加载布局作为DecorView的子View(#generateLayout()),比如有标题栏则可能是个LinearLayout,将这个子View中id为content的View赋值给mContentParent。
  2. 最后将setContentView设置的View加载到mContentParent下面去
  3. 现在布局就加载到Window里DecorView下面mContentParent中去了,根据上面WindowManager的知识,最后需要WindowManager.addView才能完成View的添加
  4. 最后在Activity的makeVisible()方法中完成这件事
  void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

Dialog中的Window

总体和Activity相似。有一点不同就是openOptionsMenu()添加的window的parentWindow是Dialog的这个window(下面一条会说明parentWindow一般都是Activity的window),这样当弹窗消息时,依附弹窗的菜单方便一起管理

Context.getSystemService(Context.WINDOW_SERVICE)

我们添加的子window都是需要依附一个Activity,除非是系统级的,所以我们通过Context.getSystemService(Context.WINDOW_SERVICE)拿到的WindowManager本质上都是从Activity的getSystemService获取到的,Activity的mWindowManager是通过内部的mWindow获取到的,也就是createLocalWindowManager绑定了当前Activity持有的Window的,所以后续创建的window都是依附于当前Activity的Window的子window,即获取到的WindowManager的parentWindow都是Activty的这个window

待解决的问题

Activity里面创建的window以及Dialog中创建出来的window如何被加载的,即在WindowManager.addView的哪个过程被用到,区别于我们在一开始的例子:parentWindow上添加window的时候都是直接addView而用不自己创建window

你可能感兴趣的:(《Android开发艺术探索》学习笔记-Window)