一、前言
Android自定義View中大致主要分為以下幾個(gè)步驟:
1、測量(onMeasure)
2、繪制(onDraw)
3、重寫touch事件(onTouchEvent)
自定義ViewGroup中呢則大致分為:
1、測量(onMeasure)
2、布局(onLayout)
3、重寫touch事件(onTouchEvent)
而這里主要講該如何去測量。測量實(shí)際上顧名思義就是要計(jì)算出我這個(gè)View在屏幕中顯示所要占用多大的空間。并且告訴我們的系統(tǒng)。
二、測量模式
ok!那么要對View進(jìn)行測量,首先我們要明白View的三種測量模式的意義。
1、EXACTLY
在沒有重寫onMeasure()方法的情況下,每個(gè)View默認(rèn)的測量方式既是EXACTLY,能夠響應(yīng)精確的dp值作為View的layout_width和layout_height,也能夠去響應(yīng)match_parent。
2、AT_MOST
AT_MOST對應(yīng)的既是響應(yīng)warp_content,如果View需要去支持warp_content的話,則必須去重寫onMeasure()方法。
3、UNSPECIFIED
UNSPECIFIED的話并不直接體現(xiàn)在xml布局文件的申明中,他代表著我們的View的大小有我們自己去在View的測量過程中自己給予,想要多大就多大。
并且需要提上一點(diǎn),這三種測量方式均是MeasureSpec類中的一個(gè)靜態(tài)整型常量。
三、測量步驟
1、重寫onMeasure()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
2、分別去測量寬高。
這里可以貼上一個(gè)模板代碼。
private int measuredWidth(int widthMeasureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}else {
result = 200;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
int specMode = MeasureSpec.getMode(widthMeasureSpec);能夠獲取到當(dāng)前的的測量模式
int specSize = MeasureSpec.getSize(widthMeasureSpec);能夠獲取當(dāng)前的實(shí)際尺寸
當(dāng)模式為EXACTLY的時(shí)候我們就只要順其自然的給原值就行。
如果不是呢?那證明是AT_MOST或UNSPECIFIED中的某一個(gè)。那么我們需要給一個(gè)給一個(gè)我們所期望的值,上面代碼將這個(gè)值給了200,實(shí)際上我們可以根據(jù)View中內(nèi)容的大小去給。
如果是UNSPECIFIED的話很明顯,我們只要給我們期望的值就行了,但是如果是AT_MOST也就是warp_content的話,我們需要取期望值和原值中最小的那一個(gè)作為我們最終的測量結(jié)果。
上訴代碼為測量width,測量height的方式完全一樣。
3、將測量結(jié)果傳遞出去。
在自定義View中,父類View這個(gè)類的onMeasure方法最終會(huì)將測量完畢的值調(diào)用setMeasuredDimension方法傳遞進(jìn)去。如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
所以我們重寫的onMeasure方法同樣的去調(diào)用setMeasuredDimension方法將測量完的值傳遞給他即可完成我們的測量。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measuredWidth(widthMeasureSpec), measuredHeight(heightMeasureSpec));
}
四、ViewGroup的測量
ViewGroup的測量重點(diǎn)就不在這個(gè)ViewGroup的自身了,而在它的每一個(gè)子View上,我們需要去測量每一個(gè)子View的實(shí)際大小。大致的代碼如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int ChildCount = getChildCount();
for (int i = 0; i < ChildCount; i++) {
measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
可以看到我們先是拿到了子View的總個(gè)數(shù),再去遍歷,通過measureChild方法一個(gè)一個(gè)的測量。測量完畢之后呢,我們就可以通過子View的對象去調(diào)用getMeasuredWidth()或getMeasuredHeight()拿到這個(gè)子View的實(shí)際尺寸了。