View的Measure流程总结
首先,Measure流程 是為了測量,并計算view的大小.寬mMeasuredWidth,高mMeasuredHeight,然后將寬高保存.為后續(xù)layout 和draw 提供數(shù)據(jù)支撐.而且measure過程不止一次.
數(shù)值保存MeasureSpec
父容器的layoutParams會確認MeasureSpec,即view的測量模式和大小
MeasureSpec包含一個32位的int值,高2位代表SpaceMode,低30位代表SpecSize.
MeasureSpec有三類.view會有兩個MeasureSpec變量,分別為widthMeasureSpec,heightMeasureSpec.
以下結(jié)論會在getChildMeasureSpec中得到驗證
EXACTLY :父容器已經(jīng)測量出所需要的精確大小,這也是childview的最終大小------match_parent,精確值.
ATMOST : child view最終的大小不能超過父容器的給的------wrap_content .
UNSPECIFIED: 不確定,源碼內(nèi)部使用-------一般在ScrollView,ListView .
MeasureSpace大多數(shù)情況下是由父布局的MeasureSpace和自己的Layoutparams確定(當然,還有margin,padding).詳情見viewgroup.getChildMeasureSpec()
View
View的關(guān)鍵方法
2. measure 父布局會在自己的onMeasure方法中,調(diào)用child.measure ,這就把measure過程轉(zhuǎn)移到了子View中。
3. onMeasure 子View會在該方法中,根據(jù)父布局給出的限制信息,和自己的content大小,來合理的測量自己的尺寸。
4. setMeasuredDimension當View測量結(jié)束后,把測量結(jié)果保存起來,具體保存在mMeasuredWidth和mMeasuredHeight中。
View的測量過程
measure()-->onMeasure()-->setMeasuredDimension()
viewGroup
viewGroup的關(guān)鍵方法
1. getChildMeasureSpec(父容器space,padding,一般是父容器的layoutparam.width或heigh)為child計算MeasureSpec。該方法為每個child的每個維度(寬、高)計算正確的MeasureSpec。目標就是把當前viewgroup的MeasureSpec和child的LayoutParams結(jié)合起來,生成最合理的結(jié)果。
//主代碼case MeasureSpec.EXACTLY:if (childDimension >= 0) {resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}一段通俗易懂的getChildMeasureSpec偽代碼
public static int getChildMeasureSpec(int 限制信息中的模式, int padding, int layoutparam.width或heigh) {獲取限制信息中的尺寸和模式。switch (限制信息中的模式) {case 當前容器的父容器,給當前容器設(shè)置了一個精確的尺寸:if (子View申請固定的尺寸LayoutParams) {你就用你自己申請的尺寸值就行了;} else if (子View希望和父容器一樣大) {你就用父容器的尺寸值就行了;} else if (子View希望包裹內(nèi)容) {你最大尺寸值為父容器的尺寸值,但是你還是要盡可能小的測量自己的尺寸,包裹你的內(nèi)容就足夠了;} break;case 當前容器的父容器,給當前容器設(shè)置了一個最大尺寸:if (子View申請固定的尺寸) {你就用你自己申請的尺寸值就行了;} else if (子View希望和父容器一樣大) {你最大尺寸值為父容器的尺寸值,但是你還是要盡可能小的測量自己的尺寸,包裹你的內(nèi)容就足夠了;} else if (子View希望包裹內(nèi)容) {你最大尺寸值為父容器的尺寸值,但是你還是要盡可能小的測量自己的尺寸,包裹你的內(nèi)容就足夠了;} break;case 當前容器的父容器,對當前容器的尺寸不限制:if (子View申請固定的尺寸) {你就用你自己申請的尺寸值就行了;} else if (子View希望和父容器一樣大) {父容器對子View尺寸不做限制。} else if (子View希望包裹內(nèi)容) {父容器對子View尺寸不做限制。}break;} return 對子View尺寸的限制信息;}這個就是對應的結(jié)論圖,前三項是方法參數(shù),后兩個為計算得到的值.
這里寫圖片描述還有這個結(jié)論
EXACTLY :父容器已經(jīng)測量出所需要的精確大小,這也是childview的最終大小------match_parent,確定值(不管你是0還是多少).
ATMOST : child view最終的大小不能超過父容器的給的------wrap_content .
UNSPECIFIED: 不確定,源碼內(nèi)部使用-------一般在ScrollView,ListView (我們一般用match_parent, wrap_content,還有確定值,這個不用).
2. measureChildren讓所有子view測量自己的尺寸,需要考慮當前ViewGroup的MeasureSpec和Padding。跳過狀態(tài)為gone的子view.
3. measureChild 測量單個view的尺寸.需要考慮當前ViewGroup的MeasureSpec和Padding.
4. measureChildWithMargins 測量單個View,需要考慮當前ViewGroup的MeasureSpec和Padding、margins.
viewGroup的測量流程
measureChildren()--> getChildMeasureSpec()-->child.measure()
protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {final LayoutParams lp = child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,//獲取測量模式mPaddingLeft + mPaddingRight, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);//測量子view}綜合流程
一個app啟動之后,view樹的measure過程從根節(jié)點DecordView開始,也就是從ViewGroup開始.
一般而言
首先從viewgroup. 測量自身measure(),然后measureChildren()開始,遵循viewgroup的測量流程,measure()-->measureChild()測量子view--> getChildMeasureSpec()為child計算測量模式-->child.measure()子view開始測量.
子view如果是viewgroup,則重復1,如果是view,則遵循view的測量流程child.measure()-->measure()-->onMeasure()-->setMeasuredDimension()保存尺寸.
遍歷到最后一層,最后一個view.
把子view的尺寸告訴父布局,讓父布局重新測量大小.
在measure過程中,ViewGroup會根據(jù)自己當前的狀況,結(jié)合子View的尺寸數(shù)據(jù),進行一個綜合評定,然后把相關(guān)信息告訴子View,然后子View在onMeasure自己的時候,一邊需要考慮到自己的content大小,一邊還要考慮的父布局的限制信息,然后綜合評定,測量出一個最優(yōu)的結(jié)果。
這里寫圖片描述measure實踐
需求
我們做這樣一個view,view需要適配所有l(wèi)ayoutParams類型.
思路
1.確定viewgroup的大小,
在onMeasure中,根據(jù)MeasuredSpace的不同,分別進行測量.
2.確定子view的大小
在layout中,用for讓每一個子view,都向右平移一些像素.
代碼
/**
- Created by chenchangjun on 17/7/18.
*/
public class MyMeasureView extends ViewGroup {
private static final int OFFSET = 50;@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int width = 0;int heigh = 0;int childCount = getChildCount();for (int i = 0; i < childCount; i++) {View child = getChildAt(i);if (child.getVisibility()==GONE){continue;}ViewGroup.LayoutParams layoutParams = child.getLayoutParams();//獲取子view的layoutParamsint childWithSpace = getChildMeasureSpec(widthMeasureSpec, 0, layoutParams.width); //獲取子view的測量模式(本容器的MeasureSpec,padding,子view的layoutParams中的width)int childHeighSpace = getChildMeasureSpec(heightMeasureSpec, 0, layoutParams.height);child.measure(childWithSpace, childHeighSpace); //子view進行測量}switch (widthMode) {case MeasureSpec.EXACTLY://本容器為match_parent或者有精確大小時,容器width大小是測量的大小width = widthSize;break;case MeasureSpec.AT_MOST://本容器為wrap_content,容器width大小是 最大子view的width大小+pading+marginwidth = getWidth(widthSize, childCount);break;}switch (heightMode) {case MeasureSpec.EXACTLY:heigh = heightSize;break;case MeasureSpec.AT_MOST:heigh = getHeigh(heightSize, childCount);break;}setMeasuredDimension(width, heigh);}@Override protected void onLayout(boolean changed, int l, int t, int r, int b) {int left = 0;int right = 0;int top = 0;int bottom = 0;int childCount = getChildCount();for (int i = 0; i < childCount; i++) { //依次 定位 每個 子viewView v = getChildAt(i);if (v.getVisibility()==GONE){continue;}left = i * OFFSET;right = left + v.getMeasuredWidth();bottom = top + v.getMeasuredHeight();v.layout(left, top, right, bottom);top += v.getMeasuredHeight();}}private int getHeigh(int heightSize, int childCount) {int heigh;heigh = heightSize;for (int i = 0; i < childCount; i++) {View view = getChildAt(i);heigh = heigh + view.getMeasuredHeight();}return heigh; }private int getWidth(int widthSize, int childCount) {int width;width = widthSize;for (int i = 0; i < childCount; i++) {View view = getChildAt(i);if (view.getVisibility()==GONE){continue;}int widthOffset = i * OFFSET + view.getMeasuredHeight();width = Math.max(width, widthOffset); //此處wrap_Content取子view的最大width}return width; }public MyMeasureView(Context context) {super(context); }public MyMeasureView(Context context, @Nullable AttributeSet attrs) {super(context, attrs); }public MyMeasureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr); }@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public MyMeasureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes); }}
###結(jié)果##參考[http://www.cnblogs.com/nanxiaojue/p/3536381.html](http://www.cnblogs.com/nanxiaojue/p/3536381.html) � [http://www.cnblogs.com/xyhuangjinfu/p/5435201.html](http://www.cnblogs.com/xyhuangjinfu/p/5435201.html)總結(jié)
以上是生活随笔為你收集整理的View的Measure流程总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: laravel框架数据迁移
- 下一篇: httpd编译安装