【转载】深刻剖析自定义View之onMeasure

1.前言

自定义View中咱们看到不少都重写了onMeasure方法,那么咱们首先得知道onMeasure是作什么的。onMeasure中文意思就是测量,因此它是用于测量View的大小,影响View大小的因素不少(父View的大小、padding、自身margin、weight),View中有一个measure方法,它会对全部View调用onMeasure方法用于测量全部View的width和height。java

2.代码分析

解决自定义View占用onMeasure的部分代码:程序员

@Override  
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
   int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
   int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
   int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
   int heightSize = MeasureSpec.getSize(heightMeasureSpec);  

   int width;  
   int height;  
   ...   

   if (widthMode == MeasureSpec.EXACTLY) {  
       // Parent has told us how big to be. So be it. 
       width = widthSize;  
   } else {  
       if (mLayout != null && mEllipsize == null) {  
           des = desired(mLayout);  
       }  

   ...  
   setMeasuredDimension(width, height);

从系统View中onMeasure方法能够看到其中出现了MeasureSpec类、widthMeasureSpec和heightMeasureSpec变量,这些变量又是从什么地方产生的以及有什么用途,首先咱们知道onMeasure是ViewGroup的onMeasure调用的,所以参数一定是父ViewGroup传入的,待稍后咱们经过系统代码展现父ViewGroup如何产生这个参数。web

3.MeasureSpec

widthMeasureSpec和heightMeasureSpec变量是用于描述View宽和高的模式与尺寸,对于measureSpec来讲,其实隐含两个信息:size和mode,measureSpec是一个int类型值共32位,其中高2位用于存储mode,低30位用于存储size,咱们能够经过MeasureSpec.getMode.和MeasureSpec.getSize方法进行分离,用于逻辑判断View具体须要的size。ide

 

enter description here

onmeasure.png

咱们知道在ViewGroup中,给View分配的控件大小并非肯定的,有可能随着具体的变化而变化,而这个变化的条件就是传给SpectMode中决定,specMode一共有三种状况:

 

MeasureSpec.EXACTLY  
    父视图但愿子视图的大小应该是父控件指定的specSize值。
  MeasureSpec.AT_MOST  
    子视图的大小最可能是specSize中指定的值,也就是说不建议子视图的大小超过specSize中给定的值。
  MeasureSpec.UNSPECIFIED  
    咱们能够随意指定视图的大小。

经过以上的这些分析,能够知道视图最终的大小由父视图,子视图以及程序员根据需求决定,良好的设计通常会根据子视图的measureSpec设置合适的布局大小。
此时有一个一问,咱们XML中设置的windth和height如何对应到onMeasure中的两个参数的呢?咱们用源码来进行讲解:布局

//ViewGroup中的获取,用不传递给ChildView
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);  
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);

private static int getRootMeasureSpec(int windowSize, int rootDimension) {  
    int measureSpec;  
    switch (rootDimension) {  

    case ViewGroup.LayoutParams.MATCH_PARENT:  
        // Window can't resize. Force root view to be windowSize. 
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);  
        break;  
    case ViewGroup.LayoutParams.WRAP_CONTENT:  
        // Window can resize. Set max size for root view. 
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);  
        break;  
    default:  
        // Window wants to be an exact size. Force root view to be that size. 
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);  
        break;  
    }  
    return measureSpec;  
}

从代码中能够看出,MATCH_PARENT对应于EXACTLY,WRAP_CONTENT对应于AT_MOST,其余状况对应于EXACTLY,它和MATCH_PARENT的区别在于size值不同。
根据以上提到的方法咱们可以得到父ViewGroup容许的高度和宽度以及模式,再根据自己View的逻辑进行计算应有的宽度和模式,而后经过setMeasuredDimension方法将自身高度和宽度进行设置,则View的测量就完成。spa

自定义经常使用

在咱们自定义View测量时,可能还会用到其余一些方法,如:设计

  • View.resolveSize(int size, int measureSpec)
    用于计算自身指望值和父视图提供值在模式下应该拥有的值,这个值可能等于自身指望值也可能低于指望值,由于父控件值影响code

  • MeasureSpec.makeMeasureSpec(int size, int mode)
    用于当咱们自定义ViewGroup的时候使用mode和size得到Specblog

  • view.getMeasuredWidth
    用于获取View的宽度测量值ip

  • view.getMeasureHeight
    用于获取View的高度测量值

  • getChildMeasureSpec
    用于获取子控件的MeasureSpec值

做者:老柏的博客
连接:http://www.jianshu.com/p/ba2e73899cc7

相关文章
相关标签/搜索