onMeasure方法

来源:互联网 发布:帝释天面具淘宝 编辑:程序博客网 时间:2024/05/21 09:38

有两个对布局界面影响很的方法,onDraw(),和onMeasure().

onDraw()比较好理解.onMeasure()就比较难理解一些,也更复杂些 ,引用文档中的说法就是:

onMeasure() is a little more involved.

其实还有另一个方面的原因就是我对这个单词measure不是很知道,然后果了下词典,就放了下心,确实是测量的意思.

实现onMeasure()方法基本需要完成下面三个方面的事情(最终结果是你自己写相应代码得出测量值并调用view的一个方法进行设置,告诉给你的view安排位置大小的父容器你要多大的空间.):

1.传递进来的参数,widthMeasureSpec,和heightMeasureSpec是你对你应该得出来的测量值的限制.

The overidden onMeasure() method is called with width and height measure specifications(widthMeasureSpec and heightMeasureSpec parameters,both are integer codes representing dimensions) which should be treated as requirements for the restrictions on the width and height measurements you should produce.
2. 你在onMeasure计算出来设置的width和height将被用来渲染组件.应当尽量在传递进来的width和height 声明之间.

虽然你也可以选择你设置的尺寸超过传递进来的声明.但是这样的话,父容器可以选择,如clipping,scrolling,或者抛出异常,或者(也许是用新的声明参数)再次调用onMeasure()

Your component's onMeasure() method should calculate a measurement width and height which will be required to render the component.it should try to stay within the specified passed in.although it can choose to exceed them(in this case,the parent can choose what to do,including clipping,scrolling,throwing an excption,or asking the onMeasure to try again,perhaps with different measurement specifications).
3.一但width和height计算好了,就应该调用View.setMeasuredDimension(int width,int height)方法,否则将导致抛出异常.

Once the width and height are calculated,the setMeasureDimension(int width,int height) method must be called with the calculated measurements.Failure to do this will result in an exceptiion being thrown
   

在Android提提供的一个自定义View示例中(在API demos 中的 view/LabelView)可以看到一个重写onMeasure()方法的

实例,也比较好理解.

01/**
02 * @see android.view.View#measure(int, int)
03 */
04@Override
05protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
06    setMeasuredDimension(measureWidth(widthMeasureSpec),
07            measureHeight(heightMeasureSpec));
08}
09 
10/**
11 * Determines the width of this view
12 * @param measureSpec A measureSpec packed into an int
13 * @return The width of the view, honoring constraints from measureSpec
14 */
15private int measureWidth(int measureSpec) {
16    int result = 0;
17    int specMode = MeasureSpec.getMode(measureSpec);
18    int specSize = MeasureSpec.getSize(measureSpec);
19 
20    if (specMode == MeasureSpec.EXACTLY) {
21        // We were told how big to be
22        result = specSize;
23    else {
24        // Measure the text
25        result = (int) mTextPaint.measureText(mText) + getPaddingLeft()
26                + getPaddingRight();
27        if (specMode == MeasureSpec.AT_MOST) {
28            // Respect AT_MOST value if that was what is called for by measureSpec
29            result = Math.min(result, specSize);
30        }
31    }
32 
33    return result;
34}

 

直接看measureWidth()

首先看到的是参数,分别代表宽度和高度的MeasureSpec

android2.2文档中对于MeasureSpec中的说明是:

一个MeasureSpec封装了从父容器传递给子容器的布局需求.

每一个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 compsized of a size and a mode.There are three possible modes:

 (1)UPSPECIFIED :父容器对于子容器没有任何限制,子容器想要多大就多大.

UNSPECIFIED The parent has not imposed any constraint on the child.It can be whatever size it wants

 (2) EXACTLY

 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间.

EXACTLY The parent has determined and exact size for the child.The child is going to be given those bounds regardless of how big it wants to be.

(3) AT_MOST

 子容器可以是声明大小内的任意大小.

AT_MOST The child can be as large as it wants up to the specified size

MeasureSpec是View类下的静态公开类,MeasureSpec中的值作为一个整型是为了减少对象的分配开支.此类用于

将size和mode打包或者解包为一个整型.

MeasureSpecs are implemented as ints to reduce object allocation.This class is provided to pack and unpack the size,mode tuple into the int

我比较好奇的是怎么样将两个值打包到一个int中,又如何解包.

MeasureSpec类代码如下 :(注释已经被我删除了,因为在上面说明了.)

01public static class MeasureSpec {
02    private static final int MODE_SHIFT = 30;
03    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
04 
05    public static final int UNSPECIFIED = 0 << MODE_SHIFT;
06    public static final int EXACTLY     = 1 << MODE_SHIFT;
07    public static final int AT_MOST     = 2 << MODE_SHIFT;
08 
09    public static int makeMeasureSpec(int size, int mode) {
10        return size + mode;
11    }
12    public static int getMode(int measureSpec) {
13        return (measureSpec & MODE_MASK);
14    }
15    public static int getSize(int measureSpec) {
16        return (measureSpec & ~MODE_MASK);
17    }  }

我无聊的将他们的十进制值打印出来了:

mode_shift=30,mode_mask=-1073741824,UNSPECIFIED=0,EXACTLY=1073741824,AT_MOST=-2147483648

然后觉得也应该将他们的二进制值打印出来,如下:

mode_shift=11110, // 30

mode_mask=11000000000000000000000000000000,

UNSPECIFIED=0, 

EXACTLY=1000000000000000000000000000000, 

AT_MOST=10000000000000000000000000000000

 

1MODE_MASK  = 0x3 << MODE_SHIFT //也就是说MODE_MASK是由11左移30位得到的.因为Java用补码表示数值.最后得到的值最高位是1所以就是负数了
对于上面的数值我们应该这样想,不要把0x3看成3而要看成二进制的11,

而把MODE_SHIFF就看成30.那为什么是二进制 的11呢?

呢,因为只有三各模式,如果有四种模式就是111了因为111三个位才可以有四种组合对吧.

我们这样来看,

UNSPECIFIED=00000000000000000000000000000000, 

      EXACTLY=01000000000000000000000000000000, 

    AT_MOST=10000000000000000000000000000000

也就是说,0,1,2

对应   00,01,10

当跟11想与时  00 &11 还是得到 00,11&01 -> 01,10&

我觉得到了这个份上相信,看我博客的也都理解了.

 return (measureSpec & ~MODE_MASK);应该是 return (measureSpec & (~MODE_MASK));



onMeasure方法在控件的父元素正要放置它的子控件时调用.它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec.

  它们指明控件可获得的空间以及关于这个空间描述的元数据.
  比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里.
    【点击下载 科学上网软件(能上youtube、facebook,享受google服务)】
  接下来的代码片段给出了如何重写onMeasure.注意,调用的本地空方法是来计算高度和宽度的.它们会译解widthHeightSpec和heightMeasureSpec值,并计算出合适的高度和宽度值.

java代码:

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  3. int measuredHeight = measureHeight(heightMeasureSpec);
  4. int measuredWidth = measureWidth(widthMeasureSpec);
  5. setMeasuredDimension(measuredHeight, measuredWidth);
  6. }

  7. private int measureHeight(int measureSpec) {


  8. // Return measured widget height.
  9. }

  10. private int measureWidth(int measureSpec) {

  11. // Return measured widget width.
  12. }
