android中onMeasure初看,深刻理解佈局之一!

今天學習android自定義組件:docs/guide/topics/ui/custom-components.htmlhtml

其中有兩個對佈局界面影響很的方法,onDraw(),和onMeasure().java

onDraw()比較好理解.onMeasure()就比較難理解一些,也更復雜些 ,引用文檔中的說法就是:android

onMeasure() is a little more involved.
其實還有另外一個方面的緣由就是我對這個單詞measure不是很知道,而後果了下詞典,就放了下心,確實是測量的意思.

實現onMeasure()方法基本須要完成下面三個方面的事情(最終結果是你本身寫相應代碼得出測量值並調用view的一個方法進行設置,告訴給你的view安排位置大小的父容器你要多大的空間.):less

1.傳遞進來的參數,widthMeasureSpec,和heightMeasureSpec是你對你應該得出來的測量值的限制.ide

 

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()方法的學習

實例,也比較好理解.ui

/**
     * @see android.view.View#measure(int, int)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
    }

    /**
     * Determines the width of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text
            result = (int) mTextPaint.measureText(mText) + getPaddingLeft()
                    + getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }

        return result;
    }

 

直接看measureWidth()this

首先看到的是參數,分別表明寬度和高度的MeasureSpecrest

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類代碼以下 :(註釋已經被我刪除了,由於在上面說明了.)

public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        public static int makeMeasureSpec(int size, int mode) {
            return size + mode;
        }
        public static int getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }  }

我無聊的將他們的十進制值打印出來了:

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

 

MODE_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));

相關文章
相關標籤/搜索