共计 3357 个字符,预计需要花费 9 分钟才能阅读完成。
PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接
ps:文章是基于 Android Api 31 来剖析源码的。
目录
1、View 的 measure 过程
1、1 View(它不是 ViewGroup)的 measure 过程
1、1、1 原始 View 的 measure 过程
1、1、2 具体 View 的 measure 过程
1、2 ViewGroup 的 measure 过程
1、View 的 measure 过程
这里 View 的 measure 的过程就是 View 的测量过程嘛,也就是去测量 View 的宽高,它分两种状况去思考,一种是具体的 View(比如说 TextView)或者说是原始的 View,一种是 ViewGroup。
1、1 View(它不是 ViewGroup)的 measure 过程
这里没有子元素 View 的 measure 过程就分为两种状况了,一种是它是 ViewGroup 的 View,一种是她不是 ViewGroup 的 View(比如说 TextView);好,咱们先剖析它不是 ViewGroup 的 measure 过程。
1、1、1 原始 View 的 measure 过程
咱们来看一下 View 的 measure 办法;
图片
看 View 的 measure 办法,它是 final 类型的对不对?那就是子类不能够重写该办法,要想从新计算 View 的宽高,那就得重写正文 1 中的 onMeasure 办法,咱们往下看 View 的 onMeasure 办法;
图片
咱们先看正文 2 中的 View 的 getDefaultSize(int size, int measureSpec) 办法;
图片
看正文 4、5,当 specMode 为 View.MeasureSpec.AT_MOST 和 View.MeasureSpec.EXACTLY 的时候,失去的 specSize 就是 View 测量后的大小,这里仅仅只是失去 View 测量后的大小哦,并不是 View 最终的大小哦,View 的最终大小是在 layout 过程确定的,不过简直所有状况下 View 的测量大小等于 View 的最终大小。
看正文 3,View.MeasureSpec.UNSPECIFIED 的模式下用于零碎外部测量,result 就为 size,而 size 是什么呢?咱们回到正文 2 中的代码,看到没有 size 是 getSuggestedMinimumWidth 办法或者 getSuggestedM-inimumHeight 办法的返回值,这里我就剖析 getSuggestedMinimum-Height 办法就好了,getSuggestedMinimumWidth 办法的原理跟 getSuggestedMinimumHeight 办法的实现原理是雷同的;好,咱们当初看一下 View 的 getSuggestedMinimumHeight 办法;
图片
看正文 6 的代码,mMinHeight 是 View 属性的 minHeight 值,默认值为 0,而 mBackground.getMinimumHeight() 就是图片的最小高度,当这个 View 设置了图片背景的时候,那么 View 的 getSuggestedMinimu-mHeight 办法就取 mMinHeight 和 图片的最小高度这 2 者的最大值,这就是 View 在 View.MeasureSpec.UNSPECIFIED 模式下测量的高;我这里举个例子,给出以下代码;
图片
看一下这个 TextView,它的 minHeight 的属性值为 50px,它加了一个背景图片 ic_launcher,假如 ic_launcher 的最小高度是 70px,那么该 TextView 在 View.MeasureSpec.UNSPECIFIED 模式下测量的高就是 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 并且宽和高的值都为 wrap_content 会怎么样呢?咱们回到 View 的 getDefaultSize(int size, int measureSpec) 办法,看正文 4、5 的代码,当 specMode 为 View.MeasureSpec.AT_MOST 和 View.MeasureSpe-c.EXACTLY 的时候,specSize 的值是一样的对不对?也就是说没有重写 onMeasure 办法的 View 的宽高值如果为 wrap_content,那么 View 的宽高值为 match_parent 时显示成果是一样的;不信?我举个例子,先看一下如下我一个 Activity 的 xml 文件的代码;
图片
假如我这个 View 标签看做是一个没有重写 onMeasure 办法的 “TextView” 标签,我执行一下 app,运行后果如下所示;
图片
看到没,这个 View 设置宽高为 wrap_content 时,间接充斥整个屏幕,而它的父容器是也充斥整个屏幕的。
看回正文 7 的代码,它又调用到了 View 的 setMeasuredDimension 办法,而 View 的 setMeasuredDimension 办法又会调用到 View 的 setMeasuredDimensionRaw 办法,也最终在 setMeasuredDi-mensionRaw 办法设置好它的测量宽高。
1、2 ViewGroup 的 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 的 setMeasuredDimen-sion 办法 又会调用到 setMeasuredDimensionRaw 办法来保留 ViewGroup(理论是 FrameLayout)的测量宽高。
假如 ViewGroup(理论是 FrameLayout)有子元素,那么正文 11 处的 count 就大于 0 对不对?那么就会执行 measureChildWithMargins 办法,而 measureChildWithMargins 办法则会调用子元素的 measure 过程,咱们看一下 ViewGroup 的 measureChildWithMargins 办法;
图片
看到正文 14 没,又到子元素递归去执行子元素的 measure 过程,直到某一层的子元素没有子元素则递归完结。