1,已知内容
1,Activity加载View的流程,首先创建出DecorView
2,由ViewRootImpl依次调用DecorView的measure、layout、draw方法
2,提问
1,measure、layout、draw功能的输入、输出有哪些?
2,measure、layout、draw功能的执行顺序(父与子之间)怎样?
3,DecorView的层次结构
1,DecorView是通过一系列的继承来实现功能的,而ViewRootImpl直接调用对象是DecorView。
2,列出DecorView继承的类的api,"×"代表该方法未被实现或是一个空方法
4-1,measure
ViewRootImpl调用部分
#ViewRootImpl.java,这里面的内容是零散的
{
Rect frame = mWinFrame; // frame given by window manager.
mWidth = frame.width();
mHeight = frame.height();
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
#ViewRootImpl.java
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
#View.java$$MeasureSpec内部类
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) { // 版本号小于等于17 为 true,否则为 false
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
#ViewRootImpl.java
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
1,输出参数为屏幕的宽、高,加上Match_Parent和Wrap_Parent的标记量
DecorView的循环调用部分
#View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
...
int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
long value = mMeasureCache.valueAt(cacheIndex);
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
}
...
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}
#DecorView.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
...
if (measure) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#FrameLayout.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
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);
...
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
...
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++) {
...
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
#ViewGroup.java
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
...
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
#View.java
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
...
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
#View.java
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
父子调用顺序:View.measure -> DecorView.onMeasure -> FrameLayout.onMeasure -> ViewGroup.measureChildWithMargins -> 新的循环【实际操作的View都是DecorView】
单个View的逻辑:1,将子View计算出来;2,依据子View将父View计算出来;3,将某些需要父View信息的子View再次计算
最终的输出函数是:View.setMeasureDimensionRaw
输出内容为:mMeasureWidth、mMeasureHeight、mPrivateFlags
所以measure最终目的就是,将DecorView下所有子View的宽和高计算出来
4-2,layout
ViewRootImpl调用部分
#ViewRootImpl.java,这里面的内容是零散的
{
Rect frame = mWinFrame; // frame given by window manager.
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
}
#ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
}
输入参数为:屏幕的宽、高
DecorView的循环调用部分
#ViewGroup.java
public final void layout(int l, int t, int r, int b) {
...
super.layout(l, t, r, b);
...
}
#View.java
public void layout(int l, int t, int r, int b) {
...
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
...
}
...
}
#View.java
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
...
return setFrame(
left + parentInsets.left - childInsets.left,
top + parentInsets.top - childInsets.top,
right + parentInsets.left + childInsets.right,
bottom + parentInsets.top + childInsets.bottom);
}
#View.java
protected boolean setFrame(int left, int top, int right, int bottom) {
...
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
...
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
...
}
...
}
#DecorView.java
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
...
}
#FrameLayout.java
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
#FrameLayout.java
void layoutChildren(int left, int top, int right, int bottom,
boolean forceLeftGravity) {
...
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
...
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
父子调用顺序:ViewGroup.layout -> View.layout -> DecorView.onLayout -> FrameLayout.onLayout -> FrameLayout.layoutChildren -> 新的循环【实际操作的View都是DecorView】
单个View的逻辑:1,将父View的定位计算出来;2,利用父View将子View的定位计算出来
最终的输出函数是:View.setFrame
输出内容为:mLeft、mTop、mRight、mBottom
所以layout最终目的就是,将DecorView下所有子View的位置计算出来。这里也可以看出,mWidth和mMeasureWidth是在不同时期计算出来内容
4-3,draw
ViewRootImpl调用部分
#ViewRootImpl.java
private void performDraw() {
...
draw(fullRedrawNeeded);
...
}
#ViewRootImpl.java
private void draw(boolean fullRedrawNeeded) {
...
drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)
...
}
#ViewRootImpl.java
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
final Canvas canvas;
try {
...
canvas = mSurface.lockCanvas(dirty);
...
}
...
try {
...
mView.draw(canvas);
...
} finally {
...
surface.unlockCanvasAndPost(canvas);
...
}
return true;
}
surface lock状态下的canvas
DecorView的循环调用部分
#DecorView.java
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
#View.java
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* 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 needed
if (!dirtyOpaque) {
drawBackground(canvas);
}
...
// Step 2, save the canvas' layers
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
}
...
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
...
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}
#ViewGroup.java
protected void dispatchDraw(Canvas canvas) {
...
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
// 继续绘制
while (transientIndex >= 0) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
break;
}
}
...
}
#ViewGroup.java
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
#DecorView.java
public void onDraw(Canvas c) {
super.onDraw(c);
mBackgroundFallback.draw(mContentRoot, c, mContentParent);
}
父子调用顺序:DecorView.draw-> View.draw-> ViewGroup.dispatchDraw-> ViewGroup.drawChild -> 新的循环【实际操作的View都是DecorView】
单个View的逻辑:"父View背景" -> "父View内容" -> "子View" -> "父View foreground、scrollbar" -> 新的循环
最终的输出函数是:略【都是对canvas直接操作】
输出内容为:canvas上绘制的内容
所以draw最终目的就是,利用measure和layout中计算出的内容,将内容显示给用户
5,回答提问
输入 | 输出 | 执行顺序 | |
measure | 屏幕宽高 + 标记 |
mMeasureWidth mMeasureHeight |
"子View" -> "父view" -> "某些需要父类信息的View" -> 新的循环 |
layout | 屏幕宽高 | mLeft、mRight、mTop、mButtom | "父View" -> "子View" -> 新的循环 |
draw | canvas | canvas上的"装饰" | "父View背景" -> "父View内容" -> "子View" -> "父View foreground、scrollbar" -> 新的循环 |
ylineSign
QQ群:644213963