安卓自定義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的寬高。