湖畔镇

View的绘制原理

View的绘制原理

Android的View绘制都要经过测量,布局和绘制三个步骤

测量

测量过程用来决定View的宽高,父布局提供了限制条件

View.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
// 判断是不是要进行测量
// 有强制布局的标志 或者MeasureSpec有变化则测量
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) {
......
// 清标记
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
......
// 测量
onMeasure(widthMeasureSpec, heightMeasureSpec);
......
// 如果没有设置标志就抛出异常
// 说明在测量出宽高后一定要调setMeasuredDimension()
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("View with id " + getId() + ": " + getClass().getName() + "#onMeasure() did not set the measured dimension by calling setMeasuredDimension()");
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}

mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}

measure()方法不可被重写,都是通过重写onMeasure()来完成测量

View.java

1
2
3
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

View的onMeasure()只调用了setMeasuredDimension()设置测量好的宽高,就是设置了mMeasuredWidthmMeasuredHeight两个变量,并设置了PFLAG_MEASURED_DIMENSION_SET标志位

View.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
// 无限制 则返回给定的大小
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
// 父布局给定最大值或指定值 则返回从MeasureSpec中解析出来的大小
result = specSize;
break;
}
return result;
}

默认实现通过getDeaultSize()来获得宽高,综合考虑传入的大小和MeasureSpec

布局

View.java

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
public void layout(int l, int t, int r, int b) {
// 需要测量先测量
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
// 调用某个setFrame方法
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);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;

// 通知布局变化
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

也是通过重写onLayout()来重写布局

View.java

1
2
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

View的onLayout()是空的,需要子类实现

View.java

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
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
// 发生了变化
changed = true;
int drawn = mPrivateFlags & PFLAG_DRAWN;

// 比较新旧宽高的变化
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

invalidate(sizeChanged);

// 更新成员变量
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
mPrivateFlags |= PFLAG_HAS_BOUNDS;

// 大小变化后做一些事
if (sizeChanged) {
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}

if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {
mPrivateFlags |= PFLAG_DRAWN;
invalidate(sizeChanged);
invalidateParentCaches();
}
mPrivateFlags |= drawn;

mBackgroundSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}

notifySubtreeAccessibilityStateChangedIfNeeded();
}
return changed;
}
1
2
3
4
5
6
7
8
private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
if (mOverlay != null) {
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
rebuildOutline();
}

onSizeChanged()也是由子类实现,大小变化时做一些事

绘制

View.java

1
2
3
4
5
6
7
8
9
public void draw(Canvas canvas) {
drawBackground(canvas);
...
onDraw(canvas);
...
dispatchDraw(canvas);
...
onDrawForeground(canvas);
}
  1. 绘制背景
  2. 如有必要,保存图层,绘制边界的渐变效果
  3. 绘制内容
  4. 绘制子元素
  5. 恢复2中保存的图层
  6. 绘制前景(滚动条等)

通过重写onDraw()来绘制内容

View.java

1
2
protected void onDraw(Canvas canvas) {
}

View的onDraw()是空的,需要子类实现

1
2
protected void dispatchDraw(Canvas canvas) {
}

也是由子类实现,在子元素被绘制之前进行控制

实例

ViewGroup

ViewGroup是View的子类

ViewGroup没有重写onMeasure(),使用的是View的默认实现

ViewGroup.java

1
2
@Override
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);

onLayout()是个抽象函数,一定要在子类中重写(因为ViewGroup就是用来布局子元素的)

ViewGroup没有重写onDraw(),所以也是空实现

LinearLayout

LinearLayout.java

1
2
3
4
5
6
7
8
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}

根据方向进行测量,以竖直方向为例

LinearLayout.java

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
mTotalLength = 0;
int maxWidth = 0;
boolean allFillParent = true;
int alternativeMaxWidth = 0;
final int count = getVirtualChildCount();
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

// 遍历子元素
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);

// 子元素为空的处理 measureNullChild()返回0 继续下一轮
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}

