自定义View(8)关于measure->onMeasur->setMeasuredDimension及getDefaultSize,resolveSizeAndState

 

  参考: http://www.cnblogs.com/xiaorenwu702/p/5185436.html

  当外层容器组件调用其内部的某个组件view1.measure(xxx)时,view1的onMeasure(xxx)就被调用。

1,measure--->onMeasure--->setMeasuredDimension 流程

  Android通过调用View的measure()方法对View进行量算,让该View的父控件知道该View想要多大的尺寸空间。

  • 在View的measure方法会首先从成员变量中读取以前缓存过的量算结果,如果能找到该缓存值,那么就基本完事了。
  • 如果没有找到缓存值,那么measure方法会执行onMeasure回调方法,measure方法会将上述的宽度和高度的限制条件依次传递给onMeasure方法。
  • onMeasure方法会完成具体的量算工作,并将量算的结果通过调用View的setMeasuredDimension方法保存到View的成员变量mMeasuredWidth 和mMeasuredHeight中。

2,measure 的工作

  判断1:

    View先查看是不是要强制量算以及这次measure中传入的MeasureSpec与上次量算的MeasureSpec是否相同,如果不是强制量算或者MeasureSpec与上次的量算的MeasureSpec相同,那么View就不必真的去量算了。

  判断2:

    如果不满足上述条件,View就考虑去做量算工作。但是在量算之前,View还想偷懒,它会以MeasureSpec计算出的key值作为键,去成员变量mMeasureCache中查找是否缓存过对应key的量算结果,如果能找到,那么就简单调用一下setMeasuredDimensionRaw方法,将从缓存中读到的量算结果保存到成员变量mMeasuredWidth和mMeasuredHeight中。

  最后:

    如果不能从mMeasureCache中读到缓存过的量算结果,那么这次View就真的不能再偷懒了,只能乖乖地调用onMeasure方法去完成实际的量算工作,并且将尺寸限制条件widthMeasureSpec和heightMeasureSpec传递给onMeasure方法。关于onMeasure方法,我们会在下面详细介绍。

2,onMeasure的工作

  a.估算宽,高最小值,不要忘记计算leftpadding,rightpadding等。

  b.解析它传来的参数,得到最终值,如果是重写的ViewGroup,要调用其中的子view的measure方法。

  c.把测量到的数据保存到mMeasuredWidth和mMeasuredHeight中。

  示范:

 1     @Override
 2     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 3 
 4         // Try for a width based on our minimum
 5         //最小宽度
 6         int w = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
 7 
 8         //如果给出的建议是0,可以手动设置一个期望值。单位是像素。同时这步一定要在resolveSizeAndState前,
 9         // 因为它可能大于widthMeasureSpec的最大值。
10         if (w == 0) w = 56;
11 
12         //计算最佳值,在其中解析了 widthMeasureSpec
13         w = resolveSizeAndState(w, widthMeasureSpec, 0);
14 
15         // Whatever the width ends up being, ask for a height that would let the pie
16         // get as big as it can
17         //最小高度
18         int h = getSuggestedMinimumHeight() + getPaddingBottom() + getPaddingTop();
19 
20         //如果给出的建议是0,可以手动设置一个期望值。单位是像素。同时这步一定要在resolveSizeAndState前,
21         // 因为它可能大于heightMeasureSpec的最大值。
22         if (h == 0) h = 56;
23 
24         //计算最佳值,在其中解析了 heightMeasureSpec
25         h = resolveSizeAndState(h, heightMeasureSpec, 0);
26 
27 
28         //将量算的结果保存到View的成员变量mMeasuredWidth 和mMeasuredHeight中。
29         setMeasuredDimension(w, h);
30 
31         // 量算完成之后,View的父控件就可以通过调用
32         // getMeasuredWidth、getMeasuredState、getMeasuredWidthAndState
33         // 这三个方法获取View的量算结果。
34 
35     }

3,setMeasuredDimension的工作

  将量算的结果通过调用View的setMeasuredDimension方法保存到View的成员变量mMeasuredWidth 和mMeasuredHeight中。

  注意这两个变量,它们的含义并不是一个纯值。

  以mMeasuredWidth为例,它是一个Int类型的值,这4个字节中有两个含义:

  • 其高位的第一个字节为第一部分,用于标记量算完的尺寸是不是达到了View想要的宽度,我们称该信息为量算的state信息。
  • 其低位的三个字节为第二部分,用于存储实际的量算到的宽度。

  getMeasuredWidth系列方法返回的就是纯宽度值,不包含state信息,把mMeasuredWidth的第一部分去掉。

1   public final int getMeasuredWidth() {
2         //MEASURED_SIZE_MASK的值为0x00ffffff,用mMeasuredWidth与掩码MEASURED_SIZE_MASK进行按位与运算,
3         //可以将返回值中的高位字节的8个bit位全置为0,从而去掉了高位字节的state信息
4         return mMeasuredWidth & MEASURED_SIZE_MASK;
5     }

4,resolveSizeAndState和getDefaultSize的区别

  前者返回的数据中包含state信息,后者只是纯大小。

 

5,什么是MeasureSpec

   MeasureSpec作用:

  A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement for either the width or the height. A MeasureSpec is comprised of a size and a mode. There are three possible modes:

  一个MeasureSpec压缩了父组件递给子组件的宽高信息,这个值最终被压缩到一个int内(如onMeasure(int widthMeasureSpec,int heightMeasureSpec)中的两个参数),也可以用MeasureSpec解析这个int得到真正的宽,高。一个MeasureSpec由大小和模式组成。

   MeasureSpec三种模式:

  • UNSPECIFIED:

  This is used by a parent to determine the desired dimension of a child View. For example, aLinearLayout may call measure() on its child with the height set to UNSPECIFIED and a width ofEXACTLY 240 to find out how tall the child View wants to be given a width of 240 pixels.

  这种情况,是由外层容器父组件决定其子组件的大小,重写onMeasure时,这种模式不用测量。交给外层容器处理。
如:父组件可以把模式为UNSPECIFED,大小为不知的高度描述信息和一个EXACTLY模式,大小为240px的宽度描述信息传给子组件的onMeasure,这时子组件不用测量这个高,它由父根据子组件的宽度计算一个合适的高度。

  This is used by the parent to impose an exact size on the child. The child must use this size, and guarantee that all of its descendants will fit within this size.

  这种模式是父组件强制要求子组件的一个值,子组件重写onMeasure时必需用它。且要保证子组件的后代都要在这个值之内。

  This is used by the parent to impose a maximum size on the child. The child must guarantee that it and all of its descendants will fit within this size.

  这种模式是父组件强制要求子组件的一个最大值,子组件要保证其与后代都要在这个值之内。  

   MeasureSpec常用的4个函数

Public Methods
static int getMode(int measureSpec) Extracts the mode from the supplied measure specification. 提取模式(上述三个模式之一)
static int getSize(int measureSpec) Extracts the size from the supplied measure specification. 提取大小值
static int makeMeasureSpec(int size, int mode) Creates a measure specification based on the supplied size and mode. 把大小和模式压缩到一个int中
static String toString(int measureSpec) Returns a String representation of the specified measure specification. 返回字符串

 

 

posted @ 2016-02-16 21:49  f9q  阅读(1521)  评论(0编辑  收藏  举报