Android 自定義 view(四)—— onMeasure 方法理解

前言:

前面咱們已經學過《Android 自定義 view(三)—— onDraw 方法理解》,那麼接下咱們還須要繼續去理解自定義view裏面的onMeasure 方法html

推薦文章:canvas

http://blog.csdn.net/a396901990/article/details/36475213?utm_source=tuicool&utm_medium=referral

onMeasure 做用

(1)通常狀況重寫onMeasure()方法做用是爲了自定義View尺寸的規則,若是你的自定義View的尺寸是根據父控件行爲一致,就不須要重寫onMeasure()方法
(2)若是不重寫onMeasure方法,那麼自定義view的尺寸默認就和父控件同樣大小,固然也能夠在佈局文件裏面寫死寬高,而重寫該方法能夠根據本身的需求設置自定義view大小ide

認識 onMeasure

(0)onMeasure (int widthMeasureSpec, int heightMeasureSpec)是view本身的方法函數

(1)onMeasure 方法簡單的理解就是是用於測量視圖的大小,主要是用來測量本身和內容的來肯定寬度和高度 佈局

(2)onMeasure有兩個參數 ( int widthMeasureSpec, int heightMeasureSpec),該參數表示控件可得到的空間以及關於這個空間描述的元數據.測試

(3)widthMeasureSpec和heightMeasureSpec這兩個值一般狀況下都是由父視圖通過計算後傳遞給子視圖的,說明父視圖會在必定程度上決定子視圖的大小。ui

認識 MeasureSpec

在測量自定義view的大小以前,咱們須要認識一個類MeasureSpec,它封裝了父佈局傳遞給子佈局的佈局要求,每一個MeasureSpec表明了一組寬度和高度的要求  MeasureSpec由size和mode組成。 this

specMode一共有三種類型,以下所示:
1. EXACTLY
表示父視圖但願子視圖的大小應該是由specSize的值來決定的,系統默認會按照這個規則來設置子視圖的大小,簡單的說(當設置width或height爲match_parent時,模式爲EXACTLY,由於子view會佔據剩餘容器的空間,因此它大小是肯定的)

2. AT_MOST
表示子視圖最多隻能是specSize中指定的大小。(當設置爲wrap_content時,模式爲AT_MOST, 表示子view的大小最可能是多少,這樣子view會根據這個上限來設置本身的尺寸)

3. UNSPECIFIED
表示開發人員能夠將視圖按照本身的意願設置成任意的大小,沒有任何限制。這種狀況比較少見,不太會用到。

onMeasure 之我見示意圖

image

 

onMeasure  實踐練習

要去自定義控件上面寫一段文字,根據寬度.net

/**
* Created by yishujun on 16/6/5.
*/
public class YView extends View {
    private Context mContext;
    //定義一個paint
    private Paint mPaint;orm

    private String mText = "測試文字,自定義view";
    //繪製時控制文本繪製的範圍
    private Rect mBound;

    public YView(Context context) {
        this(context, null);
    }

    public YView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public YView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        mBound = new Rect();
        mPaint = new Paint();
        mPaint.setTextSize(60);
        mPaint.getTextBounds(mText, 0, mText.length(), mBound);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GRAY);
        mPaint.setColor(Color.RED);
        canvas.drawText(mText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);

    }

    /**
     * 比onDraw先執行
     * <p>
     * 一個MeasureSpec封裝了父佈局傳遞給子佈局的佈局要求,每一個MeasureSpec表明了一組寬度和高度的要求。
     * 一個MeasureSpec由大小和模式組成
     * 它有三種模式:UNSPECIFIED(未指定),父元素部隊自元素施加任何束縛,子元素能夠獲得任意想要的大小;
     * EXACTLY(徹底),父元素決定自元素的確切大小,子元素將被限定在給定的邊界裏而忽略它自己大小;
     * AT_MOST(至多),子元素至多達到指定大小的值。
     * <p>
     * 它經常使用的三個函數:
     * 1.static int getMode(int measureSpec):根據提供的測量值(格式)提取模式(上述三個模式之一)
     * 2.static int getSize(int measureSpec):根據提供的測量值(格式)提取大小值(這個大小也就是咱們一般所說的大小)
     * 3.static int makeMeasureSpec(int size,int mode):根據提供的大小值和模式建立一個測量值(格式)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        final int minimumWidth = getSuggestedMinimumWidth();
        final int minimumHeight = getSuggestedMinimumHeight();
        Log.e("YView", "---minimumWidth = " + minimumWidth + "");
        Log.e("YView", "---minimumHeight = " + minimumHeight + "");
        int width = measureWidth(minimumWidth, widthMeasureSpec);
        int height = measureHeight(minimumHeight, heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    private int measureWidth(int defaultWidth, int measureSpec) {

        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        Log.e("YViewWidth", "---speSize = " + specSize + "");


        switch (specMode) {
            case MeasureSpec.AT_MOST:
                defaultWidth = (int) mPaint.measureText(mText) + getPaddingLeft() + getPaddingRight();

                Log.e("YViewWidth", "---speMode = AT_MOST");
                break;
            case MeasureSpec.EXACTLY:
                Log.e("YViewWidth", "---speMode = EXACTLY");
                defaultWidth = specSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                Log.e("YViewWidth", "---speMode = UNSPECIFIED");
                defaultWidth = Math.max(defaultWidth, specSize);
        }
        return defaultWidth;
    }


    private int measureHeight(int defaultHeight, int measureSpec) {

        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        Log.e("YViewHeight", "---speSize = " + specSize + "");

        switch (specMode) {
            case MeasureSpec.AT_MOST:
                defaultHeight = (int) (-mPaint.ascent() + mPaint.descent()) + getPaddingTop() + getPaddingBottom();
                Log.e("YViewHeight", "---speMode = AT_MOST");
                break;
            case MeasureSpec.EXACTLY:
                defaultHeight = specSize;
                Log.e("YViewHeight", "---speSize = EXACTLY");
                break;
            case MeasureSpec.UNSPECIFIED:
                defaultHeight = Math.max(defaultHeight, specSize);
                Log.e("YViewHeight", "---speSize = UNSPECIFIED");
//        1.基準點是baseline
//        2.ascent:是baseline之上至字符最高處的距離
//        3.descent:是baseline之下至字符最低處的距離
//        4.leading:是上一行字符的descent到下一行的ascent之間的距離,也就是相鄰行間的空白距離
//        5.top:是指的是最高字符到baseline的值,即ascent的最大值
//        6.bottom:是指最低字符到baseline的值,即descent的最大值

                break;
        }
        return defaultHeight;


    }
}

第一組:

{4C40CF2B-25DA-8096-C52F-E8F2520A14E8}

{E417DA7A-21FA-440C-82D5-CE896F38EB83}

{9315B26B-8826-AB33-1CF1-A8FA872940EB}

 

第二組:

{1F4378AB-04E5-BCC2-FA22-7ACBD33F9715}

{26A41B85-0661-E8F6-64FE-79E1A4216B9D}

{624CCB2E-B126-A907-F93B-8B0793E08A66}

{6A2BFBA6-0584-A507-1CD2-F172451DE606}

相關文章
相關標籤/搜索