一、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子类开始):
- public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
- if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
- widthMeasureSpec != mOldWidthMeasureSpec ||
- heightMeasureSpec != mOldHeightMeasureSpec) {
- // first clears the measured dimension flag
- mPrivateFlags &= ~MEASURED_DIMENSION_SET;
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
- }
- // measure ourselves, this should set the measured dimension flag back
- onMeasure(widthMeasureSpec, heightMeasureSpec);
- // flag not set, setMeasuredDimension() was not invoked, we raise
- // an exception to warn the developer
- 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系统定义的这个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方法源码如下:
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mOrientation == VERTICAL) {
- measureVertical(widthMeasureSpec, heightMeasureSpec);
- } else {
- measureHorizontal(widthMeasureSpec, heightMeasureSpec);
- }
- }
注:通过源码我们可以知道,首先onMeasure会判断这个布局是纵向布局还是横向布局,即对应android:orientation=""属性。下面以纵向布局来分析,源码如下,有点长:
- /**
- * Measures the children when the orientation of this LinearLayout is set
- * to {@link #VERTICAL}.
- *
- * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
- * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
- *
- * @see #getOrientation()
- * @see #setOrientation(int)
- * @see #onMeasure(int, int)
- */
- 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;
- // See how tall everyone is. Also remember max width.
- 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) {
- // Optimization: don't bother measuring children who are going to use
- // leftover space. These views will get measured again down below if
- // there is any leftover space.
- 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) {
- // heightMode is either UNSPECIFIED or AT_MOST, and this
- // child wanted to stretch to fill available space.
- // Translate that to WRAP_CONTENT so that it does not end up
- // with a height of 0
- oldHeight = 0;
- lp.height = LayoutParams.WRAP_CONTENT;
- }
- // Determine how big this child would like to be. If this or
- // previous children have given a weight, then we allow it to
- // use all available space (and we will shrink things later
- // if needed).
- 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 applicable, compute the additional offset to the child's baseline
- * we'll need later when asked {@link #getBaseline}.
- */
- if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
- mBaselineChildTop = mTotalLength;
- }
- // if we are trying to use a child index for our baseline, the above
- // book keeping only works if there are no children above it with
- // weight. fail fast to aid the developer.
- 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) {
- // The width of the linear layout will scale, and at least one
- // child said it wanted to match our width. Set a flag
- // indicating that we need to remeasure at least that view when
- // we know our width.
- 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) {
- /*
- * Widths of weighted Views are bogus if we end up
- * remeasuring, so keep them separate.
- */
- 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();
- // Account for negative margins
- final int totalLength = mTotalLength;
- mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
- lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
- }
- }
- // Add in our padding
- mTotalLength += mPaddingTop + mPaddingBottom;
- int heightSize = mTotalLength;
- // Check against our minimum height
- heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
- // Reconcile our calculated size with the heightMeasureSpec
- heightSize = resolveSize(heightSize, heightMeasureSpec);
- // Either expand children with weight to take up available space or
- // shrink them if they extend beyond our current bounds
- 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) {
- // Child said it could absorb extra space -- give him his share
- int share = (int) (childExtra * delta / weightSum);
- weightSum -= childExtra;
- delta -= share;
- final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
- mPaddingLeft + mPaddingRight +
- lp.leftMargin + lp.rightMargin, lp.width);
- // TODO: Use a field like lp.isMeasured to figure out if this
- // child has been previously measured
- if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
- // child was measured once already above...
- // base new measurement on stored values
- int childHeight = child.getMeasuredHeight() + share;
- if (childHeight < 0) {
- childHeight = 0;
- }
- child.measure(childWidthMeasureSpec,
- MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
- } else {
- // child was skipped in the loop above.
- // Measure for this first time here
- 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));
- }
- // Add in our padding
- mTotalLength += mPaddingTop + mPaddingBottom;
- // TODO: Should we recompute the heightSpec based on the new total length?
- } else {
- alternativeMaxWidth = Math.max(alternativeMaxWidth,
- weightedMaxWidth);
- }
- if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
- maxWidth = alternativeMaxWidth;
- }
- maxWidth += mPaddingLeft + mPaddingRight;
- // Check against our minimum width
- maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
- setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize);
- if (matchWidth) {
- forceUniformWidth(count, heightMeasureSpec);
- }
- }
注:
- 获取所有的子view数量,对每个子view开始处理,如果子view是GONE的,则直接跳过。
- 获取子view的 LayoutParams,在xml中定义的参数,通过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的循环,如果子view是ViewGroup那么,又开始一个新的递归,处理逻辑和上面一样,直到所有的view对 象测量结束。
- 所有的子view测量结束后,才开始对layout_weight计算,这样我们可能想到,如果父view已经被占满了, 那么有可能layout_weight大于0的view对象是不会显示的,而计算layout_weight的方法也很简单,就是用总高度减去上面分析完 mTotalLength的值,就是剩下,然后去平分给view对象,注意计算权重时优先去 android:android:weightSum(LinearLayout的xml属性)的值,如果不设置该值会计算和,所以该值既然设置了,就一 定要子view的weight的总和相等,否则平分可能不能得到预期效果。
过程分析完毕,这篇文章这里提到了LinearLayout中的layout_weight属性,这个属性对很对人来说是又恨又爱,下篇文章,我们将来总结改属性的详细用法,让大家彻底理解这个属性。
相关推荐
LOC-Measure03-04(代码step数计算工具)
MATLAB 显着性评估工具箱 该工具箱包含几乎所有显着对象检测的评估指标,包括: 电子测量 S-测量 加权 F 度量 F-测量 MAE分数 PR 曲线或条形指标 ...您可以轻松地使用此工具箱来评估 SOD 结果。...更多详情、使用方法,...
使用 GPU 实现的一键快速评估显着性对象检测,包括MAE、Max F-measure、S-measure、E-measure。 代码从可从http://dpfan.net/ 获得的 matlab 版本重新实现 使用 pytorch 实现 GPU 可以更容易地嵌入到 eval 代码中...
https://engineering.instagram.com/improving-comment-rendering-on-android-a77d5db3d82e http://ragnraok.github.io/textview-pre-render-research.html Features 1.Faster than Android TextView 2.More ...
Measure Simple Ruler Application with an emphasis on Material design. Can display metric or imperial units. Built around custom RulerView class. Created as part of Job Interview process at Tinder. ...
整个 View 树的绘图流程在ViewRoot.java类的performTraversals()函数展开,该函数所做 的工作可简单概况为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw),流程图...
用于图形检测领域的评价,具有更好的稳定性和准确性
Privacy Friendly Tape Measure Privacy Friendly Tape Measure turns the phone screen into a ruler for quick measurements on the go. It also allows to measure size of things in a 2D scene provided a ...
Android-PixelMeasuringTool 测量图片中项目之间像素的工具。 下载 通过Gradle: dependencies { implementation 'com.zekapp.library:pixelmeasuringview:1.0.10' } 用法 <app....
第4章 View的工作原理 / 174 4.1 初识ViewRoot和DecorView / 174 4.2 理解MeasureSpec / 177 4.2.1 MeasureSpec / 177 4.2.2 MeasureSpec和LayoutParams的对应关系 / 178 4.3 View的工作流程 / 183 ...
第4章 View的工作原理 174 4.1 初识ViewRoot和DecorView 174 4.2 理解MeasureSpec 177 4.2.1 MeasureSpec 177 4.2.2 MeasureSpec和LayoutParams的对应关系 178 4.3 View的工作流程 183 4.3.1 measure过程 183 ...
griffin-measure.jar
整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为根据之前设置的状态,判断是否需要重新计算视图大小(measure)、是否重新需要安置视图的位置(layout)、...
177 4.2.1 Measure Spec / 177 4.2.2 Measure Spec和Layout Params的对应关系 / 178 4.3 View的工作流程 / 183 4.3.1 measure过程 / 183 4.3.2 layout过程 / 193 4.3.3 draw过程 / 197 4.4 自定义View / 199 ...
这里回顾一下上一章所说的内容:DecorView是视图的顶级View,我们添加的布局文件是它的一个子布局,而ViewRootImpl则负责渲染视图,它调用了一个performTraveals方法使得ViewTree开始三大工作流程,然后使得View展现...
Transform-based image enhancement algorithms with performance measure参考文献,文章中公式的出处。
Human Visual System-Based Image Enhancement and Logarithmic Contrast Measure 一篇论文