View框架之measure()流程
来源:互联网 发布:数据库unique 编辑:程序博客网 时间:2024/04/30 19:26
先来张View体系中measure()流程的时序图
注意:1.以下MeasureSpec用ms来代替(ms具体会在后面介绍) 2.该时序图描述的是从ViewRootImpl开始的measure()过程,因为ViewRootImpl是window与view连接的枢纽 3.该时序图着重于大概流程及重要方法的作用,具体代码细节会放到后面介绍 4.DecorView作为顶级View,本身是一个frameLayout,所以也是一个ViewGroup
MesureSpec简介 (以下简称ms)
在view的测量过程中,ms起着非常重要的作用,ms在某种程度上来说更像是一种测量的标准。ms是一个32位的int值,前2位代表测量模式,后30位代表测量大小测量模式(Specmode)一共分为3种: UNSPECIFIED: 父容器不对子view限制,这种情况一般我们用不到,系统内部使用 EXACTLY : 父容器可以算出View的具体的大小,一般对应于 具体值或者match_parent AT_MOST : 父容器指定一个值,子view不能超过这个值,一般对应于 wrap_content
View关于MesureSpec的计算
一般情况下,一个view的ms的测量是根据父容器的ms和本身的LayoutParams来决定的,根据一些规则,在父容器中算出ms,然后父容器调用此view的measure()方法将结果传给该view,该view接着会回掉onMeasure()将结果传给该view的实现类。 但是DecorView作为顶级View是没有父容器的,所以DecorView的ms是根据窗口的大小和自身的LayoutParams来决定的。
1.DecorView的ms计算过程:
-- /** * desireWindowWidth : 代表屏幕的宽带 ,同理,dwh 代表屏幕的高度 **/ private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) { *************************省略 //计算出DecorView的ms ** childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); ** *************************省略} /** * 计算出decorView的ms* **/ private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: //如果设置的是math_parent,屏幕长度就是view长度,模式为EXACTLY measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: //如果设置的是wrap_conentt,屏幕长度就是view长度,模式为AT_MOST measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // 如果设置的是具体的数值,这个值就是View的长度,模式威EXANTLY measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec;
2**.一般View的ms计算过程:**
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //求出view的ms, //第二个参数的意思 在父容器中最大可用大小 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); //将测量好的ms,传递给子view child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}//根据父容器的ms和自身的LayoutParams求出该view的ms的具体操作 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) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}
View及ViewGroup的measure()操作
一般view的measure()方法如下:public final void measure(int widthMeasureSpec, int heightMeasureSpec) { boolean optical = isLayoutModeOptical(this); final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec; final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); final boolean needsLayout = specChanged && (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize); //判断view的ms与存好的是否一样,以及需要根据标志,需要重新测量吗 if (forceLayout || needsLayout) { 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; } // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { throw new IllegalStateException("View with id " + getId() + ": " + getClass().getName() + "#onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; }
// 将ms值保存起来,为了提高性能,如果下次进来并且标志没变,就不会再重复执行onMeasure()
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension}ViewGroup 的 measure(); View是一个容器,他不但要完成自己的测量方法,还需要遍历onMeasure()计算出下一层级的ms,下面以FrameLayout的onMeasure方法为讲解 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { //遍历计算childView的ms final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidthMeasureSpec; int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height); } //调用子类的measure()方法进行传递 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } }}
1 0
- View框架之measure()流程
- Android View框架总结(四)View布局流程之Measure
- Android View框架总结(四)View布局流程之Measure
- view工作流程解析之measure测量
- android view绘制流程之Measure
- View容器 measure 流程
- View的Measure流程
- Android自定义view之measure、layout、draw三大流程
- View的绘制流程分析之二 -- measure
- Android View measure流程详解
- Android之measure流程
- View框架之layout()流程
- View框架之draw()流程
- android view的讲解 之 view的工作流程(measure,layout,draw)(二)
- View绘制之measure过程
- Android自定义view之Measure
- Android View measure (一) 流程分析
- Android View 测量流程(Measure)完全解析
- yii2 配置sql server
- Java枚举
- 成为PHP编程高手的四条指南
- [BZOJ1036][ZJOI2008]树的统计Count
- Spring MVC 03 - 使用eclipse创建第一个spring mvc程序
- View框架之measure()流程
- 使用树莓派(raspberry pi)搭建网站(nginx+php+mysql+ddclient)
- 287. Find the Duplicate Number
- 在树莓派上搭建LAMP(Linux Apache Mysql PHP)服务器
- 网狐_经典版机器人添加说明
- 剑指offer-二叉搜索树的后序遍历序列
- Golden Dream-11月
- HDU-5952 Counting Cliques(16年ICPC沈阳赛区)(暴力DFS)
- iOS 导航栏标题文字颜色渐变