`
xixinfei
  • 浏览: 414171 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

android-View工作原理(三)视图大小计算过程(measure过程)

 
阅读更多
一、android中view的measure过程总概
	视图大小计算的过程是从根视图measure()方法开始,接着该方法会调用根视图的onMeasure()方法,onMeasure()方法会对所包含的子视图逐一执行measure()方法,如果子视图是ViewGroup子类对象(LinearLayout、FrameLayout、RelativeLayout等布局),则继续调用子视图的measure()方法,重复这一过程。如果子视图是View子类对象(Button、EditText、TextView、ImageView等),则在子视图重载的onMeasure方法内部不需要进行对子视图进行measure操作,从而一次measure过程完成。过程如下图所示:

二、measure详细过程

View中的measure()方法源码(ViewGroup类继承了View类,measure过程先从ViewGroup子类开始):

  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {  
  2.        if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||  
  3.                widthMeasureSpec != mOldWidthMeasureSpec ||  
  4.                heightMeasureSpec != mOldHeightMeasureSpec) {  
  5.   
  6.            // first clears the measured dimension flag  
  7.            mPrivateFlags &= ~MEASURED_DIMENSION_SET;  
  8.   
  9.            if (ViewDebug.TRACE_HIERARCHY) {  
  10.                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);  
  11.            }  
  12.   
  13.            // measure ourselves, this should set the measured dimension flag back  
  14.            onMeasure(widthMeasureSpec, heightMeasureSpec);  
  15.   
  16.            // flag not set, setMeasuredDimension() was not invoked, we raise  
  17.            // an exception to warn the developer  
  18.            if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {  
  19.                throw new IllegalStateException("onMeasure() did not set the"  
  20.                        + " measured dimension by calling"  
  21.                        + " setMeasuredDimension()");  
  22.            }  
  23.   
  24.            mPrivateFlags |= LAYOUT_REQUIRED;  
  25.        }  
  26.   
  27.        mOldWidthMeasureSpec = widthMeasureSpec;  
  28.        mOldHeightMeasureSpec = heightMeasureSpec;  
  29.    }  
	注:通过源码,我们看到该方法的定义中用了final关键字,说明该方法是不能被重写的,即View系统定义的这个measure框架不能被修改。参数widthMeasureSpec和heightMeasureSpec分别对应宽和高的measureSpec,当父视图对子视图进行measure操作时,会调用子视图的measure()方法,该参数得意思是父视图所提供的measure的“规格”,因为父视图为子视图提供的窗口尺寸是由父视图和子视图共同决定。该参数有两部分组成,第一部分:高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的值。
	下面我们看看源码执行过程,看注释就能很明白,首先清 除测量尺寸的标识。接着将重新测量自己的尺寸,即调用onMeasure()方法。最后是判断测量尺寸大小的标识是否已经重新赋值,如果没有则不执行setMeasuredDimension()方法。方法结束。这个方法里面主要就是调用自己的onMeasure()方法,对自己的大小尺寸进行测量。下面来介绍onMeasure()方法。

 

ViewGroup中的onMeasure方法介绍

其实在ViewGroup类中并没有重写该方法,一般在他的子类中进行重写,比如LinearLayout、RelativeLayout,下面我们以Linearlayout来分析。LinearLayout中onMeasure方法源码如下:
  1. @Override  
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.     if (mOrientation == VERTICAL) {  
  4.         measureVertical(widthMeasureSpec, heightMeasureSpec);  
  5.     } else {  
  6.         measureHorizontal(widthMeasureSpec, heightMeasureSpec);  
  7.     }  
  8. }  

 

注:通过源码我们可以知道,首先onMeasure会判断这个布局是纵向布局还是横向布局,即对应android:orientation=""属性。下面以纵向布局来分析,源码如下,有点长:

  1. /** 
  2.    * Measures the children when the orientation of this LinearLayout is set 
  3.    * to {@link #VERTICAL}. 
  4.    * 
  5.    * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 
  6.    * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 
  7.    * 
  8.    * @see #getOrientation() 
  9.    * @see #setOrientation(int) 
  10.    * @see #onMeasure(int, int) 
  11.    */  
  12.   void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {  
  13.       mTotalLength = 0;  
  14.       int maxWidth = 0;  
  15.       int alternativeMaxWidth = 0;  
  16.       int weightedMaxWidth = 0;  
  17.       boolean allFillParent = true;  
  18.       float totalWeight = 0;  
  19.   
  20.       final int count = getVirtualChildCount();  
  21.        
  22.       final int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  23.       final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  24.       boolean matchWidth = false;  
  25.       final int baselineChildIndex = mBaselineAlignedChildIndex;          
  26.       final boolean useLargestChild = mUseLargestChild;  
  27.       int largestChildHeight = Integer.MIN_VALUE;  
  28.       // See how tall everyone is. Also remember max width.  
  29.       for (int i = 0; i < count; ++i) {  
  30.           final View child = getVirtualChildAt(i);  
  31.   
  32.           if (child == null) {  
  33.               mTotalLength += measureNullChild(i);  
  34.               continue;  
  35.           }  
  36.           if (child.getVisibility() == View.GONE) {  
  37.              i += getChildrenSkipCount(child, i);  
  38.              continue;  
  39.           }  
  40.           LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();  
  41.           totalWeight += lp.weight;              
  42.           if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {  
  43.               // Optimization: don't bother measuring children who are going to use  
  44.               // leftover space. These views will get measured again down below if  
  45.               // there is any leftover space.  
  46.               final int totalLength = mTotalLength;  
  47.               mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);  
  48.           } else {  
  49.               int oldHeight = Integer.MIN_VALUE;  
  50.               if (lp.height == 0 && lp.weight > 0) {  
  51.                   // heightMode is either UNSPECIFIED or AT_MOST, and this  
  52.                   // child wanted to stretch to fill available space.  
  53.                   // Translate that to WRAP_CONTENT so that it does not end up  
  54.                   // with a height of 0  
  55.                   oldHeight = 0;  
  56.                   lp.height = LayoutParams.WRAP_CONTENT;  
  57.               }  
  58.               // Determine how big this child would like to be. If this or  
  59.               // previous children have given a weight, then we allow it to  
  60.               // use all available space (and we will shrink things later  
  61.               // if needed).  
  62.               measureChildBeforeLayout(  
  63.                      child, i, widthMeasureSpec, 0, heightMeasureSpec,  
  64.                      totalWeight == 0 ? mTotalLength : 0);  
  65.   
  66.               if (oldHeight != Integer.MIN_VALUE) {  
  67.                  lp.height = oldHeight;  
  68.               }  
  69.               final int childHeight = child.getMeasuredHeight();  
  70.               final int totalLength = mTotalLength;  
  71.               mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +  
  72.                      lp.bottomMargin + getNextLocationOffset(child));  
  73.   
  74.               if (useLargestChild) {  
  75.                   largestChildHeight = Math.max(childHeight, largestChildHeight);  
  76.               }  
  77.           }  
  78.           /** 
  79.            * If applicable, compute the additional offset to the child's baseline 
  80.            * we'll need later when asked {@link #getBaseline}. 
  81.            */  
  82.           if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {  
  83.              mBaselineChildTop = mTotalLength;  
  84.           }  
  85.           // if we are trying to use a child index for our baseline, the above  
  86.           // book keeping only works if there are no children above it with  
  87.           // weight.  fail fast to aid the developer.  
  88.           if (i < baselineChildIndex && lp.weight > 0) {  
  89.               throw new RuntimeException("A child of LinearLayout with index "  
  90.                       + "less than mBaselineAlignedChildIndex has weight > 0, which "  
  91.                       + "won't work.  Either remove the weight, or don't set "  
  92.                       + "mBaselineAlignedChildIndex.");  
  93.           }  
  94.           boolean matchWidthLocally = false;  
  95.           if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {  
  96.               // The width of the linear layout will scale, and at least one  
  97.               // child said it wanted to match our width. Set a flag  
  98.               // indicating that we need to remeasure at least that view when  
  99.               // we know our width.  
  100.               matchWidth = true;  
  101.               matchWidthLocally = true;  
  102.           }  
  103.           final int margin = lp.leftMargin + lp.rightMargin;  
  104.           final int measuredWidth = child.getMeasuredWidth() + margin;  
  105.           maxWidth = Math.max(maxWidth, measuredWidth);  
  106.           allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;  
  107.           if (lp.weight > 0) {  
  108.               /* 
  109.                * Widths of weighted Views are bogus if we end up 
  110.                * remeasuring, so keep them separate. 
  111.                */  
  112.               weightedMaxWidth = Math.max(weightedMaxWidth,  
  113.                       matchWidthLocally ? margin : measuredWidth);  
  114.           } else {  
  115.               alternativeMaxWidth = Math.max(alternativeMaxWidth,  
  116.                       matchWidthLocally ? margin : measuredWidth);  
  117.           }  
  118.           i += getChildrenSkipCount(child, i);  
  119.       }  
  120.       if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {  
  121.           mTotalLength = 0;  
  122.           for (int i = 0; i < count; ++i) {  
  123.               final View child = getVirtualChildAt(i);  
  124.               if (child == null) {  
  125.                   mTotalLength += measureNullChild(i);  
  126.                   continue;  
  127.               }  
  128.               if (child.getVisibility() == GONE) {  
  129.                   i += getChildrenSkipCount(child, i);  
  130.                   continue;  
  131.               }  
  132.               final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)  
  133.                       child.getLayoutParams();  
  134.               // Account for negative margins  
  135.               final int totalLength = mTotalLength;  
  136.               mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +  
  137.                       lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));  
  138.           }  
  139.       }  
  140.       // Add in our padding  
  141.       mTotalLength += mPaddingTop + mPaddingBottom;  
  142.       int heightSize = mTotalLength;  
  143.       // Check against our minimum height  
  144.       heightSize = Math.max(heightSize, getSuggestedMinimumHeight());          
  145.       // Reconcile our calculated size with the heightMeasureSpec  
  146.       heightSize = resolveSize(heightSize, heightMeasureSpec);          
  147.       // Either expand children with weight to take up available space or  
  148.       // shrink them if they extend beyond our current bounds  
  149.       int delta = heightSize - mTotalLength;  
  150.       if (delta != 0 && totalWeight > 0.0f) {  
  151.           float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;  
  152.           mTotalLength = 0;  
  153.           for (int i = 0; i < count; ++i) {  
  154.               final View child = getVirtualChildAt(i);  
  155.               if (child.getVisibility() == View.GONE) {  
  156.                   continue;  
  157.               }                  
  158.               LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();                  
  159.               float childExtra = lp.weight;  
  160.               if (childExtra > 0) {  
  161.                   // Child said it could absorb extra space -- give him his share  
  162.                   int share = (int) (childExtra * delta / weightSum);  
  163.                   weightSum -= childExtra;  
  164.                   delta -= share;  
  165.                   final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,  
  166.                           mPaddingLeft + mPaddingRight +  
  167.                                   lp.leftMargin + lp.rightMargin, lp.width);  
  168.   
  169.                   // TODO: Use a field like lp.isMeasured to figure out if this  
  170.                   // child has been previously measured  
  171.                   if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {  
  172.                       // child was measured once already above...  
  173.                       // base new measurement on stored values  
  174.                       int childHeight = child.getMeasuredHeight() + share;  
  175.                       if (childHeight < 0) {  
  176.                           childHeight = 0;  
  177.                       }  
  178.                       child.measure(childWidthMeasureSpec,  
  179.                               MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));  
  180.                   } else {  
  181.                       // child was skipped in the loop above.  
  182.                       // Measure for this first time here        
  183.                       child.measure(childWidthMeasureSpec,  
  184.                               MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,  
  185.                                       MeasureSpec.EXACTLY));  
  186.                   }  
  187.               }  
  188.               final int margin =  lp.leftMargin + lp.rightMargin;  
  189.               final int measuredWidth = child.getMeasuredWidth() + margin;  
  190.               maxWidth = Math.max(maxWidth, measuredWidth);  
  191.               boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&  
  192.                       lp.width == LayoutParams.MATCH_PARENT;  
  193.               alternativeMaxWidth = Math.max(alternativeMaxWidth,  
  194.                       matchWidthLocally ? margin : measuredWidth);  
  195.               allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;  
  196.               final int totalLength = mTotalLength;  
  197.               mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +  
  198.                       lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));  
  199.           }  
  200.           // Add in our padding  
  201.           mTotalLength += mPaddingTop + mPaddingBottom;  
  202.           // TODO: Should we recompute the heightSpec based on the new total length?  
  203.       } else {  
  204.           alternativeMaxWidth = Math.max(alternativeMaxWidth,  
  205.                                          weightedMaxWidth);  
  206.       }  
  207.       if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {  
  208.           maxWidth = alternativeMaxWidth;  
  209.       }  
  210.       maxWidth += mPaddingLeft + mPaddingRight;  
  211.       // Check against our minimum width  
  212.       maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());  
  213.       setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize);  
  214.       if (matchWidth) {  
  215.           forceUniformWidth(count, heightMeasureSpec);  
  216.       }  
  217.   }  
注:
  1. 获取所有的子view数量,对每个子view开始处理,如果子view是GONE的,则直接跳过。
  2. 获取子view的 LayoutParams,在xml中定义的参数,通过layout_weight定义的值累加到变量totalWeight中,然后判断如果view的 height设置为零,但weight设置的大于0,则将height的值设置为LayoutParams.WRAP_CONTENT。
  3. 然 后调用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的 大小。
  4. 子view的measure确定好以后,然后调用子view的measure方法,如果子view是View对象,则该view 的大小测量结束,开始下一个子view的循环,如果子view是ViewGroup那么,又开始一个新的递归,处理逻辑和上面一样,直到所有的view对 象测量结束。
  5. 所有的子view测量结束后,才开始对layout_weight计算,这样我们可能想到,如果父view已经被占满了, 那么有可能layout_weight大于0的view对象是不会显示的,而计算layout_weight的方法也很简单,就是用总高度减去上面分析完 mTotalLength的值,就是剩下,然后去平分给view对象,注意计算权重时优先去 android:android:weightSum(LinearLayout的xml属性)的值,如果不设置该值会计算和,所以该值既然设置了,就一 定要子view的weight的总和相等,否则平分可能不能得到预期效果。

 过程分析完毕,这篇文章这里提到了LinearLayout中的layout_weight属性,这个属性对很对人来说是又恨又爱,下篇文章,我们将来总结改属性的详细用法,让大家彻底理解这个属性。

分享到:
评论

相关推荐

    Android--开发--Adroid UI 界面绘制原理分析.rar

    同时,View还包含测量(Measure)、布局(Layout)和绘制(Draw)三个过程,这三个过程共同决定了视图在屏幕上的位置和大小。 2. **视图组(ViewGroup)**:视图组是视图的容器,它可以包含多个子视图,并负责管理这些子...

    Android-实现任何视图的分层视差效果

    这里的关键是保存每个子视图的原始位置,以便在滚动时进行计算。 ```java @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Measure children and store their original ...

    Android-HorizontalTailLayout,自定义布局的一个简单实现,它解决了将子视图与其尾随视图分组的问题。.zip

    1. **测量过程**:在Android中,每个View或ViewGroup都需要先进行测量,确定自己的大小。HorizontalTailLayout可能会根据子视图的属性和配置,来决定它们之间的间距和相对位置。 2. **布局过程**:在测量之后,布局...

    Android-培训--JFM.docx编程资料

    - **测量过程**:由`view.measure()`方法执行,它递归地测量整个视图树中每个`View`的尺寸。在这个过程中,每个`View`都会根据其父`View`提供的尺寸约束来确定自己的尺寸。 - **实际布局**:由`view.layout()`方法...

    Android自定义View,View中的原点坐标相关问题

    在Android中,视图的测量(Measure)和布局(Layout)过程也会影响原点坐标。`onMeasure()`方法用于确定View的尺寸,而`onLayout()`方法则决定了View在父容器中的位置。这两个步骤会影响到View的最终坐标系统,特别...

    View工作原理

    理解View的工作原理对于优化应用性能和开发自定义视图至关重要。本文将深入探讨View的创建、绘制、布局以及事件处理流程。 1. **View的生命周期** - **构造函数**:View的实例化通常始于XML布局文件或通过代码动态...

    Android视图的绘制流程(上) View的测量

    在本文中,我们将深入探讨View的测量(measure)过程,这是视图绘制流程的第一步。 视图的绘制流程分为三个主要阶段:测量(measure)、布局(layout)和绘制(draw)。测量阶段主要负责确定View的尺寸,布局阶段则...

    Android中View绘制流程以及invalidate()

    首先,我们需要了解Android视图的生命周期,它包括测量(Measure)、布局(Layout)和绘制(Draw)三个主要阶段: 1. **测量阶段(Measure)**:在这个阶段,每个View都会确定自己的大小。系统会调用`onMeasure()`方法,...

    android view绘图机制.doc

    这篇文档主要介绍了Android系统中View的绘图流程,包括measure、layout和draw三个关键步骤,以及ViewGroup作为视图容器如何管理子视图的测量、布局和绘制。 1. **measure操作**:measure过程用于计算视图的尺寸,...

    Android中自定义View之流式布局

    总的来说,自定义View的流式布局是Android开发中的一个重要主题,它涉及到Android UI的底层工作原理。通过自定义流式布局,开发者能够实现高度定制化的界面效果,满足各种复杂的设计需求。实践中不断学习和调试,将...

    Android-CoffinLayout-仿燃兔APP的游戏详情界面

    2. 测量(Measure):在onMeasure()方法中,根据子View的MeasureSpec和父View的要求,计算每个子View的大小。 3. 布局(Layout):在onLayout()方法中,根据测量结果确定每个子View的位置。 4. 动画与过渡效果:为了增强...

    Android丢帧原理以及解决方案.docx

    首先,我们来看Android UI的绘制过程,主要分为三个阶段:`measure`、`layout`和`draw`。`measure`阶段负责确定每个View的大小,`layout`阶段负责确定View的位置,而`draw`阶段则进行实际的绘图操作。如果这些步骤在...

    Android面试题大全.pdf

    此函数的工作可以概括为:检查是否需要重新计算视图大小(measure)、是否需要重新安置视图位置(layout)、以及是否需要重绘视图(draw)。 接着,我们来具体分析一下View绘制的两个主要步骤——measure和layout。 ...

    View的绘制过程

    本文将深入解析Android View的绘制流程,特别是其中的measure过程。 首先,当一个Activity启动时,View的绘制过程从Window对象的DecorView开始。DecorView是系统提供的根视图,它负责承载我们的布局内容。在加载...

    全面的Android view相关知识汇总整理

    - `measure()`过程根据父View的约束和自身的LayoutParams计算大小。 - `layout()`阶段,View根据测量结果确定自身位置。 - `draw()`方法执行实际的绘画操作,包括背景、边界、内容等。 除此之外,还有其他重要的...

    Android自定义View源码

    本文将深入探讨Android自定义View的源码分析,以`FdjSwitchView.java`和`FdjMineInfoView.java`为例,结合`view_mine_info.xml`布局文件和`attrs.xml`资源文件,解析自定义View的实现过程。 首先,我们来看`...

    View坐标位置XY轴详解

    在Android开发中,View是构建用户界面的基本元素,理解View的位置和坐标系统是开发者必备的知识。本文将深入探讨“View坐标位置XY轴详解”,并基于配套博客提供的详细内容进行阐述。 首先,我们要知道Android中的...

    Android UI 界面绘制原理分析.zip

    1. **测量(Measure)**:在这个阶段,每个ViewGroup会遍历其子View,根据父视图的约束或自身属性来计算子View的大小。每个View都会返回一个MeasureSpec对象,包含期望宽度和高度。 2. **布局(Layout)**:在测量...

    Android游戏编程之view源码

    Android的绘制流程遵循“测量-布局-绘制”(Measure-Layout-Draw)的顺序。在`onMeasure()`中,每个View会根据其LayoutParams和父View的要求确定自己的大小;`onLayout()`则根据测量结果确定View的位置;最后,`...

    android自定义view-动画标签-12种样式-类似小红书标签

    为了实现这种功能,可以在`onMeasure()`方法中计算视图的大小,然后在`onLayout()`中设置视图的位置。 ```java @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Measure ...

Global site tag (gtag.js) - Google Analytics