You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
publicstaticintgetChildMeasureSpec(intspec, intpadding, intchildDimension) {
intspecMode = MeasureSpec.getMode(spec);
intspecSize = MeasureSpec.getSize(spec);
intsize = Math.max(0, specSize - padding);
intresultSize = 0;
intresultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us// 父布局给了确定的尺寸caseMeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} elseif (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.// 与父布局一样大resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} elseif (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// 父布局有一个确定的最大尺寸caseMeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be itresultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} elseif (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;
} elseif (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 becaseMeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have itresultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} elseif (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should// beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} elseif (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how// big it should beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceTypereturnMeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
以上就是整个 View 和 ViewGroup 的整个 Measure 过程。
Layout
回到 ViewRootImpl 类中,执行 performLayout()过程。
privatevoidperformLayout(WindowManager.LayoutParamslp, intdesiredWindowWidth,
intdesiredWindowHeight) {
try {
// 进行放置host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
intnumViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
// 放置子 ViewintnumValidRequests = validLayoutRequesters.size();
for (inti = 0; i < numValidRequests; ++i) {
finalViewview = validLayoutRequesters.get(i);
// view.requestLayout();
}
// 再次测量measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
// 重新放置host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
// Check the valid requests again, this time without checking/clearing the// layout flags, since requests happening during the second pass get noop'dvalidLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
finalArrayList<View> finalRequesters = validLayoutRequesters;
// Post second-pass requests to the next frame// 再次检查是否仍有需要layout的View,如果有,就到下一帧再继续getRunQueue().post(newRunnable() {
@Overridepublicvoidrun() {
intnumValidRequests = finalRequesters.size();
for (inti = 0; i < numValidRequests; ++i) {
finalViewview = finalRequesters.get(i);
view.requestLayout();
}
mInLayout = false;
上述过程是先让 DecorView 进行放置,然后有需要就放置所有的子 View,调用 requestLayout 方法。然后再对整个 View Tree 进行测量,进行二次放置。
privatevoidperformDraw() {
// 主要是调用 draw 方法try {
booleancanUseAsync = draw(fullRedrawNeeded);
}
}
privatebooleandraw(booleanfullRedrawNeeded) {
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
returnfalse;
}
}
/** * @return true if drawing was successful, false if an error occurred */privatebooleandrawSoftware(Surfacesurface, AttachInfoattachInfo, intxoff, intyoff,
booleanscalingRequired, Rectdirty, RectsurfaceInsets) {
// Draw with software renderer.finalCanvascanvas;
// 获取画布canvas = mSurface.lockCanvas(dirty);
// 一系列设置之后mView.draw(canvas);
}
// DecorView.java@Overridepublicvoiddraw(Canvascanvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
// 还是调用 View 的 draw 方法publicvoiddraw(Canvascanvas) {
/* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */// Step 1, draw the background, if neededintsaveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)finalintviewFlags = mViewFlags;
booleanhorizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
booleanverticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the childrendispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);
// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...return;
}
}
在 draw 过程中,一般有 6 个步骤,2 - 5 可以省略。这里主要看 3 和 4 步。
/** * Implement this to do your drawing. * * @param canvas the canvas on which the background will be drawn */// 自定义 View 实现, 3protectedvoidonDraw(Canvascanvas) {
}
/** * Called by draw to draw the child views. This may be overridden * by derived classes to gain control just before its children are drawn * (but after its own view has been drawn). * @param canvas the canvas on which to draw the view */// 自定义 ViewGroup 实现protectedvoiddispatchDraw(Canvascanvas) {
}
View: working process
[TOC]
在上一篇文章Activity: Lifecycle and ActivityThread 分析了 Activity 启动后执行生命周期方法的基础上,走到了 performTraversals() 方法,这篇文章由此开始,讲解 View 的工作流程。
上面是该函数的三个主要过程,下面逐步去看一下每个过程分别做了什么。
View的基本介绍
ViewRoot 和 DecorView
ViewRoot 对应与 ViewRootImpl, 是连接 WindowManager 和 DecorView 的纽带,
View 的三大流程同时通过 ViewRoot 来完成。
在 ActivityThread 中,当 Activity 对象被创建完毕,会将 DecorView 添加到 Window 中,同时会创建 ViewRootImpl 对象,同 DecorView 建立关联。
Measure
这里的 measureSpec 应该很熟悉了,是一个32为的 int 型整数。高两位表示测量模式,低30位表示具体大小。
其中测量模式有三种:
UNSPECIFIED:父容器不作限制,子View想多大就多大,一般用于系统内部。
EXACTLY:精确模式,大小为SpecSize,父容器完全决定子View的大小,对应LayoutParams中的match_parent和具体数值。
AT_MOST:最大模式,大小不能大于SpecSize,也就是子View的大小有上限,对应于LayoutParams中的warp_content。
获得了 view 的测量数据后,
上述就是 View 的测量过程,ViewGroup 的测量过程类似,只不过还要测量子 View 的大小来确定自己的大小。
以 DecorView 为例:
以上就是整个 View 和 ViewGroup 的整个 Measure 过程。
Layout
回到 ViewRootImpl 类中,执行 performLayout()过程。
上述过程是先让 DecorView 进行放置,然后有需要就放置所有的子 View,调用 requestLayout 方法。然后再对整个 View Tree 进行测量,进行二次放置。
因此这里知道, FrameLayout 放置子 View 就是根据其 Gravity 放置在对应的位置,然后根据顺序进行叠加放置。
Draw
在 draw 过程中,一般有 6 个步骤,2 - 5 可以省略。这里主要看 3 和 4 步。
看一下在 FrameLayout 的是如何处理的。
到此为止,整个 View 的绘制流程就分析完毕了。
自定义 View
总结
在 ViewRootImpl 的方法 performTraversals 中,会实现 view 的整个绘制过程。
测量:View 测量自身,ViewGroup 除了测量自己,还需测量子 View 来确定自己的大小
放置:ViewGroup 放置所有的 子View
绘制:使用 Canvas 进行绘制。
The text was updated successfully, but these errors were encountered: