ViewGroup源码解读

LinearLayout

onMeasure

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);
}
}
measureVertical
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/**
* Measures the children when the orientation of this LinearLayout is set
* to {@link #VERTICAL}.
*
* @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
* @param heightMeasureSpec Vertical space requirements as imposed by the parent.
*
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onMeasure(int, int)
*/
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
//init the value of totalLength
mTotalLength = 0;
int maxWidth = 0;
int childState = 0;
int alternativeMaxWidth = 0;
int weightedMaxWidth = 0;
boolean allFillParent = true;
float totalWeight = 0;

final int count = getVirtualChildCount();

final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

boolean matchWidth = false;
boolean skippedMeasure = false;//if exactly && layoutWeight !=nul ,set skippedMeasure= true

final int baselineChildIndex = mBaselineAlignedChildIndex;
final boolean useLargestChild = mUseLargestChild;

int largestChildHeight = Integer.MIN_VALUE;

// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);

if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}

if (child.getVisibility() == View.GONE) {
i += getChildrenSkipCount(child, i);
continue;
}

if (hasDividerBeforeChildAt(i)) {
mTotalLength += mDividerHeight;
}


//caculate weightSum first
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

totalWeight += lp.weight;

//got two things ,one is the totalHeight and the other one is the tallestChildHeight
if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
// Optimization: don't bother measuring children who are going to use
// leftover space. These views will get measured again down below if
// there is any leftover space.
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) {
// heightMode is either UNSPECIFIED or AT_MOST, and this
// child wanted to stretch to fill available space.
// Translate that to WRAP_CONTENT so that it does not end up
// with a height of 0
oldHeight = 0;
lp.height = LayoutParams.WRAP_CONTENT;
}

// Determine how big this child would like to be. If this or
// previous children have given a weight, then we allow it to
// use all available space (and we will shrink things later
// if needed).
//IMPORTANT:method
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;
//find the tallest height whether totalLength or totalLength add other height
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));

//xml has a attribut layout:useLargestChild
if (useLargestChild) {
largestChildHeight = Math.max(childHeight, largestChildHeight);
}
}

/**
* If applicable, compute the additional offset to the child's baseline
* we'll need later when asked {@link #getBaseline}.
*/
if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
mBaselineChildTop = mTotalLength;
}

// if we are trying to use a child index for our baseline, the above
// book keeping only works if there are no children above it with
// weight. fail fast to aid the developer.
if (i < baselineCbaselineChildIndexhildIndex && lp.weight > 0) {
throw new RuntimeException("A child of LinearLayout with index "
+ "less than mBaselineAlignedChildIndex has weight > 0, which "
+ "won't work. Either remove the weight, or don't set "
+ "mBaselineAlignedChildIndex.");
}
//------------------start the width calculate--------------
// .........................
i += getChildrenSkipCount(child, i);
}

if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
mTotalLength += mDividerHeight;
}
// if useLargestChild
if (useLargestChild &&
(heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
//................

}

// Add in our padding
mTotalLength += mPaddingTop + mPaddingBottom;

int heightSize = mTotalLength;

// Check against our minimum height
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

// Reconcile our calculated size with the heightMeasureSpec
//may be heightSize is not useful but you should caculate the childrens size in the process,so you will get the heightSize by
//the way
int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;

// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds. If we skipped
// measurement on any children, we need to measure them now.
int delta = heightSize - mTotalLength;
if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
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;
// reMeasure here by weight influence
if (childExtra > 0) {
// Child said it could absorb extra space -- give him his share
int share = (int) (childExtra * delta / weightSum);
weightSum -= childExtra;
delta -= share;

//must:getChildMeasureSpec(parent combined view size info ,parent padding and child margin, child lp.width)
final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight +
lp.leftMargin + lp.rightMargin, lp.width);

// TODO: Use a field like lp.isMeasured to figure out if this
// child has been previously measured
if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
// child was measured once already above...
// base new measurement on stored values
int
= child.getMeasuredHeight() + share;
if (childHeight < 0) {
childHeight = 0;
}

child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
} else {
// child was skipped in the loop above.
// Measure for this first time here
child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
MeasureSpec.EXACTLY));
}

// Child may now not fit in vertical dimension.
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));
}

// Add in our padding
mTotalLength += mPaddingTop + mPaddingBottom;
// TODO: Should we recompute the heightSpec based on the new total length?
} else {
//reconsile the param of useLargestChild
//.................
}

