Android源碼_View啓動的measure、layout、draw

1,已知內容

1,Activity加載View的流程,首先創建出DecorView

2,由ViewRootImpl依次調用DecorView的measure、layout、draw方法

可參考:Android源碼_Activity加載View

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章