【轉載】深刻剖析自定義View之onMeasure

1.前言

自定義View中咱們看到不少都重寫了onMeasure方法,那麼咱們首先得知道onMeasure是作什麼的。onMeasure中文意思就是測量,因此它是用於測量View的大小,影響View大小的因素不少(父View的大小、padding、自身margin、weight),View中有一個measure方法,它會對全部View調用onMeasure方法用於測量全部View的width和height。java

2.代碼分析

解決自定義View佔用onMeasure的部分代碼:程序員

@Override  
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
   int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
   int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
   int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
   int heightSize = MeasureSpec.getSize(heightMeasureSpec);  

   int width;  
   int height;  
   ...   

   if (widthMode == MeasureSpec.EXACTLY) {  
       // Parent has told us how big to be. So be it. 
       width = widthSize;  
   } else {  
       if (mLayout != null && mEllipsize == null) {  
           des = desired(mLayout);  
       }  

   ...  
   setMeasuredDimension(width, height);

從系統View中onMeasure方法能夠看到其中出現了MeasureSpec類、widthMeasureSpec和heightMeasureSpec變量,這些變量又是從什麼地方產生的以及有什麼用途,首先咱們知道onMeasure是ViewGroup的onMeasure調用的,所以參數一定是父ViewGroup傳入的,待稍後咱們經過系統代碼展現父ViewGroup如何產生這個參數。web

3.MeasureSpec

widthMeasureSpec和heightMeasureSpec變量是用於描述View寬和高的模式與尺寸,對於measureSpec來講,其實隱含兩個信息:size和mode,measureSpec是一個int類型值共32位,其中高2位用於存儲mode,低30位用於存儲size,咱們能夠經過MeasureSpec.getMode.和MeasureSpec.getSize方法進行分離,用於邏輯判斷View具體須要的size。ide

 

enter description here

onmeasure.png

咱們知道在ViewGroup中,給View分配的控件大小並非肯定的,有可能隨着具體的變化而變化,而這個變化的條件就是傳給SpectMode中決定,specMode一共有三種狀況:

 

MeasureSpec.EXACTLY  
    父視圖但願子視圖的大小應該是父控件指定的specSize值。
  MeasureSpec.AT_MOST  
    子視圖的大小最可能是specSize中指定的值,也就是說不建議子視圖的大小超過specSize中給定的值。
  MeasureSpec.UNSPECIFIED  
    咱們能夠隨意指定視圖的大小。

經過以上的這些分析,能夠知道視圖最終的大小由父視圖,子視圖以及程序員根據需求決定,良好的設計通常會根據子視圖的measureSpec設置合適的佈局大小。
此時有一個一問,咱們XML中設置的windth和height如何對應到onMeasure中的兩個參數的呢?咱們用源碼來進行講解:佈局

//ViewGroup中的獲取,用不傳遞給ChildView
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);  
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);

private static int getRootMeasureSpec(int windowSize, int rootDimension) {  
    int measureSpec;  
    switch (rootDimension) {  

    case ViewGroup.LayoutParams.MATCH_PARENT:  
        // Window can't resize. Force root view to be windowSize. 
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);  
        break;  
    case ViewGroup.LayoutParams.WRAP_CONTENT:  
        // Window can resize. Set max size for root view. 
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);  
        break;  
    default:  
        // Window wants to be an exact size. Force root view to be that size. 
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);  
        break;  
    }  
    return measureSpec;  
}

從代碼中能夠看出,MATCH_PARENT對應於EXACTLY,WRAP_CONTENT對應於AT_MOST,其餘狀況對應於EXACTLY,它和MATCH_PARENT的區別在於size值不同。
根據以上提到的方法咱們可以得到父ViewGroup容許的高度和寬度以及模式,再根據自己View的邏輯進行計算應有的寬度和模式,而後經過setMeasuredDimension方法將自身高度和寬度進行設置,則View的測量就完成。spa

自定義經常使用

在咱們自定義View測量時,可能還會用到其餘一些方法,如:設計

  • View.resolveSize(int size, int measureSpec)
    用於計算自身指望值和父視圖提供值在模式下應該擁有的值,這個值可能等於自身指望值也可能低於指望值,由於父控件值影響code

  • MeasureSpec.makeMeasureSpec(int size, int mode)
    用於當咱們自定義ViewGroup的時候使用mode和size得到Specblog

  • view.getMeasuredWidth
    用於獲取View的寬度測量值ip

  • view.getMeasureHeight
    用於獲取View的高度測量值

  • getChildMeasureSpec
    用於獲取子控件的MeasureSpec值

做者:老柏的博客
連接:http://www.jianshu.com/p/ba2e73899cc7

相關文章
相關標籤/搜索