//deal with the width of
if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
maxWidth = alternativeMaxWidth;
}

maxWidth += mPaddingLeft + mPaddingRight;

//Check against our minimum width
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

//must:resolveSizeAndState is (your caculateSize and the combined size info to deceide the measured size)
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);

if (matchWidth) {
forceUniformWidth(count, heightMeasureSpec);
}
}
measurehorizontal
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/**
* Measures the children when the orientation of this LinearLayout is set
* to {@link #HORIZONTAL}.
*
* @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
* @param heightMeasureSpec Vertical space requirements as imposed by the parent.
*
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onMeasure(int, int)
*/
void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
//in this method you have to consider three things, the useLargestChild,the baseLine for ascent,descent
// the weight value for left space
mTotalLength = 0;
int maxHeight = 0;
int childState = 0;
int alternativeMaxHeight = 0;
int weightedMaxHeight = 0;
boolean allFillParent = true;
float totalWeight = 0;

final int count = getVirtualChildCount();

final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

boolean matchHeight = false;
boolean skippedMeasure = false;

if (mMaxAscent == null || mMaxDescent == null) {
mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
}

final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;

maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;

final boolean baselineAligned = mBaselineAligned;
final boolean useLargestChild = mUseLargestChild;

final boolean isExactly = widthMode == MeasureSpec.EXACTLY;

int largestChildWidth = Integer.MIN_VALUE;

// See how wide everyone is. Also remember max height.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);

if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}

if (child.getVisibility() == GONE) {
i += getChildrenSkipCount(child, i);
continue;
}

if (hasDividerBeforeChildAt(i)) {
mTotalLength += mDividerWidth;
}

final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();

totalWeight += lp.weight;

if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
// Optimization: don't bother measuring children who are going to use
// leftover space. These views will get measured again down below if
// there is any leftover space.
if (isExactly) {
mTotalLength += lp.leftMargin + lp.rightMargin;
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength +
lp.leftMargin + lp.rightMargin);
}

// Baseline alignment requires to measure widgets to obtain the
// baseline offset (in particular for TextViews). The following
// defeats the optimization mentioned above. Allow the child to
// use as much space as it wants because we can shrink things
// later (and re-measure).
if (baselineAligned) {
final int freeWidthSpec = MeasureSpec.makeSafeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);
final int freeHeightSpec = MeasureSpec.makeSafeMeasureSpec(
MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);
child.measure(freeWidthSpec, freeHeightSpec);
} else {
skippedMeasure = true;
}
} else {
int oldWidth = Integer.MIN_VALUE;

if (lp.width == 0 && lp.weight > 0) {
// widthMode is either UNSPECIFIED or AT_MOST, and this
// child
// wanted to stretch to fill available space. Translate that to
// WRAP_CONTENT so that it does not end up with a width of 0
oldWidth = 0;
lp.width = LayoutParams.WRAP_CONTENT;
}

// Determine how big this child would like to be. If this or
// previous children have given a weight, then we allow it to
// use all available space (and we will shrink things later
// if needed).
measureChildBeforeLayout(child, i, widthMeasureSpec,
totalWeight == 0 ? mTotalLength : 0,
heightMeasureSpec, 0);

if (oldWidth != Integer.MIN_VALUE) {
lp.width = oldWidth;
}

final int childWidth = child.getMeasuredWidth();
if (isExactly) {
mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
lp.rightMargin + getNextLocationOffset(child));
}

if (useLargestChild) {
largestChildWidth = Math.max(childWidth, largestChildWidth);
}
}

boolean matchHeightLocally = false;
if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
// The height of the linear layout will scale, and at least one
// child said it wanted to match our height. Set a flag indicating that
// we need to remeasure at least that view when we know our height.
matchHeight = true;
matchHeightLocally = true;
}

final int margin = lp.topMargin + lp.bottomMargin;
final int childHeight = child.getMeasuredHeight() + margin;
childState = combineMeasuredStates(childState, child.getMeasuredState());

if (baselineAligned) {
final int childBaseline = child.getBaseline();
if (childBaseline != -1) {
// Translates the child's vertical gravity into an index
// in the range 0..VERTICAL_GRAVITY_COUNT
final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
& Gravity.VERTICAL_GRAVITY_MASK;
final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
& ~Gravity.AXIS_SPECIFIED) >> 1;

maxAscent[index] = Math.max(maxAscent[index], childBaseline);
maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
}
}