// 子元素不可见的处理 getChildrenSkipCount()返回0 继续下一轮
if (child.getVisibility() == View.GONE) {
i += getChildrenSkipCount(child, i);
continue;
}

// 处理分割线
if (hasDividerBeforeChildAt(i)) {
mTotalLength += mDividerHeight;
}

LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;

if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
skippedMeasure = true;
} else {
int oldHeight = Integer.MIN_VALUE;
if (lp.height == 0 && lp.weight > 0) {
oldHeight = 0;
lp.height = LayoutParams.WRAP_CONTENT;
}

// 测量子元素
measureChildBeforeLayout(child, i, widthMeasureSpec, 0, heightMeasureSpec, totalWeight == 0 ? mTotalLength : 0);

if (oldHeight != Integer.MIN_VALUE) {
lp.height = oldHeight;
}

final int childHeight = child.getMeasuredHeight();
final int totalLength = mTotalLength;

// 获得新的总高度 子元素高度+Margin+偏移
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));

if (useLargestChild) {
largestChildHeight = Math.max(childHeight, largestChildHeight);
}
}

......

boolean matchWidthLocally = false;
// 有子元素是MATCH_PARENT的 并且没有确切的宽度 需要重新测量
if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
matchWidth = true;
matchWidthLocally = true;
}

// 计算宽度
final int margin = lp.leftMargin + lp.rightMargin;
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);
childState = combineMeasuredStates(childState, child.getMeasuredState());

allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
if (lp.weight > 0) {
weightedMaxWidth = Math.max(weightedMaxWidth, matchWidthLocally ? margin : measuredWidth);
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth, matchWidthLocally ? margin : measuredWidth);
}

i += getChildrenSkipCount(child, i);
}

// 最后一个分割线
if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
mTotalLength += mDividerHeight;
}

if (useLargestChild && (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
......
}

// 计算Padding
mTotalLength += mPaddingTop + mPaddingBottom;
int heightSize = mTotalLength;
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
int delta = heightSize - mTotalLength;

if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
// 计算总weight
float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}

LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
// 分配给带weight的子元素额外空间
int share = (int) (childExtra * delta / weightSum);
weightSum -= childExtra;
delta -= share;

// 用默认方式获得子元素的WidthMeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin, lp.width);

if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
// 说明上面测量过
int childHeight = child.getMeasuredHeight() + share;
if (childHeight < 0) {
childHeight = 0;
}
// 测量子元素
// 这里HeightMeasureSpec是自己构造出来的 明确的指定大小
child.measure(childWidthMeasureSpec, MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
} else {
child.measure(childWidthMeasureSpec, MeasureSpec.makeMeasureSpec(share > 0 ? share : 0, MeasureSpec.EXACTLY));
}

childState = combineMeasuredStates(childState, child.getMeasuredState() & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
}

final int margin = lp.leftMargin + lp.rightMargin;
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);

boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT;
alternativeMaxWidth = Math.max(alternativeMaxWidth, matchWidthLocally ? margin : measuredWidth);
allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;

final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}

mTotalLength += mPaddingTop + mPaddingBottom;
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth, weightedMaxWidth);
if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
......
}
}

if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
maxWidth = alternativeMaxWidth;
}

maxWidth += mPaddingLeft + mPaddingRight;
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

// 设置测量的宽高
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), heightSizeAndState);

if (matchWidth) {
forceUniformWidth(count, heightMeasureSpec);
}
}
  1. 遍历子元素,子元素未设置weight则进行测量,否则跳过
  2. 如果有跳过测量的元素或有剩余的空间,则进行第二次遍历,处理设置weight的元素
  3. setMeasuredDimension()

ViewGroup.java

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
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// spec是父元素的MeasureSpec 表示父元素的限制
// padding在这里是子元素的左右margin+父元素的左右padding
// childDimension是子元素LayoutParams.width
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);

// 参考宽度=父元素给定的宽度-各种边距
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;

