概述
总体流程
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
measure 用来测量 View 的宽和高,它的流程分为View的measure流程和ViewGroup的measure流程,只不过ViewGroup的measure流程除了要完成自己的测量,还要遍历地调用子元素的measure()方法。
performTraversals会依次调用 performMeasure、 performLayout performDraw
performMeasure中会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了,这样就完成了一次measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个View树的遍历。
View的Measure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
ViewGroup, 它不只要测量自身,还要遍历地调用子元素的 measure()方法。ViewGroup 中没有定义 onMeasure() 方法,定义了 measureChildren()方法:
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); }
调用 child.getLayoutParams()方法来获得子元素的 LayoutParams 属性,
获取子元素的 MeasureSpec 并调用子元素的 measure()方法进行测量。
public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; case MeasureSpec.AT_MOST: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST;//1 } break; case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
根据父容器的 MeasureSpec 模式再结合子元素的 LayoutParams 属性来得出的 子元素的 MeasureSpec 属性。
如果父容器的 MeasureSpec 属性为 AT_MOST,子元素的 LayoutParams 属性为 WRAP_CONTENT,则子元素的MeasureSpec属性也为AT_MOST,它的SpecSize值为父容器的SpecSize 减去 padding 的值。
相关推荐
测量过程始于`ViewRootImpl`的`performTraversals`方法,它会调用`performMeasure`来测量根View。`performMeasure`进一步调用`View`的`measure`方法。尽管`measure`方法是final的,不允许子类直接重写,但其内部调用...
这两者在绘制屏幕时,都会经历`performTraversals`、`performMeasure`、`performLayout`和`performDraw`等步骤,遍历整棵视图树,完成测量、布局和绘制的过程。 尽管`RelativeLayout`在某些情况下性能相对较慢,但...
- `performTraversals`是绘制流程的起点,它调用`performMeasure`,使用`MeasureSpec`确定View的大小。 - `MeasureSpec`包含尺寸模式(EXACTLY, AT_MOST, UNSPECIFIED)和大小值,指示View应如何测量自身。 - `...
- `performTraversals()`是绘制流程的起点,它会调用`performMeasure()`,传入两个MeasureSpec,用于确定View的大小。 - `measure()`过程根据父View的约束和自身的LayoutParams计算大小。 - `layout()`阶段,View...
这是Android视图树遍历的核心方法,包含了`performMeasure()`, `performLayout()`, 和 `performDraw()`等步骤,用于确定View的大小、位置以及绘制视图。 8. **LayoutParams**: `LayoutParams`是View在特定容器...
前言 上一篇文章,笔者详细讲述了View三...上一篇文章提到,三大流程始于ViewRootImpl#performTraversals方法,在该方法内通过调用performMeasure、performLayout、performDraw这三个方法来进行measure、layout、draw流
在`performTraversals()`中,我们看到`performMeasure()`被调用,传入了两个参数`childWidthMeasureSpec`和`childHeightMeasureSpec`。这两个参数是MeasureSpec对象,它们包含了父View对子View的尺寸要求。 ...
当系统准备显示视图时,会依次调用`performMeasure`、`performLayout`和`performDraw`,来确定View的大小、位置和外观。 `performDraw`方法是绘制流程的起点。此方法内部会检查`mFullRedrawNeeded`标志,以确定是否...