maxHeight = Math.max(maxHeight, childHeight);

allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
if (lp.weight > 0) {
/*
* Heights of weighted Views are bogus if we end up
* remeasuring, so keep them separate.
*/
weightedMaxHeight = Math.max(weightedMaxHeight,
matchHeightLocally ? margin : childHeight);
} else {
alternativeMaxHeight = Math.max(alternativeMaxHeight,
matchHeightLocally ? margin : childHeight);
}

i += getChildrenSkipCount(child, i);
}

if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
mTotalLength += mDividerWidth;
}

// Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
// the most common case
if (maxAscent[INDEX_TOP] != -1 ||
maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
maxAscent[INDEX_BOTTOM] != -1 ||
maxAscent[INDEX_FILL] != -1) {
final int ascent = Math.max(maxAscent[INDEX_FILL],
Math.max(maxAscent[INDEX_CENTER_VERTICAL],
Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
final int descent = Math.max(maxDescent[INDEX_FILL],
Math.max(maxDescent[INDEX_CENTER_VERTICAL],
Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
maxHeight = Math.max(maxHeight, ascent + descent);
}

if (useLargestChild &&
(widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;

for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);

if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}

if (child.getVisibility() == GONE) {
i += getChildrenSkipCount(child, i);
continue;
}

final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
if (isExactly) {
mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}
}
}

// Add in our padding
mTotalLength += mPaddingLeft + mPaddingRight;

int widthSize = mTotalLength;

// Check against our minimum width
widthSize = Math.max(widthSize, getSuggestedMinimumWidth());

// Reconcile our calculated size with the widthMeasureSpec
int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
widthSize = widthSizeAndState & MEASURED_SIZE_MASK;

// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds. If we skipped
// measurement on any children, we need to measure them now.
int delta = widthSize - mTotalLength;
if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;

maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
maxHeight = -1;

mTotalLength = 0;

for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);

if (child == null || child.getVisibility() == View.GONE) {
continue;
}

final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();

float childExtra = lp.weight;
if (childExtra > 0) {
// Child said it could absorb extra space -- give him his share
int share = (int) (childExtra * delta / weightSum);
weightSum -= childExtra;
delta -= share;

final int childHeightMeasureSpec = getChildMeasureSpec(
heightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
lp.height);

// TODO: Use a field like lp.isMeasured to figure out if this
// child has been previously measured
if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
// child was measured once already above ... base new measurement
// on stored values
int childWidth = child.getMeasuredWidth() + share;
if (childWidth < 0) {
childWidth = 0;
}

child.measure(
MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
childHeightMeasureSpec);
} else {
// child was skipped in the loop above. Measure for this first time here
child.measure(MeasureSpec.makeMeasureSpec(
share > 0 ? share : 0, MeasureSpec.EXACTLY),
childHeightMeasureSpec);
}

// Child may now not fit in horizontal dimension.
childState = combineMeasuredStates(childState,
child.getMeasuredState() & MEASURED_STATE_MASK);
}

if (isExactly) {
mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}

boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
lp.height == LayoutParams.MATCH_PARENT;

final int margin = lp.topMargin + lp .bottomMargin;
int childHeight = child.getMeasuredHeight() + margin;
maxHeight = Math.max(maxHeight, childHeight);
alternativeMaxHeight = Math.max(alternativeMaxHeight,
matchHeightLocally ? margin : childHeight);

allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;

if (baselineAligned) {
final int childBaseline = child.getBaseline();
if (childBaseline != -1) {
// Translates the child's vertical gravity into an index in the range 0..2
final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
& Gravity.VERTICAL_GRAVITY_MASK;
final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
& ~Gravity.AXIS_SPECIFIED) >> 1;

maxAscent[index] = Math.max(maxAscent[index], childBaseline);
maxDescent[index] = Math.max(maxDescent[index],
childHeight - childBaseline);
}
}
}

// Add in our padding
mTotalLength += mPaddingLeft + mPaddingRight;
// TODO: Should we update widthSize with the new total length?

// Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
// the most common case
if (maxAscent[INDEX_TOP] != -1 ||
maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
maxAscent[INDEX_BOTTOM] != -1 ||
maxAscent[INDEX_FILL] != -1) {
final int ascent = Math.max(maxAscent[INDEX_FILL],
Math.max(maxAscent[INDEX_CENTER_VERTICAL],
Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
final int descent = Math.max(maxDescent[INDEX_FILL],
Math.max(maxDescent[INDEX_CENTER_VERTICAL],
Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
maxHeight = Math.max(maxHeight, ascent + descent);
}
} else {
alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);

// We have no limit, so make all weighted views as wide as the largest child.
// Children will have already been measured once.
if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);

if (child == null || child.getVisibility() == View.GONE) {
continue;
}

final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();

float childExtra = lp.weight;
if (childExtra > 0) {
child.measure(
MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
MeasureSpec.EXACTLY));
}
}
}
}

if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
maxHeight = alternativeMaxHeight;
}

maxHeight += mPaddingTop + mPaddingBottom;

// Check against our minimum height
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());

setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
resolveSizeAndState(maxHeight, heightMeasureSpec,
(childState<<MEASURED_HEIGHT_STATE_SHIFT)));

if (matchHeight) {
forceUniformHeight(count, widthMeasureSpec);
}
}

onLayout

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);
}
}
layoutVertical
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
/**
* Position the children during a layout pass if the orientation of this
* LinearLayout is set to {@link #VERTICAL}.
*
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onLayout(boolean, int, int, int, int)
* @param left
* @param top
* @param right
* @param bottom
*
* #IMPORTANT calculate the place with the grivaty;
*/
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;


//start ------children's display zone all --------
int childTop;
int childLeft;

// Where right end of child should go
final int width = right - left;
int childRight = width - mPaddingRight;

// Space available for child
int childSpace = width - paddingLeft - mPaddingRight;

final int count = getVirtualChildCount();

final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;//垂直mask
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;//水平mask

//get start Y position for children
switch (majorGravity) {
case Gravity.BOTTOM:
// mTotalLength contains the padding already
childTop = mPaddingTop + bottom - top - mTotalLength;
break;

// mTotalLength contains the padding already
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
break;

case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
}

// ------start to decide where the child to hold the place (childTop and childLeft)--------
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();

int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
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;
//must:setFrame or any other similar method use (chidLeft,childTop,childWidth,childHeight)
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);

i += getChildrenSkipCount(child, i);
}
}
}
layoutHorizontal
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
/**
STEPS:
#1. get childTotalHeight,getChildRightMost;
#2. get start for Horizontal x position by gravity;
#3. caculate every children's left and top by gravity;
#4. setFrame for every children itself;

* Position the children during a layout pass if the orientation of this
* LinearLayout is set to {@link #HORIZONTAL}.
*
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onLayout(boolean, int, int, int, int)
* @param left
* @param top
* @param right
* @param bottom
* #IMPORTANT:baseLineFunction in the process of layout
*/
void layoutHorizontal(int left, int top, int right, int bottom) {
final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;

int childTop;
int childLeft;

// Where bottom of child should go
final int height = bottom - top;
int childBottom = height - mPaddingBottom;

// Space available for child
int childSpace = height - paddingTop - mPaddingBottom;

final int count = getVirtualChildCount();

final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;

final boolean baselineAligned = mBaselineAligned;

final int[] maxAscent = mMaxAscent;
final int[] maxDescent = mMaxDescent;

final int layoutDirection = getLayoutDirection();
switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
case Gravity.RIGHT:
// mTotalLength contains the padding already
childLeft = mPaddingLeft + right - left - mTotalLength;
break;

case Gravity.CENTER_HORIZONTAL:
// mTotalLength contains the padding already
childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
break;

case Gravity.LEFT:
default:
childLeft = mPaddingLeft;
break;
}

int start = 0;
int dir = 1;
//In case of RTL, start drawing from the last child.
if (isLayoutRtl) {
start = count - 1;
dir = -1;
}

for (int i = 0; i < count; i++) {
int childIndex = start + dir * i;
final View child = getVirtualChildAt(childIndex);

if (child == null) {
childLeft += measureNullChild(childIndex);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
int childBaseline = -1;

final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();

if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
childBaseline = child.getBaseline();
}

int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}

switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.TOP:
childTop = paddingTop + lp.topMargin;
if (childBaseline != -1) {
childTop += maxAscent[INDEX_TOP] - childBaseline;
}
break;

