blob: 6476cdc4cc1ed81c80a4939836c579a4575adb25 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
23import android.content.res.TypedArray;
Adam Powellfcca00a2010-11-30 21:26:29 -080024import android.graphics.Canvas;
25import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.util.AttributeSet;
27import android.view.Gravity;
28import android.view.View;
29import android.view.ViewDebug;
30import android.view.ViewGroup;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080031import android.view.accessibility.AccessibilityEvent;
32import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.widget.RemoteViews.RemoteView;
34
Tor Norbyed9273d62013-05-30 15:59:53 -070035import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
39/**
40 * A Layout that arranges its children in a single column or a single row. The direction of
41 * the row can be set by calling {@link #setOrientation(int) setOrientation()}.
42 * You can also specify gravity, which specifies the alignment of all the child elements by
43 * calling {@link #setGravity(int) setGravity()} or specify that specific children
44 * grow to fill up any remaining space in the layout by setting the <em>weight</em> member of
45 * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
46 * The default orientation is horizontal.
47 *
Scott Main4c359b72012-07-24 15:51:27 -070048 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/linear.html">Linear Layout</a>
49 * guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070050 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 * <p>
52 * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
53 * for layout attributes </p>
Romain Guy9295ada2010-06-15 11:33:24 -070054 *
55 * @attr ref android.R.styleable#LinearLayout_baselineAligned
56 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
57 * @attr ref android.R.styleable#LinearLayout_gravity
58 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
59 * @attr ref android.R.styleable#LinearLayout_orientation
60 * @attr ref android.R.styleable#LinearLayout_weightSum
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 */
62@RemoteView
63public class LinearLayout extends ViewGroup {
Tor Norbyed9273d62013-05-30 15:59:53 -070064 /** @hide */
65 @IntDef({HORIZONTAL, VERTICAL})
66 @Retention(RetentionPolicy.SOURCE)
67 public @interface OrientationMode {}
68
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 public static final int HORIZONTAL = 0;
70 public static final int VERTICAL = 1;
71
Tor Norbyed9273d62013-05-30 15:59:53 -070072 /** @hide */
73 @IntDef(flag = true,
74 value = {
75 SHOW_DIVIDER_NONE,
76 SHOW_DIVIDER_BEGINNING,
77 SHOW_DIVIDER_MIDDLE,
78 SHOW_DIVIDER_END
79 })
80 @Retention(RetentionPolicy.SOURCE)
81 public @interface DividerMode {}
82
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 /**
Adam Powellfcca00a2010-11-30 21:26:29 -080084 * Don't show any dividers.
85 */
86 public static final int SHOW_DIVIDER_NONE = 0;
87 /**
88 * Show a divider at the beginning of the group.
89 */
90 public static final int SHOW_DIVIDER_BEGINNING = 1;
91 /**
92 * Show dividers between each item in the group.
93 */
94 public static final int SHOW_DIVIDER_MIDDLE = 2;
95 /**
96 * Show a divider at the end of the group.
97 */
98 public static final int SHOW_DIVIDER_END = 4;
99
100 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 * Whether the children of this layout are baseline aligned. Only applicable
102 * if {@link #mOrientation} is horizontal.
103 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700104 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 private boolean mBaselineAligned = true;
106
107 /**
108 * If this layout is part of another layout that is baseline aligned,
109 * use the child at this index as the baseline.
110 *
111 * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
112 * with whether the children of this layout are baseline aligned.
113 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700114 @ViewDebug.ExportedProperty(category = "layout")
Karl Rosaenc1f9c402009-08-12 17:18:33 -0700115 private int mBaselineAlignedChildIndex = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116
117 /**
118 * The additional offset to the child's baseline.
119 * We'll calculate the baseline of this layout as we measure vertically; for
120 * horizontal linear layouts, the offset of 0 is appropriate.
121 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700122 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 private int mBaselineChildTop = 0;
124
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700125 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 private int mOrientation;
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700127
Fabrice Di Megliobce84d22011-06-02 15:57:01 -0700128 @ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
129 @ViewDebug.FlagToString(mask = -1,
130 equals = -1, name = "NONE"),
131 @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY,
132 equals = Gravity.NO_GRAVITY,name = "NONE"),
133 @ViewDebug.FlagToString(mask = Gravity.TOP,
134 equals = Gravity.TOP, name = "TOP"),
135 @ViewDebug.FlagToString(mask = Gravity.BOTTOM,
136 equals = Gravity.BOTTOM, name = "BOTTOM"),
137 @ViewDebug.FlagToString(mask = Gravity.LEFT,
138 equals = Gravity.LEFT, name = "LEFT"),
139 @ViewDebug.FlagToString(mask = Gravity.RIGHT,
140 equals = Gravity.RIGHT, name = "RIGHT"),
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700141 @ViewDebug.FlagToString(mask = Gravity.START,
142 equals = Gravity.START, name = "START"),
143 @ViewDebug.FlagToString(mask = Gravity.END,
144 equals = Gravity.END, name = "END"),
Fabrice Di Megliobce84d22011-06-02 15:57:01 -0700145 @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
146 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
147 @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
148 equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"),
149 @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL,
150 equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"),
151 @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL,
152 equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"),
153 @ViewDebug.FlagToString(mask = Gravity.CENTER,
154 equals = Gravity.CENTER, name = "CENTER"),
155 @ViewDebug.FlagToString(mask = Gravity.FILL,
156 equals = Gravity.FILL, name = "FILL"),
Fabrice Di Meglioc46f7ff2011-06-06 18:23:10 -0700157 @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
158 equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
Jon Miranda4597e982014-07-29 07:25:49 -0700159 }, formatToHexString = true)
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700160 private int mGravity = Gravity.START | Gravity.TOP;
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700161
162 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 private int mTotalLength;
164
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700165 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 private float mWeightSum;
167
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700168 @ViewDebug.ExportedProperty(category = "layout")
Romain Guy5b1b2412010-01-21 19:09:51 -0800169 private boolean mUseLargestChild;
170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 private int[] mMaxAscent;
172 private int[] mMaxDescent;
173
174 private static final int VERTICAL_GRAVITY_COUNT = 4;
175
176 private static final int INDEX_CENTER_VERTICAL = 0;
177 private static final int INDEX_TOP = 1;
178 private static final int INDEX_BOTTOM = 2;
Romain Guy5b1b2412010-01-21 19:09:51 -0800179 private static final int INDEX_FILL = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
Adam Powellfcca00a2010-11-30 21:26:29 -0800181 private Drawable mDivider;
182 private int mDividerWidth;
183 private int mDividerHeight;
184 private int mShowDividers;
185 private int mDividerPadding;
186
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 public LinearLayout(Context context) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700188 this(context, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 }
190
191 public LinearLayout(Context context, AttributeSet attrs) {
Romain Guy9295ada2010-06-15 11:33:24 -0700192 this(context, attrs, 0);
193 }
194
Alan Viverette617feb92013-09-09 18:09:13 -0700195 public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
196 this(context, attrs, defStyleAttr, 0);
197 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198
Alan Viverette617feb92013-09-09 18:09:13 -0700199 public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
200 super(context, attrs, defStyleAttr, defStyleRes);
201
202 final TypedArray a = context.obtainStyledAttributes(
203 attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204
205 int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);
206 if (index >= 0) {
207 setOrientation(index);
208 }
209
210 index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);
211 if (index >= 0) {
212 setGravity(index);
213 }
214
215 boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);
216 if (!baselineAligned) {
217 setBaselineAligned(baselineAligned);
218 }
219
220 mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);
221
222 mBaselineAlignedChildIndex =
223 a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
224
Romain Guy9295ada2010-06-15 11:33:24 -0700225 mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
Romain Guy5b1b2412010-01-21 19:09:51 -0800226
Adam Powellfcca00a2010-11-30 21:26:29 -0800227 setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
228 mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
229 mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 a.recycle();
232 }
233
234 /**
Adam Powellfcca00a2010-11-30 21:26:29 -0800235 * Set how dividers should be shown between items in this layout
236 *
237 * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
238 * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
239 * or {@link #SHOW_DIVIDER_NONE} to show no dividers.
240 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700241 public void setShowDividers(@DividerMode int showDividers) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800242 if (showDividers != mShowDividers) {
243 requestLayout();
244 }
245 mShowDividers = showDividers;
246 }
247
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700248 @Override
249 public boolean shouldDelayChildPressedState() {
250 return false;
251 }
252
Adam Powellfcca00a2010-11-30 21:26:29 -0800253 /**
254 * @return A flag set indicating how dividers should be shown around items.
255 * @see #setShowDividers(int)
256 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700257 @DividerMode
Adam Powellfcca00a2010-11-30 21:26:29 -0800258 public int getShowDividers() {
259 return mShowDividers;
260 }
261
262 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700263 * @return the divider Drawable that will divide each item.
264 *
265 * @see #setDividerDrawable(Drawable)
266 *
267 * @attr ref android.R.styleable#LinearLayout_divider
268 */
269 public Drawable getDividerDrawable() {
270 return mDivider;
271 }
272
273 /**
Adam Powellfcca00a2010-11-30 21:26:29 -0800274 * Set a drawable to be used as a divider between items.
Philip Milne1018fb42012-03-13 12:00:04 -0700275 *
Adam Powellfcca00a2010-11-30 21:26:29 -0800276 * @param divider Drawable that will divide each item.
Philip Milne1018fb42012-03-13 12:00:04 -0700277 *
Adam Powellfcca00a2010-11-30 21:26:29 -0800278 * @see #setShowDividers(int)
Philip Milne1018fb42012-03-13 12:00:04 -0700279 *
280 * @attr ref android.R.styleable#LinearLayout_divider
Adam Powellfcca00a2010-11-30 21:26:29 -0800281 */
282 public void setDividerDrawable(Drawable divider) {
283 if (divider == mDivider) {
284 return;
285 }
286 mDivider = divider;
287 if (divider != null) {
288 mDividerWidth = divider.getIntrinsicWidth();
289 mDividerHeight = divider.getIntrinsicHeight();
290 } else {
291 mDividerWidth = 0;
292 mDividerHeight = 0;
293 }
294 setWillNotDraw(divider == null);
295 requestLayout();
296 }
297
Adam Powell696cba52011-03-29 10:38:16 -0700298 /**
299 * Set padding displayed on both ends of dividers.
300 *
301 * @param padding Padding value in pixels that will be applied to each end
302 *
303 * @see #setShowDividers(int)
304 * @see #setDividerDrawable(Drawable)
305 * @see #getDividerPadding()
306 */
307 public void setDividerPadding(int padding) {
308 mDividerPadding = padding;
309 }
310
311 /**
312 * Get the padding size used to inset dividers in pixels
313 *
314 * @see #setShowDividers(int)
315 * @see #setDividerDrawable(Drawable)
316 * @see #setDividerPadding(int)
317 */
318 public int getDividerPadding() {
319 return mDividerPadding;
320 }
321
Adam Powell640a66e2011-04-29 10:18:53 -0700322 /**
323 * Get the width of the current divider drawable.
324 *
325 * @hide Used internally by framework.
326 */
327 public int getDividerWidth() {
328 return mDividerWidth;
329 }
330
Adam Powellfcca00a2010-11-30 21:26:29 -0800331 @Override
332 protected void onDraw(Canvas canvas) {
333 if (mDivider == null) {
334 return;
335 }
336
337 if (mOrientation == VERTICAL) {
338 drawDividersVertical(canvas);
339 } else {
340 drawDividersHorizontal(canvas);
341 }
342 }
343
344 void drawDividersVertical(Canvas canvas) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800345 final int count = getVirtualChildCount();
Adam Powellfcca00a2010-11-30 21:26:29 -0800346 for (int i = 0; i < count; i++) {
347 final View child = getVirtualChildAt(i);
348
Adam Powell35aecd52011-07-01 13:43:49 -0700349 if (child != null && child.getVisibility() != GONE) {
Adam Powell696cba52011-03-29 10:38:16 -0700350 if (hasDividerBeforeChildAt(i)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700351 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Vladimir Baryshnikov20761fc2012-01-17 14:59:48 -0800352 final int top = child.getTop() - lp.topMargin - mDividerHeight;
Adam Powellfcca00a2010-11-30 21:26:29 -0800353 drawHorizontalDivider(canvas, top);
Adam Powellfcca00a2010-11-30 21:26:29 -0800354 }
Adam Powellfcca00a2010-11-30 21:26:29 -0800355 }
356 }
357
Adam Powell696cba52011-03-29 10:38:16 -0700358 if (hasDividerBeforeChildAt(count)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700359 final View child = getVirtualChildAt(count - 1);
360 int bottom = 0;
361 if (child == null) {
362 bottom = getHeight() - getPaddingBottom() - mDividerHeight;
363 } else {
364 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
365 bottom = child.getBottom() + lp.bottomMargin;
366 }
367 drawHorizontalDivider(canvas, bottom);
Adam Powellfcca00a2010-11-30 21:26:29 -0800368 }
369 }
370
371 void drawDividersHorizontal(Canvas canvas) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800372 final int count = getVirtualChildCount();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700373 final boolean isLayoutRtl = isLayoutRtl();
Adam Powellfcca00a2010-11-30 21:26:29 -0800374 for (int i = 0; i < count; i++) {
375 final View child = getVirtualChildAt(i);
376
Adam Powell35aecd52011-07-01 13:43:49 -0700377 if (child != null && child.getVisibility() != GONE) {
Adam Powell696cba52011-03-29 10:38:16 -0700378 if (hasDividerBeforeChildAt(i)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700379 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700380 final int position;
381 if (isLayoutRtl) {
382 position = child.getRight() + lp.rightMargin;
383 } else {
384 position = child.getLeft() - lp.leftMargin - mDividerWidth;
385 }
386 drawVerticalDivider(canvas, position);
Adam Powellfcca00a2010-11-30 21:26:29 -0800387 }
Adam Powellfcca00a2010-11-30 21:26:29 -0800388 }
389 }
390
Adam Powell696cba52011-03-29 10:38:16 -0700391 if (hasDividerBeforeChildAt(count)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700392 final View child = getVirtualChildAt(count - 1);
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700393 int position;
Adam Powell35aecd52011-07-01 13:43:49 -0700394 if (child == null) {
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700395 if (isLayoutRtl) {
396 position = getPaddingLeft();
397 } else {
398 position = getWidth() - getPaddingRight() - mDividerWidth;
399 }
Adam Powell35aecd52011-07-01 13:43:49 -0700400 } else {
401 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700402 if (isLayoutRtl) {
403 position = child.getLeft() - lp.leftMargin - mDividerWidth;
404 } else {
405 position = child.getRight() + lp.rightMargin;
406 }
Adam Powell35aecd52011-07-01 13:43:49 -0700407 }
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700408 drawVerticalDivider(canvas, position);
Adam Powellfcca00a2010-11-30 21:26:29 -0800409 }
410 }
411
412 void drawHorizontalDivider(Canvas canvas, int top) {
413 mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
414 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
415 mDivider.draw(canvas);
416 }
417
418 void drawVerticalDivider(Canvas canvas, int left) {
419 mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
420 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
421 mDivider.draw(canvas);
422 }
423
424 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 * <p>Indicates whether widgets contained within this layout are aligned
426 * on their baseline or not.</p>
427 *
428 * @return true when widgets are baseline-aligned, false otherwise
429 */
430 public boolean isBaselineAligned() {
431 return mBaselineAligned;
432 }
433
434 /**
435 * <p>Defines whether widgets contained in this layout are
436 * baseline-aligned or not.</p>
437 *
438 * @param baselineAligned true to align widgets on their baseline,
439 * false otherwise
440 *
441 * @attr ref android.R.styleable#LinearLayout_baselineAligned
442 */
443 @android.view.RemotableViewMethod
444 public void setBaselineAligned(boolean baselineAligned) {
445 mBaselineAligned = baselineAligned;
446 }
447
Romain Guy9295ada2010-06-15 11:33:24 -0700448 /**
449 * When true, all children with a weight will be considered having
450 * the minimum size of the largest child. If false, all children are
451 * measured normally.
452 *
453 * @return True to measure children with a weight using the minimum
454 * size of the largest child, false otherwise.
Philip Milne1018fb42012-03-13 12:00:04 -0700455 *
456 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
Romain Guy9295ada2010-06-15 11:33:24 -0700457 */
458 public boolean isMeasureWithLargestChildEnabled() {
459 return mUseLargestChild;
460 }
461
462 /**
463 * When set to true, all children with a weight will be considered having
464 * the minimum size of the largest child. If false, all children are
465 * measured normally.
466 *
467 * Disabled by default.
468 *
469 * @param enabled True to measure children with a weight using the
470 * minimum size of the largest child, false otherwise.
Philip Milne1018fb42012-03-13 12:00:04 -0700471 *
472 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
Romain Guy9295ada2010-06-15 11:33:24 -0700473 */
474 @android.view.RemotableViewMethod
475 public void setMeasureWithLargestChildEnabled(boolean enabled) {
476 mUseLargestChild = enabled;
477 }
478
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 @Override
480 public int getBaseline() {
481 if (mBaselineAlignedChildIndex < 0) {
482 return super.getBaseline();
483 }
484
485 if (getChildCount() <= mBaselineAlignedChildIndex) {
486 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
487 + "set to an index that is out of bounds.");
488 }
489
490 final View child = getChildAt(mBaselineAlignedChildIndex);
491 final int childBaseline = child.getBaseline();
492
493 if (childBaseline == -1) {
494 if (mBaselineAlignedChildIndex == 0) {
495 // this is just the default case, safe to return -1
496 return -1;
497 }
498 // the user picked an index that points to something that doesn't
499 // know how to calculate its baseline.
500 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
501 + "points to a View that doesn't know how to get its baseline.");
502 }
503
504 // TODO: This should try to take into account the virtual offsets
505 // (See getNextLocationOffset and getLocationOffset)
506 // We should add to childTop:
507 // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
508 // and also add:
509 // getLocationOffset(child)
510 int childTop = mBaselineChildTop;
511
512 if (mOrientation == VERTICAL) {
513 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
514 if (majorGravity != Gravity.TOP) {
515 switch (majorGravity) {
516 case Gravity.BOTTOM:
517 childTop = mBottom - mTop - mPaddingBottom - mTotalLength;
518 break;
519
520 case Gravity.CENTER_VERTICAL:
521 childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) -
522 mTotalLength) / 2;
523 break;
524 }
525 }
526 }
527
528 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
529 return childTop + lp.topMargin + childBaseline;
530 }
531
532 /**
533 * @return The index of the child that will be used if this layout is
534 * part of a larger layout that is baseline aligned, or -1 if none has
535 * been set.
536 */
537 public int getBaselineAlignedChildIndex() {
538 return mBaselineAlignedChildIndex;
539 }
540
541 /**
542 * @param i The index of the child that will be used if this layout is
543 * part of a larger layout that is baseline aligned.
544 *
545 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
546 */
547 @android.view.RemotableViewMethod
548 public void setBaselineAlignedChildIndex(int i) {
549 if ((i < 0) || (i >= getChildCount())) {
550 throw new IllegalArgumentException("base aligned child index out "
551 + "of range (0, " + getChildCount() + ")");
552 }
553 mBaselineAlignedChildIndex = i;
554 }
555
556 /**
557 * <p>Returns the view at the specified index. This method can be overriden
558 * to take into account virtual children. Refer to
559 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
560 * for an example.</p>
561 *
562 * @param index the child's index
563 * @return the child at the specified index
564 */
565 View getVirtualChildAt(int index) {
566 return getChildAt(index);
567 }
568
569 /**
570 * <p>Returns the virtual number of children. This number might be different
571 * than the actual number of children if the layout can hold virtual
572 * children. Refer to
573 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
574 * for an example.</p>
575 *
576 * @return the virtual number of children
577 */
578 int getVirtualChildCount() {
579 return getChildCount();
580 }
581
582 /**
583 * Returns the desired weights sum.
584 *
585 * @return A number greater than 0.0f if the weight sum is defined, or
586 * a number lower than or equals to 0.0f if not weight sum is
587 * to be used.
588 */
589 public float getWeightSum() {
590 return mWeightSum;
591 }
592
593 /**
594 * Defines the desired weights sum. If unspecified the weights sum is computed
595 * at layout time by adding the layout_weight of each child.
596 *
597 * This can be used for instance to give a single child 50% of the total
598 * available space by giving it a layout_weight of 0.5 and setting the
599 * weightSum to 1.0.
600 *
601 * @param weightSum a number greater than 0.0f, or a number lower than or equals
602 * to 0.0f if the weight sum should be computed from the children's
603 * layout_weight
604 */
605 @android.view.RemotableViewMethod
606 public void setWeightSum(float weightSum) {
607 mWeightSum = Math.max(0.0f, weightSum);
608 }
609
610 @Override
611 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
612 if (mOrientation == VERTICAL) {
613 measureVertical(widthMeasureSpec, heightMeasureSpec);
614 } else {
615 measureHorizontal(widthMeasureSpec, heightMeasureSpec);
616 }
617 }
618
619 /**
Adam Powell696cba52011-03-29 10:38:16 -0700620 * Determines where to position dividers between children.
621 *
622 * @param childIndex Index of child to check for preceding divider
623 * @return true if there should be a divider before the child at childIndex
624 * @hide Pending API consideration. Currently only used internally by the system.
625 */
626 protected boolean hasDividerBeforeChildAt(int childIndex) {
627 if (childIndex == 0) {
628 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
629 } else if (childIndex == getChildCount()) {
630 return (mShowDividers & SHOW_DIVIDER_END) != 0;
Adam Powellbf68f832011-06-20 18:23:30 -0700631 } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
632 boolean hasVisibleViewBefore = false;
633 for (int i = childIndex - 1; i >= 0; i--) {
634 if (getChildAt(i).getVisibility() != GONE) {
635 hasVisibleViewBefore = true;
636 break;
637 }
638 }
639 return hasVisibleViewBefore;
Adam Powell696cba52011-03-29 10:38:16 -0700640 }
Adam Powellbf68f832011-06-20 18:23:30 -0700641 return false;
Adam Powell696cba52011-03-29 10:38:16 -0700642 }
643
644 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 * Measures the children when the orientation of this LinearLayout is set
646 * to {@link #VERTICAL}.
647 *
648 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
649 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
650 *
651 * @see #getOrientation()
652 * @see #setOrientation(int)
653 * @see #onMeasure(int, int)
654 */
655 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
656 mTotalLength = 0;
657 int maxWidth = 0;
Dianne Hackborn189ee182010-12-02 21:48:53 -0800658 int childState = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 int alternativeMaxWidth = 0;
660 int weightedMaxWidth = 0;
661 boolean allFillParent = true;
662 float totalWeight = 0;
663
664 final int count = getVirtualChildCount();
665
666 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
667 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
668
669 boolean matchWidth = false;
Alan Viverette65be9cc2014-02-24 18:21:09 -0800670 boolean skippedMeasure = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671
672 final int baselineChildIndex = mBaselineAlignedChildIndex;
Romain Guy5b1b2412010-01-21 19:09:51 -0800673 final boolean useLargestChild = mUseLargestChild;
674
675 int largestChildHeight = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676
677 // See how tall everyone is. Also remember max width.
678 for (int i = 0; i < count; ++i) {
679 final View child = getVirtualChildAt(i);
680
681 if (child == null) {
682 mTotalLength += measureNullChild(i);
683 continue;
684 }
685
686 if (child.getVisibility() == View.GONE) {
687 i += getChildrenSkipCount(child, i);
688 continue;
689 }
690
Adam Powell696cba52011-03-29 10:38:16 -0700691 if (hasDividerBeforeChildAt(i)) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800692 mTotalLength += mDividerHeight;
693 }
694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
696
697 totalWeight += lp.weight;
698
699 if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
700 // Optimization: don't bother measuring children who are going to use
701 // leftover space. These views will get measured again down below if
702 // there is any leftover space.
Romain Guy053b4802010-02-05 15:34:33 -0800703 final int totalLength = mTotalLength;
704 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
Alan Viverette65be9cc2014-02-24 18:21:09 -0800705 skippedMeasure = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 } else {
Romain Guy5b1b2412010-01-21 19:09:51 -0800707 int oldHeight = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708
Romain Guy5b1b2412010-01-21 19:09:51 -0800709 if (lp.height == 0 && lp.weight > 0) {
Gilles Debunnef5c6eff2010-02-09 19:08:36 -0800710 // heightMode is either UNSPECIFIED or AT_MOST, and this
711 // child wanted to stretch to fill available space.
712 // Translate that to WRAP_CONTENT so that it does not end up
713 // with a height of 0
714 oldHeight = 0;
715 lp.height = LayoutParams.WRAP_CONTENT;
Romain Guy5b1b2412010-01-21 19:09:51 -0800716 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717
Gilles Debunnef5c6eff2010-02-09 19:08:36 -0800718 // Determine how big this child would like to be. If this or
Romain Guy5b1b2412010-01-21 19:09:51 -0800719 // previous children have given a weight, then we allow it to
720 // use all available space (and we will shrink things later
721 // if needed).
722 measureChildBeforeLayout(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 child, i, widthMeasureSpec, 0, heightMeasureSpec,
724 totalWeight == 0 ? mTotalLength : 0);
725
Romain Guy5b1b2412010-01-21 19:09:51 -0800726 if (oldHeight != Integer.MIN_VALUE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 lp.height = oldHeight;
Romain Guy5b1b2412010-01-21 19:09:51 -0800728 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729
Romain Guy5b1b2412010-01-21 19:09:51 -0800730 final int childHeight = child.getMeasuredHeight();
Romain Guy053b4802010-02-05 15:34:33 -0800731 final int totalLength = mTotalLength;
732 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
733 lp.bottomMargin + getNextLocationOffset(child));
Romain Guy5b1b2412010-01-21 19:09:51 -0800734
735 if (useLargestChild) {
736 largestChildHeight = Math.max(childHeight, largestChildHeight);
737 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 }
739
740 /**
741 * If applicable, compute the additional offset to the child's baseline
742 * we'll need later when asked {@link #getBaseline}.
743 */
744 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
745 mBaselineChildTop = mTotalLength;
746 }
747
748 // if we are trying to use a child index for our baseline, the above
749 // book keeping only works if there are no children above it with
750 // weight. fail fast to aid the developer.
751 if (i < baselineChildIndex && lp.weight > 0) {
752 throw new RuntimeException("A child of LinearLayout with index "
753 + "less than mBaselineAlignedChildIndex has weight > 0, which "
754 + "won't work. Either remove the weight, or don't set "
755 + "mBaselineAlignedChildIndex.");
756 }
757
758 boolean matchWidthLocally = false;
Romain Guy980a9382010-01-08 15:06:28 -0800759 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 // The width of the linear layout will scale, and at least one
761 // child said it wanted to match our width. Set a flag
762 // indicating that we need to remeasure at least that view when
763 // we know our width.
764 matchWidth = true;
765 matchWidthLocally = true;
766 }
767
768 final int margin = lp.leftMargin + lp.rightMargin;
769 final int measuredWidth = child.getMeasuredWidth() + margin;
770 maxWidth = Math.max(maxWidth, measuredWidth);
Dianne Hackborn189ee182010-12-02 21:48:53 -0800771 childState = combineMeasuredStates(childState, child.getMeasuredState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772
Romain Guy980a9382010-01-08 15:06:28 -0800773 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 if (lp.weight > 0) {
775 /*
776 * Widths of weighted Views are bogus if we end up
777 * remeasuring, so keep them separate.
778 */
779 weightedMaxWidth = Math.max(weightedMaxWidth,
780 matchWidthLocally ? margin : measuredWidth);
781 } else {
782 alternativeMaxWidth = Math.max(alternativeMaxWidth,
783 matchWidthLocally ? margin : measuredWidth);
784 }
785
786 i += getChildrenSkipCount(child, i);
787 }
Romain Guy5b1b2412010-01-21 19:09:51 -0800788
Adam Powell696cba52011-03-29 10:38:16 -0700789 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800790 mTotalLength += mDividerHeight;
791 }
792
Adam Powellf8ac6b72011-05-23 18:14:09 -0700793 if (useLargestChild &&
794 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
Romain Guy5b1b2412010-01-21 19:09:51 -0800795 mTotalLength = 0;
796
797 for (int i = 0; i < count; ++i) {
798 final View child = getVirtualChildAt(i);
799
800 if (child == null) {
801 mTotalLength += measureNullChild(i);
802 continue;
803 }
804
805 if (child.getVisibility() == GONE) {
806 i += getChildrenSkipCount(child, i);
807 continue;
808 }
809
810 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
811 child.getLayoutParams();
Romain Guy053b4802010-02-05 15:34:33 -0800812 // Account for negative margins
813 final int totalLength = mTotalLength;
814 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
815 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
Romain Guy5b1b2412010-01-21 19:09:51 -0800816 }
817 }
818
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 // Add in our padding
820 mTotalLength += mPaddingTop + mPaddingBottom;
821
822 int heightSize = mTotalLength;
823
824 // Check against our minimum height
825 heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
826
827 // Reconcile our calculated size with the heightMeasureSpec
Dianne Hackborn189ee182010-12-02 21:48:53 -0800828 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
829 heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
831 // Either expand children with weight to take up available space or
Alan Viverette65be9cc2014-02-24 18:21:09 -0800832 // shrink them if they extend beyond our current bounds. If we skipped
833 // measurement on any children, we need to measure them now.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 int delta = heightSize - mTotalLength;
Alan Viverette65be9cc2014-02-24 18:21:09 -0800835 if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
837
838 mTotalLength = 0;
839
840 for (int i = 0; i < count; ++i) {
841 final View child = getVirtualChildAt(i);
842
843 if (child.getVisibility() == View.GONE) {
844 continue;
845 }
846
847 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
848
849 float childExtra = lp.weight;
850 if (childExtra > 0) {
851 // Child said it could absorb extra space -- give him his share
852 int share = (int) (childExtra * delta / weightSum);
853 weightSum -= childExtra;
854 delta -= share;
855
856 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
857 mPaddingLeft + mPaddingRight +
858 lp.leftMargin + lp.rightMargin, lp.width);
859
860 // TODO: Use a field like lp.isMeasured to figure out if this
861 // child has been previously measured
862 if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
863 // child was measured once already above...
864 // base new measurement on stored values
865 int childHeight = child.getMeasuredHeight() + share;
866 if (childHeight < 0) {
867 childHeight = 0;
868 }
869
870 child.measure(childWidthMeasureSpec,
871 MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
872 } else {
873 // child was skipped in the loop above.
874 // Measure for this first time here
875 child.measure(childWidthMeasureSpec,
876 MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
877 MeasureSpec.EXACTLY));
878 }
Dianne Hackborn189ee182010-12-02 21:48:53 -0800879
880 // Child may now not fit in vertical dimension.
881 childState = combineMeasuredStates(childState, child.getMeasuredState()
882 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 }
884
885 final int margin = lp.leftMargin + lp.rightMargin;
886 final int measuredWidth = child.getMeasuredWidth() + margin;
887 maxWidth = Math.max(maxWidth, measuredWidth);
888
889 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
Romain Guy980a9382010-01-08 15:06:28 -0800890 lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891
892 alternativeMaxWidth = Math.max(alternativeMaxWidth,
893 matchWidthLocally ? margin : measuredWidth);
894
Romain Guy980a9382010-01-08 15:06:28 -0800895 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896
Romain Guy053b4802010-02-05 15:34:33 -0800897 final int totalLength = mTotalLength;
898 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
899 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 }
901
902 // Add in our padding
Romain Guy053b4802010-02-05 15:34:33 -0800903 mTotalLength += mPaddingTop + mPaddingBottom;
904 // TODO: Should we recompute the heightSpec based on the new total length?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 } else {
906 alternativeMaxWidth = Math.max(alternativeMaxWidth,
907 weightedMaxWidth);
Adam Powellf8ac6b72011-05-23 18:14:09 -0700908
909
910 // We have no limit, so make all weighted views as tall as the largest child.
911 // Children will have already been measured once.
Adam Powelleabc9192012-05-04 14:49:46 -0700912 if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
Adam Powellf8ac6b72011-05-23 18:14:09 -0700913 for (int i = 0; i < count; i++) {
914 final View child = getVirtualChildAt(i);
915
916 if (child == null || child.getVisibility() == View.GONE) {
917 continue;
918 }
919
920 final LinearLayout.LayoutParams lp =
921 (LinearLayout.LayoutParams) child.getLayoutParams();
922
923 float childExtra = lp.weight;
924 if (childExtra > 0) {
925 child.measure(
926 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
927 MeasureSpec.EXACTLY),
928 MeasureSpec.makeMeasureSpec(largestChildHeight,
929 MeasureSpec.EXACTLY));
930 }
931 }
932 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 }
934
935 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
936 maxWidth = alternativeMaxWidth;
937 }
938
939 maxWidth += mPaddingLeft + mPaddingRight;
940
941 // Check against our minimum width
942 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
943
Dianne Hackborn189ee182010-12-02 21:48:53 -0800944 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
945 heightSizeAndState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946
947 if (matchWidth) {
948 forceUniformWidth(count, heightMeasureSpec);
949 }
950 }
951
952 private void forceUniformWidth(int count, int heightMeasureSpec) {
953 // Pretend that the linear layout has an exact size.
954 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
955 MeasureSpec.EXACTLY);
956 for (int i = 0; i< count; ++i) {
957 final View child = getVirtualChildAt(i);
958 if (child.getVisibility() != GONE) {
959 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
960
Romain Guy980a9382010-01-08 15:06:28 -0800961 if (lp.width == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 // Temporarily force children to reuse their old measured height
963 // FIXME: this may not be right for something like wrapping text?
964 int oldHeight = lp.height;
965 lp.height = child.getMeasuredHeight();
966
967 // Remeasue with new dimensions
968 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
969 lp.height = oldHeight;
970 }
971 }
972 }
973 }
974
975 /**
976 * Measures the children when the orientation of this LinearLayout is set
977 * to {@link #HORIZONTAL}.
978 *
979 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
980 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
981 *
982 * @see #getOrientation()
983 * @see #setOrientation(int)
984 * @see #onMeasure(int, int)
985 */
986 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
987 mTotalLength = 0;
988 int maxHeight = 0;
Dianne Hackborn189ee182010-12-02 21:48:53 -0800989 int childState = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 int alternativeMaxHeight = 0;
991 int weightedMaxHeight = 0;
992 boolean allFillParent = true;
993 float totalWeight = 0;
994
995 final int count = getVirtualChildCount();
996
997 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
998 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
999
1000 boolean matchHeight = false;
Alan Viverette65be9cc2014-02-24 18:21:09 -08001001 boolean skippedMeasure = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002
1003 if (mMaxAscent == null || mMaxDescent == null) {
1004 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
1005 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
1006 }
1007
1008 final int[] maxAscent = mMaxAscent;
1009 final int[] maxDescent = mMaxDescent;
1010
1011 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1012 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1013
1014 final boolean baselineAligned = mBaselineAligned;
Romain Guy5b1b2412010-01-21 19:09:51 -08001015 final boolean useLargestChild = mUseLargestChild;
Romain Guyc3520902010-02-25 11:01:01 -08001016
1017 final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
Romain Guy5b1b2412010-01-21 19:09:51 -08001018
1019 int largestChildWidth = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020
1021 // See how wide everyone is. Also remember max height.
1022 for (int i = 0; i < count; ++i) {
1023 final View child = getVirtualChildAt(i);
1024
1025 if (child == null) {
1026 mTotalLength += measureNullChild(i);
1027 continue;
1028 }
1029
1030 if (child.getVisibility() == GONE) {
1031 i += getChildrenSkipCount(child, i);
1032 continue;
1033 }
1034
Adam Powell696cba52011-03-29 10:38:16 -07001035 if (hasDividerBeforeChildAt(i)) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001036 mTotalLength += mDividerWidth;
1037 }
1038
Romain Guy5b1b2412010-01-21 19:09:51 -08001039 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1040 child.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041
1042 totalWeight += lp.weight;
1043
1044 if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
1045 // Optimization: don't bother measuring children who are going to use
1046 // leftover space. These views will get measured again down below if
1047 // there is any leftover space.
Romain Guyc3520902010-02-25 11:01:01 -08001048 if (isExactly) {
1049 mTotalLength += lp.leftMargin + lp.rightMargin;
1050 } else {
1051 final int totalLength = mTotalLength;
1052 mTotalLength = Math.max(totalLength, totalLength +
1053 lp.leftMargin + lp.rightMargin);
1054 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055
1056 // Baseline alignment requires to measure widgets to obtain the
Romain Guy6c5664a2010-02-24 18:55:22 -08001057 // baseline offset (in particular for TextViews). The following
1058 // defeats the optimization mentioned above. Allow the child to
1059 // use as much space as it wants because we can shrink things
1060 // later (and re-measure).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 if (baselineAligned) {
1062 final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
1063 child.measure(freeSpec, freeSpec);
Alan Viverette65be9cc2014-02-24 18:21:09 -08001064 } else {
1065 skippedMeasure = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 }
1067 } else {
1068 int oldWidth = Integer.MIN_VALUE;
1069
1070 if (lp.width == 0 && lp.weight > 0) {
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08001071 // widthMode is either UNSPECIFIED or AT_MOST, and this
1072 // child
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 // wanted to stretch to fill available space. Translate that to
1074 // WRAP_CONTENT so that it does not end up with a width of 0
1075 oldWidth = 0;
1076 lp.width = LayoutParams.WRAP_CONTENT;
1077 }
1078
1079 // Determine how big this child would like to be. If this or
1080 // previous children have given a weight, then we allow it to
1081 // use all available space (and we will shrink things later
1082 // if needed).
1083 measureChildBeforeLayout(child, i, widthMeasureSpec,
1084 totalWeight == 0 ? mTotalLength : 0,
1085 heightMeasureSpec, 0);
1086
1087 if (oldWidth != Integer.MIN_VALUE) {
1088 lp.width = oldWidth;
1089 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001090
1091 final int childWidth = child.getMeasuredWidth();
Romain Guyc3520902010-02-25 11:01:01 -08001092 if (isExactly) {
1093 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
1094 getNextLocationOffset(child);
1095 } else {
1096 final int totalLength = mTotalLength;
1097 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
1098 lp.rightMargin + getNextLocationOffset(child));
1099 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001100
1101 if (useLargestChild) {
1102 largestChildWidth = Math.max(childWidth, largestChildWidth);
1103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 }
1105
1106 boolean matchHeightLocally = false;
Romain Guy980a9382010-01-08 15:06:28 -08001107 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 // The height of the linear layout will scale, and at least one
1109 // child said it wanted to match our height. Set a flag indicating that
1110 // we need to remeasure at least that view when we know our height.
1111 matchHeight = true;
1112 matchHeightLocally = true;
1113 }
1114
1115 final int margin = lp.topMargin + lp.bottomMargin;
1116 final int childHeight = child.getMeasuredHeight() + margin;
Dianne Hackborn189ee182010-12-02 21:48:53 -08001117 childState = combineMeasuredStates(childState, child.getMeasuredState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118
1119 if (baselineAligned) {
1120 final int childBaseline = child.getBaseline();
1121 if (childBaseline != -1) {
1122 // Translates the child's vertical gravity into an index
1123 // in the range 0..VERTICAL_GRAVITY_COUNT
1124 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1125 & Gravity.VERTICAL_GRAVITY_MASK;
1126 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1127 & ~Gravity.AXIS_SPECIFIED) >> 1;
1128
1129 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1130 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
1131 }
1132 }
1133
1134 maxHeight = Math.max(maxHeight, childHeight);
1135
Romain Guy980a9382010-01-08 15:06:28 -08001136 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 if (lp.weight > 0) {
1138 /*
1139 * Heights of weighted Views are bogus if we end up
1140 * remeasuring, so keep them separate.
1141 */
1142 weightedMaxHeight = Math.max(weightedMaxHeight,
1143 matchHeightLocally ? margin : childHeight);
1144 } else {
1145 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1146 matchHeightLocally ? margin : childHeight);
1147 }
1148
1149 i += getChildrenSkipCount(child, i);
1150 }
1151
Adam Powell696cba52011-03-29 10:38:16 -07001152 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001153 mTotalLength += mDividerWidth;
1154 }
1155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1157 // the most common case
1158 if (maxAscent[INDEX_TOP] != -1 ||
1159 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1160 maxAscent[INDEX_BOTTOM] != -1 ||
1161 maxAscent[INDEX_FILL] != -1) {
1162 final int ascent = Math.max(maxAscent[INDEX_FILL],
1163 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1164 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1165 final int descent = Math.max(maxDescent[INDEX_FILL],
1166 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1167 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1168 maxHeight = Math.max(maxHeight, ascent + descent);
1169 }
1170
Adam Powellf8ac6b72011-05-23 18:14:09 -07001171 if (useLargestChild &&
1172 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
Romain Guy5b1b2412010-01-21 19:09:51 -08001173 mTotalLength = 0;
1174
1175 for (int i = 0; i < count; ++i) {
1176 final View child = getVirtualChildAt(i);
1177
1178 if (child == null) {
1179 mTotalLength += measureNullChild(i);
1180 continue;
1181 }
1182
1183 if (child.getVisibility() == GONE) {
1184 i += getChildrenSkipCount(child, i);
1185 continue;
1186 }
1187
1188 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1189 child.getLayoutParams();
Romain Guyc3520902010-02-25 11:01:01 -08001190 if (isExactly) {
1191 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
1192 getNextLocationOffset(child);
1193 } else {
1194 final int totalLength = mTotalLength;
1195 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
1196 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1197 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001198 }
1199 }
1200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 // Add in our padding
1202 mTotalLength += mPaddingLeft + mPaddingRight;
1203
1204 int widthSize = mTotalLength;
1205
1206 // Check against our minimum width
1207 widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
1208
1209 // Reconcile our calculated size with the widthMeasureSpec
Dianne Hackborn189ee182010-12-02 21:48:53 -08001210 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
1211 widthSize = widthSizeAndState & MEASURED_SIZE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212
1213 // Either expand children with weight to take up available space or
Alan Viverette65be9cc2014-02-24 18:21:09 -08001214 // shrink them if they extend beyond our current bounds. If we skipped
1215 // measurement on any children, we need to measure them now.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 int delta = widthSize - mTotalLength;
Alan Viverette65be9cc2014-02-24 18:21:09 -08001217 if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
1219
1220 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1221 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1222 maxHeight = -1;
1223
1224 mTotalLength = 0;
1225
1226 for (int i = 0; i < count; ++i) {
1227 final View child = getVirtualChildAt(i);
1228
1229 if (child == null || child.getVisibility() == View.GONE) {
1230 continue;
1231 }
1232
1233 final LinearLayout.LayoutParams lp =
1234 (LinearLayout.LayoutParams) child.getLayoutParams();
1235
1236 float childExtra = lp.weight;
1237 if (childExtra > 0) {
1238 // Child said it could absorb extra space -- give him his share
1239 int share = (int) (childExtra * delta / weightSum);
1240 weightSum -= childExtra;
1241 delta -= share;
1242
1243 final int childHeightMeasureSpec = getChildMeasureSpec(
1244 heightMeasureSpec,
1245 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
1246 lp.height);
1247
1248 // TODO: Use a field like lp.isMeasured to figure out if this
1249 // child has been previously measured
1250 if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
1251 // child was measured once already above ... base new measurement
1252 // on stored values
1253 int childWidth = child.getMeasuredWidth() + share;
1254 if (childWidth < 0) {
1255 childWidth = 0;
1256 }
1257
1258 child.measure(
1259 MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
1260 childHeightMeasureSpec);
1261 } else {
1262 // child was skipped in the loop above. Measure for this first time here
1263 child.measure(MeasureSpec.makeMeasureSpec(
1264 share > 0 ? share : 0, MeasureSpec.EXACTLY),
1265 childHeightMeasureSpec);
1266 }
Dianne Hackborn189ee182010-12-02 21:48:53 -08001267
1268 // Child may now not fit in horizontal dimension.
1269 childState = combineMeasuredStates(childState,
1270 child.getMeasuredState() & MEASURED_STATE_MASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 }
1272
Romain Guyc3520902010-02-25 11:01:01 -08001273 if (isExactly) {
1274 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
1275 getNextLocationOffset(child);
1276 } else {
1277 final int totalLength = mTotalLength;
1278 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
1279 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1280 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281
1282 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
Romain Guy980a9382010-01-08 15:06:28 -08001283 lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001284
1285 final int margin = lp.topMargin + lp .bottomMargin;
1286 int childHeight = child.getMeasuredHeight() + margin;
1287 maxHeight = Math.max(maxHeight, childHeight);
1288 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1289 matchHeightLocally ? margin : childHeight);
1290
Romain Guy980a9382010-01-08 15:06:28 -08001291 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292
1293 if (baselineAligned) {
1294 final int childBaseline = child.getBaseline();
1295 if (childBaseline != -1) {
1296 // Translates the child's vertical gravity into an index in the range 0..2
1297 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1298 & Gravity.VERTICAL_GRAVITY_MASK;
1299 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1300 & ~Gravity.AXIS_SPECIFIED) >> 1;
1301
1302 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1303 maxDescent[index] = Math.max(maxDescent[index],
1304 childHeight - childBaseline);
1305 }
1306 }
1307 }
1308
1309 // Add in our padding
1310 mTotalLength += mPaddingLeft + mPaddingRight;
Romain Guy053b4802010-02-05 15:34:33 -08001311 // TODO: Should we update widthSize with the new total length?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312
1313 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1314 // the most common case
1315 if (maxAscent[INDEX_TOP] != -1 ||
1316 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1317 maxAscent[INDEX_BOTTOM] != -1 ||
1318 maxAscent[INDEX_FILL] != -1) {
1319 final int ascent = Math.max(maxAscent[INDEX_FILL],
1320 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1321 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1322 final int descent = Math.max(maxDescent[INDEX_FILL],
1323 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1324 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1325 maxHeight = Math.max(maxHeight, ascent + descent);
1326 }
1327 } else {
1328 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
Adam Powellf8ac6b72011-05-23 18:14:09 -07001329
1330 // We have no limit, so make all weighted views as wide as the largest child.
1331 // Children will have already been measured once.
Adam Powelleabc9192012-05-04 14:49:46 -07001332 if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
Adam Powellf8ac6b72011-05-23 18:14:09 -07001333 for (int i = 0; i < count; i++) {
1334 final View child = getVirtualChildAt(i);
1335
1336 if (child == null || child.getVisibility() == View.GONE) {
1337 continue;
1338 }
1339
1340 final LinearLayout.LayoutParams lp =
1341 (LinearLayout.LayoutParams) child.getLayoutParams();
1342
1343 float childExtra = lp.weight;
1344 if (childExtra > 0) {
1345 child.measure(
1346 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
1347 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
1348 MeasureSpec.EXACTLY));
1349 }
1350 }
1351 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 }
1353
1354 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
1355 maxHeight = alternativeMaxHeight;
1356 }
1357
1358 maxHeight += mPaddingTop + mPaddingBottom;
1359
1360 // Check against our minimum height
1361 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
1362
Dianne Hackborn189ee182010-12-02 21:48:53 -08001363 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
1364 resolveSizeAndState(maxHeight, heightMeasureSpec,
1365 (childState<<MEASURED_HEIGHT_STATE_SHIFT)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366
1367 if (matchHeight) {
1368 forceUniformHeight(count, widthMeasureSpec);
1369 }
1370 }
1371
1372 private void forceUniformHeight(int count, int widthMeasureSpec) {
1373 // Pretend that the linear layout has an exact size. This is the measured height of
1374 // ourselves. The measured height should be the max height of the children, changed
Fabrice Di Megliofc53e582012-10-25 11:45:04 -07001375 // to accommodate the heightMeasureSpec from the parent
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
1377 MeasureSpec.EXACTLY);
1378 for (int i = 0; i < count; ++i) {
1379 final View child = getVirtualChildAt(i);
1380 if (child.getVisibility() != GONE) {
1381 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
1382
Romain Guy980a9382010-01-08 15:06:28 -08001383 if (lp.height == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 // Temporarily force children to reuse their old measured width
1385 // FIXME: this may not be right for something like wrapping text?
1386 int oldWidth = lp.width;
1387 lp.width = child.getMeasuredWidth();
1388
1389 // Remeasure with new dimensions
1390 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
1391 lp.width = oldWidth;
1392 }
1393 }
1394 }
1395 }
1396
1397 /**
1398 * <p>Returns the number of children to skip after measuring/laying out
1399 * the specified child.</p>
1400 *
1401 * @param child the child after which we want to skip children
1402 * @param index the index of the child after which we want to skip children
1403 * @return the number of children to skip, 0 by default
1404 */
1405 int getChildrenSkipCount(View child, int index) {
1406 return 0;
1407 }
1408
1409 /**
1410 * <p>Returns the size (width or height) that should be occupied by a null
1411 * child.</p>
1412 *
1413 * @param childIndex the index of the null child
1414 * @return the width or height of the child depending on the orientation
1415 */
1416 int measureNullChild(int childIndex) {
1417 return 0;
1418 }
1419
1420 /**
1421 * <p>Measure the child according to the parent's measure specs. This
1422 * method should be overriden by subclasses to force the sizing of
1423 * children. This method is called by {@link #measureVertical(int, int)} and
1424 * {@link #measureHorizontal(int, int)}.</p>
1425 *
1426 * @param child the child to measure
1427 * @param childIndex the index of the child in this view
1428 * @param widthMeasureSpec horizontal space requirements as imposed by the parent
1429 * @param totalWidth extra space that has been used up by the parent horizontally
1430 * @param heightMeasureSpec vertical space requirements as imposed by the parent
1431 * @param totalHeight extra space that has been used up by the parent vertically
1432 */
1433 void measureChildBeforeLayout(View child, int childIndex,
1434 int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
1435 int totalHeight) {
1436 measureChildWithMargins(child, widthMeasureSpec, totalWidth,
1437 heightMeasureSpec, totalHeight);
1438 }
1439
1440 /**
1441 * <p>Return the location offset of the specified child. This can be used
1442 * by subclasses to change the location of a given widget.</p>
1443 *
1444 * @param child the child for which to obtain the location offset
1445 * @return the location offset in pixels
1446 */
1447 int getLocationOffset(View child) {
1448 return 0;
1449 }
1450
1451 /**
1452 * <p>Return the size offset of the next sibling of the specified child.
1453 * This can be used by subclasses to change the location of the widget
1454 * following <code>child</code>.</p>
1455 *
1456 * @param child the child whose next sibling will be moved
1457 * @return the location offset of the next child in pixels
1458 */
1459 int getNextLocationOffset(View child) {
1460 return 0;
1461 }
1462
1463 @Override
1464 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1465 if (mOrientation == VERTICAL) {
Philip Milnead365cc2012-09-27 14:38:46 -07001466 layoutVertical(l, t, r, b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 } else {
Philip Milnead365cc2012-09-27 14:38:46 -07001468 layoutHorizontal(l, t, r, b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 }
1470 }
1471
1472 /**
1473 * Position the children during a layout pass if the orientation of this
1474 * LinearLayout is set to {@link #VERTICAL}.
1475 *
1476 * @see #getOrientation()
1477 * @see #setOrientation(int)
1478 * @see #onLayout(boolean, int, int, int, int)
Philip Milnead365cc2012-09-27 14:38:46 -07001479 * @param left
1480 * @param top
1481 * @param right
1482 * @param bottom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 */
Philip Milnead365cc2012-09-27 14:38:46 -07001484 void layoutVertical(int left, int top, int right, int bottom) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 final int paddingLeft = mPaddingLeft;
1486
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001487 int childTop;
Romain Guy5b1b2412010-01-21 19:09:51 -08001488 int childLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001489
1490 // Where right end of child should go
Philip Milnead365cc2012-09-27 14:38:46 -07001491 final int width = right - left;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 int childRight = width - mPaddingRight;
1493
1494 // Space available for child
1495 int childSpace = width - paddingLeft - mPaddingRight;
1496
1497 final int count = getVirtualChildCount();
1498
1499 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001500 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001502 switch (majorGravity) {
1503 case Gravity.BOTTOM:
1504 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001505 childTop = mPaddingTop + bottom - top - mTotalLength;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001506 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001508 // mTotalLength contains the padding already
1509 case Gravity.CENTER_VERTICAL:
Philip Milnead365cc2012-09-27 14:38:46 -07001510 childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001511 break;
1512
1513 case Gravity.TOP:
1514 default:
1515 childTop = mPaddingTop;
1516 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 }
Adam Powellfcca00a2010-11-30 21:26:29 -08001518
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001519 for (int i = 0; i < count; i++) {
1520 final View child = getVirtualChildAt(i);
1521 if (child == null) {
1522 childTop += measureNullChild(i);
1523 } else if (child.getVisibility() != GONE) {
1524 final int childWidth = child.getMeasuredWidth();
1525 final int childHeight = child.getMeasuredHeight();
1526
1527 final LinearLayout.LayoutParams lp =
1528 (LinearLayout.LayoutParams) child.getLayoutParams();
1529
1530 int gravity = lp.gravity;
1531 if (gravity < 0) {
1532 gravity = minorGravity;
1533 }
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07001534 final int layoutDirection = getLayoutDirection();
Fabrice Di Meglioc0053222011-06-13 12:16:51 -07001535 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
Fabrice Di Megliode35cee2011-06-01 15:13:50 -07001536 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 case Gravity.CENTER_HORIZONTAL:
1538 childLeft = paddingLeft + ((childSpace - childWidth) / 2)
1539 + lp.leftMargin - lp.rightMargin;
1540 break;
1541
1542 case Gravity.RIGHT:
1543 childLeft = childRight - childWidth - lp.rightMargin;
1544 break;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001545
1546 case Gravity.LEFT:
Romain Guy611cd3f2009-12-15 12:00:37 -08001547 default:
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001548 childLeft = paddingLeft + lp.leftMargin;
Romain Guy611cd3f2009-12-15 12:00:37 -08001549 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 }
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001551
Adam Powell696cba52011-03-29 10:38:16 -07001552 if (hasDividerBeforeChildAt(i)) {
1553 childTop += mDividerHeight;
1554 }
1555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001556 childTop += lp.topMargin;
1557 setChildFrame(child, childLeft, childTop + getLocationOffset(child),
1558 childWidth, childHeight);
1559 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
1560
1561 i += getChildrenSkipCount(child, i);
1562 }
1563 }
1564 }
1565
1566 /**
1567 * Position the children during a layout pass if the orientation of this
1568 * LinearLayout is set to {@link #HORIZONTAL}.
1569 *
1570 * @see #getOrientation()
1571 * @see #setOrientation(int)
1572 * @see #onLayout(boolean, int, int, int, int)
Philip Milnead365cc2012-09-27 14:38:46 -07001573 * @param left
1574 * @param top
1575 * @param right
1576 * @param bottom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 */
Philip Milnead365cc2012-09-27 14:38:46 -07001578 void layoutHorizontal(int left, int top, int right, int bottom) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001579 final boolean isLayoutRtl = isLayoutRtl();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 final int paddingTop = mPaddingTop;
1581
Romain Guy5b1b2412010-01-21 19:09:51 -08001582 int childTop;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001583 int childLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584
1585 // Where bottom of child should go
Philip Milnead365cc2012-09-27 14:38:46 -07001586 final int height = bottom - top;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 int childBottom = height - mPaddingBottom;
1588
1589 // Space available for child
1590 int childSpace = height - paddingTop - mPaddingBottom;
1591
1592 final int count = getVirtualChildCount();
1593
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001594 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1596
1597 final boolean baselineAligned = mBaselineAligned;
1598
1599 final int[] maxAscent = mMaxAscent;
1600 final int[] maxDescent = mMaxDescent;
1601
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07001602 final int layoutDirection = getLayoutDirection();
Fabrice Di Meglioc0053222011-06-13 12:16:51 -07001603 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001604 case Gravity.RIGHT:
1605 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001606 childLeft = mPaddingLeft + right - left - mTotalLength;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001607 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001608
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001609 case Gravity.CENTER_HORIZONTAL:
1610 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001611 childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001612 break;
1613
1614 case Gravity.LEFT:
1615 default:
1616 childLeft = mPaddingLeft;
1617 break;
1618 }
1619
1620 int start = 0;
1621 int dir = 1;
1622 //In case of RTL, start drawing from the last child.
1623 if (isLayoutRtl) {
1624 start = count - 1;
1625 dir = -1;
Adam Powellfcca00a2010-11-30 21:26:29 -08001626 }
1627
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 for (int i = 0; i < count; i++) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001629 int childIndex = start + dir * i;
1630 final View child = getVirtualChildAt(childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631
1632 if (child == null) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001633 childLeft += measureNullChild(childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 } else if (child.getVisibility() != GONE) {
1635 final int childWidth = child.getMeasuredWidth();
1636 final int childHeight = child.getMeasuredHeight();
1637 int childBaseline = -1;
1638
1639 final LinearLayout.LayoutParams lp =
1640 (LinearLayout.LayoutParams) child.getLayoutParams();
1641
Romain Guy980a9382010-01-08 15:06:28 -08001642 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 childBaseline = child.getBaseline();
1644 }
1645
1646 int gravity = lp.gravity;
1647 if (gravity < 0) {
1648 gravity = minorGravity;
1649 }
1650
1651 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
1652 case Gravity.TOP:
1653 childTop = paddingTop + lp.topMargin;
1654 if (childBaseline != -1) {
1655 childTop += maxAscent[INDEX_TOP] - childBaseline;
1656 }
1657 break;
1658
1659 case Gravity.CENTER_VERTICAL:
Romain Guy5b1b2412010-01-21 19:09:51 -08001660 // Removed support for baseline alignment when layout_gravity or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001661 // gravity == center_vertical. See bug #1038483.
1662 // Keep the code around if we need to re-enable this feature
1663 // if (childBaseline != -1) {
1664 // // Align baselines vertically only if the child is smaller than us
1665 // if (childSpace - childHeight > 0) {
1666 // childTop = paddingTop + (childSpace / 2) - childBaseline;
1667 // } else {
1668 // childTop = paddingTop + (childSpace - childHeight) / 2;
1669 // }
1670 // } else {
1671 childTop = paddingTop + ((childSpace - childHeight) / 2)
1672 + lp.topMargin - lp.bottomMargin;
1673 break;
1674
1675 case Gravity.BOTTOM:
1676 childTop = childBottom - childHeight - lp.bottomMargin;
1677 if (childBaseline != -1) {
1678 int descent = child.getMeasuredHeight() - childBaseline;
1679 childTop -= (maxDescent[INDEX_BOTTOM] - descent);
1680 }
1681 break;
Romain Guy611cd3f2009-12-15 12:00:37 -08001682 default:
1683 childTop = paddingTop;
1684 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 }
1686
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001687 if (hasDividerBeforeChildAt(childIndex)) {
Adam Powell696cba52011-03-29 10:38:16 -07001688 childLeft += mDividerWidth;
1689 }
1690
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001691 childLeft += lp.leftMargin;
1692 setChildFrame(child, childLeft + getLocationOffset(child), childTop,
1693 childWidth, childHeight);
1694 childLeft += childWidth + lp.rightMargin +
1695 getNextLocationOffset(child);
1696
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001697 i += getChildrenSkipCount(child, childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698 }
1699 }
1700 }
1701
1702 private void setChildFrame(View child, int left, int top, int width, int height) {
1703 child.layout(left, top, left + width, top + height);
1704 }
1705
1706 /**
1707 * Should the layout be a column or a row.
Tor Norbyed9273d62013-05-30 15:59:53 -07001708 * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default
1709 * value is {@link #HORIZONTAL}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 *
1711 * @attr ref android.R.styleable#LinearLayout_orientation
1712 */
Tor Norbyed9273d62013-05-30 15:59:53 -07001713 public void setOrientation(@OrientationMode int orientation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001714 if (mOrientation != orientation) {
1715 mOrientation = orientation;
1716 requestLayout();
1717 }
1718 }
1719
1720 /**
1721 * Returns the current orientation.
1722 *
1723 * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
1724 */
Tor Norbyed9273d62013-05-30 15:59:53 -07001725 @OrientationMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 public int getOrientation() {
1727 return mOrientation;
1728 }
1729
1730 /**
1731 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
1732 * this layout has a VERTICAL orientation, this controls where all the child
1733 * views are placed if there is extra vertical space. If this layout has a
1734 * HORIZONTAL orientation, this controls the alignment of the children.
1735 *
1736 * @param gravity See {@link android.view.Gravity}
1737 *
1738 * @attr ref android.R.styleable#LinearLayout_gravity
1739 */
1740 @android.view.RemotableViewMethod
1741 public void setGravity(int gravity) {
1742 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001743 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -07001744 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 }
1746
1747 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
1748 gravity |= Gravity.TOP;
1749 }
1750
1751 mGravity = gravity;
1752 requestLayout();
1753 }
1754 }
1755
1756 @android.view.RemotableViewMethod
1757 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001758 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1759 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
1760 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001761 requestLayout();
1762 }
1763 }
1764
1765 @android.view.RemotableViewMethod
1766 public void setVerticalGravity(int verticalGravity) {
1767 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1768 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1769 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1770 requestLayout();
1771 }
1772 }
1773
1774 @Override
1775 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1776 return new LinearLayout.LayoutParams(getContext(), attrs);
1777 }
1778
1779 /**
1780 * Returns a set of layout parameters with a width of
Romain Guy980a9382010-01-08 15:06:28 -08001781 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1783 * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1784 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1785 * and the height to {@link LayoutParams#WRAP_CONTENT}.
1786 */
1787 @Override
1788 protected LayoutParams generateDefaultLayoutParams() {
1789 if (mOrientation == HORIZONTAL) {
1790 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1791 } else if (mOrientation == VERTICAL) {
Romain Guy980a9382010-01-08 15:06:28 -08001792 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001793 }
1794 return null;
1795 }
1796
1797 @Override
1798 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1799 return new LayoutParams(p);
1800 }
1801
1802
1803 // Override to allow type-checking of LayoutParams.
1804 @Override
1805 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1806 return p instanceof LinearLayout.LayoutParams;
1807 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001808
1809 @Override
1810 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1811 super.onInitializeAccessibilityEvent(event);
1812 event.setClassName(LinearLayout.class.getName());
1813 }
1814
1815 @Override
1816 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1817 super.onInitializeAccessibilityNodeInfo(info);
1818 info.setClassName(LinearLayout.class.getName());
1819 }
1820
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 /**
1822 * Per-child layout information associated with ViewLinearLayout.
1823 *
1824 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
1825 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
1826 */
1827 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1828 /**
1829 * Indicates how much of the extra space in the LinearLayout will be
1830 * allocated to the view associated with these LayoutParams. Specify
1831 * 0 if the view should not be stretched. Otherwise the extra pixels
1832 * will be pro-rated among all views whose weight is greater than 0.
1833 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001834 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001835 public float weight;
1836
1837 /**
1838 * Gravity for the view associated with these LayoutParams.
1839 *
1840 * @see android.view.Gravity
1841 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001842 @ViewDebug.ExportedProperty(category = "layout", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 @ViewDebug.IntToString(from = -1, to = "NONE"),
1844 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
1845 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
1846 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
1847 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
1848 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -07001849 @ViewDebug.IntToString(from = Gravity.START, to = "START"),
1850 @ViewDebug.IntToString(from = Gravity.END, to = "END"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
1852 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
1853 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
1854 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
1855 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
1856 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
1857 })
1858 public int gravity = -1;
1859
1860 /**
1861 * {@inheritDoc}
1862 */
1863 public LayoutParams(Context c, AttributeSet attrs) {
1864 super(c, attrs);
1865 TypedArray a =
1866 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
1867
1868 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
1869 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
1870
1871 a.recycle();
1872 }
1873
1874 /**
1875 * {@inheritDoc}
1876 */
1877 public LayoutParams(int width, int height) {
1878 super(width, height);
1879 weight = 0;
1880 }
1881
1882 /**
1883 * Creates a new set of layout parameters with the specified width, height
1884 * and weight.
1885 *
Romain Guy980a9382010-01-08 15:06:28 -08001886 * @param width the width, either {@link #MATCH_PARENT},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887 * {@link #WRAP_CONTENT} or a fixed size in pixels
Romain Guy980a9382010-01-08 15:06:28 -08001888 * @param height the height, either {@link #MATCH_PARENT},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889 * {@link #WRAP_CONTENT} or a fixed size in pixels
1890 * @param weight the weight
1891 */
1892 public LayoutParams(int width, int height, float weight) {
1893 super(width, height);
1894 this.weight = weight;
1895 }
1896
1897 /**
1898 * {@inheritDoc}
1899 */
1900 public LayoutParams(ViewGroup.LayoutParams p) {
1901 super(p);
1902 }
1903
1904 /**
1905 * {@inheritDoc}
1906 */
Alan Viverette0a0e1552013-08-07 13:24:09 -07001907 public LayoutParams(ViewGroup.MarginLayoutParams source) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001908 super(source);
1909 }
1910
Alan Viverette0a0e1552013-08-07 13:24:09 -07001911 /**
1912 * Copy constructor. Clones the width, height, margin values, weight,
1913 * and gravity of the source.
1914 *
1915 * @param source The layout params to copy from.
1916 */
1917 public LayoutParams(LayoutParams source) {
1918 super(source);
1919
1920 this.weight = source.weight;
1921 this.gravity = source.gravity;
1922 }
1923
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 @Override
1925 public String debug(String output) {
1926 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
1927 ", height=" + sizeToString(height) + " weight=" + weight + "}";
1928 }
1929 }
1930}