日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

View的Measure流程总结

發布時間:2025/3/20 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 View的Measure流程总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先,Measure流程 是為了測量,并計算view的大小.寬mMeasuredWidth,高mMeasuredHeight,然后將寬高保存.為后續layout 和draw 提供數據支撐.而且measure過程不止一次.

數值保存MeasureSpec

父容器的layoutParams會確認MeasureSpec,即view的測量模式和大小
MeasureSpec包含一個32位的int值,高2位代表SpaceMode,低30位代表SpecSize.
MeasureSpec有三類.view會有兩個MeasureSpec變量,分別為widthMeasureSpec,heightMeasureSpec.

以下結論會在getChildMeasureSpec中得到驗證

  • EXACTLY :父容器已經測量出所需要的精確大小,這也是childview的最終大小------match_parent,精確值.

  • ATMOST : child view最終的大小不能超過父容器的給的------wrap_content .

  • UNSPECIFIED: 不確定,源碼內部使用-------一般在ScrollView,ListView .

  • MeasureSpace大多數情況下是由父布局的MeasureSpace和自己的Layoutparams確定(當然,還有margin,padding).詳情見viewgroup.getChildMeasureSpec()

    View

    View的關鍵方法

    2. measure 父布局會在自己的onMeasure方法中,調用child.measure ,這就把measure過程轉移到了子View中。

    3. onMeasure 子View會在該方法中,根據父布局給出的限制信息,和自己的content大小,來合理的測量自己的尺寸。

    4. setMeasuredDimension當View測量結束后,把測量結果保存起來,具體保存在mMeasuredWidth和mMeasuredHeight中。

    View的測量過程

    measure()-->onMeasure()-->setMeasuredDimension()

    viewGroup

    viewGroup的關鍵方法

    1. getChildMeasureSpec(父容器space,padding,一般是父容器的layoutparam.width或heigh)為child計算MeasureSpec。該方法為每個child的每個維度(寬、高)計算正確的MeasureSpec。目標就是把當前viewgroup的MeasureSpec和child的LayoutParams結合起來,生成最合理的結果。

    //主代碼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 當前容器的父容器,給當前容器設置了一個精確的尺寸:if (子View申請固定的尺寸LayoutParams) {你就用你自己申請的尺寸值就行了;} else if (子View希望和父容器一樣大) {你就用父容器的尺寸值就行了;} else if (子View希望包裹內容) {你最大尺寸值為父容器的尺寸值,但是你還是要盡可能小的測量自己的尺寸,包裹你的內容就足夠了;} break;case 當前容器的父容器,給當前容器設置了一個最大尺寸:if (子View申請固定的尺寸) {你就用你自己申請的尺寸值就行了;} else if (子View希望和父容器一樣大) {你最大尺寸值為父容器的尺寸值,但是你還是要盡可能小的測量自己的尺寸,包裹你的內容就足夠了;} else if (子View希望包裹內容) {你最大尺寸值為父容器的尺寸值,但是你還是要盡可能小的測量自己的尺寸,包裹你的內容就足夠了;} break;case 當前容器的父容器,對當前容器的尺寸不限制:if (子View申請固定的尺寸) {你就用你自己申請的尺寸值就行了;} else if (子View希望和父容器一樣大) {父容器對子View尺寸不做限制。} else if (子View希望包裹內容) {父容器對子View尺寸不做限制。}break;} return 對子View尺寸的限制信息;}

    這個就是對應的結論圖,前三項是方法參數,后兩個為計算得到的值.

    這里寫圖片描述

    還有這個結論

    • EXACTLY :父容器已經測量出所需要的精確大小,這也是childview的最終大小------match_parent,確定值(不管你是0還是多少).

    • ATMOST : child view最終的大小不能超過父容器的給的------wrap_content .

    • UNSPECIFIED: 不確定,源碼內部使用-------一般在ScrollView,ListView (我們一般用match_parent, wrap_content,還有確定值,這個不用).

    2. measureChildren讓所有子view測量自己的尺寸,需要考慮當前ViewGroup的MeasureSpec和Padding。跳過狀態為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過程從根節點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會根據自己當前的狀況,結合子View的尺寸數據,進行一個綜合評定,然后把相關信息告訴子View,然后子View在onMeasure自己的時候,一邊需要考慮到自己的content大小,一邊還要考慮的父布局的限制信息,然后綜合評定,測量出一個最優的結果。

    這里寫圖片描述

    measure實踐

    需求

    我們做這樣一個view,view需要適配所有layoutParams類型.

    思路

    1.確定viewgroup的大小,
    在onMeasure中,根據MeasuredSpace的不同,分別進行測量.

    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;}

    2.確定子view的大小
    在layout中,用for讓每一個子view,都向右平移一些像素.

    for (int i = 0; i < childCount; i++) { //依次 定位 每個 子viewView v = getChildAt(i);left = i * OFFSET;right = left + v.getMeasuredWidth();bottom = top + v.getMeasuredHeight();v.layout(left, top, right, bottom);top += v.getMeasuredHeight();}

    代碼

    /**

    • 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); }

    }

    ###結果![image.png](http://upload-images.jianshu.io/upload_images/1848340-03269afaf2ae972b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)##參考[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)

    總結

    以上是生活随笔為你收集整理的View的Measure流程总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。