case Gravity.CENTER_VERTICAL:
// Removed support for baseline alignment when layout_gravity or
// gravity == center_vertical. See bug #1038483.
// Keep the code around if we need to re-enable this feature
// if (childBaseline != -1) {
// // Align baselines vertically only if the child is smaller than us
// if (childSpace - childHeight > 0) {
// childTop = paddingTop + (childSpace / 2) - childBaseline;
// } else {
// childTop = paddingTop + (childSpace - childHeight) / 2;
// }
// } else {
childTop = paddingTop + ((childSpace - childHeight) / 2)
+ lp.topMargin - lp.bottomMargin;
break;

case Gravity.BOTTOM:
childTop = childBottom - childHeight - lp.bottomMargin;
if (childBaseline != -1) {
int descent = child.getMeasuredHeight() - childBaseline;
childTop -= (maxDescent[INDEX_BOTTOM] - descent);
}
break;
default:
childTop = paddingTop;
break;
}

if (hasDividerBeforeChildAt(childIndex)) {
childLeft += mDividerWidth;
}

childLeft += lp.leftMargin;
setChildFrame(child, childLeft + getLocationOffset(child), childTop,
childWidth, childHeight);
childLeft += childWidth + lp.rightMargin +
getNextLocationOffset(child);

i += getChildrenSkipCount(child, childIndex);
}
}
}

onDraw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//ref android.R.styleable#LinearLayout_divider
/**
draw for backgroud and ondraw for content;here we just need some dividers;
**/
@Override
protected void onDraw(Canvas canvas) {
if (mDivider == null) {
return;
}

if (mOrientation == VERTICAL) {
drawDividersVertical(canvas);
} else {
drawDividersHorizontal(canvas);
}
}

FrameLayout

onMeasure

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
/**
* # 1.find the biggest width and height from children
* # 2.setMeasureDmension() ,widthMeasureSpec,drawbale,max children size
* # 3.i am a warp_content and my children is match_parent,should reMeasure them
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();

// i am a warp_content
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();

int maxHeight = 0;
int maxWidth = 0;
int childState = 0;

// find the biggest width and height and then add the children with match_parent
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//must:getChildMeasureSpec and child.measure.to get the child's measured size;
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());
// width and height decide by how big the parent is
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}

// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}

//must:resloveSizeAndState
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));

count = mMatchParentChildren.size();

// these counts need remeasure wheter for it's width nor for it's height
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);
//must:use MeasureSpec.makeMeasureSpec to get the spec
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
//must:getChildMeasureSpec (p spec p padding c layoutparam)[p-p-c]
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 {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}

onLayout

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
/**
# 1. gravity ->horizontal: need my left and right and center_horizontal
# gravity->vertical: need my top and bottom and center_vertical
**/

void layoutChildren(int left, int top, int right, int bottom,
boolean forceLeftGravity) {
final int count = getChildCount();

//getPaddingLeftWithForeground() set the value by layout:forground
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
//how to find the childTop if child's lp.gravity is 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;
}
//left ,top,width,height is enough
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}

onDraw

Nothing to do

RelativeLayout

onMeasure

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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/**
* here we ignore baseLineView and RTL
# 1. sortChildren get the result mSortedHorizontalChildren and mSortedVerticalChildren
# 2. measureChildHorizontal or Vertical influnce by relative layout and view desired demision
# 3.i am warpContent height,i should reconside height [child vertical center,center parent, alingn bottom];i am warpContent width,i should reconside width[child horizontal center,center parent ,align right]
# 4.do with layout_gravity ,move me as a single block
# 5. setMeasuredDimension at last as usual

* @param widthMeasureSpec
* @param heightMeasureSpec
*/

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mDirtyHierarchy) {
mDirtyHierarchy = false;
//must :you should sort the children first
sortChildren();
}

int myWidth = -1;
int myHeight = -1;

int width = 0;
int height = 0;

final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

// Record our dimensions if they are known;
if (widthMode != MeasureSpec.UNSPECIFIED) {
myWidth = widthSize;
}

if (heightMode != MeasureSpec.UNSPECIFIED) {
myHeight = heightSize;
}

if (widthMode == MeasureSpec.EXACTLY) {
width = myWidth;
}

if (heightMode == MeasureSpec.EXACTLY) {
height = myHeight;
}

View ignore = null;
int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;

int left = Integer.MAX_VALUE;
int top = Integer.MAX_VALUE;
int right = Integer.MIN_VALUE;
int bottom = Integer.MIN_VALUE;

boolean offsetHorizontalAxis = false;
boolean offsetVerticalAxis = false;

if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
ignore = findViewById(mIgnoreGravity);
}

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

//.............................