switch (specMode) {
// 父元素指定了一个确定值
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
// lp.width有确定值
// 则取该值 模式为EXACTLY
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// lp.width为match_parent
// 取参考宽度 模式为EXACTLY
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// lp.width为wrap_content
// 取参考宽度 模式为AT_MOST
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// 父元素给定最大值
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// lp.width有确定值
// 则取该值 模式为EXACTLY
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// lp.width为match_parent
// 但父元素大小也不确定
// 取参考宽度 模式为AT_MOST
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// lp.width为wrap_content
// 取参考宽度 模式为AT_MOST
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// 父元素无限制
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// lp.width有确定值
// 则取该值 模式为EXACTLY
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// lp.width为match_parent
// 但父元素大小也不确定
// 取参考宽度 模式为UNSPECIFIED
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// lp.width为wrap_content
// 取参考宽度 模式为UNSPECIFIED
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

布局生成MeasureSpec的默认方法,综合考虑父元素传入的MeasureSpec和子元素的LayoutParams

LinearLayout.java

1
2
3
4
5
6
7
8
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
}

也是根据方向进行布局,以竖直方向为例

LinearLayout.java

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
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;

final int width = right - left;
int childRight = width - mPaddingRight;
int childSpace = width - paddingLeft - mPaddingRight;

final int count = getVirtualChildCount();
// 可见主Gravity是竖直方向的 次Gravity是水平方向的
final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;

// 考察竖直方向的Gravity 这里用的是父布局的值
switch (majorGravity) {
case Gravity.BOTTOM:
childTop = mPaddingTop + (bottom - top - mTotalLength);
break;
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
break;
case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
}

// 遍历子元素
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
// 如果子元素没有设置Gravity 那么设置父Gravity的次Gravity给它
// 对于一个竖直布局来说 子元素的水平Gravity才是有效的
int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}

final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);

// 考察子元素的水平Gravity
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2) + lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = paddingLeft + lp.leftMargin;
break;
}

// 处理分割线
if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
}

// 增加上边距
childTop += lp.topMargin;

// 子元素布局
setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight);

// 增加高度+下边距+偏移
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
i += getChildrenSkipCount(child, i);
}
}
}

先根据父布局的Gravity来确定竖直方向的位置,然后遍历子元素LayoutParams中的Gravity来确定水平方向的位置并调用子元素的layout()

LinearLayout.java

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void onDraw(Canvas canvas) {
if (mDivider == null) {
return;
}
if (mOrientation == VERTICAL) {
drawDividersVertical(canvas);
} else {
drawDividersHorizontal(canvas);
}
}

也是根据方向进行绘制,对mDivider进行了判断,可见主要就是进行了分割线的绘制,以竖直方向为例

LinearLayout.java

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
void drawDividersVertical(Canvas canvas) {
final int count = getVirtualChildCount();

// 遍历绘制每个子元素之前的分割线
// 综合考虑子元素的位置以及Divider设置
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child != null && child.getVisibility() != GONE) {
if (hasDividerBeforeChildAt(i)) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int top = child.getTop() - lp.topMargin - mDividerHeight;
drawHorizontalDivider(canvas, top);
}
}
}

// 最后的分割线
if (hasDividerBeforeChildAt(count)) {
final View child = getLastNonGoneChild();
int bottom = 0;
if (child == null) {
bottom = getHeight() - getPaddingBottom() - mDividerHeight;
} else {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
bottom = child.getBottom() + lp.bottomMargin;
}
drawHorizontalDivider(canvas, bottom);
}
}

RelativeLayout

RelativeLayout.java

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
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;

// 整理子元素
// 水平和竖直方向各有一个数组,用来保存排序的子元素 排序是根据依赖 比如A依赖B B依赖C 存放顺序就是C-B-A
// 通过DependencyGraph来完成
if (mDirtyHierarchy) {
mDirtyHierarchy = false;
sortChildren();
}

