当前位置: 首页 > 后端技术 > Java

Android中View工作流的测量过程

时间:2023-04-01 22:24:50 Java

PS:本文为转载文章,原文可读性会更好,文末有原文链接ps:文章为基于AndroidApi31来分析源码。目录1.View的测量过程1.1View的测量过程(不是ViewGroup)1.1.1原始View的测量过程1.1.2具体View的测量过程1.2ViewGroup的测量过程1.View的测量过程这里是测量过程View的宽度,也就是测量View的宽高。它分两种情况考虑,一种是特定的View(比如TextView)或者原来的View,一种是ViewGroup。1.1View的测量过程(不是ViewGroup)View没有子元素的测量过程有两种情况,一种是是ViewGroup的View,一种是不是ViewGroup的View(比如TextView);好吧,我们先分析一下,不是ViewGroup的measure过程。1,1,1原始View的measure过程我们先来看看View的measure方法;看图中View的measure方法,是final类型的吧?也就是说,子类不能重写这个方法。如果要重新计算View的宽高,必须要重写注1中的onMeasure方法,下面看View的onMeasure方法;我们先看Note2中的View。getDefaultSize(intsize,intmeasureSpec)方法;看图中注释4和5,当specMode为View.MeasureSpec.AT_MOST和View.MeasureSpec.EXACTLY时,得到的specSize是View的实测尺寸,这里只是为了得到View的实测尺寸,并不是最终的视图的大小。View的最终大小是在布局过程中确定的,但几乎在所有情况下,View的测量大小等于View的最终大小。见注3,View.MeasureSpec.UNSPECIFIED模式用于系统内部测量,结果为size,什么是size?我们回到注释2中的代码,发现getSuggestedMinimumWidth方法或getSuggestedM-inimumHeight方法的返回值都没有size。这里我就分析getSuggestedMinimum-Height这个方法。getSuggestedMinimumWidth方法的原理与getSuggestedMinimumHeight方法相同。;好了,我们现在来看View的getSuggestedMinimumHeight方法;图片看注释6中的代码,mMinHeight是View属性的minHeight值,默认值为0,mBackground.getMinimumHeight()是图片的最小高度,当View设置图片为背景时,然后View的getSuggestedMinimu-mHeight方法取mMinHeight的最大值和图片的最小高度,即View.MeasureSpec.UNSPECIFIED模式下View测量的高度;我这里举个例子,给出如下代码;picture看这个TextView,它的minHeight属性值为50px,它添加了一个背景图片ic_launcher,假设ic_launcher的最小高度为70px,那么在View.MeasureSpec.UNSPECIFIED模式下测得的TextView的高度为70px。我们看一下注释2中的setMeasuredDimension方法;见图中注7,View的setMeasuredDimension方法调用了View的setMeasuredDimensionRaw方法;见图中View的setMeasuredDimensionRaw方法,它只是将View的测量宽高赋值给mMeasuredWidth,mMeasuredHeight保存下来。1,1,2具体View的measure过程下面是具体的View(比如TextView,ImageView等),他们的measure有什么区别?针对具体的View,重写其onMeasure方法;下面以TextView为例来谈谈TextView的measure,因为View的measure方法是final的,所以只需要看TextView的onMeasure方法即可;见图中注释8、9,TextView重写了onMeasure方法,将widthMode特殊处理为View.MeasureSpec.AT_MOST,heightMode特殊处理为View.MeasureS-pec.AT_MOST;如果TextView不重写onMeasure并且width和height的值都是wrap_content呢?我们回到View的getDefaultSize(intsize,intmeasureSpec)方法,看注释4和注释5中的代码,当specMode为View.MeasureSpec.AT_MOST和View.MeasureSpe-c.EXACTLY时,specSize的值是一样的吧??也就是说,如果没有重写onMeasure方法的View的宽高值为wrap_content,那么View的宽高值为match_parent时显示效果是一样的;你不相信吗?我举个例子,先看我的Activity的一个xml文件的代码如下;图中假设我的View标签是一个“TextView”标签,没有重写onMeasure方法,我执行app,运行结果如下;你看到图片了吗?当这个View的宽高设置为wrap_content时,它直接充满了整个屏幕,它的父容器也充满了整个屏幕。回顾一下Note7中的代码,它调用了View的setMeasuredDimension方法,View的setMeasuredDimension方法又调用了View的setMeasuredDimensionRaw方法,最后在setMeasuredDi-mensionRaw方法中设置了它的测量宽高。1.2ViewGroup的measure过程如果ViewGroup去measure,它不仅对自己进行measure,还会对它的子元素(如果有子元素)进行measure,每个子元素递归执行measure过程;ViewGroup是一个抽象类,提供了一个measureChildWithMargins方法,分发给子元素进行测量。另外,每个具体的ViewGroup容器的测量过程是不同的,所以ViewGroup并没有重写onMeasure方法,而是在ViewGroup的子类中重写了onMeasure方法;我们以FrameLayout为例来分析,因为View的每个子类都不能重写measure方法,但是measure方法会调用子类的onMeasure方法,所以从FrameLayout的onMeasure方法可以看出;看图片的注解11处的代码,如果这个ViewGroup(其实是FrameLayout)没有子元素,那么count为0,那么for循环里面的语句就不会走吧?然后就是自己测量,也就是执行13处注释的代码会调用View的setMeasuredDimension方法,View的setMeasuredDimension方法会调用setMeasuredDimensionRaw方法保存ViewGroup(其实是FrameLayout)的测量宽高。假设ViewGroup(其实是FrameLayout)有子元素,那么注释11处的count是大于0的吧?然后会执行measureChildWithMargins方法,measureChildWithMargins方法会调用子元素的measure过程。我们看一下ViewGroup的measureChildWithMargins方法;图见注释14,然后递归到子元素去执行子元素的measure过程,直到某个层的子元素没有子元素时递归结束。