前置知识
Window
是什么?Window
是一个组件,View
是由Window
呈现出来的。Window
实际上就是管理着View
,对Window
的操作最终都会转化成对View
的操作。- 经常使用的
Window
:Activity
、Dialog
、PopupWindow
、Toast
等。系统中常见的Window
:StatusBar
、NavigationBar
、InputMethod
(软键盘)等。StatusBar
、NavigationBar
等是在单独的进程中使用的。
问题
1.WindowManager.LayoutParam
(继承ViewGroup.LayoutParams
)类很重要,flags
,type
等等。
type
,相当于Window 的类型,主要分为3大类。
- 1-99对应应用Window(token 必须设置为Activity 的token,即Context需要设置为Activity)。Activity 中的Window 对应的type 是
TYPE_BASE_APPLICATION
,Dialog
默认的是TYPE_APPLICATION
,WindowManager.LayoutParams
默认构造函对应的type也是TYPE_APPLICATION
。 - 1000-1999对应子Window。PopupWindow 默认是
TYPE_APPLICATION_PANEL
。 - 2000-2999 对应的是系统Window。
Toast
对应TYPE_TOAST
,StatusBar
对应TYPE_STATUS_BAR
,NavigationBar
对应TYPE_NAVIGATION_BAR
,键盘对应着”TYPE_INPUT_METHOD”。
flags
,各种不一样属性,控制Window 的一些特殊显示。
2.Dialog
和PopupWindow
的异同。
Dialog
和PopupWindoow
都会在一个新的Window
中展示。Dialog
不能使用ApplicationContext
,上面就可以知道Dailog
是应用Window
,只能使用Activity
。PopupWindow
不能在Activity.onCreate
中创建。PopupWindow
默认是一个子Window
,需要在Activity
创建以后使用。PopupWindow
一定需要设置width
和height
,默认是0。Dialog
是创建了一个PhoneWindow
然后获取的DecorView
,所有默认有Title,而PopupWindow
没有。PopupWindow
默认不会响应Back键,可以设置popupWindow.setFocusable(true);
。- 通过
Activity
来管理Dialog
的时候,Activity.mManagedDialogs
,当Activity
后台销毁的时候再次进入可以恢复(参考Dialog.onSaveInstanceState
、Dialog.onRestoreInstanceState
)。而PopupWindow
没有恢复机制。
至于网上很多说PopupWindow
是阻塞式的而Dialog
是非阻塞式的,是非常误解人的。关于坑爹的PopupWindow的“阻塞”争议问题:Android没有真正的“阻塞式”对话框
3.Toast
展示以后可以将Toast点击的事件传递到下面的View,Dialog
以及Popupindow
都不行,跟WindowManager.LayoutParams.flags
有关。
4.Activity
设置windowSoftInputMode
属性的时候,会对Activity
的Window
有一些影响。
5.一个App 中有多少个Window
?应该是某个状态下Window
的个数,Activity
+Dialog
+PopupWindow
+Toast
+WindowManage.addView
。
基本操作
WindowManager
继承ViewManager
,基本的操作如下。操作的实际上是View
。WindowManagerImpl
保存着一族对应的mViews
、mRoots
、mParams
。对于每一个展示的Window
实际上对应着mViews[k]
、mRoots[k]
、mParams[k]
。具体可以看后面的流程分析。
|
|
流程分析
Window.setStatusBarColor
流程(api 21以上)。
StatusBar
、NavigationBar
都是一个View展示在最上面和最下面??
1.Window.setStatusBarColor`。
|
|
2.DecorView.updateColorViews
。从代码中看就是修改了mNavigationColorViewState
和mStatusColorViewState
里面包含的View
的backgroundColor
。
|
|
3.DecorView.updateColorViewInt
|
|
从setStatusBarColor
(api 21以上)这个流程可以分析出来,Activity
中DecoeView
的StatusBar
和NavigationBar
对应的是一个普通的View
(系统中的StatusBar
和NavigationBar
都对应着一个Window
,StatusBar
中展示的电量、wifi等都是Window
中的View
,NavigationBar
中展示的返回键、Home键等也是Window
中的View
)。可以通过反射改变StatusBar
颜色和大小来验证(看源码也行的)DecoeView
对应着的是普通的View
。
|
|
WindowManager.addView()
流程。
1.执行到WindowManagerImpl.addView()
(WindowManagerImpl
继承WindowManager
)。
|
|
2.执行到WindowManagerGlobal.addView()
方法。
|
|
一个WindowManager
中对应着一系列的mViews
、mRoots
、mParams
,也就是WindowManager
管理着多个Window
,每一个Window
都对应着mViews[k]
、mRoots[k]
、mParams[k]
。如下所示。
|
|
3.执行到ViewRootImpl.setView()
方法。ViewRootImpl
相当于View
和ViewManager
之间的桥梁。里面的代码比较的长,会执行到ViewRootImpl.requestLayout()
。
4.执行到ViewRootImpl.requestLayout()
方法以及mWindowSession.addToDisplay
,其中ViewRootImpl.requestLayout()
会接着执行到了ViewRootImpl.scheduleTraversals()
。
|
|
5.执行到TraversalRunnable.run()
,最后执行到了ViewRootImpl.doTraversal()
。
|
|
6.执行到ViewRootImpl.performTraversals()
。计算希望的大小,
判断是否是第一次,判断视图是否修改(例如:内容区域),判断Window 是否需要调整大小,判断是否改变了View 的可见性。最后会执行到ViewRootImpl.performMeasure()
、ViewRootImpl.performLayout()
、ViewRootImpl.performDraw()
7.ViewRootImpl.performMeasure()
会调用View.measure
对View进行测量;ViewRootImpl.performLayout()
会调用View.layout
对View进行布局。ViewRootImpl.performDraw()
会调用ViewRootImpl.draw
,然后会调用到ViewRootImpl.drawSoftware
,最后就会调用到View.draw
。这一系列的操作完成以后就会相当于展示创建了一个View,大小、位置以及视图也完全处理完毕。
8.第4步中执行到ViewRootImpl.requestLayout()
以后的过程已经分析完毕,相当于是对View 进行了测量、布局以及绘制。接下来执行mWindowSession.addToDisplay
。mWindowSession
对应的是一个IWindowSession.aidl
,Java 层实现的类是com.android.server.wm.Session
。
|
|
9.执行WindowManagerService.addWindow
。这里就是Window
的添加等,没有细看。
总结:通过WindowManager
对Window
的操作实际上就是对里面View
的操作,展示的也是里面View
的信息。
WindowManager.removeView()
流程。
这里有一个问题需要考虑一下,通过Activity.getWindowManager().removeView(View view)
,这里removeView的参数是什么?是Activity中设置的
View中的一个子
View`吗?
addView
第2步就有说明,这个View
其实是一个整体的View
,不是某个View
中的子View
,而是一个Window 中展示的View。
1.直接看到WindowManagerGlobal.removeView()
。
|
|
2.执行WindowManagerGlobal.removeViewLocked
|
|
总结:removeView
也是对View
的操作。
PopupWindow
显示流程
showAsDropDown(View anchor)
/showAtLocation(View parent, int gravity, int x, int y)
到 preparePopup(WindowManager.LayoutParams p)
,最后invokePopup(WindowManager.LayoutParams p)
,这里不再分析。
Dialog
显示流程
show()
,这里不再分析。
其它内容
1.查看当前Window的信息
。
|
|
2.adb shell dumpsys window
查看当前所有的Window
信息,包括Window
的大小、位置。下面一个例子,拿中Window #3
来展示mAttrs
就展示了大小,mHasSurface
展示了起始位置。WindownManage.LayoutParams.mTitle 对应了名称。
|
|
一个详细的例子。下面总共有9个Window(0,1,..8)
,可以看到NavigationBar
,StatusBar
(StatusBarWindowManager
管理着StatusBar Window
的)等其实都是Window
。也非常好验证,将StatusBar
向下滑动唤出全屏的通知栏,执行adb shell dumpsys window
会发现StatusBar 变成了全屏(从mAttrs
可以看出)。
PS:PhoneStatusBar.java
控制着StatusBar
和NavigationBar
。详情可以查看源码frameworks.base.packages.SystemUI
包下面的代码,包含了StatusBar
、NavigationBar
、截屏Window
、锁屏Window
等。
|
|
3.PhoneWindowManager.java
拦截键盘消息(比如home操作、截屏操作等)的处理类。
4.通过Android Studio
查看View Hierarchy
等信息。分析布局更加全面。
|
|