measureChildren的工作原理

简介:

无论是在重写View还是ViewGroup的时候,尤其是ViewGrop的时候,往往不可避免的重写onMeasure方法,我们一定会调用setMeasuredDimension()将测量好的宽高值传递进去。也不免调用measureChildren方法,来测量所有的子View的大小,下面我们看看measureChildren方法是如何工作的。这对我们重写onMeasure无疑是很有帮助的。因为一般我们都会看到这一行代码


// 计算出所有的childView的宽和高  
        measureChildren(widthMeasureSpec, heightMeasureSpec);  


但是它到底测量到什么程度,满足不满足我们自定义ViewGroup对下面一系列child尺寸的测量需求,不知道这个我们写代码就心里没底。所以我们有必要扒出它的老底来看看,由此来决定我们是否可以直接使用这个方法,还是由于我们有更多的效果要实现,有更多的因素需要考虑,这个方法不能满足需求,需要自己写方法来测量child。


同时我们在有必要重新写方法来测量child的时候,我们也要从自带方法的思路开始扩展。


说了一大堆,总之这个问题很重要。

下面要了解它的工作原理,我们还是要来看看源码:

(一)首先是measureChildren

 /** 
 * 遍历所有的子view去测量自己(跳过GONE类型View) 
 * @param widthMeasureSpec 从父容器传递给子容器的布局需求(宽)
 * @param heightMeasureSpec 从父容器传递给子容器的布局需求(高)
 */  
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {  
    final int size = mChildrenCount;  
    final View[] children = mChildren;  
    for (int i = 0; i < size; ++i) {  
        final View child = children[i];  
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {  
            measureChild(child, widthMeasureSpec, heightMeasureSpec);  
        }  
    }  
}  

这部分很简单,也就是遍历所有的子View,如果View的状态不是GONE就调用measureChild去进行下一步的测量。


(二)所以我们再来看一下measureChild


