“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022”
Related articles: DecorView Android View DecorView Android View DecorView View rendering process (3) -Activity View -WindowManager Android View rendering process (4) -Activity View -ViewRootImpl Android View rendering process (5) -Measure Android View View drawing process (six) -Layout Android View drawing process (seven) -Draw
Last article the View drawing process Layout (Layout) logic through the analysis of the main content of the source code is introduced, the main method call is onLayout method and layoutChildren, we introduce the View drawing process in the last step Draw Draw.
Draw
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
private void performDraw() { ... //fullRedrawNeeded, which determines whether to redraw all views draw(fullRedrawNeeded); . }Copy the code
Then execute the draw() method:
private void draw(boolean fullRedrawNeeded) { ... Final Rect dirty = mDirty; // Get mDirty. Final Rect dirty = mDirty; if (mSurfaceHolder ! = null) { // The app owns the surface, we won't draw. dirty.setEmpty(); if (animating) { if (mScroller ! = null) { mScroller.abortAnimation(); } disposeResizeBuffer(); } return; } // If fullRedrawNeeded is true, the dirty area is set to the entire screen, indicating that the entire view needs to be drawn. Need to draw all views the if (fullRedrawNeeded) {mAttachInfo. MIgnoreDirtyState = true; Dirty. Set (0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); }... if (! drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { return; }}Copy the code
The above method finally calls drawSoftware, which then executes to mView.draw(canvas) after drawSoftware.
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) { final Canvas canvas; // This canvas is the canvas on which we want to draw things. Canvas = msurface.lockCanvas (dirty); . // Canvas supports bitmap density, which is related to phone resolution canvas.setDensity(mDensity); . if (! canvas.isOpaque() || yoff ! = 0 || xoff ! = 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); }... canvas.translate(-xoff, -yoff); . // Start drawing mview.draw (canvas); . / / submitted need to draw something surface. UnlockCanvasAndPost (canvas); }Copy the code
Mview.draw (canvas) to start the actual drawing:
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
int saveCount;
//绘制背景
if (!dirtyOpaque) {
drawBackground(canvas);
}
// 如果可以跳过2和5步
final int viewFlags = mViewFlags;
//判断是否有绘制衰退边缘的标示
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
// 如果没有绘制衰退边缘只需要3,4,6步
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// 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);
// we're done...
return;
}
/*
* Here we do the full fledged routine...
* (this is an uncommon case where speed matters less,
* this is why we repeat some of the tests that have been
* done above)
*/
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
// Step 2, save the canvas' layers
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
// overlapping fades produce odd-looking artifacts
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
// also clip horizontal fades if necessary
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
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);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// 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);
}
Copy the code
Draw process is more complex, but the logic is still very clear, focus on the above at the beginning of the marker: DirtyOpaque, the meaning of the flag bit is to judge whether the current view is transparent, if it is to execute the next step is not transparent, simple, transparent can see drawing, then the drawing process is mainly divided into six steps, but the main is a step, the next will be to: 1) : the background of the map view (2) : Save layer information (not important) 3) : Draw the content of the View 4) : Draw the sub-view (same process as measure, layout) 5) : Draw shadows (not important) 6) : Draw other parts of the View \
InitViewGroup (); / / initViewGroup(); / / initViewGroup(); / / initViewGroup();
private void initViewGroup() { // ViewGroup doesn't draw by default if (! debugDraw()) { setFlags(WILL_NOT_DRAW, DRAW_MASK); }... }Copy the code
As you can see from the second comment, ViewGroup does not draw by default. On line 4, call setFlags to set the WILL_NOT_DRAW flag.
1 final int privateFlags = mPrivateFlags; 2 final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE && 3 (mAttachInfo == null || ! mAttachInfo.mIgnoreDirtyState);Copy the code
The setFlags method is to change the value of mPrivateFlags in the View. We set WILL_NOT_DRAW so that dirtyOpaque will be true, so if (! DirtyOpaque does not hold, and the onDraw method is not executed.
Below for the above steps, focus on the source code
Draw the background
Private void Drawable (Canvas Canvas) {// Get final Drawable background = mBackground; if (background == null) { return; } // Determine background Drawable bounds setBackgroundBounds(); . Final int scrollX = mScrollX; final int scrollX = mScrollX; final int scrollY = mScrollY; if ((scrollX | scrollY) == 0) { background.draw(canvas); } else { canvas.translate(scrollX, scrollY); Draw background.draw(canvas); Canvas. Translate (-scrollx, -scrolly); drawable (-scrollx, -scrolly); }}Copy the code
Draw the content
public void onDraw(Canvas c) { super.onDraw(c); mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent, mStatusColorViewState.view, mNavigationColorViewState.view); }Copy the code
Super is also an empty implementation that needs to be implemented by itself, depending on how the different components are implemented
Draw the son View
The dispatchDraw process of the ViewGroup is to start the animation that was first added to the layout, then determine the drawing area, and iterate over the drawing View. DrawChild (ViewGroup drawChild, ViewGroup drawChild, ViewGroup drawChild, ViewGroup drawChild)
protected void dispatchDraw(Canvas canvas) { boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode); final int childrenCount = mChildrenCount; final View[] children = mChildren; int flags = mGroupFlags; // Does the ViewGroup have a child View entry animation, if there is bound to the View // start animation controller... Int clipSaveCount = 0; Final Boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; // Remove the padding if (clipToPadding) {clipSaveCount = canvas.save(); canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop, mScrollX + mRight - mLeft - mPaddingRight, mScrollY + mBottom - mTop - mPaddingBottom); }... for (int i = 0; i < childrenCount; I++) {// the View from mTransientViews is added by addTransientView. They are just one item rendered by the container 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); }}... }Copy the code
Draw a View:
protected boolean drawChild(Canvas canvas, View child, DrawingTime (canvas, this, drawingTime) {return child. Draw (canvas, this, drawingTime); }Copy the code
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { boolean drawingWithRenderNode = mAttachInfo ! = null && mAttachInfo.mHardwareAccelerated && hardwareAcceleratedCanvas; . Draw (canvas); draw(canvas); draw(canvas); draw(canvas); drawingWithDrawingCache) { if (drawingWithRenderNode) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ((DisplayListCanvas) canvas).drawRenderNode(renderNode); } else { // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); } } } else if (cache ! = null) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; if (layerType == LAYER_TYPE_NONE) { // no layer paint, use temporary paint to draw bitmap Paint cachePaint = parent.mCachePaint; if (cachePaint == null) { cachePaint = new Paint(); cachePaint.setDither(false); parent.mCachePaint = cachePaint; } cachePaint.setAlpha((int) (alpha * 255)); Canvas. DrawBitmap (cache, 0.0 f, 0.0 f, cachePaint); } else { // use layer paint to draw the bitmap, merging the two alphas, but also restore int layerPaintAlpha = mLayerPaint.getAlpha(); mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha)); Canvas. DrawBitmap (cache, 0.0 f, 0.0 f, mLayerPaint); mLayerPaint.setAlpha(layerPaintAlpha); }}}Copy the code
The main content of the above source code and the previous analysis of measure and layout, first determine whether there is a cache, that is, whether it has been drawn once before, if not, will call draw(canvas) method, start normal drawing, otherwise use cache to display. Code Chinese Academy of Sciences see, it has been drawn on the sub-view, and the sub-view will call its own draw method to draw itself, so constantly traversing the sub-view and sub-view of their own drawing, so that the View tree to complete the drawing.
For custom views, if you need to draw something, just re-draw onDraw.
Draw other parts
This is the part of the View except for the background, content, and child views.
Public void onDrawForeground(Canvas) {onDrawScrollIndicators(Canvas); // DrawScrollBar onDrawScrollBars(canvas); Drawable foreground = mForegroundInfo! Drawable foreground = mForegroundInfo! = null ? mForegroundInfo.mDrawable : null; if (foreground ! = null) { if (mForegroundInfo.mBoundsChanged) { mForegroundInfo.mBoundsChanged = false; final Rect selfBounds = mForegroundInfo.mSelfBounds; final Rect overlayBounds = mForegroundInfo.mOverlayBounds; if (mForegroundInfo.mInsidePadding) { selfBounds.set(0, 0, getWidth(), getHeight()); } else { selfBounds.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()); } final int ld = getLayoutDirection(); Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(), foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld); foreground.setBounds(overlayBounds); } foreground.draw(canvas); }}Copy the code
I won’t go into details about 2 and 5, but mostly draw things like shadows and edge colors
In fact, the theme of the idea and measure and layout, familiar with custom View students, for drawing Draw method is not unfamiliar, all kinds of Draw type method, shape and so on, these source code roughly understand the process, the actual development process, Still need practice, more operation will make perfect, written is the real thing.
The next article summarizes the View drawing process, the above has introduced the wrong place also please correct, welcome to leave a message