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);
。。。
}
- 首先检查是否有mContentParent(setContentView方法可能会重复调用覆盖原有布局,所以这里需要判断),没有则:通过installDecor()先创建DecorView(#generateDecor(),同时会绑定当前Window),然后根据主题设置加载布局作为DecorView的子View(#generateLayout()),比如有标题栏则可能是个LinearLayout,将这个子View中id为content的View赋值给mContentParent。
- 最后将setContentView设置的View加载到mContentParent下面去
- 现在布局就加载到Window里DecorView下面mContentParent中去了,根据上面WindowManager的知识,最后需要WindowManager.addView才能完成View的添加
- 最后在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