......

// 水平方向子元素的处理
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
LayoutParams params = (LayoutParams) child.getLayoutParams();
int[] rules = params.getRules(layoutDirection);
// 应用水平方向布局规则
// 即通过LEFT_OF这类的规则找到关联元素 获得它的LayoutParams 用来调整自己的LayoutParams
// 主要是mLeft和mRight
applyHorizontalSizeRules(params, myWidth, rules);
// 水平方向测量子元素
measureChildHorizontal(child, params, myWidth, myHeight);
// 定位子元素 调整mLeft和mRight
if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
offsetHorizontalAxis = true;
}
}
}

// 竖直方向子元素的处理
views = mSortedVerticalChildren;
count = views.length;
int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
// 应用竖直方向布局规则
// 即通过ABOVE这类的规则找到关联元素 获得它的LayoutParams 用来调整自己的LayoutParams
// 主要是mTop和mBottom
applyVerticalSizeRules(params, myHeight, child.getBaseline());
// 测量子元素
measureChild(child, params, myWidth, myHeight);
// 定位子元素 调整mTop和mBottom
if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
offsetVerticalAxis = true;
}

// 如果宽度是包裹子元素的 如下处理
if (isWrapContentWidth) {
if (isLayoutRtl()) {
if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
width = Math.max(width, myWidth - params.mLeft);
} else {
width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
}
} else {
if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
width = Math.max(width, params.mRight);
} else {
width = Math.max(width, params.mRight + params.rightMargin);
}
}
}

// 如果高度是包裹子元素的 如下处理
if (isWrapContentHeight) {
if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
height = Math.max(height, params.mBottom);
} else {
height = Math.max(height, params.mBottom + params.bottomMargin);
}
}

if (child != ignore || verticalGravity) {
left = Math.min(left, params.mLeft - params.leftMargin);
top = Math.min(top, params.mTop - params.topMargin);
}

if (child != ignore || horizontalGravity) {
right = Math.max(right, params.mRight + params.rightMargin);
bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
}
}
}

// 各种布局参数的设置
// 居中 尾部对齐(右对齐/底部对齐)
// Gravity调整
......

setMeasuredDimension(width, height);
}
  1. 如有必要,解析子元素的依赖关系,按顺序保存在全局数组里
  2. 水平方向处理子元素,应用布局规则,水平测量子元素
  3. 竖直方向处理子元素,应用布局规则,测量子元素
  4. 调整居中/右对齐/底部对齐/Gravity
  5. setMeasuredDimension()

RelativeLayout.java

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
RelativeLayout.LayoutParams st = (RelativeLayout.LayoutParams) child.getLayoutParams();
child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
}
}
}

onLayout()很简单,因为测量的时候每个子元素的边界都保存在布局参数里了,这里直接用它们进行布局

RelativeLayout并没有重写onDraw(),所以是空实现

FrameLayout

FrameLayout.java

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
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
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);
childState = combineMeasuredStates(childState, child.getMeasuredState());

if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}

// 处理Padding
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

// 检查宽高
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

// 检查前景宽高
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}

// 设置宽高
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));

// 处理match_parent的子元素
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

final int childWidthMeasureSpec;
// 水平方向
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin);
// 设置明确的宽度
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
} else {
// 默认实现获得MeasureSpec
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width);
}

final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
// 设置明确的高度
final int height = Math.max(0, getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
} else {
// 默认实现获得MeasureSpec
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() + lp.topMargin + lp.bottomMargin, lp.height);
}

// 测量子元素
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
  1. 遍历测量子元素
  2. setMeasuredDimension()
  3. 如果有布局参数是match_parent 的,需要用新的宽度得到MeasureSpec再次测量

FrameLayout.java

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
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false);
}

void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();

for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();

int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}

final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

// 水平方向
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}

// 竖直方向
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 + lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}

遍历子元素,根据Gravity参数布局

FrameLayout也没有重写onDraw(), 为空实现

分享