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


前言:

前面我們已經學過《Android 自定義 view(三)—— onDraw 方法理解》,那么接下我們還需要繼續去理解自定義view里面的onMeasure 方法

推薦文章:

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

onMeasure 作用

(1)一般情況重寫onMeasure()方法作用是為了自定義View尺寸的規則,如果你的自定義View的尺寸是根據父控件行為一致,就不需要重寫onMeasure()方法
(2)如果不重寫onMeasure方法,那么自定義view的尺寸默認就和父控件一樣大小,當然也可以在布局文件里面寫死寬高,而重寫該方法可以根據自己的需求設置自定義view大小

認識 onMeasure

(0)onMeasure (int widthMeasureSpec, int heightMeasureSpec)是view自己的方法

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

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

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

認識 MeasureSpec

在測量自定義view的大小之前,我們需要認識一個類MeasureSpec,它封裝了父布局傳遞給子布局的布局要求,每個MeasureSpec代表了一組寬度和高度的要求  MeasureSpec由size和mode組成。

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  實踐練習

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

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

    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}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM