生活随笔
收集整理的這篇文章主要介紹了
View工作原理(三)视图大小计算过程(measure过程)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一、android中view的measure過程總概
視圖大小計算的過程是從根視圖measure()方法開始,接著該方法會調用根視圖的onMeasure()方法,onMeasure()方法會對所包含的子視圖逐一執(zhí)行measure()方法,如果子視圖是ViewGroup子類對象(LinearLayout、FrameLayout、RelativeLayout等布局),則繼續(xù)調用子視圖的measure()方法,重復這一過程。如果子視圖是View子類對象(Button、EditText、TextView、ImageView等),則在子視圖重載的onMeasure方法內部不需要進行對子視圖進行measure操作,從而一次measure過程完成。過程如下圖所示:
二、measure詳細過程
View中的measure()方法源碼(ViewGroup類繼承了View類,measure過程先從ViewGroup子類開始):
[java] view plaincopy
public?final?void?measure(int?widthMeasureSpec,?int?heightMeasureSpec)?{?????????if?((mPrivateFlags?&?FORCE_LAYOUT)?==?FORCE_LAYOUT?||?????????????????widthMeasureSpec?!=?mOldWidthMeasureSpec?||?????????????????heightMeasureSpec?!=?mOldHeightMeasureSpec)?{????????????????????????????mPrivateFlags?&=?~MEASURED_DIMENSION_SET;???????????????if?(ViewDebug.TRACE_HIERARCHY)?{?????????????????ViewDebug.trace(this,?ViewDebug.HierarchyTraceType.ON_MEASURE);?????????????}????????????????????????????onMeasure(widthMeasureSpec,?heightMeasureSpec);?????????????????????????????????????????if?((mPrivateFlags?&?MEASURED_DIMENSION_SET)?!=?MEASURED_DIMENSION_SET)?{?????????????????throw?new?IllegalStateException("onMeasure()?did?not?set?the"?????????????????????????+?"?measured?dimension?by?calling"?????????????????????????+?"?setMeasuredDimension()");?????????????}???????????????mPrivateFlags?|=?LAYOUT_REQUIRED;?????????}???????????mOldWidthMeasureSpec?=?widthMeasureSpec;?????????mOldHeightMeasureSpec?=?heightMeasureSpec;?????}??
注:通過源碼,我們看到該方法的定義中用了final關鍵字,說明該方法是不能被重寫的,即View系統(tǒng)定義的這個measure框架不能被修改。參數(shù)widthMeasureSpec和heightMeasureSpec分別對應寬和高的measureSpec,當父視圖對子視圖進行measure操作時,會調用子視圖的measure()方法,該參數(shù)得意思是父視圖所提供的measure的“規(guī)格”,因為父視圖為子視圖提供的窗口尺寸是由父視圖和子視圖共同決定。該參數(shù)有兩部分組成,第一部分:高16位表示specMode,定義在MeasureSpec類中,有三種類型:MeasureSpec.EXACTLY:表示明確的尺寸大小,?MeasureSpec.AT_MOST:表示最大大小,?MeasureSpec.UNSPECIFIED:不確定大小。第二部分:低16位表示size,即父view的大小,這就是為什么我們在重寫onmeasure方法是需要:int?specMode?=?MeasureSpec.getMod(spec);?int?specSize?=?MeasureSpec.getSize(spec)這樣調用。specMode一般都為MeasureSpec.EXACTLY?,而size分別對應屏幕寬,高。也就是Window第一次掉用的view,一般都是這個值,而對于子view來說,這個值就是你在xml定義的屬性??android:layout_width和android:layout_height的值。
下面我們看看源碼執(zhí)行過程,看注釋就能很明白,首先清?除測量尺寸的標識。接著將重新測量自己的尺寸,即調用onMeasure()方法。最后是判斷測量尺寸大小的標識是否已經重新賦值,如果沒有則不執(zhí)行setMeasuredDimension()方法。方法結束。這個方法里面主要就是調用自己的onMeasure()方法,對自己的大小尺寸進行測量。下面來介紹onMeasure()方法。
?
ViewGroup中的onMeasure方法介紹
其實在ViewGroup類中并沒有重寫該方法,一般在他的子類中進行重寫,比如LinearLayout、RelativeLayout,下面我們以Linearlayout來分析。LinearLayout中onMeasure方法源碼如下:
[java] view plaincopy
@Override??protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{??????if?(mOrientation?==?VERTICAL)?{??????????measureVertical(widthMeasureSpec,?heightMeasureSpec);??????}?else?{??????????measureHorizontal(widthMeasureSpec,?heightMeasureSpec);??????}??}??
?
注:通過源碼我們可以知道,首先onMeasure會判斷這個布局是縱向布局還是橫向布局,即對應android:orientation=""屬性。下面以縱向布局來分析,源碼如下,有點長:
[java] view plaincopy
??????????????void?measureVertical(int?widthMeasureSpec,?int?heightMeasureSpec)?{????????mTotalLength?=?0;????????int?maxWidth?=?0;????????int?alternativeMaxWidth?=?0;????????int?weightedMaxWidth?=?0;????????boolean?allFillParent?=?true;????????float?totalWeight?=?0;??????????final?int?count?=?getVirtualChildCount();???????????????final?int?widthMode?=?MeasureSpec.getMode(widthMeasureSpec);????????final?int?heightMode?=?MeasureSpec.getMode(heightMeasureSpec);????????boolean?matchWidth?=?false;????????final?int?baselineChildIndex?=?mBaselineAlignedChildIndex;????????????????final?boolean?useLargestChild?=?mUseLargestChild;????????int?largestChildHeight?=?Integer.MIN_VALUE;????????????????for?(int?i?=?0;?i?<?count;?++i)?{????????????final?View?child?=?getVirtualChildAt(i);??????????????if?(child?==?null)?{????????????????mTotalLength?+=?measureNullChild(i);????????????????continue;????????????}????????????if?(child.getVisibility()?==?View.GONE)?{???????????????i?+=?getChildrenSkipCount(child,?i);???????????????continue;????????????}????????????LinearLayout.LayoutParams?lp?=?(LinearLayout.LayoutParams)?child.getLayoutParams();????????????totalWeight?+=?lp.weight;????????????????????????if?(heightMode?==?MeasureSpec.EXACTLY?&&?lp.height?==?0?&&?lp.weight?>?0)?{????????????????????????????????????????????????????????????????final?int?totalLength?=?mTotalLength;????????????????mTotalLength?=?Math.max(totalLength,?totalLength?+?lp.topMargin?+?lp.bottomMargin);????????????}?else?{????????????????int?oldHeight?=?Integer.MIN_VALUE;????????????????if?(lp.height?==?0?&&?lp.weight?>?0)?{????????????????????????????????????????????????????????????????????????????????????????????????????oldHeight?=?0;????????????????????lp.height?=?LayoutParams.WRAP_CONTENT;????????????????}????????????????????????????????????????????????????????????????????????????????measureChildBeforeLayout(???????????????????????child,?i,?widthMeasureSpec,?0,?heightMeasureSpec,???????????????????????totalWeight?==?0???mTotalLength?:?0);??????????????????if?(oldHeight?!=?Integer.MIN_VALUE)?{???????????????????lp.height?=?oldHeight;????????????????}????????????????final?int?childHeight?=?child.getMeasuredHeight();????????????????final?int?totalLength?=?mTotalLength;????????????????mTotalLength?=?Math.max(totalLength,?totalLength?+?childHeight?+?lp.topMargin?+???????????????????????lp.bottomMargin?+?getNextLocationOffset(child));??????????????????if?(useLargestChild)?{????????????????????largestChildHeight?=?Math.max(childHeight,?largestChildHeight);????????????????}????????????}???????????????????????????if?((baselineChildIndex?>=?0)?&&?(baselineChildIndex?==?i?+?1))?{???????????????mBaselineChildTop?=?mTotalLength;????????????}????????????????????????????????????????????????if?(i?<?baselineChildIndex?&&?lp.weight?>?0)?{????????????????throw?new?RuntimeException("A?child?of?LinearLayout?with?index?"????????????????????????+?"less?than?mBaselineAlignedChildIndex?has?weight?>?0,?which?"????????????????????????+?"won't?work.??Either?remove?the?weight,?or?don't?set?"????????????????????????+?"mBaselineAlignedChildIndex.");????????????}????????????boolean?matchWidthLocally?=?false;????????????if?(widthMode?!=?MeasureSpec.EXACTLY?&&?lp.width?==?LayoutParams.MATCH_PARENT)?{????????????????????????????????????????????????????????????????????????????????matchWidth?=?true;????????????????matchWidthLocally?=?true;????????????}????????????final?int?margin?=?lp.leftMargin?+?lp.rightMargin;????????????final?int?measuredWidth?=?child.getMeasuredWidth()?+?margin;????????????maxWidth?=?Math.max(maxWidth,?measuredWidth);????????????allFillParent?=?allFillParent?&&?lp.width?==?LayoutParams.MATCH_PARENT;????????????if?(lp.weight?>?0)?{???????????????????????????????????weightedMaxWidth?=?Math.max(weightedMaxWidth,????????????????????????matchWidthLocally???margin?:?measuredWidth);????????????}?else?{????????????????alternativeMaxWidth?=?Math.max(alternativeMaxWidth,????????????????????????matchWidthLocally???margin?:?measuredWidth);????????????}????????????i?+=?getChildrenSkipCount(child,?i);????????}????????if?(useLargestChild?&&?heightMode?==?MeasureSpec.AT_MOST)?{????????????mTotalLength?=?0;????????????for?(int?i?=?0;?i?<?count;?++i)?{????????????????final?View?child?=?getVirtualChildAt(i);????????????????if?(child?==?null)?{????????????????????mTotalLength?+=?measureNullChild(i);????????????????????continue;????????????????}????????????????if?(child.getVisibility()?==?GONE)?{????????????????????i?+=?getChildrenSkipCount(child,?i);????????????????????continue;????????????????}????????????????final?LinearLayout.LayoutParams?lp?=?(LinearLayout.LayoutParams)????????????????????????child.getLayoutParams();????????????????????????????????final?int?totalLength?=?mTotalLength;????????????????mTotalLength?=?Math.max(totalLength,?totalLength?+?largestChildHeight?+????????????????????????lp.topMargin?+?lp.bottomMargin?+?getNextLocationOffset(child));????????????}????????}????????????????mTotalLength?+=?mPaddingTop?+?mPaddingBottom;????????int?heightSize?=?mTotalLength;????????????????heightSize?=?Math.max(heightSize,?getSuggestedMinimumHeight());????????????????????????heightSize?=?resolveSize(heightSize,?heightMeasureSpec);????????????????????????????????int?delta?=?heightSize?-?mTotalLength;????????if?(delta?!=?0?&&?totalWeight?>?0.0f)?{????????????float?weightSum?=?mWeightSum?>?0.0f???mWeightSum?:?totalWeight;????????????mTotalLength?=?0;????????????for?(int?i?=?0;?i?<?count;?++i)?{????????????????final?View?child?=?getVirtualChildAt(i);????????????????if?(child.getVisibility()?==?View.GONE)?{????????????????????continue;????????????????}????????????????????????????????LinearLayout.LayoutParams?lp?=?(LinearLayout.LayoutParams)?child.getLayoutParams();????????????????????????????????float?childExtra?=?lp.weight;????????????????if?(childExtra?>?0)?{????????????????????????????????????????int?share?=?(int)?(childExtra?*?delta?/?weightSum);????????????????????weightSum?-=?childExtra;????????????????????delta?-=?share;????????????????????final?int?childWidthMeasureSpec?=?getChildMeasureSpec(widthMeasureSpec,????????????????????????????mPaddingLeft?+?mPaddingRight?+????????????????????????????????????lp.leftMargin?+?lp.rightMargin,?lp.width);??????????????????????????????????????????????????????????????if?((lp.height?!=?0)?||?(heightMode?!=?MeasureSpec.EXACTLY))?{????????????????????????????????????????????????????????????????????????int?childHeight?=?child.getMeasuredHeight()?+?share;????????????????????????if?(childHeight?<?0)?{????????????????????????????childHeight?=?0;????????????????????????}????????????????????????child.measure(childWidthMeasureSpec,????????????????????????????????MeasureSpec.makeMeasureSpec(childHeight,?MeasureSpec.EXACTLY));????????????????????}?else?{????????????????????????????????????????????????????????????????????????child.measure(childWidthMeasureSpec,????????????????????????????????MeasureSpec.makeMeasureSpec(share?>?0???share?:?0,????????????????????????????????????????MeasureSpec.EXACTLY));????????????????????}????????????????}????????????????final?int?margin?=??lp.leftMargin?+?lp.rightMargin;????????????????final?int?measuredWidth?=?child.getMeasuredWidth()?+?margin;????????????????maxWidth?=?Math.max(maxWidth,?measuredWidth);????????????????boolean?matchWidthLocally?=?widthMode?!=?MeasureSpec.EXACTLY?&&????????????????????????lp.width?==?LayoutParams.MATCH_PARENT;????????????????alternativeMaxWidth?=?Math.max(alternativeMaxWidth,????????????????????????matchWidthLocally???margin?:?measuredWidth);????????????????allFillParent?=?allFillParent?&&?lp.width?==?LayoutParams.MATCH_PARENT;????????????????final?int?totalLength?=?mTotalLength;????????????????mTotalLength?=?Math.max(totalLength,?totalLength?+?child.getMeasuredHeight()?+????????????????????????lp.topMargin?+?lp.bottomMargin?+?getNextLocationOffset(child));????????????}????????????????????????mTotalLength?+=?mPaddingTop?+?mPaddingBottom;????????????????????}?else?{????????????alternativeMaxWidth?=?Math.max(alternativeMaxWidth,???????????????????????????????????????????weightedMaxWidth);????????}????????if?(!allFillParent?&&?widthMode?!=?MeasureSpec.EXACTLY)?{????????????maxWidth?=?alternativeMaxWidth;????????}????????maxWidth?+=?mPaddingLeft?+?mPaddingRight;????????????????maxWidth?=?Math.max(maxWidth,?getSuggestedMinimumWidth());????????setMeasuredDimension(resolveSize(maxWidth,?widthMeasureSpec),?heightSize);????????if?(matchWidth)?{????????????forceUniformWidth(count,?heightMeasureSpec);????????}????}??
注:
獲取所有的子view數(shù)量,對每個子view開始處理,如果子view是GONE的,則直接跳過。獲取子view的LayoutParams,在xml中定義的參數(shù),通過layout_weight定義的值累加到變量totalWeight中,然后判斷如果view的height設置為零,但weight設置的大于0,則將height的值設置為LayoutParams.WRAP_CONTENT。然后調用measureChildWithMargins方法,該方法處理的邏輯:計算子view的measureSpec,即specMode和specSize,調用方法為:getChildMeasureSpec,調用兩次,分別?計算寬和高,getChildMeasureSpec內部根據父view的measure和子view的layout_width和layout_height屬性計算子view的measure。getChildMeasureSpec計算子view的measure,總結如下:1.如果在xml中指定了子view的具體大小,那么計算結果不管父的measure是什么,結果都是EXACITY+child_size,2.如果子view的height指定的值為FILL_PARENT,則返回的結果為:EXACITY+size,原因很簡單:因為FILL_PARENT的意思是充滿這個父view,所以返回的子view的measure就是view的大小。3.如果子view的大小為wrap_content,那么返回的結果都為AT_MOST+size,原因是:最大不能超過父view的大小。子view的measure確定好以后,然后調用子view的measure方法,如果子view是View對象,則該view的大小測量結束,開始下一個子view的循環(huán),如果子view是ViewGroup那么,又開始一個新的遞歸,處理邏輯和上面一樣,直到所有的view對象測量結束。所有的子view測量結束后,才開始對layout_weight計算,這樣我們可能想到,如果父view已經被占滿了,那么有可能layout_weight大于0的view對象是不會顯示的,而計算layout_weight的方法也很簡單,就是用總高度減去上面分析完mTotalLength的值,就是剩下,然后去平分給view對象,注意計算權重時優(yōu)先去android:android:weightSum(LinearLayout的xml屬性)的值,如果不設置該值會計算和,所以該值既然設置了,就一定要子view的weight的總和相等,否則平分可能不能得到預期效果。
?過程分析完畢,這篇文章這里提到了LinearLayout中的layout_weight屬性,這個屬性對很對人來說是又恨又愛,下篇文章,我們將來總結改屬性的詳細用法,讓大家徹底理解這個屬性。
總結
以上是生活随笔為你收集整理的View工作原理(三)视图大小计算过程(measure过程)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。