Loading...
avatar

Android图形系统—View.draw解析

图文概括

流程

  1. 绘制背景
  2. 绘制内容
  3. 分发子View绘制
  4. 绘制装饰

源码分析

View.draw

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
public void draw(Canvas canvas) {
....
// 1. 绘制本身View背景
if (!dirtyOpaque) {
drawBackground(canvas);
}

if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
// 2. 绘制内容,默认空实现 需复写
if (!dirtyOpaque) onDraw(canvas);

// 3. 绘制 children
dispatchDraw(canvas);

drawAutofilledHighlight(canvas);

// 4. 分发Draw (单一View空实现,ViewGroup见下面分析)
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}

// 5. 绘制装饰 (前景色,滚动条)
onDrawForeground(canvas);

return;
}
....
}

//绘制背景
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}

// 根据在 layout 过程中获取的 View 的位置参数,来设置背景的边界
setBackgroundBounds();

// 先尝试用HWUI绘制
if (canvas.isHardwareAccelerated() && mAttachInfo != null
&& mAttachInfo.mThreadedRenderer != null) {
mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);

final RenderNode renderNode = mBackgroundRenderNode;
if (renderNode != null && renderNode.isValid()) {
setBackgroundRenderNodeProperties(renderNode);
((DisplayListCanvas) canvas).drawRenderNode(renderNode);
return;
}
}

final int scrollX = mScrollX;
final int scrollY = mScrollY;
if ((scrollX | scrollY) == 0) {
//调用 Drawable 的 draw 方法来进行背景的绘制
background.draw(canvas);
} else {
// 若 mScrollX 和 mScrollY 有值,则对 canvas 的坐标进行偏移平移画布
canvas.translate(scrollX, scrollY);
//调用 Drawable 的 draw 方法来进行背景的绘制
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}

// 绘制View本身内容,空实现,子类必须复写
protected void onDraw(Canvas canvas) {
}
// 单一View无子View,空实现
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);
}
//调用 Drawable 的 draw 方法,绘制前景色
foreground.draw(canvas);
}
}

ViewGroup.draw

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
public void draw(Canvas canvas) {
// 调用View.draw 方法
}


@Override
protected void dispatchDraw(Canvas canvas) {
...
// 1. 遍历子View
final int childrenCount = mChildrenCount;
...
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;
}
}

final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
// 调用 drawChild 方法,进行子元素绘制
more |= drawChild(canvas, child, drawingTime);
}
}
....
}
-

推荐阅读:图形系统总结

Author: Afree
Link: https://afree8909.github.io/blog/2019/11/20/Android%E5%9B%BE%E5%BD%A2%E7%B3%BB%E7%BB%9F%E2%80%94View.draw%E8%A7%A3%E6%9E%90/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.

Comment