view的测量是发生在activity的onResume之后,所以在resume后才可以获取到view是我大小
1.测量步骤:从ViewGroup到子view一层一层遍历测量,先测量子view的大小,父view的大小是根据子view的大小计算出来的
Android源码中的测量过程是从performMeasure开始的,然后是measure,onMeasure,最后setMeasuredDimension
原生:
1、measure->onMeasure
/frameworks/base/core/java/android/view/View.java
在measure中如果不是强制测量并且尺寸和上一次尺寸一致,是不需要调用onMeasure的,也就是不需要再次测量
15482 /** 15483 * <p> 15484 * This is called to find out how big a view should be. The parent 15485 * supplies constraint information in the width and height parameters. 15486 * </p> 15487 * 15488 * <p> 15489 * The actual measurement work of a view is performed in 15490 * {@link #onMeasure(int, int)}, called by this method. Therefore, only 15491 * {@link #onMeasure(int, int)} can and must be overridden by subclasses. 15492 * </p> 15493 * 15494 * 15495 * @param widthMeasureSpec Horizontal space requirements as imposed by the 15496 * parent 15497 * @param heightMeasureSpec Vertical space requirements as imposed by the 15498 * parent 15499 * 15500 * @see #onMeasure(int, int) 15501 */
15502 public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
15503 if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
15504 widthMeasureSpec != mOldWidthMeasureSpec ||
15505 heightMeasureSpec != mOldHeightMeasureSpec) {
15506
15507 // first clears the measured dimension flag
15508 mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
15509
15510 resolveRtlPropertiesIfNeeded();
15511
15512 // measure ourselves, this should set the measured dimension flag back
15513 onMeasure(widthMeasureSpec, heightMeasureSpec);//这是测量大小的真正地方
15514
15515 // flag not set, setMeasuredDimension() was not invoked, we raise
15516 // an exception to warn the developer
15517 if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
15518 throw new IllegalStateException("onMeasure() did not set the"
15519 + " measured dimension by calling"
15520 + " setMeasuredDimension()");
15521 }
15522
15523 mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
15524 }
15525
15526 mOldWidthMeasureSpec = widthMeasureSpec;
15527 mOldHeightMeasureSpec = heightMeasureSpec;
15528 }
view.java的onMeasure十分简单
15576 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
15577 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
15578 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
15579 }
但是这个onMeasure其实是单个view的测量,因为所有的控件都是继承自view,所以如果你自定义控件重写的onMeasure就是这个,如果你不重写,那就默认调用这个了。跑题了,测量开始是从DecorView开始的,而DecorView是继承自FrameLayout的,所以调用的onMeasure是DecorView的。同时ViewGroup是没有重写onMeasure的,是在LinearLayout等中重写的。
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java
关于DecorView的onMeasure很少有人分析,因为这里还是会调用其父类的onMeasure,也就是FrameLayout的onMeasure。这里读一下DecorView的onMeasure函数
2126 @Override
2127 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
2128 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();//获取屏幕尺寸
2129 final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;//是否横屏
2130
2131 final int widthMode = getMode(widthMeasureSpec);
2132 final int heightMode = getMode(heightMeasureSpec);
2133
2134 boolean fixedWidth = false;
2135 if (widthMode == AT_MOST) {
2136 final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;//style里的窗口大小配置值
2137 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
2138 final int w;
2139 if (tvw.type == TypedValue.TYPE_DIMENSION) {
2140 w = (int) tvw.getDimension(metrics);
2141 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
2142 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
2143 } else {
2144 w = 0;
2145 }
2146
2147 if (w > 0) {
2148 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
2149 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
2150 Math.min(w, widthSize), EXACTLY);
2151 fixedWidth = true;
2152 }
2153 }
2154 }
2155
2156 if (heightMode == AT_MOST) {
2157 final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;//style里的窗口大小配置值
2158 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
2159 final int h;
2160 if (tvh.type == TypedValue.TYPE_DIMENSION) {
2161 h = (int) tvh.getDimension(metrics);
2162 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
2163 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
2164 } else {
2165 h = 0;
2166 }
2167
2168 if (h > 0) {
2169 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
2170 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
2171 Math.min(h, heightSize), EXACTLY);
//比如更改style里面的窗口比例,可以改变窗口大小,应该就是这里取值。DecorView的大小确定就是在这
2172 }
2173 }
2174 }
2175
2176 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
2177
2178 int width = getMeasuredWidth();
2179 boolean measure = false;
2180
2181 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
2182
2183 if (!fixedWidth && widthMode == AT_MOST) {
2184 final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
2185 if (tv.type != TypedValue.TYPE_NULL) {
2186 final int min;
2187 if (tv.type == TypedValue.TYPE_DIMENSION) {
2188 min = (int)tv.getDimension(metrics);
2189 } else if (tv.type == TypedValue.TYPE_FRACTION) {
2190 min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
2191 } else {
2192 min = 0;
2193 }
2194
2195 if (width < min) {
2196 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
2197 measure = true;//如果fix找不到值,就在这里重新取值、赋值
2198 }
2199 }
2200 }
2201
2202 // TODO: Support height?
2203
2204 if (measure) {
2205 super.onMeasure(widthMeasureSpec, heightMeasureSpec);//重新取值后onMeasure
2206 }
2207 }
2、进入frameworks/base/core/java/android/widget/FrameLayout.java
FrameLayout的onMeasure
代码太长,分析主要逻辑
1.measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
调回ViewGroup的此函数,这个里面会给高宽,加上margin和padding,然后进入子view的measure(),这里就进入了view树的循环测量了
child都测量完毕以后,这里设置父view的高宽
3.match_parent的子view会再次测量,而且要求count大于1
这里的原因,借用别人的讲解,确实有道理,而且也说明每种布局根据其自身特性会有不同不同的测量策略。为什么会有count大于1这个条件呢,我这里举个例子:如果FrameLayout有两个子View,在Fragment的宽和高都是WRAP_CONTENT,子View的宽和高都是MATCH_PARENT,所以第一个View一定的宽高一定是自己View本来的宽高,就不受FrameLayout的影响。假设距上100,距左100,这时候FrameLayout的真实宽高就是100+view的宽或高,而第二个View则不是自己View本来的宽高了,而变成了FrameLayout的宽高了,主要就是这个意思。
4. 还有一个重要的函数getChildMeasureSpec
这个是确定ziview尺寸和模式的
到这里大概的测量流程就差不多了,子view的尺寸结果会保存在layoutparam中,父view据此计算自己的尺寸