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