前言:
前面我們已經學過《Android 自定義 view(三)—— onDraw 方法理解》,那么接下我們還需要繼續去理解自定義view里面的onMeasure 方法
推薦文章:
http://blog.csdn.net/a396901990/article/details/36475213?utm_source=tuicool&utm_medium=referralonMeasure 作用
(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 之我見示意圖
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;
}
}
第一組: