View绘制流程—performTraversals的后续逻辑

本文详细解析了Android中View的绘制流程,尤其是performTraversals之后的measure过程。介绍了MeasureSpec的三种模式及其对View尺寸的影响,以及View和ViewGroup的measure过程。在Activity启动时获取View宽高的注意事项和解决方案也进行了探讨。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

我们知道View绘制流程是在performTraversals()这个方法内展开的(perdormDraw(),perdormMeasure(),perdormLayout()),之前我们分析过app是如何抵达performTraversals这个方法的(戳我)
那么此篇的目的是在performTraversals之后更为具体的逻辑。

铺垫

在展开具体的讨论之前,我们需要知道一些基础知识点。

  • 基础认知1
    1.在ActivityThread中,当Acitivty对象被创建完毕后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。
    2.ViewRootImpl是连接WindowManager和DecorView的纽带。
    3.View三大流程均是藉由ViewRoot来完成的。

  • 基础认知2
    1.measure过程决定View的宽高,当这个过程执行完成,可以通过getMeasureWidth和getMeasureHeight来获取View测量后的宽高,至于获取的结果究竟和实际情况是否相符,这个是有特殊情况的,但大部分时间是相符的。
    2.Layout过程决定View的四个顶点坐标和实际View的宽高,完成之后可以通过getTop,getBottom,getLeft,getRight来拿到View四个顶点的位置。并能通过getWidth(),getHeaght()来获取最终实际的宽高。
    3.Draw过程决定View的显示,只有draw完成之后View内从才能显示到屏幕上。

  • 基础认知3
    1.DecorView是顶级View,它是一个FrameLayout。
    2.DecorView它内部包含一个竖直方向的LinearLayout。这个LinearLayout里有上下俩部分(android主题相关),上面是标题栏,下面是内容栏。
    3.在Activity中我们通过setContentView所设置的布局文件其实是被加到内容栏之中的。内容栏的系统id是R.id.content,这个内容栏和DecorView一样也是个FramentLayout
    4.获取内容栏:findViewById(R.android.id.content),获取内容栏中的View,content.getChildAt(0)

  • MeasureSpec基本认识
    1.MeasureSpec很大程度上决定了一个View的尺寸规格(View的尺寸规格还受父布局影响)
    2.MeasureSpec是一个32位的int值,高2位代表SpecMode—测量模式,低30位代表SpecSize—在某种测量模式下的规格大小
    3.MeasureSpec通过将specMode和specSize打包成一个int值来避免过多的对象内存分配
    4.MeasureSpec不仅仅是由LayoutParams来决定,LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View的宽高。但DecorView有点特殊,它的MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定。MeasureSpec一旦确定后,onMeasure中就可以确定View的测量宽/高。
    5.对于我们普通布局中的View,它的Measure是由ViewGroup传递而来。受父控件的宽高影响,同时也受padding,margin影响。
    6.SpecMode有三种类型:
    a.unspecified
    父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
    b.exactly
    父容器已经检测出View所需要的精确大小,这个时候View最终代销就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这俩种模式。
    c.at_most
    父容器指定了一个可用大小即specSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于layoutParams中的wrap_content

View工作流程

measure确定View的测量宽高
layout决定View的四个顶点,以及最终宽高
draw将View绘制到屏幕

Measure过程

1.若执行的对象是View,则只需要measure自己。
2.若执行的对象是ViewGroup,measure了自己之后,还会去mesure自己的子元素。

View的measure过程

1.final View.java#measure() -> View.onMeasure // 由View的measure方法为入口,该方法为final类型,子类无法去重写实现。接着去调用View的onMeasure方法,

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
		int measuredWidth =  getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(measuredWidth,measuredHeight);
    }

2.View.onMeasure() -> getDefaultSize()

    public static int getDefaultSize(int size, int measureSpec) {
    
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
    
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

a> case MeasureSpec.A

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值