自定義View中咱們看到不少都重寫了onMeasure方法,那麼咱們首先得知道onMeasure是作什麼的。onMeasure中文意思就是測量,因此它是用於測量View的大小,影響View大小的因素不少(父View的大小、padding、自身margin、weight),View中有一個measure方法,它會對全部View調用onMeasure方法用於測量全部View的width和height。java
解決自定義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
widthMeasureSpec和heightMeasureSpec變量是用於描述View寬和高的模式與尺寸,對於measureSpec來講,其實隱含兩個信息:size和mode,measureSpec是一個int類型值共32位,其中高2位用於存儲mode,低30位用於存儲size,咱們能夠經過MeasureSpec.getMode.和MeasureSpec.getSize方法進行分離,用於邏輯判斷View具體須要的size。ide
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