View[] views = mSortedHorizontalChildren;
int count = views.length;

for (int i = 0; i < count; i++) {
View child = views[i];
if (child.getVisibility() != GONE) {
//output:Viewgroup.LayoutParams(width,height)->Viewgroup.MarginLayoutParams(add leftmargin......)->Real
//tivelayout.LayoutParams(add mLeft,mTop......)
LayoutParams params = (LayoutParams) child.getLayoutParams();
int[] rules = params.getRules(layoutDirection);

//aqurire params.left and params.right from the rules.
applyHorizontalSizeRules(params, myWidth, rules);

measureChildHorizontal(child, params, myWidth, myHeight);

if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
offsetHorizontalAxis = true;
}
}
}

views = mSortedVerticalChildren;
count = views.length;
final 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();

//aqurire params.top and params.bttom from the rules.
applyVerticalSizeRules(params, myHeight, child.getBaseline());
measureChild(child, params, myWidth, myHeight);
if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
offsetVerticalAxis = true;
}

if (isWrapContentWidth) {
if (isLayoutRtl()) {
//......
} 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);
}
}
}

// Use the top-start-most laid out view as the baseline. RTL offsets are
// applied later, so we can use the left-most edge as the starting edge.
View baselineView = null;
LayoutParams baselineParams = null;
for (int i = 0; i < count; i++) {
//......................
}
mBaselineView = baselineView;

if (isWrapContentWidth) {
// Width already has left padding in it since it was calculated by looking at
// the right of each child view
width += mPaddingRight;

if (mLayoutParams != null && mLayoutParams.width >= 0) {
width = Math.max(width, mLayoutParams.width);
}

width = Math.max(width, getSuggestedMinimumWidth());
//must:last check aginst with widthMeasureSpec
width = resolveSize(width, widthMeasureSpec);

if (offsetHorizontalAxis) {
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
final int[] rules = params.getRules(layoutDirection);
if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
centerHorizontal(child, params, width);
} else if (rules[ALIGN_PARENT_RIGHT] != 0) {
final int childWidth = child.getMeasuredWidth();
params.mLeft = width - mPaddingRight - childWidth;
params.mRight = params.mLeft + childWidth;
}
}
}
}
}

if (isWrapContentHeight) {
// Height already has top padding in it since it was calculated by looking at
// the bottom of each child view
height += mPaddingBottom;

if (mLayoutParams != null && mLayoutParams.height >= 0) {
height = Math.max(height, mLayoutParams.height);
}

height = Math.max(height, getSuggestedMinimumHeight());
//must:last check aginst with widthMeasureSpec
height = resolveSize(height, heightMeasureSpec);

if (offsetVerticalAxis) {
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
final int[] rules = params.getRules(layoutDirection);
if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
centerVertical(child, params, height);
} else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
final int childHeight = child.getMeasuredHeight();
params.mTop = height - mPaddingBottom - childHeight;
params.mBottom = params.mTop + childHeight;
}
}
}
}
}

if (horizontalGravity || verticalGravity) {
final Rect selfBounds = mSelfBounds;
selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
height - mPaddingBottom);

final Rect contentBounds = mContentBounds;
Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
layoutDirection);

final int horizontalOffset = contentBounds.left - left;
final int verticalOffset = contentBounds.top - top;
if (horizontalOffset != 0 || verticalOffset != 0) {
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE && child != ignore) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
if (horizontalGravity) {
params.mLeft += horizontalOffset;
params.mRight += horizontalOffset;
}
if (verticalGravity) {
params.mTop += verticalOffset;
params.mBottom += verticalOffset;
}
}
}
}
}

if (isLayoutRtl()) {
//.......................
}

//must:finally set the width and height
setMeasuredDimension(width, height);
}

onLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
simply layout every child by the values calculate in onMeasure,mLeft,mTop,mRight,mBottom
**/
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// The layout has actually already been performed and the positions
// cached. Apply the cached values to the children.
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);
}
}
}

onDraw

doNothing

总结

Measure递归的起点

