关于ViewRootImpl中measure相关代码的分析
2014-12-25 14:55
337 查看
首先先看下在一个Activity创建到DecorView的measure函数被调用过程的时序图:
![](https://img-blog.csdn.net/20141225145749109?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbHVvemlyb25n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
可以看到这些主要在ViewRootImpl的performTraversals()函数中完成。如下是preformTraversals()函数,我们关注一下标红的这几行代码
可以看到View的measure工作最后主要由measureHierarchy()完成,并将Window的LayoutParameter传入到函数内:
这里,我们分析一下传进去给DecorView的measure函数的widthMeasureSpec和heightMeasureSpec到底是怎么样的?特别是他的mode到底是怎么确定的:
从这个函数可以看出如果Window的lp是ViewGroup.LayoutParams.MATCH_PARENT那么传入DecorView的measure函数的MeasureSpec的mode就是EXACTLY。而如果如果Window的lp是ViewGroup.LayoutParams.WRAP_CONTENT那么传入DecorView的measure函数的MeasureSpec的mode就是AT_MOST。
通过分析Framework的源代码,特别是FrameLayout的onMeasure()函数,还可以得出如果下结论:
ViewGroup的measure()和onMeasure()的确可能被多次调用。以FrameLayout的onMeasure为例,首先遍历所有的child,调用measureChildWithMargins(child,
widthMeasureSpec, 0, heightMeasureSpec, 0);测量子View的尺寸。这里的measureSpec采用父View给他的measureSpec。.之后如果child的LayoutParameter的高或者宽有一个为match_parent会调整measureSpec再次重新对对应的子View测量一遍,即调用子View的measure函数重新计算一遍: child.measure(childWidthMeasureSpec, childHeightMeasureSpec);。这个是完全可以理解的,因为child为match_parent,而上次测量的时候还不知道父View的尺寸,自然需要重新测量一遍了。
可以看到这些主要在ViewRootImpl的performTraversals()函数中完成。如下是preformTraversals()函数,我们关注一下标红的这几行代码
private void performTraversals() { // cache mView since it is used so much below... final View host = mView; if (DEBUG_LIFECYCLE || DEBUG_DEFAULT) { Xlog.v(TAG, "ViewRoot performTraversals+ : mFirst = " + mFirst + ", this = " + this); } if (DBG) { System.out.println("======================================"); System.out.println("performTraversals"); host.debug(); } if (host == null || !mAdded) return; mIsInTraversal = true; mWillDrawSoon = true; boolean windowSizeMayChange = false; boolean newSurface = false; boolean surfaceChanged = false; <span style="background-color: rgb(255, 0, 0);"> WindowManager.LayoutParams lp = mWindowAttributes;</span> <span style="white-space:pre"> </span>............. // Ask host how big it wants to be <span style="background-color: rgb(255, 0, 0);"> windowSizeMayChange |= measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight);</span> ...................... }
可以看到View的measure工作最后主要由measureHierarchy()完成,并将Window的LayoutParameter传入到函数内:
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) { int childWidthMeasureSpec; int childHeightMeasureSpec; boolean windowSizeMayChange = false; if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { Xlog.v(TAG, "ViewRoot measure+ " + host + " in display " + desiredWindowWidth + "x" + desiredWindowHeight + ", lp = " + lp + ", this = " + this); } boolean goodMeasure = false; if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) { // On large screens, we don't want to allow dialogs to just // stretch to fill the entire width of the screen to display // one line of text. First try doing the layout at a smaller // size to see if it will fit. final DisplayMetrics packageMetrics = res.getDisplayMetrics(); res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true); int baseSize = 0; if (mTmpValue.type == TypedValue.TYPE_DIMENSION) { baseSize = (int)mTmpValue.getDimension(packageMetrics); } if (DEBUG_DIALOG) { Xlog.v(TAG, "Window " + mView + ": baseSize=" + baseSize + ", this = " + this); } if (baseSize != 0 && desiredWindowWidth > baseSize) { <span style="background-color: rgb(255, 0, 0);"> childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);</span> performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); if (DEBUG_DIALOG) { Xlog.v(TAG, "Window " + mView + ": measured (" + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ") " + this); } if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { goodMeasure = true; } else { // Didn't fit in that size... try expanding a bit. baseSize = (baseSize+desiredWindowWidth)/2; if (DEBUG_DIALOG) { Xlog.v(TAG, "Window " + mView + ": next baseSize=" + baseSize + " " + this); } childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width); <span style="background-color: rgb(255, 0, 0);"> performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);</span> if (DEBUG_DIALOG) { Xlog.v(TAG, "Window " + mView + ": measured (" + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ") " + this); } if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) { if (DEBUG_DIALOG) { Xlog.v(TAG, "Good!"); } goodMeasure = true; } } } } if (!goodMeasure) { childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) { windowSizeMayChange = true; } } if (DBG) { System.out.println("======================================"); System.out.println("performTraversals -- after measure"); host.debug(); } if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { Xlog.v(TAG, "ViewRoot measure-: host measured size = (" + host.getMeasuredWidth() + "x" + host.getMeasuredHeight() + "), windowSizeMayChange = " + windowSizeMayChange + ", this = " + this); } return windowSizeMayChange; }
这里,我们分析一下传进去给DecorView的measure函数的widthMeasureSpec和heightMeasureSpec到底是怎么样的?特别是他的mode到底是怎么确定的:
<span style="white-space:pre"> </span>childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);显然childWidthMeasureSpec由如下函数确定getRootMeasureSpec(),而这里的lp就是刚刚说的windown的LayoutParam。现在看一下getRootMeasureSpec():
private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: 4000 // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
从这个函数可以看出如果Window的lp是ViewGroup.LayoutParams.MATCH_PARENT那么传入DecorView的measure函数的MeasureSpec的mode就是EXACTLY。而如果如果Window的lp是ViewGroup.LayoutParams.WRAP_CONTENT那么传入DecorView的measure函数的MeasureSpec的mode就是AT_MOST。
通过分析Framework的源代码,特别是FrameLayout的onMeasure()函数,还可以得出如果下结论:
ViewGroup的measure()和onMeasure()的确可能被多次调用。以FrameLayout的onMeasure为例,首先遍历所有的child,调用measureChildWithMargins(child,
widthMeasureSpec, 0, heightMeasureSpec, 0);测量子View的尺寸。这里的measureSpec采用父View给他的measureSpec。.之后如果child的LayoutParameter的高或者宽有一个为match_parent会调整measureSpec再次重新对对应的子View测量一遍,即调用子View的measure函数重新计算一遍: child.measure(childWidthMeasureSpec, childHeightMeasureSpec);。这个是完全可以理解的,因为child为match_parent,而上次测量的时候还不知道父View的尺寸,自然需要重新测量一遍了。
相关文章推荐
- 关于在jsp页面写java代码的分析(限定用户查看相关的信息)
- openstack关于libvirt相关代码的分析(一)
- 关于android view属性的归属以及如何在代码中设置相关属性总结
- 输入事件from InputDispatcher to ViewRootImpl 的分析
- 关于NEW Marquee不间断滚动代码的参数分析
- 嵌入式Linux内核移植相关代码分析
- 嵌入式Linux启动相关代码分析
- 嵌入式Linux内核移植相关代码分析
- 引用邹老大的关于相关表删除的代码,我学好多:
- uClinux内核移植相关代码分析
- 关于从NAND Flash启动的问题,2440 启动问题 , 拷贝4k程序 ,启动代码分析
- uClinux内核移植相关代码分析
- 数据链表的相关代码与分析
- SAP ABAP/4学习--TABLEVIEW 的代码分析
- SD--关于销售订单的VBAP-WERKS决定代码摘抄分析
- 嵌入式Linux内核移植相关代码分析
- 关于数据集的相关代码
- 关于DCMS代码部署相关的build指令
- 关于Basic程序解释器及编译原理的简单化(1)---Basic器的语法分析及主要代码
- 关于WININET的异步使用的代码和一些相关知识