/** 
 * 测量单个视图,将宽高和padding加在一起后交给getChildMeasureSpec去获得最终的测量值 
 * @param child 需要测量的子视图 
<pre name="code" class="java"> * @param widthMeasureSpec 从父容器传递给子容器的布局需求(宽)
 * @param heightMeasureSpec 从父容器传递给子容器的布局需求(高)
*/ protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { // 取得子视图的布局参数 final LayoutParams lp = child.getLayoutParams(); // 通过getChildMeasureSpec获取最终的宽高详细测量值 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); // 将计算好的宽高详细测量值传入measure方法,完成最后的测量 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
 

这个方法就是对一个子视图进行测量,其中一个重要的方法就是getChildMeasureSpec(),

(三)所以我们再来看一下getChildMeasureSpec

/** 
 * 
 * 结合父view的MeasureSpec与子view的LayoutParams信息去找到最好的结果 
 * (子view的确切大小由两方面共同决定:父view的MeasureSpec 子view的LayoutParams属性) 
 *  
 * @param spec 父view的MeasureSpec
 * @param padding view当前尺寸的的内边距和外边距(padding,margin) 
 * @param childDimension child在当前尺寸下的布局参数宽高值(LayoutParam.width,height) 
 */  
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {  
    //父view的模式和大小  
    int specMode = MeasureSpec.getMode(spec);     
    int specSize = MeasureSpec.getSize(spec);     
  
    //通过父view计算出的子view = 父大小-边距(父要求的大小,但子view不一定用这个值)   
    int size = Math.max(0, specSize - padding);  
  
    //子view想要的实际大小和模式(需要计算)  
    int resultSize = 0;  
    int resultMode = 0;  
  
    //通过1.父view的MeasureSpec 2.子view的LayoutParams属性这两点来确定子view的大小  
    switch (specMode) {  
    // 当父view的模式为EXACITY时,父view强加给子view确切的值  
    case MeasureSpec.EXACTLY:  
        // 当子view的LayoutParams>0也就是有确切的值  
        if (childDimension >= 0) {  
            //子view大小为子自身所赋的值,模式大小为EXACTLY  
            resultSize = childDimension;  
            resultMode = MeasureSpec.EXACTLY;  
        // 当子view的LayoutParams为MATCH_PARENT时(-1)  
        } else if (childDimension == LayoutParams.MATCH_PARENT) {  
            //子view大小为父view大小,模式为EXACTLY  
            resultSize = size;  
            resultMode = MeasureSpec.EXACTLY;  
        // 当子view的LayoutParams为WRAP_CONTENT时(-2)      
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
            //子view决定自己的大小,但最大不能超过父view,模式为AT_MOST  
            resultSize = size;  
            resultMode = MeasureSpec.AT_MOST;  
        }  
        break;  
  
    // 当父view的模式为AT_MOST时,父view强加给子view一个最大的值。  
    case MeasureSpec.AT_MOST:  
        // 道理同上  
        if (childDimension >= 0) {  
            resultSize = childDimension;  
            resultMode = MeasureSpec.EXACTLY;  
        } else if (childDimension == LayoutParams.MATCH_PARENT) {  
            resultSize = size;  
            resultMode = MeasureSpec.AT_MOST;  
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
            resultSize = size;  
            resultMode = MeasureSpec.AT_MOST;  
        }  
        break;  
  
    // 当父view的模式为UNSPECIFIED时,子view为想要的值  
    case MeasureSpec.UNSPECIFIED:  
        if (childDimension >= 0) {  
            // 子view大小为子自身所赋的值  
            resultSize = childDimension;  
            resultMode = MeasureSpec.EXACTLY;  
        } else if (childDimension == LayoutParams.MATCH_PARENT) {  
            // 因为父view为UNSPECIFIED,所以MATCH_PARENT的话子类大小为0  
            resultSize = 0;  
            resultMode = MeasureSpec.UNSPECIFIED;  
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
            // 因为父view为UNSPECIFIED,所以WRAP_CONTENT的话子类大小为0  
            resultSize = 0;  
            resultMode = MeasureSpec.UNSPECIFIED;  
        }  
        break;  
    }  
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  
}  


总而言之,这些判断和设置其实就是根据三种模式以及传入的尺寸要求,还有需要考虑的padding和margin之后,比较全面的计算出了一个测量值,了解了这些之后我们就可以确定什么时候需要自己写关于子视图的测量部分,什么时候我们只需要简单的一行代码:

// 计算出所有的childView的宽和高  
        measureChildren(widthMeasureSpec, heightMeasureSpec);  

就可以满足我们的需求了,所以一切还是按需来处理。


在我个人看来,这个方法考虑的比我最初想象的要全面多了,看来除了有比较特殊的需求,大部分的时候都是可以直接使用这个方法的。这还是省了不少事的。

如果您对我提到的模式或者是重写过程不大了解的,具体的关于重写onMeasure内容请详见我的另外一篇博客:

http://blog.csdn.net/sunmc1204953974/article/details/38454267


希望大家能有所收获,我也是学生,有什么写的不好的地方还请多多指教!

目录
相关文章
|
1月前
解释一下ConditionVariable的工作原理。
解释一下ConditionVariable的工作原理。
28 6
|
6月前
|
编解码
A/D和D/A工作原理
A/D(模数转换)和D/A(数模转换)是两种常见的信号转换技术,用于将模拟信号转换为数字信号(A/D)或将数字信号转换为模拟信号(D/A)。以下是对这两种技术的工作原理的详细介绍。 A/D转换器的工作原理: A/D转换器是一种将连续的模拟信号转换为离散的数字信号的设备。它由两个主要部分组成:采样和量化。 1. 采样: 采样是将连续的模拟信号在一定时间间隔内进行离散化的过程。A/D转换器使用一个称为采样保持电路的设备来完成这一过程。采样保持电路在一段时间内对模拟信号进行采样,并将其保持在一个电容中。采样过程中的时间间隔称为采样周期,其决定了采样率。采样率越高,转换的数字信号越接近原始模拟信号
119 0
|
6月前
|
vr&ar 芯片
三级管集电极开路电路工作原理详细分析
三级管集电极开路电路工作原理详细分析
65 0
|
6月前
|
定位技术 UED
卫星电话的工作原理简介
卫星电话的工作原理简介
328 0
|
芯片 SoC
FinFET工作原理、结构和应用特性介绍
FinFET的全称是Fin Field-Effect Transistor。它是一种新型互补金属氧化物半导体晶体管。FinFET 的名称是基于晶体管和鳍片形状的相似性。
9460 0
FinFET工作原理、结构和应用特性介绍
|
1月前
|
存储 缓存 IDE
CAN通信的基本原理与实现方法
CAN通信的基本原理与实现方法
143 0
|
8天前
|
前端开发
iStack详解(一)——iStack基本原理
iStack详解(一)——iStack基本原理
25 4
|
19天前
|
运维 监控 NoSQL
RedisShake的基本原理
RedisShake的基本原理
19 0
|
9月前
输入子系统一(输入子系统工作原理)
输入子系统一(输入子系统工作原理)
36 0
|
4月前
|
自然语言处理 JavaScript 前端开发