blob: 72f51c91da5ffa2a38711827026580d92b58a031 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Gilles Debunnef5c6eff2010-02-09 19:08:36 -080019import com.android.internal.R;
20
Tor Norbyed9273d62013-05-30 15:59:53 -070021import android.annotation.IntDef;
Scott Kennedy3a1fa102015-03-05 19:15:11 -080022import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
24import android.content.res.TypedArray;
Adam Powellfcca00a2010-11-30 21:26:29 -080025import android.graphics.Canvas;
26import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.util.AttributeSet;
28import android.view.Gravity;
29import android.view.View;
30import android.view.ViewDebug;
31import android.view.ViewGroup;
32import android.widget.RemoteViews.RemoteView;
33
Tor Norbyed9273d62013-05-30 15:59:53 -070034import java.lang.annotation.Retention;
35import java.lang.annotation.RetentionPolicy;
36
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037
38/**
39 * A Layout that arranges its children in a single column or a single row. The direction of
40 * the row can be set by calling {@link #setOrientation(int) setOrientation()}.
41 * You can also specify gravity, which specifies the alignment of all the child elements by
42 * calling {@link #setGravity(int) setGravity()} or specify that specific children
43 * grow to fill up any remaining space in the layout by setting the <em>weight</em> member of
44 * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
45 * The default orientation is horizontal.
46 *
Scott Main4c359b72012-07-24 15:51:27 -070047 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/linear.html">Linear Layout</a>
48 * guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070049 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 * <p>
51 * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
52 * for layout attributes </p>
Romain Guy9295ada2010-06-15 11:33:24 -070053 *
54 * @attr ref android.R.styleable#LinearLayout_baselineAligned
55 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
56 * @attr ref android.R.styleable#LinearLayout_gravity
57 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
58 * @attr ref android.R.styleable#LinearLayout_orientation
59 * @attr ref android.R.styleable#LinearLayout_weightSum
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 */
61@RemoteView
62public class LinearLayout extends ViewGroup {
Tor Norbyed9273d62013-05-30 15:59:53 -070063 /** @hide */
64 @IntDef({HORIZONTAL, VERTICAL})
65 @Retention(RetentionPolicy.SOURCE)
66 public @interface OrientationMode {}
67
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 public static final int HORIZONTAL = 0;
69 public static final int VERTICAL = 1;
70
Tor Norbyed9273d62013-05-30 15:59:53 -070071 /** @hide */
72 @IntDef(flag = true,
73 value = {
74 SHOW_DIVIDER_NONE,
75 SHOW_DIVIDER_BEGINNING,
76 SHOW_DIVIDER_MIDDLE,
77 SHOW_DIVIDER_END
78 })
79 @Retention(RetentionPolicy.SOURCE)
80 public @interface DividerMode {}
81
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 /**
Adam Powellfcca00a2010-11-30 21:26:29 -080083 * Don't show any dividers.
84 */
85 public static final int SHOW_DIVIDER_NONE = 0;
86 /**
87 * Show a divider at the beginning of the group.
88 */
89 public static final int SHOW_DIVIDER_BEGINNING = 1;
90 /**
91 * Show dividers between each item in the group.
92 */
93 public static final int SHOW_DIVIDER_MIDDLE = 2;
94 /**
95 * Show a divider at the end of the group.
96 */
97 public static final int SHOW_DIVIDER_END = 4;
98
99 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 * Whether the children of this layout are baseline aligned. Only applicable
101 * if {@link #mOrientation} is horizontal.
102 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700103 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private boolean mBaselineAligned = true;
105
106 /**
107 * If this layout is part of another layout that is baseline aligned,
108 * use the child at this index as the baseline.
109 *
110 * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
111 * with whether the children of this layout are baseline aligned.
112 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700113 @ViewDebug.ExportedProperty(category = "layout")
Karl Rosaenc1f9c402009-08-12 17:18:33 -0700114 private int mBaselineAlignedChildIndex = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
116 /**
117 * The additional offset to the child's baseline.
118 * We'll calculate the baseline of this layout as we measure vertically; for
119 * horizontal linear layouts, the offset of 0 is appropriate.
120 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700121 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 private int mBaselineChildTop = 0;
123
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700124 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 private int mOrientation;
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700126
Fabrice Di Megliobce84d22011-06-02 15:57:01 -0700127 @ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
128 @ViewDebug.FlagToString(mask = -1,
129 equals = -1, name = "NONE"),
130 @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY,
131 equals = Gravity.NO_GRAVITY,name = "NONE"),
132 @ViewDebug.FlagToString(mask = Gravity.TOP,
133 equals = Gravity.TOP, name = "TOP"),
134 @ViewDebug.FlagToString(mask = Gravity.BOTTOM,
135 equals = Gravity.BOTTOM, name = "BOTTOM"),
136 @ViewDebug.FlagToString(mask = Gravity.LEFT,
137 equals = Gravity.LEFT, name = "LEFT"),
138 @ViewDebug.FlagToString(mask = Gravity.RIGHT,
139 equals = Gravity.RIGHT, name = "RIGHT"),
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700140 @ViewDebug.FlagToString(mask = Gravity.START,
141 equals = Gravity.START, name = "START"),
142 @ViewDebug.FlagToString(mask = Gravity.END,
143 equals = Gravity.END, name = "END"),
Fabrice Di Megliobce84d22011-06-02 15:57:01 -0700144 @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
145 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
146 @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
147 equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"),
148 @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL,
149 equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"),
150 @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL,
151 equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"),
152 @ViewDebug.FlagToString(mask = Gravity.CENTER,
153 equals = Gravity.CENTER, name = "CENTER"),
154 @ViewDebug.FlagToString(mask = Gravity.FILL,
155 equals = Gravity.FILL, name = "FILL"),
Fabrice Di Meglioc46f7ff2011-06-06 18:23:10 -0700156 @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
157 equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
Jon Miranda4597e982014-07-29 07:25:49 -0700158 }, formatToHexString = true)
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700159 private int mGravity = Gravity.START | Gravity.TOP;
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700160
161 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private int mTotalLength;
163
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700164 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 private float mWeightSum;
166
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700167 @ViewDebug.ExportedProperty(category = "layout")
Romain Guy5b1b2412010-01-21 19:09:51 -0800168 private boolean mUseLargestChild;
169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 private int[] mMaxAscent;
171 private int[] mMaxDescent;
172
173 private static final int VERTICAL_GRAVITY_COUNT = 4;
174
175 private static final int INDEX_CENTER_VERTICAL = 0;
176 private static final int INDEX_TOP = 1;
177 private static final int INDEX_BOTTOM = 2;
Romain Guy5b1b2412010-01-21 19:09:51 -0800178 private static final int INDEX_FILL = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179
Adam Powellfcca00a2010-11-30 21:26:29 -0800180 private Drawable mDivider;
181 private int mDividerWidth;
182 private int mDividerHeight;
183 private int mShowDividers;
184 private int mDividerPadding;
185
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 public LinearLayout(Context context) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700187 this(context, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 }
189
Scott Kennedy3a1fa102015-03-05 19:15:11 -0800190 public LinearLayout(Context context, @Nullable AttributeSet attrs) {
Romain Guy9295ada2010-06-15 11:33:24 -0700191 this(context, attrs, 0);
192 }
193
Scott Kennedy3a1fa102015-03-05 19:15:11 -0800194 public LinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
Alan Viverette617feb92013-09-09 18:09:13 -0700195 this(context, attrs, defStyleAttr, 0);
196 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197
Alan Viverette617feb92013-09-09 18:09:13 -0700198 public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
199 super(context, attrs, defStyleAttr, defStyleRes);
200
201 final TypedArray a = context.obtainStyledAttributes(
202 attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203
204 int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);
205 if (index >= 0) {
206 setOrientation(index);
207 }
208
209 index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);
210 if (index >= 0) {
211 setGravity(index);
212 }
213
214 boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);
215 if (!baselineAligned) {
216 setBaselineAligned(baselineAligned);
217 }
218
219 mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);
220
221 mBaselineAlignedChildIndex =
222 a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
223
Romain Guy9295ada2010-06-15 11:33:24 -0700224 mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
Romain Guy5b1b2412010-01-21 19:09:51 -0800225
Adam Powellfcca00a2010-11-30 21:26:29 -0800226 setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
227 mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
228 mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 a.recycle();
231 }
232
233 /**
Adam Powellfcca00a2010-11-30 21:26:29 -0800234 * Set how dividers should be shown between items in this layout
235 *
236 * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
237 * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
238 * or {@link #SHOW_DIVIDER_NONE} to show no dividers.
239 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700240 public void setShowDividers(@DividerMode int showDividers) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800241 if (showDividers != mShowDividers) {
242 requestLayout();
243 }
244 mShowDividers = showDividers;
245 }
246
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700247 @Override
248 public boolean shouldDelayChildPressedState() {
249 return false;
250 }
251
Adam Powellfcca00a2010-11-30 21:26:29 -0800252 /**
253 * @return A flag set indicating how dividers should be shown around items.
254 * @see #setShowDividers(int)
255 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700256 @DividerMode
Adam Powellfcca00a2010-11-30 21:26:29 -0800257 public int getShowDividers() {
258 return mShowDividers;
259 }
260
261 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700262 * @return the divider Drawable that will divide each item.
263 *
264 * @see #setDividerDrawable(Drawable)
265 *
266 * @attr ref android.R.styleable#LinearLayout_divider
267 */
268 public Drawable getDividerDrawable() {
269 return mDivider;
270 }
271
272 /**
Adam Powellfcca00a2010-11-30 21:26:29 -0800273 * Set a drawable to be used as a divider between items.
Philip Milne1018fb42012-03-13 12:00:04 -0700274 *
Adam Powellfcca00a2010-11-30 21:26:29 -0800275 * @param divider Drawable that will divide each item.
Philip Milne1018fb42012-03-13 12:00:04 -0700276 *
Adam Powellfcca00a2010-11-30 21:26:29 -0800277 * @see #setShowDividers(int)
Philip Milne1018fb42012-03-13 12:00:04 -0700278 *
279 * @attr ref android.R.styleable#LinearLayout_divider
Adam Powellfcca00a2010-11-30 21:26:29 -0800280 */
281 public void setDividerDrawable(Drawable divider) {
282 if (divider == mDivider) {
283 return;
284 }
285 mDivider = divider;
286 if (divider != null) {
287 mDividerWidth = divider.getIntrinsicWidth();
288 mDividerHeight = divider.getIntrinsicHeight();
289 } else {
290 mDividerWidth = 0;
291 mDividerHeight = 0;
292 }
293 setWillNotDraw(divider == null);
294 requestLayout();
295 }
296
Adam Powell696cba52011-03-29 10:38:16 -0700297 /**
298 * Set padding displayed on both ends of dividers.
299 *
300 * @param padding Padding value in pixels that will be applied to each end
301 *
302 * @see #setShowDividers(int)
303 * @see #setDividerDrawable(Drawable)
304 * @see #getDividerPadding()
305 */
306 public void setDividerPadding(int padding) {
307 mDividerPadding = padding;
308 }
309
310 /**
311 * Get the padding size used to inset dividers in pixels
312 *
313 * @see #setShowDividers(int)
314 * @see #setDividerDrawable(Drawable)
315 * @see #setDividerPadding(int)
316 */
317 public int getDividerPadding() {
318 return mDividerPadding;
319 }
320
Adam Powell640a66e2011-04-29 10:18:53 -0700321 /**
322 * Get the width of the current divider drawable.
323 *
324 * @hide Used internally by framework.
325 */
326 public int getDividerWidth() {
327 return mDividerWidth;
328 }
329
Adam Powellfcca00a2010-11-30 21:26:29 -0800330 @Override
331 protected void onDraw(Canvas canvas) {
332 if (mDivider == null) {
333 return;
334 }
335
336 if (mOrientation == VERTICAL) {
337 drawDividersVertical(canvas);
338 } else {
339 drawDividersHorizontal(canvas);
340 }
341 }
342
343 void drawDividersVertical(Canvas canvas) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800344 final int count = getVirtualChildCount();
Adam Powellfcca00a2010-11-30 21:26:29 -0800345 for (int i = 0; i < count; i++) {
346 final View child = getVirtualChildAt(i);
347
Adam Powell35aecd52011-07-01 13:43:49 -0700348 if (child != null && child.getVisibility() != GONE) {
Adam Powell696cba52011-03-29 10:38:16 -0700349 if (hasDividerBeforeChildAt(i)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700350 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Vladimir Baryshnikov20761fc2012-01-17 14:59:48 -0800351 final int top = child.getTop() - lp.topMargin - mDividerHeight;
Adam Powellfcca00a2010-11-30 21:26:29 -0800352 drawHorizontalDivider(canvas, top);
Adam Powellfcca00a2010-11-30 21:26:29 -0800353 }
Adam Powellfcca00a2010-11-30 21:26:29 -0800354 }
355 }
356
Adam Powell696cba52011-03-29 10:38:16 -0700357 if (hasDividerBeforeChildAt(count)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700358 final View child = getVirtualChildAt(count - 1);
359 int bottom = 0;
360 if (child == null) {
361 bottom = getHeight() - getPaddingBottom() - mDividerHeight;
362 } else {
363 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
364 bottom = child.getBottom() + lp.bottomMargin;
365 }
366 drawHorizontalDivider(canvas, bottom);
Adam Powellfcca00a2010-11-30 21:26:29 -0800367 }
368 }
369
370 void drawDividersHorizontal(Canvas canvas) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800371 final int count = getVirtualChildCount();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700372 final boolean isLayoutRtl = isLayoutRtl();
Adam Powellfcca00a2010-11-30 21:26:29 -0800373 for (int i = 0; i < count; i++) {
374 final View child = getVirtualChildAt(i);
375
Adam Powell35aecd52011-07-01 13:43:49 -0700376 if (child != null && child.getVisibility() != GONE) {
Adam Powell696cba52011-03-29 10:38:16 -0700377 if (hasDividerBeforeChildAt(i)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700378 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700379 final int position;
380 if (isLayoutRtl) {
381 position = child.getRight() + lp.rightMargin;
382 } else {
383 position = child.getLeft() - lp.leftMargin - mDividerWidth;
384 }
385 drawVerticalDivider(canvas, position);
Adam Powellfcca00a2010-11-30 21:26:29 -0800386 }
Adam Powellfcca00a2010-11-30 21:26:29 -0800387 }
388 }
389
Adam Powell696cba52011-03-29 10:38:16 -0700390 if (hasDividerBeforeChildAt(count)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700391 final View child = getVirtualChildAt(count - 1);
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700392 int position;
Adam Powell35aecd52011-07-01 13:43:49 -0700393 if (child == null) {
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700394 if (isLayoutRtl) {
395 position = getPaddingLeft();
396 } else {
397 position = getWidth() - getPaddingRight() - mDividerWidth;
398 }
Adam Powell35aecd52011-07-01 13:43:49 -0700399 } else {
400 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700401 if (isLayoutRtl) {
402 position = child.getLeft() - lp.leftMargin - mDividerWidth;
403 } else {
404 position = child.getRight() + lp.rightMargin;
405 }
Adam Powell35aecd52011-07-01 13:43:49 -0700406 }
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700407 drawVerticalDivider(canvas, position);
Adam Powellfcca00a2010-11-30 21:26:29 -0800408 }
409 }
410
411 void drawHorizontalDivider(Canvas canvas, int top) {
412 mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
413 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
414 mDivider.draw(canvas);
415 }
416
417 void drawVerticalDivider(Canvas canvas, int left) {
418 mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
419 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
420 mDivider.draw(canvas);
421 }
422
423 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 * <p>Indicates whether widgets contained within this layout are aligned
425 * on their baseline or not.</p>
426 *
427 * @return true when widgets are baseline-aligned, false otherwise
428 */
429 public boolean isBaselineAligned() {
430 return mBaselineAligned;
431 }
432
433 /**
434 * <p>Defines whether widgets contained in this layout are
435 * baseline-aligned or not.</p>
436 *
437 * @param baselineAligned true to align widgets on their baseline,
438 * false otherwise
439 *
440 * @attr ref android.R.styleable#LinearLayout_baselineAligned
441 */
442 @android.view.RemotableViewMethod
443 public void setBaselineAligned(boolean baselineAligned) {
444 mBaselineAligned = baselineAligned;
445 }
446
Romain Guy9295ada2010-06-15 11:33:24 -0700447 /**
448 * When true, all children with a weight will be considered having
449 * the minimum size of the largest child. If false, all children are
450 * measured normally.
451 *
452 * @return True to measure children with a weight using the minimum
453 * size of the largest child, false otherwise.
Philip Milne1018fb42012-03-13 12:00:04 -0700454 *
455 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
Romain Guy9295ada2010-06-15 11:33:24 -0700456 */
457 public boolean isMeasureWithLargestChildEnabled() {
458 return mUseLargestChild;
459 }
460
461 /**
462 * When set to true, all children with a weight will be considered having
463 * the minimum size of the largest child. If false, all children are
464 * measured normally.
465 *
466 * Disabled by default.
467 *
468 * @param enabled True to measure children with a weight using the
469 * minimum size of the largest child, false otherwise.
Philip Milne1018fb42012-03-13 12:00:04 -0700470 *
471 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
Romain Guy9295ada2010-06-15 11:33:24 -0700472 */
473 @android.view.RemotableViewMethod
474 public void setMeasureWithLargestChildEnabled(boolean enabled) {
475 mUseLargestChild = enabled;
476 }
477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 @Override
479 public int getBaseline() {
480 if (mBaselineAlignedChildIndex < 0) {
481 return super.getBaseline();
482 }
483
484 if (getChildCount() <= mBaselineAlignedChildIndex) {
485 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
486 + "set to an index that is out of bounds.");
487 }
488
489 final View child = getChildAt(mBaselineAlignedChildIndex);
490 final int childBaseline = child.getBaseline();
491
492 if (childBaseline == -1) {
493 if (mBaselineAlignedChildIndex == 0) {
494 // this is just the default case, safe to return -1
495 return -1;
496 }
497 // the user picked an index that points to something that doesn't
498 // know how to calculate its baseline.
499 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
500 + "points to a View that doesn't know how to get its baseline.");
501 }
502
503 // TODO: This should try to take into account the virtual offsets
504 // (See getNextLocationOffset and getLocationOffset)
505 // We should add to childTop:
506 // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
507 // and also add:
508 // getLocationOffset(child)
509 int childTop = mBaselineChildTop;
510
511 if (mOrientation == VERTICAL) {
512 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
513 if (majorGravity != Gravity.TOP) {
514 switch (majorGravity) {
515 case Gravity.BOTTOM:
516 childTop = mBottom - mTop - mPaddingBottom - mTotalLength;
517 break;
518
519 case Gravity.CENTER_VERTICAL:
520 childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) -
521 mTotalLength) / 2;
522 break;
523 }
524 }
525 }
526
527 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
528 return childTop + lp.topMargin + childBaseline;
529 }
530
531 /**
532 * @return The index of the child that will be used if this layout is
533 * part of a larger layout that is baseline aligned, or -1 if none has
534 * been set.
535 */
536 public int getBaselineAlignedChildIndex() {
537 return mBaselineAlignedChildIndex;
538 }
539
540 /**
541 * @param i The index of the child that will be used if this layout is
542 * part of a larger layout that is baseline aligned.
543 *
544 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
545 */
546 @android.view.RemotableViewMethod
547 public void setBaselineAlignedChildIndex(int i) {
548 if ((i < 0) || (i >= getChildCount())) {
549 throw new IllegalArgumentException("base aligned child index out "
550 + "of range (0, " + getChildCount() + ")");
551 }
552 mBaselineAlignedChildIndex = i;
553 }
554
555 /**
556 * <p>Returns the view at the specified index. This method can be overriden
557 * to take into account virtual children. Refer to
558 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
559 * for an example.</p>
560 *
561 * @param index the child's index
562 * @return the child at the specified index
563 */
564 View getVirtualChildAt(int index) {
565 return getChildAt(index);
566 }
567
568 /**
569 * <p>Returns the virtual number of children. This number might be different
570 * than the actual number of children if the layout can hold virtual
571 * children. Refer to
572 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
573 * for an example.</p>
574 *
575 * @return the virtual number of children
576 */
577 int getVirtualChildCount() {
578 return getChildCount();
579 }
580
581 /**
582 * Returns the desired weights sum.
583 *
584 * @return A number greater than 0.0f if the weight sum is defined, or
585 * a number lower than or equals to 0.0f if not weight sum is
586 * to be used.
587 */
588 public float getWeightSum() {
589 return mWeightSum;
590 }
591
592 /**
593 * Defines the desired weights sum. If unspecified the weights sum is computed
594 * at layout time by adding the layout_weight of each child.
595 *
596 * This can be used for instance to give a single child 50% of the total
597 * available space by giving it a layout_weight of 0.5 and setting the
598 * weightSum to 1.0.
599 *
600 * @param weightSum a number greater than 0.0f, or a number lower than or equals
601 * to 0.0f if the weight sum should be computed from the children's
602 * layout_weight
603 */
604 @android.view.RemotableViewMethod
605 public void setWeightSum(float weightSum) {
606 mWeightSum = Math.max(0.0f, weightSum);
607 }
608
609 @Override
610 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
611 if (mOrientation == VERTICAL) {
612 measureVertical(widthMeasureSpec, heightMeasureSpec);
613 } else {
614 measureHorizontal(widthMeasureSpec, heightMeasureSpec);
615 }
616 }
617
618 /**
Adam Powell696cba52011-03-29 10:38:16 -0700619 * Determines where to position dividers between children.
620 *
621 * @param childIndex Index of child to check for preceding divider
622 * @return true if there should be a divider before the child at childIndex
623 * @hide Pending API consideration. Currently only used internally by the system.
624 */
625 protected boolean hasDividerBeforeChildAt(int childIndex) {
626 if (childIndex == 0) {
627 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
628 } else if (childIndex == getChildCount()) {
629 return (mShowDividers & SHOW_DIVIDER_END) != 0;
Adam Powellbf68f832011-06-20 18:23:30 -0700630 } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
631 boolean hasVisibleViewBefore = false;
632 for (int i = childIndex - 1; i >= 0; i--) {
633 if (getChildAt(i).getVisibility() != GONE) {
634 hasVisibleViewBefore = true;
635 break;
636 }
637 }
638 return hasVisibleViewBefore;
Adam Powell696cba52011-03-29 10:38:16 -0700639 }
Adam Powellbf68f832011-06-20 18:23:30 -0700640 return false;
Adam Powell696cba52011-03-29 10:38:16 -0700641 }
642
643 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 * Measures the children when the orientation of this LinearLayout is set
645 * to {@link #VERTICAL}.
646 *
647 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
648 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
649 *
650 * @see #getOrientation()
651 * @see #setOrientation(int)
652 * @see #onMeasure(int, int)
653 */
654 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
655 mTotalLength = 0;
656 int maxWidth = 0;
Dianne Hackborn189ee182010-12-02 21:48:53 -0800657 int childState = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 int alternativeMaxWidth = 0;
659 int weightedMaxWidth = 0;
660 boolean allFillParent = true;
661 float totalWeight = 0;
662
663 final int count = getVirtualChildCount();
664
665 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
666 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
667
668 boolean matchWidth = false;
Alan Viverette65be9cc2014-02-24 18:21:09 -0800669 boolean skippedMeasure = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670
671 final int baselineChildIndex = mBaselineAlignedChildIndex;
Romain Guy5b1b2412010-01-21 19:09:51 -0800672 final boolean useLargestChild = mUseLargestChild;
673
674 int largestChildHeight = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675
676 // See how tall everyone is. Also remember max width.
677 for (int i = 0; i < count; ++i) {
678 final View child = getVirtualChildAt(i);
679
680 if (child == null) {
681 mTotalLength += measureNullChild(i);
682 continue;
683 }
684
685 if (child.getVisibility() == View.GONE) {
686 i += getChildrenSkipCount(child, i);
687 continue;
688 }
689
Adam Powell696cba52011-03-29 10:38:16 -0700690 if (hasDividerBeforeChildAt(i)) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800691 mTotalLength += mDividerHeight;
692 }
693
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
695
696 totalWeight += lp.weight;
697
698 if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
699 // Optimization: don't bother measuring children who are going to use
700 // leftover space. These views will get measured again down below if
701 // there is any leftover space.
Romain Guy053b4802010-02-05 15:34:33 -0800702 final int totalLength = mTotalLength;
703 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
Alan Viverette65be9cc2014-02-24 18:21:09 -0800704 skippedMeasure = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 } else {
Romain Guy5b1b2412010-01-21 19:09:51 -0800706 int oldHeight = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707
Romain Guy5b1b2412010-01-21 19:09:51 -0800708 if (lp.height == 0 && lp.weight > 0) {
Gilles Debunnef5c6eff2010-02-09 19:08:36 -0800709 // heightMode is either UNSPECIFIED or AT_MOST, and this
710 // child wanted to stretch to fill available space.
711 // Translate that to WRAP_CONTENT so that it does not end up
712 // with a height of 0
713 oldHeight = 0;
714 lp.height = LayoutParams.WRAP_CONTENT;
Romain Guy5b1b2412010-01-21 19:09:51 -0800715 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716
Gilles Debunnef5c6eff2010-02-09 19:08:36 -0800717 // Determine how big this child would like to be. If this or
Romain Guy5b1b2412010-01-21 19:09:51 -0800718 // previous children have given a weight, then we allow it to
719 // use all available space (and we will shrink things later
720 // if needed).
721 measureChildBeforeLayout(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 child, i, widthMeasureSpec, 0, heightMeasureSpec,
723 totalWeight == 0 ? mTotalLength : 0);
724
Romain Guy5b1b2412010-01-21 19:09:51 -0800725 if (oldHeight != Integer.MIN_VALUE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 lp.height = oldHeight;
Romain Guy5b1b2412010-01-21 19:09:51 -0800727 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728
Romain Guy5b1b2412010-01-21 19:09:51 -0800729 final int childHeight = child.getMeasuredHeight();
Romain Guy053b4802010-02-05 15:34:33 -0800730 final int totalLength = mTotalLength;
731 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
732 lp.bottomMargin + getNextLocationOffset(child));
Romain Guy5b1b2412010-01-21 19:09:51 -0800733
734 if (useLargestChild) {
735 largestChildHeight = Math.max(childHeight, largestChildHeight);
736 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 }
738
739 /**
740 * If applicable, compute the additional offset to the child's baseline
741 * we'll need later when asked {@link #getBaseline}.
742 */
743 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
744 mBaselineChildTop = mTotalLength;
745 }
746
747 // if we are trying to use a child index for our baseline, the above
748 // book keeping only works if there are no children above it with
749 // weight. fail fast to aid the developer.
750 if (i < baselineChildIndex && lp.weight > 0) {
751 throw new RuntimeException("A child of LinearLayout with index "
752 + "less than mBaselineAlignedChildIndex has weight > 0, which "
753 + "won't work. Either remove the weight, or don't set "
754 + "mBaselineAlignedChildIndex.");
755 }
756
757 boolean matchWidthLocally = false;
Romain Guy980a9382010-01-08 15:06:28 -0800758 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 // The width of the linear layout will scale, and at least one
760 // child said it wanted to match our width. Set a flag
761 // indicating that we need to remeasure at least that view when
762 // we know our width.
763 matchWidth = true;
764 matchWidthLocally = true;
765 }
766
767 final int margin = lp.leftMargin + lp.rightMargin;
768 final int measuredWidth = child.getMeasuredWidth() + margin;
769 maxWidth = Math.max(maxWidth, measuredWidth);
Dianne Hackborn189ee182010-12-02 21:48:53 -0800770 childState = combineMeasuredStates(childState, child.getMeasuredState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771
Romain Guy980a9382010-01-08 15:06:28 -0800772 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 if (lp.weight > 0) {
774 /*
775 * Widths of weighted Views are bogus if we end up
776 * remeasuring, so keep them separate.
777 */
778 weightedMaxWidth = Math.max(weightedMaxWidth,
779 matchWidthLocally ? margin : measuredWidth);
780 } else {
781 alternativeMaxWidth = Math.max(alternativeMaxWidth,
782 matchWidthLocally ? margin : measuredWidth);
783 }
784
785 i += getChildrenSkipCount(child, i);
786 }
Romain Guy5b1b2412010-01-21 19:09:51 -0800787
Adam Powell696cba52011-03-29 10:38:16 -0700788 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800789 mTotalLength += mDividerHeight;
790 }
791
Adam Powellf8ac6b72011-05-23 18:14:09 -0700792 if (useLargestChild &&
793 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
Romain Guy5b1b2412010-01-21 19:09:51 -0800794 mTotalLength = 0;
795
796 for (int i = 0; i < count; ++i) {
797 final View child = getVirtualChildAt(i);
798
799 if (child == null) {
800 mTotalLength += measureNullChild(i);
801 continue;
802 }
803
804 if (child.getVisibility() == GONE) {
805 i += getChildrenSkipCount(child, i);
806 continue;
807 }
808
809 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
810 child.getLayoutParams();
Romain Guy053b4802010-02-05 15:34:33 -0800811 // Account for negative margins
812 final int totalLength = mTotalLength;
813 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
814 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
Romain Guy5b1b2412010-01-21 19:09:51 -0800815 }
816 }
817
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 // Add in our padding
819 mTotalLength += mPaddingTop + mPaddingBottom;
820
821 int heightSize = mTotalLength;
822
823 // Check against our minimum height
824 heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
825
826 // Reconcile our calculated size with the heightMeasureSpec
Dianne Hackborn189ee182010-12-02 21:48:53 -0800827 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
828 heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829
830 // Either expand children with weight to take up available space or
Alan Viverette65be9cc2014-02-24 18:21:09 -0800831 // shrink them if they extend beyond our current bounds. If we skipped
832 // measurement on any children, we need to measure them now.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 int delta = heightSize - mTotalLength;
Alan Viverette65be9cc2014-02-24 18:21:09 -0800834 if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
836
837 mTotalLength = 0;
838
839 for (int i = 0; i < count; ++i) {
840 final View child = getVirtualChildAt(i);
841
842 if (child.getVisibility() == View.GONE) {
843 continue;
844 }
845
846 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
847
848 float childExtra = lp.weight;
849 if (childExtra > 0) {
850 // Child said it could absorb extra space -- give him his share
851 int share = (int) (childExtra * delta / weightSum);
852 weightSum -= childExtra;
853 delta -= share;
854
855 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
856 mPaddingLeft + mPaddingRight +
857 lp.leftMargin + lp.rightMargin, lp.width);
858
859 // TODO: Use a field like lp.isMeasured to figure out if this
860 // child has been previously measured
861 if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
862 // child was measured once already above...
863 // base new measurement on stored values
864 int childHeight = child.getMeasuredHeight() + share;
865 if (childHeight < 0) {
866 childHeight = 0;
867 }
868
869 child.measure(childWidthMeasureSpec,
870 MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
871 } else {
872 // child was skipped in the loop above.
873 // Measure for this first time here
874 child.measure(childWidthMeasureSpec,
875 MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
876 MeasureSpec.EXACTLY));
877 }
Dianne Hackborn189ee182010-12-02 21:48:53 -0800878
879 // Child may now not fit in vertical dimension.
880 childState = combineMeasuredStates(childState, child.getMeasuredState()
881 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 }
883
884 final int margin = lp.leftMargin + lp.rightMargin;
885 final int measuredWidth = child.getMeasuredWidth() + margin;
886 maxWidth = Math.max(maxWidth, measuredWidth);
887
888 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
Romain Guy980a9382010-01-08 15:06:28 -0800889 lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890
891 alternativeMaxWidth = Math.max(alternativeMaxWidth,
892 matchWidthLocally ? margin : measuredWidth);
893
Romain Guy980a9382010-01-08 15:06:28 -0800894 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895
Romain Guy053b4802010-02-05 15:34:33 -0800896 final int totalLength = mTotalLength;
897 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
898 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 }
900
901 // Add in our padding
Romain Guy053b4802010-02-05 15:34:33 -0800902 mTotalLength += mPaddingTop + mPaddingBottom;
903 // TODO: Should we recompute the heightSpec based on the new total length?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 } else {
905 alternativeMaxWidth = Math.max(alternativeMaxWidth,
906 weightedMaxWidth);
Adam Powellf8ac6b72011-05-23 18:14:09 -0700907
908
909 // We have no limit, so make all weighted views as tall as the largest child.
910 // Children will have already been measured once.
Adam Powelleabc9192012-05-04 14:49:46 -0700911 if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
Adam Powellf8ac6b72011-05-23 18:14:09 -0700912 for (int i = 0; i < count; i++) {
913 final View child = getVirtualChildAt(i);
914
915 if (child == null || child.getVisibility() == View.GONE) {
916 continue;
917 }
918
919 final LinearLayout.LayoutParams lp =
920 (LinearLayout.LayoutParams) child.getLayoutParams();
921
922 float childExtra = lp.weight;
923 if (childExtra > 0) {
924 child.measure(
925 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
926 MeasureSpec.EXACTLY),
927 MeasureSpec.makeMeasureSpec(largestChildHeight,
928 MeasureSpec.EXACTLY));
929 }
930 }
931 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 }
933
934 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
935 maxWidth = alternativeMaxWidth;
936 }
937
938 maxWidth += mPaddingLeft + mPaddingRight;
939
940 // Check against our minimum width
941 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
942
Dianne Hackborn189ee182010-12-02 21:48:53 -0800943 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
944 heightSizeAndState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945
946 if (matchWidth) {
947 forceUniformWidth(count, heightMeasureSpec);
948 }
949 }
950
951 private void forceUniformWidth(int count, int heightMeasureSpec) {
952 // Pretend that the linear layout has an exact size.
953 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
954 MeasureSpec.EXACTLY);
955 for (int i = 0; i< count; ++i) {
956 final View child = getVirtualChildAt(i);
957 if (child.getVisibility() != GONE) {
958 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
959
Romain Guy980a9382010-01-08 15:06:28 -0800960 if (lp.width == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 // Temporarily force children to reuse their old measured height
962 // FIXME: this may not be right for something like wrapping text?
963 int oldHeight = lp.height;
964 lp.height = child.getMeasuredHeight();
965
966 // Remeasue with new dimensions
967 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
968 lp.height = oldHeight;
969 }
970 }
971 }
972 }
973
974 /**
975 * Measures the children when the orientation of this LinearLayout is set
976 * to {@link #HORIZONTAL}.
977 *
978 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
979 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
980 *
981 * @see #getOrientation()
982 * @see #setOrientation(int)
983 * @see #onMeasure(int, int)
984 */
985 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
986 mTotalLength = 0;
987 int maxHeight = 0;
Dianne Hackborn189ee182010-12-02 21:48:53 -0800988 int childState = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 int alternativeMaxHeight = 0;
990 int weightedMaxHeight = 0;
991 boolean allFillParent = true;
992 float totalWeight = 0;
993
994 final int count = getVirtualChildCount();
995
996 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
997 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
998
999 boolean matchHeight = false;
Alan Viverette65be9cc2014-02-24 18:21:09 -08001000 boolean skippedMeasure = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001
1002 if (mMaxAscent == null || mMaxDescent == null) {
1003 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
1004 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
1005 }
1006
1007 final int[] maxAscent = mMaxAscent;
1008 final int[] maxDescent = mMaxDescent;
1009
1010 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1011 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1012
1013 final boolean baselineAligned = mBaselineAligned;
Romain Guy5b1b2412010-01-21 19:09:51 -08001014 final boolean useLargestChild = mUseLargestChild;
Romain Guyc3520902010-02-25 11:01:01 -08001015
1016 final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
Romain Guy5b1b2412010-01-21 19:09:51 -08001017
1018 int largestChildWidth = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019
1020 // See how wide everyone is. Also remember max height.
1021 for (int i = 0; i < count; ++i) {
1022 final View child = getVirtualChildAt(i);
1023
1024 if (child == null) {
1025 mTotalLength += measureNullChild(i);
1026 continue;
1027 }
1028
1029 if (child.getVisibility() == GONE) {
1030 i += getChildrenSkipCount(child, i);
1031 continue;
1032 }
1033
Adam Powell696cba52011-03-29 10:38:16 -07001034 if (hasDividerBeforeChildAt(i)) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001035 mTotalLength += mDividerWidth;
1036 }
1037
Romain Guy5b1b2412010-01-21 19:09:51 -08001038 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1039 child.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040
1041 totalWeight += lp.weight;
1042
1043 if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
1044 // Optimization: don't bother measuring children who are going to use
1045 // leftover space. These views will get measured again down below if
1046 // there is any leftover space.
Romain Guyc3520902010-02-25 11:01:01 -08001047 if (isExactly) {
1048 mTotalLength += lp.leftMargin + lp.rightMargin;
1049 } else {
1050 final int totalLength = mTotalLength;
1051 mTotalLength = Math.max(totalLength, totalLength +
1052 lp.leftMargin + lp.rightMargin);
1053 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054
1055 // Baseline alignment requires to measure widgets to obtain the
Romain Guy6c5664a2010-02-24 18:55:22 -08001056 // baseline offset (in particular for TextViews). The following
1057 // defeats the optimization mentioned above. Allow the child to
1058 // use as much space as it wants because we can shrink things
1059 // later (and re-measure).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 if (baselineAligned) {
Filip Gruszczynskib6824bf2015-04-13 09:16:25 -07001061 final int freeWidthSpec = MeasureSpec.makeMeasureSpec(
1062 MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);
1063 final int freeHeightSpec = MeasureSpec.makeMeasureSpec(
1064 MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);
1065 child.measure(freeWidthSpec, freeHeightSpec);
Alan Viverette65be9cc2014-02-24 18:21:09 -08001066 } else {
1067 skippedMeasure = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 }
1069 } else {
1070 int oldWidth = Integer.MIN_VALUE;
1071
1072 if (lp.width == 0 && lp.weight > 0) {
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08001073 // widthMode is either UNSPECIFIED or AT_MOST, and this
1074 // child
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 // wanted to stretch to fill available space. Translate that to
1076 // WRAP_CONTENT so that it does not end up with a width of 0
1077 oldWidth = 0;
1078 lp.width = LayoutParams.WRAP_CONTENT;
1079 }
1080
1081 // Determine how big this child would like to be. If this or
1082 // previous children have given a weight, then we allow it to
1083 // use all available space (and we will shrink things later
1084 // if needed).
1085 measureChildBeforeLayout(child, i, widthMeasureSpec,
1086 totalWeight == 0 ? mTotalLength : 0,
1087 heightMeasureSpec, 0);
1088
1089 if (oldWidth != Integer.MIN_VALUE) {
1090 lp.width = oldWidth;
1091 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001092
1093 final int childWidth = child.getMeasuredWidth();
Romain Guyc3520902010-02-25 11:01:01 -08001094 if (isExactly) {
1095 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
1096 getNextLocationOffset(child);
1097 } else {
1098 final int totalLength = mTotalLength;
1099 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
1100 lp.rightMargin + getNextLocationOffset(child));
1101 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001102
1103 if (useLargestChild) {
1104 largestChildWidth = Math.max(childWidth, largestChildWidth);
1105 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 }
1107
1108 boolean matchHeightLocally = false;
Romain Guy980a9382010-01-08 15:06:28 -08001109 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 // The height of the linear layout will scale, and at least one
1111 // child said it wanted to match our height. Set a flag indicating that
1112 // we need to remeasure at least that view when we know our height.
1113 matchHeight = true;
1114 matchHeightLocally = true;
1115 }
1116
1117 final int margin = lp.topMargin + lp.bottomMargin;
1118 final int childHeight = child.getMeasuredHeight() + margin;
Dianne Hackborn189ee182010-12-02 21:48:53 -08001119 childState = combineMeasuredStates(childState, child.getMeasuredState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120
1121 if (baselineAligned) {
1122 final int childBaseline = child.getBaseline();
1123 if (childBaseline != -1) {
1124 // Translates the child's vertical gravity into an index
1125 // in the range 0..VERTICAL_GRAVITY_COUNT
1126 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1127 & Gravity.VERTICAL_GRAVITY_MASK;
1128 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1129 & ~Gravity.AXIS_SPECIFIED) >> 1;
1130
1131 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1132 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
1133 }
1134 }
1135
1136 maxHeight = Math.max(maxHeight, childHeight);
1137
Romain Guy980a9382010-01-08 15:06:28 -08001138 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 if (lp.weight > 0) {
1140 /*
1141 * Heights of weighted Views are bogus if we end up
1142 * remeasuring, so keep them separate.
1143 */
1144 weightedMaxHeight = Math.max(weightedMaxHeight,
1145 matchHeightLocally ? margin : childHeight);
1146 } else {
1147 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1148 matchHeightLocally ? margin : childHeight);
1149 }
1150
1151 i += getChildrenSkipCount(child, i);
1152 }
1153
Adam Powell696cba52011-03-29 10:38:16 -07001154 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001155 mTotalLength += mDividerWidth;
1156 }
1157
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1159 // the most common case
1160 if (maxAscent[INDEX_TOP] != -1 ||
1161 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1162 maxAscent[INDEX_BOTTOM] != -1 ||
1163 maxAscent[INDEX_FILL] != -1) {
1164 final int ascent = Math.max(maxAscent[INDEX_FILL],
1165 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1166 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1167 final int descent = Math.max(maxDescent[INDEX_FILL],
1168 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1169 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1170 maxHeight = Math.max(maxHeight, ascent + descent);
1171 }
1172
Adam Powellf8ac6b72011-05-23 18:14:09 -07001173 if (useLargestChild &&
1174 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
Romain Guy5b1b2412010-01-21 19:09:51 -08001175 mTotalLength = 0;
1176
1177 for (int i = 0; i < count; ++i) {
1178 final View child = getVirtualChildAt(i);
1179
1180 if (child == null) {
1181 mTotalLength += measureNullChild(i);
1182 continue;
1183 }
1184
1185 if (child.getVisibility() == GONE) {
1186 i += getChildrenSkipCount(child, i);
1187 continue;
1188 }
1189
1190 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1191 child.getLayoutParams();
Romain Guyc3520902010-02-25 11:01:01 -08001192 if (isExactly) {
1193 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
1194 getNextLocationOffset(child);
1195 } else {
1196 final int totalLength = mTotalLength;
1197 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
1198 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1199 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001200 }
1201 }
1202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 // Add in our padding
1204 mTotalLength += mPaddingLeft + mPaddingRight;
1205
1206 int widthSize = mTotalLength;
1207
1208 // Check against our minimum width
1209 widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
1210
1211 // Reconcile our calculated size with the widthMeasureSpec
Dianne Hackborn189ee182010-12-02 21:48:53 -08001212 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
1213 widthSize = widthSizeAndState & MEASURED_SIZE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214
1215 // Either expand children with weight to take up available space or
Alan Viverette65be9cc2014-02-24 18:21:09 -08001216 // shrink them if they extend beyond our current bounds. If we skipped
1217 // measurement on any children, we need to measure them now.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 int delta = widthSize - mTotalLength;
Alan Viverette65be9cc2014-02-24 18:21:09 -08001219 if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
1221
1222 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1223 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1224 maxHeight = -1;
1225
1226 mTotalLength = 0;
1227
1228 for (int i = 0; i < count; ++i) {
1229 final View child = getVirtualChildAt(i);
1230
1231 if (child == null || child.getVisibility() == View.GONE) {
1232 continue;
1233 }
1234
1235 final LinearLayout.LayoutParams lp =
1236 (LinearLayout.LayoutParams) child.getLayoutParams();
1237
1238 float childExtra = lp.weight;
1239 if (childExtra > 0) {
1240 // Child said it could absorb extra space -- give him his share
1241 int share = (int) (childExtra * delta / weightSum);
1242 weightSum -= childExtra;
1243 delta -= share;
1244
1245 final int childHeightMeasureSpec = getChildMeasureSpec(
1246 heightMeasureSpec,
1247 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
1248 lp.height);
1249
1250 // TODO: Use a field like lp.isMeasured to figure out if this
1251 // child has been previously measured
1252 if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
1253 // child was measured once already above ... base new measurement
1254 // on stored values
1255 int childWidth = child.getMeasuredWidth() + share;
1256 if (childWidth < 0) {
1257 childWidth = 0;
1258 }
1259
1260 child.measure(
1261 MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
1262 childHeightMeasureSpec);
1263 } else {
1264 // child was skipped in the loop above. Measure for this first time here
1265 child.measure(MeasureSpec.makeMeasureSpec(
1266 share > 0 ? share : 0, MeasureSpec.EXACTLY),
1267 childHeightMeasureSpec);
1268 }
Dianne Hackborn189ee182010-12-02 21:48:53 -08001269
1270 // Child may now not fit in horizontal dimension.
1271 childState = combineMeasuredStates(childState,
1272 child.getMeasuredState() & MEASURED_STATE_MASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 }
1274
Romain Guyc3520902010-02-25 11:01:01 -08001275 if (isExactly) {
1276 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
1277 getNextLocationOffset(child);
1278 } else {
1279 final int totalLength = mTotalLength;
1280 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
1281 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283
1284 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
Romain Guy980a9382010-01-08 15:06:28 -08001285 lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286
1287 final int margin = lp.topMargin + lp .bottomMargin;
1288 int childHeight = child.getMeasuredHeight() + margin;
1289 maxHeight = Math.max(maxHeight, childHeight);
1290 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1291 matchHeightLocally ? margin : childHeight);
1292
Romain Guy980a9382010-01-08 15:06:28 -08001293 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294
1295 if (baselineAligned) {
1296 final int childBaseline = child.getBaseline();
1297 if (childBaseline != -1) {
1298 // Translates the child's vertical gravity into an index in the range 0..2
1299 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1300 & Gravity.VERTICAL_GRAVITY_MASK;
1301 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1302 & ~Gravity.AXIS_SPECIFIED) >> 1;
1303
1304 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1305 maxDescent[index] = Math.max(maxDescent[index],
1306 childHeight - childBaseline);
1307 }
1308 }
1309 }
1310
1311 // Add in our padding
1312 mTotalLength += mPaddingLeft + mPaddingRight;
Romain Guy053b4802010-02-05 15:34:33 -08001313 // TODO: Should we update widthSize with the new total length?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314
1315 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1316 // the most common case
1317 if (maxAscent[INDEX_TOP] != -1 ||
1318 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1319 maxAscent[INDEX_BOTTOM] != -1 ||
1320 maxAscent[INDEX_FILL] != -1) {
1321 final int ascent = Math.max(maxAscent[INDEX_FILL],
1322 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1323 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1324 final int descent = Math.max(maxDescent[INDEX_FILL],
1325 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1326 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1327 maxHeight = Math.max(maxHeight, ascent + descent);
1328 }
1329 } else {
1330 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
Adam Powellf8ac6b72011-05-23 18:14:09 -07001331
1332 // We have no limit, so make all weighted views as wide as the largest child.
1333 // Children will have already been measured once.
Adam Powelleabc9192012-05-04 14:49:46 -07001334 if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
Adam Powellf8ac6b72011-05-23 18:14:09 -07001335 for (int i = 0; i < count; i++) {
1336 final View child = getVirtualChildAt(i);
1337
1338 if (child == null || child.getVisibility() == View.GONE) {
1339 continue;
1340 }
1341
1342 final LinearLayout.LayoutParams lp =
1343 (LinearLayout.LayoutParams) child.getLayoutParams();
1344
1345 float childExtra = lp.weight;
1346 if (childExtra > 0) {
1347 child.measure(
1348 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
1349 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
1350 MeasureSpec.EXACTLY));
1351 }
1352 }
1353 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 }
1355
1356 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
1357 maxHeight = alternativeMaxHeight;
1358 }
1359
1360 maxHeight += mPaddingTop + mPaddingBottom;
1361
1362 // Check against our minimum height
1363 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
1364
Dianne Hackborn189ee182010-12-02 21:48:53 -08001365 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
1366 resolveSizeAndState(maxHeight, heightMeasureSpec,
1367 (childState<<MEASURED_HEIGHT_STATE_SHIFT)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368
1369 if (matchHeight) {
1370 forceUniformHeight(count, widthMeasureSpec);
1371 }
1372 }
1373
1374 private void forceUniformHeight(int count, int widthMeasureSpec) {
1375 // Pretend that the linear layout has an exact size. This is the measured height of
1376 // ourselves. The measured height should be the max height of the children, changed
Fabrice Di Megliofc53e582012-10-25 11:45:04 -07001377 // to accommodate the heightMeasureSpec from the parent
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
1379 MeasureSpec.EXACTLY);
1380 for (int i = 0; i < count; ++i) {
1381 final View child = getVirtualChildAt(i);
1382 if (child.getVisibility() != GONE) {
1383 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
1384
Romain Guy980a9382010-01-08 15:06:28 -08001385 if (lp.height == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 // Temporarily force children to reuse their old measured width
1387 // FIXME: this may not be right for something like wrapping text?
1388 int oldWidth = lp.width;
1389 lp.width = child.getMeasuredWidth();
1390
1391 // Remeasure with new dimensions
1392 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
1393 lp.width = oldWidth;
1394 }
1395 }
1396 }
1397 }
1398
1399 /**
1400 * <p>Returns the number of children to skip after measuring/laying out
1401 * the specified child.</p>
1402 *
1403 * @param child the child after which we want to skip children
1404 * @param index the index of the child after which we want to skip children
1405 * @return the number of children to skip, 0 by default
1406 */
1407 int getChildrenSkipCount(View child, int index) {
1408 return 0;
1409 }
1410
1411 /**
1412 * <p>Returns the size (width or height) that should be occupied by a null
1413 * child.</p>
1414 *
1415 * @param childIndex the index of the null child
1416 * @return the width or height of the child depending on the orientation
1417 */
1418 int measureNullChild(int childIndex) {
1419 return 0;
1420 }
1421
1422 /**
1423 * <p>Measure the child according to the parent's measure specs. This
1424 * method should be overriden by subclasses to force the sizing of
1425 * children. This method is called by {@link #measureVertical(int, int)} and
1426 * {@link #measureHorizontal(int, int)}.</p>
1427 *
1428 * @param child the child to measure
1429 * @param childIndex the index of the child in this view
1430 * @param widthMeasureSpec horizontal space requirements as imposed by the parent
1431 * @param totalWidth extra space that has been used up by the parent horizontally
1432 * @param heightMeasureSpec vertical space requirements as imposed by the parent
1433 * @param totalHeight extra space that has been used up by the parent vertically
1434 */
1435 void measureChildBeforeLayout(View child, int childIndex,
1436 int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
1437 int totalHeight) {
1438 measureChildWithMargins(child, widthMeasureSpec, totalWidth,
1439 heightMeasureSpec, totalHeight);
1440 }
1441
1442 /**
1443 * <p>Return the location offset of the specified child. This can be used
1444 * by subclasses to change the location of a given widget.</p>
1445 *
1446 * @param child the child for which to obtain the location offset
1447 * @return the location offset in pixels
1448 */
1449 int getLocationOffset(View child) {
1450 return 0;
1451 }
1452
1453 /**
1454 * <p>Return the size offset of the next sibling of the specified child.
1455 * This can be used by subclasses to change the location of the widget
1456 * following <code>child</code>.</p>
1457 *
1458 * @param child the child whose next sibling will be moved
1459 * @return the location offset of the next child in pixels
1460 */
1461 int getNextLocationOffset(View child) {
1462 return 0;
1463 }
1464
1465 @Override
1466 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1467 if (mOrientation == VERTICAL) {
Philip Milnead365cc2012-09-27 14:38:46 -07001468 layoutVertical(l, t, r, b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 } else {
Philip Milnead365cc2012-09-27 14:38:46 -07001470 layoutHorizontal(l, t, r, b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 }
1472 }
1473
1474 /**
1475 * Position the children during a layout pass if the orientation of this
1476 * LinearLayout is set to {@link #VERTICAL}.
1477 *
1478 * @see #getOrientation()
1479 * @see #setOrientation(int)
1480 * @see #onLayout(boolean, int, int, int, int)
Philip Milnead365cc2012-09-27 14:38:46 -07001481 * @param left
1482 * @param top
1483 * @param right
1484 * @param bottom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 */
Philip Milnead365cc2012-09-27 14:38:46 -07001486 void layoutVertical(int left, int top, int right, int bottom) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 final int paddingLeft = mPaddingLeft;
1488
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001489 int childTop;
Romain Guy5b1b2412010-01-21 19:09:51 -08001490 int childLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491
1492 // Where right end of child should go
Philip Milnead365cc2012-09-27 14:38:46 -07001493 final int width = right - left;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 int childRight = width - mPaddingRight;
1495
1496 // Space available for child
1497 int childSpace = width - paddingLeft - mPaddingRight;
1498
1499 final int count = getVirtualChildCount();
1500
1501 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001502 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001504 switch (majorGravity) {
1505 case Gravity.BOTTOM:
1506 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001507 childTop = mPaddingTop + bottom - top - mTotalLength;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001508 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001510 // mTotalLength contains the padding already
1511 case Gravity.CENTER_VERTICAL:
Philip Milnead365cc2012-09-27 14:38:46 -07001512 childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001513 break;
1514
1515 case Gravity.TOP:
1516 default:
1517 childTop = mPaddingTop;
1518 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001519 }
Adam Powellfcca00a2010-11-30 21:26:29 -08001520
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 for (int i = 0; i < count; i++) {
1522 final View child = getVirtualChildAt(i);
1523 if (child == null) {
1524 childTop += measureNullChild(i);
1525 } else if (child.getVisibility() != GONE) {
1526 final int childWidth = child.getMeasuredWidth();
1527 final int childHeight = child.getMeasuredHeight();
1528
1529 final LinearLayout.LayoutParams lp =
1530 (LinearLayout.LayoutParams) child.getLayoutParams();
1531
1532 int gravity = lp.gravity;
1533 if (gravity < 0) {
1534 gravity = minorGravity;
1535 }
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07001536 final int layoutDirection = getLayoutDirection();
Fabrice Di Meglioc0053222011-06-13 12:16:51 -07001537 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
Fabrice Di Megliode35cee2011-06-01 15:13:50 -07001538 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 case Gravity.CENTER_HORIZONTAL:
1540 childLeft = paddingLeft + ((childSpace - childWidth) / 2)
1541 + lp.leftMargin - lp.rightMargin;
1542 break;
1543
1544 case Gravity.RIGHT:
1545 childLeft = childRight - childWidth - lp.rightMargin;
1546 break;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001547
1548 case Gravity.LEFT:
Romain Guy611cd3f2009-12-15 12:00:37 -08001549 default:
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001550 childLeft = paddingLeft + lp.leftMargin;
Romain Guy611cd3f2009-12-15 12:00:37 -08001551 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 }
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001553
Adam Powell696cba52011-03-29 10:38:16 -07001554 if (hasDividerBeforeChildAt(i)) {
1555 childTop += mDividerHeight;
1556 }
1557
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001558 childTop += lp.topMargin;
1559 setChildFrame(child, childLeft, childTop + getLocationOffset(child),
1560 childWidth, childHeight);
1561 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
1562
1563 i += getChildrenSkipCount(child, i);
1564 }
1565 }
1566 }
1567
1568 /**
1569 * Position the children during a layout pass if the orientation of this
1570 * LinearLayout is set to {@link #HORIZONTAL}.
1571 *
1572 * @see #getOrientation()
1573 * @see #setOrientation(int)
1574 * @see #onLayout(boolean, int, int, int, int)
Philip Milnead365cc2012-09-27 14:38:46 -07001575 * @param left
1576 * @param top
1577 * @param right
1578 * @param bottom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001579 */
Philip Milnead365cc2012-09-27 14:38:46 -07001580 void layoutHorizontal(int left, int top, int right, int bottom) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001581 final boolean isLayoutRtl = isLayoutRtl();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001582 final int paddingTop = mPaddingTop;
1583
Romain Guy5b1b2412010-01-21 19:09:51 -08001584 int childTop;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001585 int childLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586
1587 // Where bottom of child should go
Philip Milnead365cc2012-09-27 14:38:46 -07001588 final int height = bottom - top;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 int childBottom = height - mPaddingBottom;
1590
1591 // Space available for child
1592 int childSpace = height - paddingTop - mPaddingBottom;
1593
1594 final int count = getVirtualChildCount();
1595
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001596 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1598
1599 final boolean baselineAligned = mBaselineAligned;
1600
1601 final int[] maxAscent = mMaxAscent;
1602 final int[] maxDescent = mMaxDescent;
1603
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07001604 final int layoutDirection = getLayoutDirection();
Fabrice Di Meglioc0053222011-06-13 12:16:51 -07001605 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001606 case Gravity.RIGHT:
1607 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001608 childLeft = mPaddingLeft + right - left - mTotalLength;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001609 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001610
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001611 case Gravity.CENTER_HORIZONTAL:
1612 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001613 childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001614 break;
1615
1616 case Gravity.LEFT:
1617 default:
1618 childLeft = mPaddingLeft;
1619 break;
1620 }
1621
1622 int start = 0;
1623 int dir = 1;
1624 //In case of RTL, start drawing from the last child.
1625 if (isLayoutRtl) {
1626 start = count - 1;
1627 dir = -1;
Adam Powellfcca00a2010-11-30 21:26:29 -08001628 }
1629
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 for (int i = 0; i < count; i++) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001631 int childIndex = start + dir * i;
1632 final View child = getVirtualChildAt(childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633
1634 if (child == null) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001635 childLeft += measureNullChild(childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 } else if (child.getVisibility() != GONE) {
1637 final int childWidth = child.getMeasuredWidth();
1638 final int childHeight = child.getMeasuredHeight();
1639 int childBaseline = -1;
1640
1641 final LinearLayout.LayoutParams lp =
1642 (LinearLayout.LayoutParams) child.getLayoutParams();
1643
Romain Guy980a9382010-01-08 15:06:28 -08001644 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 childBaseline = child.getBaseline();
1646 }
1647
1648 int gravity = lp.gravity;
1649 if (gravity < 0) {
1650 gravity = minorGravity;
1651 }
1652
1653 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
1654 case Gravity.TOP:
1655 childTop = paddingTop + lp.topMargin;
1656 if (childBaseline != -1) {
1657 childTop += maxAscent[INDEX_TOP] - childBaseline;
1658 }
1659 break;
1660
1661 case Gravity.CENTER_VERTICAL:
Romain Guy5b1b2412010-01-21 19:09:51 -08001662 // Removed support for baseline alignment when layout_gravity or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 // gravity == center_vertical. See bug #1038483.
1664 // Keep the code around if we need to re-enable this feature
1665 // if (childBaseline != -1) {
1666 // // Align baselines vertically only if the child is smaller than us
1667 // if (childSpace - childHeight > 0) {
1668 // childTop = paddingTop + (childSpace / 2) - childBaseline;
1669 // } else {
1670 // childTop = paddingTop + (childSpace - childHeight) / 2;
1671 // }
1672 // } else {
1673 childTop = paddingTop + ((childSpace - childHeight) / 2)
1674 + lp.topMargin - lp.bottomMargin;
1675 break;
1676
1677 case Gravity.BOTTOM:
1678 childTop = childBottom - childHeight - lp.bottomMargin;
1679 if (childBaseline != -1) {
1680 int descent = child.getMeasuredHeight() - childBaseline;
1681 childTop -= (maxDescent[INDEX_BOTTOM] - descent);
1682 }
1683 break;
Romain Guy611cd3f2009-12-15 12:00:37 -08001684 default:
1685 childTop = paddingTop;
1686 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 }
1688
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001689 if (hasDividerBeforeChildAt(childIndex)) {
Adam Powell696cba52011-03-29 10:38:16 -07001690 childLeft += mDividerWidth;
1691 }
1692
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 childLeft += lp.leftMargin;
1694 setChildFrame(child, childLeft + getLocationOffset(child), childTop,
1695 childWidth, childHeight);
1696 childLeft += childWidth + lp.rightMargin +
1697 getNextLocationOffset(child);
1698
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001699 i += getChildrenSkipCount(child, childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001700 }
1701 }
1702 }
1703
1704 private void setChildFrame(View child, int left, int top, int width, int height) {
1705 child.layout(left, top, left + width, top + height);
1706 }
1707
1708 /**
1709 * Should the layout be a column or a row.
Tor Norbyed9273d62013-05-30 15:59:53 -07001710 * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default
1711 * value is {@link #HORIZONTAL}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 *
1713 * @attr ref android.R.styleable#LinearLayout_orientation
1714 */
Tor Norbyed9273d62013-05-30 15:59:53 -07001715 public void setOrientation(@OrientationMode int orientation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 if (mOrientation != orientation) {
1717 mOrientation = orientation;
1718 requestLayout();
1719 }
1720 }
1721
1722 /**
1723 * Returns the current orientation.
1724 *
1725 * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
1726 */
Tor Norbyed9273d62013-05-30 15:59:53 -07001727 @OrientationMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 public int getOrientation() {
1729 return mOrientation;
1730 }
1731
1732 /**
1733 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
1734 * this layout has a VERTICAL orientation, this controls where all the child
1735 * views are placed if there is extra vertical space. If this layout has a
1736 * HORIZONTAL orientation, this controls the alignment of the children.
1737 *
1738 * @param gravity See {@link android.view.Gravity}
1739 *
1740 * @attr ref android.R.styleable#LinearLayout_gravity
1741 */
1742 @android.view.RemotableViewMethod
1743 public void setGravity(int gravity) {
1744 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001745 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -07001746 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001747 }
1748
1749 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
1750 gravity |= Gravity.TOP;
1751 }
1752
1753 mGravity = gravity;
1754 requestLayout();
1755 }
1756 }
1757
1758 @android.view.RemotableViewMethod
1759 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001760 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1761 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
1762 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 requestLayout();
1764 }
1765 }
1766
1767 @android.view.RemotableViewMethod
1768 public void setVerticalGravity(int verticalGravity) {
1769 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1770 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1771 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1772 requestLayout();
1773 }
1774 }
1775
1776 @Override
1777 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1778 return new LinearLayout.LayoutParams(getContext(), attrs);
1779 }
1780
1781 /**
1782 * Returns a set of layout parameters with a width of
Romain Guy980a9382010-01-08 15:06:28 -08001783 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1785 * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1786 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1787 * and the height to {@link LayoutParams#WRAP_CONTENT}.
1788 */
1789 @Override
1790 protected LayoutParams generateDefaultLayoutParams() {
1791 if (mOrientation == HORIZONTAL) {
1792 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1793 } else if (mOrientation == VERTICAL) {
Romain Guy980a9382010-01-08 15:06:28 -08001794 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 }
1796 return null;
1797 }
1798
1799 @Override
1800 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1801 return new LayoutParams(p);
1802 }
1803
1804
1805 // Override to allow type-checking of LayoutParams.
1806 @Override
1807 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1808 return p instanceof LinearLayout.LayoutParams;
1809 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001810
1811 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -08001812 public CharSequence getAccessibilityClassName() {
1813 return LinearLayout.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001814 }
1815
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 /**
1817 * Per-child layout information associated with ViewLinearLayout.
1818 *
1819 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
1820 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
1821 */
1822 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1823 /**
1824 * Indicates how much of the extra space in the LinearLayout will be
1825 * allocated to the view associated with these LayoutParams. Specify
1826 * 0 if the view should not be stretched. Otherwise the extra pixels
1827 * will be pro-rated among all views whose weight is greater than 0.
1828 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001829 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 public float weight;
1831
1832 /**
1833 * Gravity for the view associated with these LayoutParams.
1834 *
1835 * @see android.view.Gravity
1836 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001837 @ViewDebug.ExportedProperty(category = "layout", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 @ViewDebug.IntToString(from = -1, to = "NONE"),
1839 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
1840 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
1841 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
1842 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
1843 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -07001844 @ViewDebug.IntToString(from = Gravity.START, to = "START"),
1845 @ViewDebug.IntToString(from = Gravity.END, to = "END"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
1847 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
1848 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
1849 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
1850 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
1851 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
1852 })
1853 public int gravity = -1;
1854
1855 /**
1856 * {@inheritDoc}
1857 */
1858 public LayoutParams(Context c, AttributeSet attrs) {
1859 super(c, attrs);
1860 TypedArray a =
1861 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
1862
1863 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
1864 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
1865
1866 a.recycle();
1867 }
1868
1869 /**
1870 * {@inheritDoc}
1871 */
1872 public LayoutParams(int width, int height) {
1873 super(width, height);
1874 weight = 0;
1875 }
1876
1877 /**
1878 * Creates a new set of layout parameters with the specified width, height
1879 * and weight.
1880 *
Romain Guy980a9382010-01-08 15:06:28 -08001881 * @param width the width, either {@link #MATCH_PARENT},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 * {@link #WRAP_CONTENT} or a fixed size in pixels
Romain Guy980a9382010-01-08 15:06:28 -08001883 * @param height the height, either {@link #MATCH_PARENT},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884 * {@link #WRAP_CONTENT} or a fixed size in pixels
1885 * @param weight the weight
1886 */
1887 public LayoutParams(int width, int height, float weight) {
1888 super(width, height);
1889 this.weight = weight;
1890 }
1891
1892 /**
1893 * {@inheritDoc}
1894 */
1895 public LayoutParams(ViewGroup.LayoutParams p) {
1896 super(p);
1897 }
1898
1899 /**
1900 * {@inheritDoc}
1901 */
Alan Viverette0a0e1552013-08-07 13:24:09 -07001902 public LayoutParams(ViewGroup.MarginLayoutParams source) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 super(source);
1904 }
1905
Alan Viverette0a0e1552013-08-07 13:24:09 -07001906 /**
1907 * Copy constructor. Clones the width, height, margin values, weight,
1908 * and gravity of the source.
1909 *
1910 * @param source The layout params to copy from.
1911 */
1912 public LayoutParams(LayoutParams source) {
1913 super(source);
1914
1915 this.weight = source.weight;
1916 this.gravity = source.gravity;
1917 }
1918
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001919 @Override
1920 public String debug(String output) {
1921 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
1922 ", height=" + sizeToString(height) + " weight=" + weight + "}";
1923 }
1924 }
1925}