前言
我们知道父布局根据自身和子布局的要求给子布局生成测量模式和测量尺寸,并封装在MeasureSpec对象里,最终传递给子布局让它最后确定自身的尺寸。
很自然就会想到,既然子布局是从父布局拿的测量结果,父布局又从它的父布局拿测量结果,最终到ViewTree的顶点根View是谁测量的呢?
循着这个问题,从源码角度一探究竟。
通过本篇文章,你将了解到:
1、Window 尺寸测量。
2、根View 尺寸测量。
3、Window、ViewRootImpl、View 三者关系。
1、Window 尺寸测量
一个小Demo
通过WindowManager.addView(xx)展示一个悬浮窗:
private void showView() {
//获取WindowManager实例
wm = (WindowManager) App.getApplication().getSystemService(Context.WINDOW_SERVICE);
//设置LayoutParams属性
layoutParams = new WindowManager.LayoutParams();
//宽高尺寸
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.format = PixelFormat.TRANSPARENT;
//设置背景阴暗
layoutParams.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
layoutParams.dimAmount = 0.6f;
//Window类型
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
//构造TextView
TextView myView = new TextView(this);
myView.setText("hello window");
//设置背景为红色
myView.setBackgroundResource(R.color.colorRed);
FrameLayout.LayoutParams myParam = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 400);
myParam.gravity = Gravity.CENTER;
myView.setLayoutParams(myParam);
//myFrameLayout 作为rootView
FrameLayout myFrameLayout = new FrameLayout(this);
//设置背景为绿色
myFrameLayout.setBackgroundColor(Color.GREEN);
myFrameLayout.addView(myView);
//添加到window
wm.addView(myFrameLayout, layoutParams);
}
上述代码简单概述如下:
1、构造TextView,设置其背景为红色。
2、构造FrameLayout,设置其背景为绿色。
3、将TextView作为子View添加到FrameLayout。
4、将FrameLayout作为RootView(根View)添加到Window里。
注意到:
wm.addView(myFrameLayout, layoutParams);
layoutParams 里重点关注宽、高字段的值,我们知道这是给Window的尺寸约束,以宽为例,设置不同的值,看看其效果:
1、wrap_content
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
[图片上传失败…(image-f8d0f1-1616851800488)]
可以看出:RootView(FrameLayout) 包裹着TextView,两者宽度一致。
2、match_parent
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
可以看出:RootView(FrameLayout) 宽充满屏幕。
3、设置具体的值
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = 800;
可以看出:RootView(FrameLayout) 宽没充满屏幕,屏幕宽1080px。
结合上述三张图,我们有理由相信,wm.addView(myFrameLayout, layoutParams) 里的layoutParams 是用来约束myFrameLayout(RootView),那么Window尺寸是怎么来的呢?
Window 尺寸的确定
从wm.addView(xx)开始分析,WindowManager 是个接口,其实现类是:WindowManagerImpl。
#WindowManagerImpl.java
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
//赋值token,在启动Dialog/PopupDialog 会判断该值
applyDefaultToken(params);
//mGlobal 为单例