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實(shí)際是以View的形式存在的捏顺,WindowManager添加View時會創(chuàng)建對應(yīng)的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也實(shí)現(xiàn)自這個接口
- WindowManager的實(shí)現(xiàn)類是WindowManagerImpl
- WindowManagerImpl的方法實(shí)現(xiàn)實(shí)際上是操作WindowManagerGlobal纬黎,WindowManagerGlobal是單例
- addView時幅骄,創(chuàng)建對應(yīng)的ViewRootImpl,WindowManagerImpl中維護(hù):
mViews存放View對象本今;
mRoots存放View對應(yīng)的ViewRootImpl對象拆座;
addView的mParentWindow來源于createLocalWindowManager,通過createLocalWindowManager設(shè)置parentWindow(后面會說到設(shè)置所有依附于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的實(shí)現(xiàn)是Session類挪凑,這里是個IPC,是通過WindowManagerService的openSession創(chuàng)建出來的,最后將任務(wù)交給了WindowManagerService的addWindow方法
- mWindow是ViewRootImpl構(gòu)造方法創(chuàng)建的逛艰,是個IWindow.Stub躏碳,也是用了IPC
Activity的Window
在Activity的attach方法中創(chuàng)建:
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
所以Activity中的window就是PhoneWindow,并且設(shè)置了WindowControllerCallback就是Activity自己散怖,Activity中的一些回調(diào)就來自這個window
Activity的setContentView方法
調(diào)用了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方法可能會重復(fù)調(diào)用覆蓋原有布局丹诀,所以這里需要判斷)钝的,沒有則:通過installDecor()先創(chuàng)建DecorView(#generateDecor()翁垂,同時會綁定當(dāng)前Window),然后根據(jù)主題設(shè)置加載布局作為DecorView的子View(#generateLayout())硝桩,比如有標(biāo)題欄則可能是個LinearLayout沿猜,將這個子View中id為content的View賦值給mContentParent。
- 最后將setContentView設(shè)置的View加載到mContentParent下面去
- 現(xiàn)在布局就加載到Window里DecorView下面mContentParent中去了碗脊,根據(jù)上面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相似。有一點(diǎn)不同就是openOptionsMenu()添加的window的parentWindow是Dialog的這個window(下面一條會說明parentWindow一般都是Activity的window),這樣當(dāng)彈窗消息時祈坠,依附彈窗的菜單方便一起管理
Context.getSystemService(Context.WINDOW_SERVICE)
我們添加的子window都是需要依附一個Activity害碾,除非是系統(tǒng)級的,所以我們通過Context.getSystemService(Context.WINDOW_SERVICE)拿到的WindowManager本質(zhì)上都是從Activity的getSystemService獲取到的赦拘,Activity的mWindowManager是通過內(nèi)部的mWindow獲取到的慌随,也就是createLocalWindowManager綁定了當(dāng)前Activity持有的Window的,所以后續(xù)創(chuàng)建的window都是依附于當(dāng)前Activity的Window的子window躺同,即獲取到的WindowManager的parentWindow都是Activty的這個window
待解決的問題
Activity里面創(chuàng)建的window以及Dialog中創(chuàng)建出來的window如何被加載的阁猜,即在WindowManager.addView的哪個過程被用到,區(qū)別于我們在一開始的例子:parentWindow上添加window的時候都是直接addView而用不自己創(chuàng)建window