onMeasure() 为什么会执行多次?Android 一个困惑很久的问题!

前言

我们知道父布局根据自身和子布局的要求给子布局生成测量模式和测量尺寸,并封装在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 为单例
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值