1. 程式人生 > >安卓自定義view繪製尺寸

安卓自定義view繪製尺寸

我們知道View在螢幕上顯示出來要先經過measure和layout. 在呼叫onMeasure(int widthSpec, int heightSpec)方法時,要涉及到MeasureSpec的使用,MeasureSpec有3種模式分別是UNSPECIFIED, EXACTLY和AT_MOST, 那麼這些模式和我們平時設定的layout引數fill_parent, wrap_content有什麼關係呢。經過程式碼測試就知道,當我們設定width或height為fill_parent時,容器在佈局時呼叫子 view的measure方法傳入的模式是EXACTLY,因為子view會佔據剩餘容器的空間,所以它大小是確定的。而當設定為 wrap_content時,容器傳進去的是AT_MOST, 表示子view的大小最多是多少,這樣子view會根據這個上限來設定自己的尺寸。當子view的大小設定為精確值時,容器傳入的是EXACTLY, 而MeasureSpec的UNSPECIFIED模式目前還沒有發現在什麼情況下使用。

MeasureSpec它常用的三個函式:

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

  2.static int getSize(int measureSpec):根據提供的測量值(格式)提取大小值(這個大小也就是我們通常所說的大小)

  3.static int makeMeasureSpec(int size,int mode):根據提供的大小值和模式建立一個測量值(格式)

  這個類的使用呢,通常在view元件的onMeasure方法裡面呼叫但也有少數例外,看看幾個例子:

a.首先一個我們常用到的一個有用的函式,View.resolveSize(int size,int measureSpec)

public static int resolveSize(int size, int measureSpec)
{

     int result = size;         

             int specMode = MeasureSpec.getMode(measureSpec);        

             int specSize =  MeasureSpec.getSize(measureSpec);  


             switch (specMode) 
             {        

                     case MeasureSpec.UNSPECIFIED:             
                             result = size;           

                             break;


                     case MeasureSpec.AT_MOST:             
                             result = Math.min(size, specSize);            

                             break;


                     case MeasureSpec.EXACTLY:             
                             result = specSize;            

                             break;         

             }


             return result;

}
上面既然要用到measureSpec值,那自然表示這個函式通常是在onMeasure方法裡面呼叫的。簡單說一下,這個方法的主要作用就是根據你提供的大小和模式,返回你想要的大小值,這個裡面根據傳入模式的不同來做相應的處理。

  再看看MeasureSpec.makeMeasureSpec方法,實際上這個方法很簡單

public static int makeMeasureSpec(int size, int mode)
{

    return size + mode;         

}
這樣大家不難理解size跟measureSpec區別了。看看它的使用吧,ListView.measureItem(View child)

private void measureItem(View child)
{

      ViewGroup.LayoutParams p = child.getLayoutParams();

      if (p == null) 
              {

           p = new ViewGroup.LayoutParams(

                   ViewGroup.LayoutParams.MATCH_PARENT,

                   ViewGroup.LayoutParams.WRAP_CONTENT);

      }



      int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,

              mListPadding.left + mListPadding.right, p.width); 
          int lpHeight = p.height;

              int childHeightSpec;



     if (lpHeight > 0) 
             {

           childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);

     } 
       else
       {

              childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);

        }

   child.measure(childWidthSpec, childHeightSpec);

}
measureSpec方法通常在ViewGroup中用到,它可以根據模式(MeasureSpec裡面的三個)可以調節子元素的大小。

  注意,使用EXACTLY和AT_MOST通常是一樣的效 果,如果你要區別他們,那麼你就要使用上面的函式View.resolveSize(int size,int measureSpec)返回一個size值,然後使用你的view呼叫setMeasuredDimension(int,int)函式。

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)

{

      mMeasuredWidth = measuredWidth;

      mMeasuredHeight = measuredHeight;



      mPrivateFlags |= MEASURED_DIMENSION_SET;     

}

然後你呼叫view.getMeasuredWidth,view.getMeasuredHeigth 返回的就是上面函式裡的mMeasuredWidth,mMeasuredHeight的值。

我們可以通過重寫onmeasure來自定義測量過程。如果view沒有重寫onmeasure方法,預設會直接呼叫getdefaultsize來獲得view的寬高。