View的测量

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树的循环测量了

2.setMeasuredDimension

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据此计算自己的尺寸 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值