复制代码


       边界参数——widthMeasureSpec和heightMeasureSpec ,效率的原因以整数的方式传入。

      

     MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。

    它有三种模式:

                    UNSPECIFIED(未指定),     父元素不对自元素施加任何束缚,子元素可以得到任意想要的大小;

                    EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;

                    AT_MOST(至多),子元素至多达到指定大小的值。

   它常用的三个函数:

    1.static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)

  2.static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)

  3.static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)

     这个类的使用呢,通常在view组件的onMeasure方法里面调用但也有少数例外

 

       在它们使用之前,首先要做的是使用MeasureSpec类的静态方法getMode和getSize来译解,如下面的片段所示:

java代码:

  1. int specMode = MeasureSpec.getMode(measureSpec);
  2. int specSize = MeasureSpec.getSize(measureSpec);
复制代码


       依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;如果是EXACTLY,specSize 代表的是精确的尺寸;如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。
  当以EXACT方式标记测量尺寸,父元素会坚持在一个指定的精确尺寸区域放置View。在父元素问子元素要多大空间时,AT_MOST指示者会说给我最大的范围。在很多情况下,你得到的值都是相同的。
  在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。

  接下来的框架代码给出了处理View测量的典型实现:

java代码:

  1. @Override

  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

  3. int measuredHeight = measureHeight(heightMeasureSpec);

  4. int measuredWidth = measureWidth(widthMeasureSpec);

  5. setMeasuredDimension(measuredHeight, measuredWidth);

  6. }

  7. private int measureHeight(int measureSpec) {

  8. int specMode = MeasureSpec.getMode(measureSpec);
  9. int specSize = MeasureSpec.getSize(measureSpec);

  10. // Default size if no limits are specified.

  11. int result = 500;
  12. if (specMode == MeasureSpec.AT_MOST){

  13. // Calculate the ideal size of your
  14. // control within this maximum size.
  15. // If your control fills the available
  16. // space return the outer bound.

  17. result = specSize;

  18. else if (specMode == MeasureSpec.EXACTLY){

  19. // If your control can fit within these bounds return that value.
  20. result = specSize;
  21. }

  22. return result;
  23. }

  24. private int measureWidth(int measureSpec) {
  25. int specMode = MeasureSpec.getMode(measureSpec);
  26. int specSize = MeasureSpec.getSize(measureSpec);

  27. // Default size if no limits are specified.
  28. int result = 500;
  29. if (specMode == MeasureSpec.AT_MOST){
  30. // Calculate the ideal size of your control
  31. // within this maximum size.
  32. // If your control fills the available space
  33. // return the outer bound.
  34. result = specSize;


  35. else if (specMode == MeasureSpec.EXACTLY){
  36. // If your control can fit within these bounds return that value.

  37. result = specSize;
  38. }

  39. return result;
  40. }


0 0
原创粉丝点击