前置知识
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]
。具体可以看后面的流程分析。
// ViewManager.java
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
流程分析
Window.setStatusBarColor
流程(api 21以上)。
StatusBar
、NavigationBar
都是一个View展示在最上面和最下面??
1.Window.setStatusBarColor`。
// PhoneWindow.java
@Override
public void setStatusBarColor(int color) {
mStatusBarColor = color;
mForcedStatusBarColor = true;
if (mDecor != null) {
// 直接调用到了DecorView.updateColorViews()
mDecor.updateColorViews(null, false /* animate */);
}
}
2.DecorView.updateColorViews
。从代码中看就是修改了mNavigationColorViewState
和mStatusColorViewState
里面包含的View
的backgroundColor
。
// DecorView.java
private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
// insets null animate false
WindowManager.LayoutParams attrs = getAttributes();
// 获取系统状态栏的显示属性
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
if (!mIsFloating && ActivityManager.isHighEndGfx()) {
boolean disallowAnimate = !isLaidOut();
disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
mLastWindowFlags = attrs.flags;
if (insets != null) {
// 省略相关代码
}
// 这里相当于是横竖屏判断
boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
// 更新Navigation
updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor,
navBarSize, navBarToRightEdge, 0 /* rightInset */,
animate && !disallowAnimate);
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
// 更新Status
updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
mLastTopInset, false /* matchVertical */, statusBarRightInset,
animate && !disallowAnimate);
}
// 省略代码
return insets;
}
3.DecorView.updateColorViewInt
// DecorView.java
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) {
// animate false
state.present = (sysUiVis & state.systemUiHideFlag) == 0
&& (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
&& ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
|| force);
boolean show = state.present
&& (color & Color.BLACK) != 0
&& ((mWindow.getAttributes().flags & state.translucentFlag) == 0 || force);
boolean showView = show && !isResizing() && size > 0;
boolean visibilityChanged = false;
View view = state.view;
int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
// 横屏和竖屏展示的为位置不一样,分别对应着设置的horizontalGravity和verticalGravity
int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
if (view == null) {
if (showView) {
// 为空会创建。相当于创建了Navigation 和Status View
// 即改变StatusBar/NavigationBar 实际上是改变了一个View 的颜色
state.view = view = new View(mContext);
view.setBackgroundColor(color);
view.setTransitionName(state.transitionName);
view.setId(state.id);
visibilityChanged = true;
view.setVisibility(INVISIBLE);
state.targetVisibility = VISIBLE;
LayoutParams lp = new LayoutPara