一. 前言 1. 作用 绘制 View视图。
二. 单一View的Draw过程 1. 原理 ① View绘制自身,包括背景和内容。
② 绘制装饰,包括滚动指示器,滚动条和前景。
2. 过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 public void draw (Canvas canvas) { ... int saveCount; if (!dirtyOpaque) { drawBackground(canvas); } final int viewFlags = mViewFlags; if (!verticalEdges && !horizontalEdges) { if (!dirtyOpaque) { onDraw(canvas); } dispatchDraw(canvas); onDrawForeground(canvas); return ; } ... } private void drawBackground (Canvas canvas) { final Drawable background = mBackground; if (background == null ) { return ; } setBackgroundBounds(); ..... final int scrollX = mScrollX; final int scrollY = mScrollY; if ((scrollX | scrollY) == 0 ) { background.draw(canvas); } else { canvas.translate(scrollX, scrollY); background.draw(canvas); canvas.translate(-scrollX, -scrollY); } } protected void onDraw (Canvas canvas) { ... } protected void dispatchDraw (Canvas canvas) { ... } public void onDrawForeground (Canvas canvas) { onDrawScrollIndicators(canvas); onDrawScrollBars(canvas); final 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); } }
总结:
draw方法里面:依次调用drawBackground -> onDraw -> dispatchDraw(空实现) -> onDrawForeground
三. 多个View,ViewGroup的Draw过程 1. 原理 ① ViewGroup绘制自身,包括背景、内容。
② ViewGroup遍历子View & 绘制其所有子View。
③ 绘制装饰,包括滚动指示器,滚动条和前景。
2. 过程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public void draw (Canvas canvas) { ... int saveCount; if (!dirtyOpaque) { drawBackground(canvas); } final int viewFlags = mViewFlags; if (!verticalEdges && !horizontalEdges) { if (!dirtyOpaque) { onDraw(canvas); } dispatchDraw(canvas); onDrawForeground(canvas); return ; } ... }
由于 步骤2:drawBackground、步骤3:onDraw、步骤5:onDrawForeground,与单一View的draw过程类似,此处不作过多描述。
进入与单一View不同的 dispatchDraw过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 protected void dispatchDraw (Canvas canvas) { ...... final int childrenCount = mChildrenCount; ...... for (int i = 0 ; i < childrenCount; i++) { ...... if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() != null ) { more |= drawChild(canvas, transientChild, drawingTime); } .... } } protected boolean drawChild (Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this , drawingTime); }
总结:
draw方法里面:依次调用drawBackground -> onDraw -> dispatchDraw(系统已经写好) -> onDrawForeground
参考文章 (4)自定义View Draw过程- 最易懂的自定义View原理系列