1
2
ViewRootImpl:performTraversals()->getRootMeasureSpec()->MeasureSpec.makeMeasureSpec()
->performMeasure()->View:measure()
ViewRoorImpl:getRootMeasureSpec
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @param windowSize
* The available width or height of the window
*
* @param rootDimension
* The layout params for one dimension (width or height) of the
* window.
*
* @return The measure spec to use to measure the root view.
*/
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {

case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
//省略...

}
return measureSpec;
}
MeasureSpec:makeMeasureSpec
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
/**
* Creates a measure specification based on the supplied size and mode.
*
* The mode must always be one of the following:
* <ul>
* <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
* <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
* <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
* </ul>
*
* <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
* implementation was such that the order of arguments did not matter
* and overflow in either value could impact the resulting MeasureSpec.
* {@link android.widget.RelativeLayout} was affected by this bug.
* Apps targeting API levels greater than 17 will get the fixed, more strict
* behavior.</p>
*
* @param size the size of the measure specification
* @param mode the mode of the measure specification
* @return the measure specification based on size and mode
*/
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}

测量常用方法

第一步是获取MeasureSpec

布局的第一步是getMeasureSpec,然后是onMeasure(),然后………..

让child去获取测量宽高
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
//ViewGroup:
/**
* Does the hard part of measureChildren: figuring out the MeasureSpec to
* pass to a particular child. This method figures out the right MeasureSpec
* for one dimension (height or width) of one child view.
*
* The goal is to combine information from our MeasureSpec with the
* LayoutParams of the child to get the best possible results. For example,
* if the this view knows its size (because its MeasureSpec has a mode of
* EXACTLY), and the child has indicated in its LayoutParams that it wants
* to be the same size as the parent, the parent should ask the child to
* layout given an exact size.
*
* @param spec The requirements for this view
* @param padding The padding of this view for the current dimension and
* margins, if applicable
* @param childDimension How big the child wants to be in the current
* dimension
* @return a MeasureSpec integer for the child
*/
//parentSpec;lpadding like parent's padding + child's own margin;lp.width or lp.height
// parentSpec and child's layoutParam result in childSpec
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
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) {
// Parent has imposed an exact size on us
//1.child know exactly how big it wants,the result is exactly
//2.other results influnce by its parent
// except the parent is exactly and the child uses warp_content,the result is at_most
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
和parent给的宽高信息进行综合考虑
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
//View:
/**
* Utility to reconcile a desired size and state, with constraints imposed
* by a MeasureSpec. Will take the desired size, unless a different size
* is imposed by the constraints. The returned value is a compound integer,
* with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and
* optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the
* resulting size is smaller than the size the view wants to be.
*
* @param size How big the view wants to be.
* @param measureSpec Constraints imposed by the parent.
* @param childMeasuredState Size information bit mask for the view's
* children.
* @return Size information bit mask as defined by
* {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
*/
//measuredSize , MeasureSpec from parent , 0
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
final int result;
switch (specMode) {
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
result = size;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
生成最终的宽高信息
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
//View:
/**
* Creates a measure specification based on the supplied size and mode.
*
* The mode must always be one of the following:
* <ul>
* <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
* <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
* <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
* </ul>
*
* <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
* implementation was such that the order of arguments did not matter
* and overflow in either value could impact the resulting MeasureSpec.
* {@link android.widget.RelativeLayout} was affected by this bug.
* Apps targeting API levels greater than 17 will get the fixed, more strict
* behavior.</p>
*
* @param size the size of the measure specification
* @param mode the mode of the measure specification
* @return the measure specification based on size and mode
*/
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
通知影响的children重新测量
存储最终的测量宽高值
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
//View:  

/**
* <p>This method must be called by {@link #onMeasure(int, int)} to store the
* measured width and measured height. Failing to do so will trigger an
* exception at measurement time.</p>
*
* @param measuredWidth The measured width of this view. May be a complex
* bit mask as defined by {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
* @param measuredHeight The measured height of this view. May be a complex
* bit mask as defined by {@link #MEASURED_SIZE_MASK} and
* {@link #MEASURED_STATE_TOO_SMALL}.
*/
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;

measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}

layout之前

从getMeasureSpec到onMeasure,接下来是setFrame,然后才是onLayout,再是…..

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
/**
* Assign a size and position to this view.
*
* This is called from layout.
*
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
* @return true if the new size and position are different than the
* previous ones
* {@hide}
*/
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;

if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}

if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;

// Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN;

int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
//compare current size and the pre one ,to decide whether to notify onSizeChange
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

// Invalidate our old position
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) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= PFLAG_DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}

// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;

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

notifySubtreeAccessibilityStateChangedIfNeeded();
}
return changed;
}

Draw与onDraw

Draw 是用来drawBackground and drawForeground(eg:滚动条)的

onDraw是用来drawContent的