blob: ad60a953dafa247330b6b1e8529786e0fc1c5df0 [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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.Context;
22import android.content.res.TypedArray;
Adam Powellfcca00a2010-11-30 21:26:29 -080023import android.graphics.Canvas;
24import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.util.AttributeSet;
26import android.view.Gravity;
27import android.view.View;
28import android.view.ViewDebug;
29import android.view.ViewGroup;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080030import android.view.accessibility.AccessibilityEvent;
31import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.widget.RemoteViews.RemoteView;
33
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034
35/**
36 * A Layout that arranges its children in a single column or a single row. The direction of
37 * the row can be set by calling {@link #setOrientation(int) setOrientation()}.
38 * You can also specify gravity, which specifies the alignment of all the child elements by
39 * calling {@link #setGravity(int) setGravity()} or specify that specific children
40 * grow to fill up any remaining space in the layout by setting the <em>weight</em> member of
41 * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
42 * The default orientation is horizontal.
43 *
Scott Main4c359b72012-07-24 15:51:27 -070044 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/linear.html">Linear Layout</a>
45 * guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070046 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 * <p>
48 * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
49 * for layout attributes </p>
Romain Guy9295ada2010-06-15 11:33:24 -070050 *
51 * @attr ref android.R.styleable#LinearLayout_baselineAligned
52 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
53 * @attr ref android.R.styleable#LinearLayout_gravity
54 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
55 * @attr ref android.R.styleable#LinearLayout_orientation
56 * @attr ref android.R.styleable#LinearLayout_weightSum
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 */
58@RemoteView
59public class LinearLayout extends ViewGroup {
60 public static final int HORIZONTAL = 0;
61 public static final int VERTICAL = 1;
62
63 /**
Adam Powellfcca00a2010-11-30 21:26:29 -080064 * Don't show any dividers.
65 */
66 public static final int SHOW_DIVIDER_NONE = 0;
67 /**
68 * Show a divider at the beginning of the group.
69 */
70 public static final int SHOW_DIVIDER_BEGINNING = 1;
71 /**
72 * Show dividers between each item in the group.
73 */
74 public static final int SHOW_DIVIDER_MIDDLE = 2;
75 /**
76 * Show a divider at the end of the group.
77 */
78 public static final int SHOW_DIVIDER_END = 4;
79
80 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 * Whether the children of this layout are baseline aligned. Only applicable
82 * if {@link #mOrientation} is horizontal.
83 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -070084 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private boolean mBaselineAligned = true;
86
87 /**
88 * If this layout is part of another layout that is baseline aligned,
89 * use the child at this index as the baseline.
90 *
91 * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
92 * with whether the children of this layout are baseline aligned.
93 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -070094 @ViewDebug.ExportedProperty(category = "layout")
Karl Rosaenc1f9c402009-08-12 17:18:33 -070095 private int mBaselineAlignedChildIndex = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096
97 /**
98 * The additional offset to the child's baseline.
99 * We'll calculate the baseline of this layout as we measure vertically; for
100 * horizontal linear layouts, the offset of 0 is appropriate.
101 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700102 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private int mBaselineChildTop = 0;
104
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700105 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private int mOrientation;
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700107
Fabrice Di Megliobce84d22011-06-02 15:57:01 -0700108 @ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
109 @ViewDebug.FlagToString(mask = -1,
110 equals = -1, name = "NONE"),
111 @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY,
112 equals = Gravity.NO_GRAVITY,name = "NONE"),
113 @ViewDebug.FlagToString(mask = Gravity.TOP,
114 equals = Gravity.TOP, name = "TOP"),
115 @ViewDebug.FlagToString(mask = Gravity.BOTTOM,
116 equals = Gravity.BOTTOM, name = "BOTTOM"),
117 @ViewDebug.FlagToString(mask = Gravity.LEFT,
118 equals = Gravity.LEFT, name = "LEFT"),
119 @ViewDebug.FlagToString(mask = Gravity.RIGHT,
120 equals = Gravity.RIGHT, name = "RIGHT"),
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700121 @ViewDebug.FlagToString(mask = Gravity.START,
122 equals = Gravity.START, name = "START"),
123 @ViewDebug.FlagToString(mask = Gravity.END,
124 equals = Gravity.END, name = "END"),
Fabrice Di Megliobce84d22011-06-02 15:57:01 -0700125 @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
126 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
127 @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
128 equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"),
129 @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL,
130 equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"),
131 @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL,
132 equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"),
133 @ViewDebug.FlagToString(mask = Gravity.CENTER,
134 equals = Gravity.CENTER, name = "CENTER"),
135 @ViewDebug.FlagToString(mask = Gravity.FILL,
136 equals = Gravity.FILL, name = "FILL"),
Fabrice Di Meglioc46f7ff2011-06-06 18:23:10 -0700137 @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
138 equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
Dianne Hackbornd6847842010-01-12 18:14:19 -0800139 })
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700140 private int mGravity = Gravity.START | Gravity.TOP;
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700141
142 @ViewDebug.ExportedProperty(category = "measurement")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 private int mTotalLength;
144
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700145 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 private float mWeightSum;
147
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700148 @ViewDebug.ExportedProperty(category = "layout")
Romain Guy5b1b2412010-01-21 19:09:51 -0800149 private boolean mUseLargestChild;
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 private int[] mMaxAscent;
152 private int[] mMaxDescent;
153
154 private static final int VERTICAL_GRAVITY_COUNT = 4;
155
156 private static final int INDEX_CENTER_VERTICAL = 0;
157 private static final int INDEX_TOP = 1;
158 private static final int INDEX_BOTTOM = 2;
Romain Guy5b1b2412010-01-21 19:09:51 -0800159 private static final int INDEX_FILL = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160
Adam Powellfcca00a2010-11-30 21:26:29 -0800161 private Drawable mDivider;
162 private int mDividerWidth;
163 private int mDividerHeight;
164 private int mShowDividers;
165 private int mDividerPadding;
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 public LinearLayout(Context context) {
168 super(context);
169 }
170
171 public LinearLayout(Context context, AttributeSet attrs) {
Romain Guy9295ada2010-06-15 11:33:24 -0700172 this(context, attrs, 0);
173 }
174
175 public LinearLayout(Context context, AttributeSet attrs, int defStyle) {
176 super(context, attrs, defStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
Adam Powell3f7f7ac2010-12-05 16:44:38 -0800178 TypedArray a = context.obtainStyledAttributes(attrs,
179 com.android.internal.R.styleable.LinearLayout, defStyle, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
181 int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);
182 if (index >= 0) {
183 setOrientation(index);
184 }
185
186 index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);
187 if (index >= 0) {
188 setGravity(index);
189 }
190
191 boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);
192 if (!baselineAligned) {
193 setBaselineAligned(baselineAligned);
194 }
195
196 mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);
197
198 mBaselineAlignedChildIndex =
199 a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
200
Romain Guy9295ada2010-06-15 11:33:24 -0700201 mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
Romain Guy5b1b2412010-01-21 19:09:51 -0800202
Adam Powellfcca00a2010-11-30 21:26:29 -0800203 setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
204 mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
205 mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 a.recycle();
208 }
209
210 /**
Adam Powellfcca00a2010-11-30 21:26:29 -0800211 * Set how dividers should be shown between items in this layout
212 *
213 * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
214 * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
215 * or {@link #SHOW_DIVIDER_NONE} to show no dividers.
216 */
217 public void setShowDividers(int showDividers) {
218 if (showDividers != mShowDividers) {
219 requestLayout();
220 }
221 mShowDividers = showDividers;
222 }
223
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700224 @Override
225 public boolean shouldDelayChildPressedState() {
226 return false;
227 }
228
Adam Powellfcca00a2010-11-30 21:26:29 -0800229 /**
230 * @return A flag set indicating how dividers should be shown around items.
231 * @see #setShowDividers(int)
232 */
233 public int getShowDividers() {
234 return mShowDividers;
235 }
236
237 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700238 * @return the divider Drawable that will divide each item.
239 *
240 * @see #setDividerDrawable(Drawable)
241 *
242 * @attr ref android.R.styleable#LinearLayout_divider
243 */
244 public Drawable getDividerDrawable() {
245 return mDivider;
246 }
247
248 /**
Adam Powellfcca00a2010-11-30 21:26:29 -0800249 * Set a drawable to be used as a divider between items.
Philip Milne1018fb42012-03-13 12:00:04 -0700250 *
Adam Powellfcca00a2010-11-30 21:26:29 -0800251 * @param divider Drawable that will divide each item.
Philip Milne1018fb42012-03-13 12:00:04 -0700252 *
Adam Powellfcca00a2010-11-30 21:26:29 -0800253 * @see #setShowDividers(int)
Philip Milne1018fb42012-03-13 12:00:04 -0700254 *
255 * @attr ref android.R.styleable#LinearLayout_divider
Adam Powellfcca00a2010-11-30 21:26:29 -0800256 */
257 public void setDividerDrawable(Drawable divider) {
258 if (divider == mDivider) {
259 return;
260 }
261 mDivider = divider;
262 if (divider != null) {
263 mDividerWidth = divider.getIntrinsicWidth();
264 mDividerHeight = divider.getIntrinsicHeight();
265 } else {
266 mDividerWidth = 0;
267 mDividerHeight = 0;
268 }
269 setWillNotDraw(divider == null);
270 requestLayout();
271 }
272
Adam Powell696cba52011-03-29 10:38:16 -0700273 /**
274 * Set padding displayed on both ends of dividers.
275 *
276 * @param padding Padding value in pixels that will be applied to each end
277 *
278 * @see #setShowDividers(int)
279 * @see #setDividerDrawable(Drawable)
280 * @see #getDividerPadding()
281 */
282 public void setDividerPadding(int padding) {
283 mDividerPadding = padding;
284 }
285
286 /**
287 * Get the padding size used to inset dividers in pixels
288 *
289 * @see #setShowDividers(int)
290 * @see #setDividerDrawable(Drawable)
291 * @see #setDividerPadding(int)
292 */
293 public int getDividerPadding() {
294 return mDividerPadding;
295 }
296
Adam Powell640a66e2011-04-29 10:18:53 -0700297 /**
298 * Get the width of the current divider drawable.
299 *
300 * @hide Used internally by framework.
301 */
302 public int getDividerWidth() {
303 return mDividerWidth;
304 }
305
Adam Powellfcca00a2010-11-30 21:26:29 -0800306 @Override
307 protected void onDraw(Canvas canvas) {
308 if (mDivider == null) {
309 return;
310 }
311
312 if (mOrientation == VERTICAL) {
313 drawDividersVertical(canvas);
314 } else {
315 drawDividersHorizontal(canvas);
316 }
317 }
318
319 void drawDividersVertical(Canvas canvas) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800320 final int count = getVirtualChildCount();
Adam Powellfcca00a2010-11-30 21:26:29 -0800321 for (int i = 0; i < count; i++) {
322 final View child = getVirtualChildAt(i);
323
Adam Powell35aecd52011-07-01 13:43:49 -0700324 if (child != null && child.getVisibility() != GONE) {
Adam Powell696cba52011-03-29 10:38:16 -0700325 if (hasDividerBeforeChildAt(i)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700326 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Vladimir Baryshnikov20761fc2012-01-17 14:59:48 -0800327 final int top = child.getTop() - lp.topMargin - mDividerHeight;
Adam Powellfcca00a2010-11-30 21:26:29 -0800328 drawHorizontalDivider(canvas, top);
Adam Powellfcca00a2010-11-30 21:26:29 -0800329 }
Adam Powellfcca00a2010-11-30 21:26:29 -0800330 }
331 }
332
Adam Powell696cba52011-03-29 10:38:16 -0700333 if (hasDividerBeforeChildAt(count)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700334 final View child = getVirtualChildAt(count - 1);
335 int bottom = 0;
336 if (child == null) {
337 bottom = getHeight() - getPaddingBottom() - mDividerHeight;
338 } else {
339 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
340 bottom = child.getBottom() + lp.bottomMargin;
341 }
342 drawHorizontalDivider(canvas, bottom);
Adam Powellfcca00a2010-11-30 21:26:29 -0800343 }
344 }
345
346 void drawDividersHorizontal(Canvas canvas) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800347 final int count = getVirtualChildCount();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700348 final boolean isLayoutRtl = isLayoutRtl();
Adam Powellfcca00a2010-11-30 21:26:29 -0800349 for (int i = 0; i < count; i++) {
350 final View child = getVirtualChildAt(i);
351
Adam Powell35aecd52011-07-01 13:43:49 -0700352 if (child != null && child.getVisibility() != GONE) {
Adam Powell696cba52011-03-29 10:38:16 -0700353 if (hasDividerBeforeChildAt(i)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700354 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700355 final int position;
356 if (isLayoutRtl) {
357 position = child.getRight() + lp.rightMargin;
358 } else {
359 position = child.getLeft() - lp.leftMargin - mDividerWidth;
360 }
361 drawVerticalDivider(canvas, position);
Adam Powellfcca00a2010-11-30 21:26:29 -0800362 }
Adam Powellfcca00a2010-11-30 21:26:29 -0800363 }
364 }
365
Adam Powell696cba52011-03-29 10:38:16 -0700366 if (hasDividerBeforeChildAt(count)) {
Adam Powell35aecd52011-07-01 13:43:49 -0700367 final View child = getVirtualChildAt(count - 1);
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700368 int position;
Adam Powell35aecd52011-07-01 13:43:49 -0700369 if (child == null) {
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700370 if (isLayoutRtl) {
371 position = getPaddingLeft();
372 } else {
373 position = getWidth() - getPaddingRight() - mDividerWidth;
374 }
Adam Powell35aecd52011-07-01 13:43:49 -0700375 } else {
376 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700377 if (isLayoutRtl) {
378 position = child.getLeft() - lp.leftMargin - mDividerWidth;
379 } else {
380 position = child.getRight() + lp.rightMargin;
381 }
Adam Powell35aecd52011-07-01 13:43:49 -0700382 }
Fabrice Di Meglio4ab69682012-07-02 19:12:40 -0700383 drawVerticalDivider(canvas, position);
Adam Powellfcca00a2010-11-30 21:26:29 -0800384 }
385 }
386
387 void drawHorizontalDivider(Canvas canvas, int top) {
388 mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
389 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
390 mDivider.draw(canvas);
391 }
392
393 void drawVerticalDivider(Canvas canvas, int left) {
394 mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
395 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
396 mDivider.draw(canvas);
397 }
398
399 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 * <p>Indicates whether widgets contained within this layout are aligned
401 * on their baseline or not.</p>
402 *
403 * @return true when widgets are baseline-aligned, false otherwise
404 */
405 public boolean isBaselineAligned() {
406 return mBaselineAligned;
407 }
408
409 /**
410 * <p>Defines whether widgets contained in this layout are
411 * baseline-aligned or not.</p>
412 *
413 * @param baselineAligned true to align widgets on their baseline,
414 * false otherwise
415 *
416 * @attr ref android.R.styleable#LinearLayout_baselineAligned
417 */
418 @android.view.RemotableViewMethod
419 public void setBaselineAligned(boolean baselineAligned) {
420 mBaselineAligned = baselineAligned;
421 }
422
Romain Guy9295ada2010-06-15 11:33:24 -0700423 /**
424 * When true, all children with a weight will be considered having
425 * the minimum size of the largest child. If false, all children are
426 * measured normally.
427 *
428 * @return True to measure children with a weight using the minimum
429 * size of the largest child, false otherwise.
Philip Milne1018fb42012-03-13 12:00:04 -0700430 *
431 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
Romain Guy9295ada2010-06-15 11:33:24 -0700432 */
433 public boolean isMeasureWithLargestChildEnabled() {
434 return mUseLargestChild;
435 }
436
437 /**
438 * When set to true, all children with a weight will be considered having
439 * the minimum size of the largest child. If false, all children are
440 * measured normally.
441 *
442 * Disabled by default.
443 *
444 * @param enabled True to measure children with a weight using the
445 * minimum size of the largest child, false otherwise.
Philip Milne1018fb42012-03-13 12:00:04 -0700446 *
447 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
Romain Guy9295ada2010-06-15 11:33:24 -0700448 */
449 @android.view.RemotableViewMethod
450 public void setMeasureWithLargestChildEnabled(boolean enabled) {
451 mUseLargestChild = enabled;
452 }
453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 @Override
455 public int getBaseline() {
456 if (mBaselineAlignedChildIndex < 0) {
457 return super.getBaseline();
458 }
459
460 if (getChildCount() <= mBaselineAlignedChildIndex) {
461 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
462 + "set to an index that is out of bounds.");
463 }
464
465 final View child = getChildAt(mBaselineAlignedChildIndex);
466 final int childBaseline = child.getBaseline();
467
468 if (childBaseline == -1) {
469 if (mBaselineAlignedChildIndex == 0) {
470 // this is just the default case, safe to return -1
471 return -1;
472 }
473 // the user picked an index that points to something that doesn't
474 // know how to calculate its baseline.
475 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
476 + "points to a View that doesn't know how to get its baseline.");
477 }
478
479 // TODO: This should try to take into account the virtual offsets
480 // (See getNextLocationOffset and getLocationOffset)
481 // We should add to childTop:
482 // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
483 // and also add:
484 // getLocationOffset(child)
485 int childTop = mBaselineChildTop;
486
487 if (mOrientation == VERTICAL) {
488 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
489 if (majorGravity != Gravity.TOP) {
490 switch (majorGravity) {
491 case Gravity.BOTTOM:
492 childTop = mBottom - mTop - mPaddingBottom - mTotalLength;
493 break;
494
495 case Gravity.CENTER_VERTICAL:
496 childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) -
497 mTotalLength) / 2;
498 break;
499 }
500 }
501 }
502
503 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
504 return childTop + lp.topMargin + childBaseline;
505 }
506
507 /**
508 * @return The index of the child that will be used if this layout is
509 * part of a larger layout that is baseline aligned, or -1 if none has
510 * been set.
511 */
512 public int getBaselineAlignedChildIndex() {
513 return mBaselineAlignedChildIndex;
514 }
515
516 /**
517 * @param i The index of the child that will be used if this layout is
518 * part of a larger layout that is baseline aligned.
519 *
520 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
521 */
522 @android.view.RemotableViewMethod
523 public void setBaselineAlignedChildIndex(int i) {
524 if ((i < 0) || (i >= getChildCount())) {
525 throw new IllegalArgumentException("base aligned child index out "
526 + "of range (0, " + getChildCount() + ")");
527 }
528 mBaselineAlignedChildIndex = i;
529 }
530
531 /**
532 * <p>Returns the view at the specified index. This method can be overriden
533 * to take into account virtual children. Refer to
534 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
535 * for an example.</p>
536 *
537 * @param index the child's index
538 * @return the child at the specified index
539 */
540 View getVirtualChildAt(int index) {
541 return getChildAt(index);
542 }
543
544 /**
545 * <p>Returns the virtual number of children. This number might be different
546 * than the actual number of children if the layout can hold virtual
547 * children. Refer to
548 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
549 * for an example.</p>
550 *
551 * @return the virtual number of children
552 */
553 int getVirtualChildCount() {
554 return getChildCount();
555 }
556
557 /**
558 * Returns the desired weights sum.
559 *
560 * @return A number greater than 0.0f if the weight sum is defined, or
561 * a number lower than or equals to 0.0f if not weight sum is
562 * to be used.
563 */
564 public float getWeightSum() {
565 return mWeightSum;
566 }
567
568 /**
569 * Defines the desired weights sum. If unspecified the weights sum is computed
570 * at layout time by adding the layout_weight of each child.
571 *
572 * This can be used for instance to give a single child 50% of the total
573 * available space by giving it a layout_weight of 0.5 and setting the
574 * weightSum to 1.0.
575 *
576 * @param weightSum a number greater than 0.0f, or a number lower than or equals
577 * to 0.0f if the weight sum should be computed from the children's
578 * layout_weight
579 */
580 @android.view.RemotableViewMethod
581 public void setWeightSum(float weightSum) {
582 mWeightSum = Math.max(0.0f, weightSum);
583 }
584
585 @Override
586 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
587 if (mOrientation == VERTICAL) {
588 measureVertical(widthMeasureSpec, heightMeasureSpec);
589 } else {
590 measureHorizontal(widthMeasureSpec, heightMeasureSpec);
591 }
592 }
593
594 /**
Adam Powell696cba52011-03-29 10:38:16 -0700595 * Determines where to position dividers between children.
596 *
597 * @param childIndex Index of child to check for preceding divider
598 * @return true if there should be a divider before the child at childIndex
599 * @hide Pending API consideration. Currently only used internally by the system.
600 */
601 protected boolean hasDividerBeforeChildAt(int childIndex) {
602 if (childIndex == 0) {
603 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
604 } else if (childIndex == getChildCount()) {
605 return (mShowDividers & SHOW_DIVIDER_END) != 0;
Adam Powellbf68f832011-06-20 18:23:30 -0700606 } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
607 boolean hasVisibleViewBefore = false;
608 for (int i = childIndex - 1; i >= 0; i--) {
609 if (getChildAt(i).getVisibility() != GONE) {
610 hasVisibleViewBefore = true;
611 break;
612 }
613 }
614 return hasVisibleViewBefore;
Adam Powell696cba52011-03-29 10:38:16 -0700615 }
Adam Powellbf68f832011-06-20 18:23:30 -0700616 return false;
Adam Powell696cba52011-03-29 10:38:16 -0700617 }
618
619 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 * Measures the children when the orientation of this LinearLayout is set
621 * to {@link #VERTICAL}.
622 *
623 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
624 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
625 *
626 * @see #getOrientation()
627 * @see #setOrientation(int)
628 * @see #onMeasure(int, int)
629 */
630 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
631 mTotalLength = 0;
632 int maxWidth = 0;
Dianne Hackborn189ee182010-12-02 21:48:53 -0800633 int childState = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 int alternativeMaxWidth = 0;
635 int weightedMaxWidth = 0;
636 boolean allFillParent = true;
637 float totalWeight = 0;
638
639 final int count = getVirtualChildCount();
640
641 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
642 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
643
644 boolean matchWidth = false;
645
646 final int baselineChildIndex = mBaselineAlignedChildIndex;
Romain Guy5b1b2412010-01-21 19:09:51 -0800647 final boolean useLargestChild = mUseLargestChild;
648
649 int largestChildHeight = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650
651 // See how tall everyone is. Also remember max width.
652 for (int i = 0; i < count; ++i) {
653 final View child = getVirtualChildAt(i);
654
655 if (child == null) {
656 mTotalLength += measureNullChild(i);
657 continue;
658 }
659
660 if (child.getVisibility() == View.GONE) {
661 i += getChildrenSkipCount(child, i);
662 continue;
663 }
664
Adam Powell696cba52011-03-29 10:38:16 -0700665 if (hasDividerBeforeChildAt(i)) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800666 mTotalLength += mDividerHeight;
667 }
668
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
670
671 totalWeight += lp.weight;
672
673 if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
674 // Optimization: don't bother measuring children who are going to use
675 // leftover space. These views will get measured again down below if
676 // there is any leftover space.
Romain Guy053b4802010-02-05 15:34:33 -0800677 final int totalLength = mTotalLength;
678 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 } else {
Romain Guy5b1b2412010-01-21 19:09:51 -0800680 int oldHeight = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681
Romain Guy5b1b2412010-01-21 19:09:51 -0800682 if (lp.height == 0 && lp.weight > 0) {
Gilles Debunnef5c6eff2010-02-09 19:08:36 -0800683 // heightMode is either UNSPECIFIED or AT_MOST, and this
684 // child wanted to stretch to fill available space.
685 // Translate that to WRAP_CONTENT so that it does not end up
686 // with a height of 0
687 oldHeight = 0;
688 lp.height = LayoutParams.WRAP_CONTENT;
Romain Guy5b1b2412010-01-21 19:09:51 -0800689 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690
Gilles Debunnef5c6eff2010-02-09 19:08:36 -0800691 // Determine how big this child would like to be. If this or
Romain Guy5b1b2412010-01-21 19:09:51 -0800692 // previous children have given a weight, then we allow it to
693 // use all available space (and we will shrink things later
694 // if needed).
695 measureChildBeforeLayout(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 child, i, widthMeasureSpec, 0, heightMeasureSpec,
697 totalWeight == 0 ? mTotalLength : 0);
698
Romain Guy5b1b2412010-01-21 19:09:51 -0800699 if (oldHeight != Integer.MIN_VALUE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 lp.height = oldHeight;
Romain Guy5b1b2412010-01-21 19:09:51 -0800701 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702
Romain Guy5b1b2412010-01-21 19:09:51 -0800703 final int childHeight = child.getMeasuredHeight();
Romain Guy053b4802010-02-05 15:34:33 -0800704 final int totalLength = mTotalLength;
705 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
706 lp.bottomMargin + getNextLocationOffset(child));
Romain Guy5b1b2412010-01-21 19:09:51 -0800707
708 if (useLargestChild) {
709 largestChildHeight = Math.max(childHeight, largestChildHeight);
710 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 }
712
713 /**
714 * If applicable, compute the additional offset to the child's baseline
715 * we'll need later when asked {@link #getBaseline}.
716 */
717 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
718 mBaselineChildTop = mTotalLength;
719 }
720
721 // if we are trying to use a child index for our baseline, the above
722 // book keeping only works if there are no children above it with
723 // weight. fail fast to aid the developer.
724 if (i < baselineChildIndex && lp.weight > 0) {
725 throw new RuntimeException("A child of LinearLayout with index "
726 + "less than mBaselineAlignedChildIndex has weight > 0, which "
727 + "won't work. Either remove the weight, or don't set "
728 + "mBaselineAlignedChildIndex.");
729 }
730
731 boolean matchWidthLocally = false;
Romain Guy980a9382010-01-08 15:06:28 -0800732 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 // The width of the linear layout will scale, and at least one
734 // child said it wanted to match our width. Set a flag
735 // indicating that we need to remeasure at least that view when
736 // we know our width.
737 matchWidth = true;
738 matchWidthLocally = true;
739 }
740
741 final int margin = lp.leftMargin + lp.rightMargin;
742 final int measuredWidth = child.getMeasuredWidth() + margin;
743 maxWidth = Math.max(maxWidth, measuredWidth);
Dianne Hackborn189ee182010-12-02 21:48:53 -0800744 childState = combineMeasuredStates(childState, child.getMeasuredState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745
Romain Guy980a9382010-01-08 15:06:28 -0800746 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 if (lp.weight > 0) {
748 /*
749 * Widths of weighted Views are bogus if we end up
750 * remeasuring, so keep them separate.
751 */
752 weightedMaxWidth = Math.max(weightedMaxWidth,
753 matchWidthLocally ? margin : measuredWidth);
754 } else {
755 alternativeMaxWidth = Math.max(alternativeMaxWidth,
756 matchWidthLocally ? margin : measuredWidth);
757 }
758
759 i += getChildrenSkipCount(child, i);
760 }
Romain Guy5b1b2412010-01-21 19:09:51 -0800761
Adam Powell696cba52011-03-29 10:38:16 -0700762 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800763 mTotalLength += mDividerHeight;
764 }
765
Adam Powellf8ac6b72011-05-23 18:14:09 -0700766 if (useLargestChild &&
767 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
Romain Guy5b1b2412010-01-21 19:09:51 -0800768 mTotalLength = 0;
769
770 for (int i = 0; i < count; ++i) {
771 final View child = getVirtualChildAt(i);
772
773 if (child == null) {
774 mTotalLength += measureNullChild(i);
775 continue;
776 }
777
778 if (child.getVisibility() == GONE) {
779 i += getChildrenSkipCount(child, i);
780 continue;
781 }
782
783 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
784 child.getLayoutParams();
Romain Guy053b4802010-02-05 15:34:33 -0800785 // Account for negative margins
786 final int totalLength = mTotalLength;
787 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
788 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
Romain Guy5b1b2412010-01-21 19:09:51 -0800789 }
790 }
791
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 // Add in our padding
793 mTotalLength += mPaddingTop + mPaddingBottom;
794
795 int heightSize = mTotalLength;
796
797 // Check against our minimum height
798 heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
799
800 // Reconcile our calculated size with the heightMeasureSpec
Dianne Hackborn189ee182010-12-02 21:48:53 -0800801 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
802 heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803
804 // Either expand children with weight to take up available space or
805 // shrink them if they extend beyond our current bounds
806 int delta = heightSize - mTotalLength;
807 if (delta != 0 && totalWeight > 0.0f) {
808 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
809
810 mTotalLength = 0;
811
812 for (int i = 0; i < count; ++i) {
813 final View child = getVirtualChildAt(i);
814
815 if (child.getVisibility() == View.GONE) {
816 continue;
817 }
818
819 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
820
821 float childExtra = lp.weight;
822 if (childExtra > 0) {
823 // Child said it could absorb extra space -- give him his share
824 int share = (int) (childExtra * delta / weightSum);
825 weightSum -= childExtra;
826 delta -= share;
827
828 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
829 mPaddingLeft + mPaddingRight +
830 lp.leftMargin + lp.rightMargin, lp.width);
831
832 // TODO: Use a field like lp.isMeasured to figure out if this
833 // child has been previously measured
834 if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
835 // child was measured once already above...
836 // base new measurement on stored values
837 int childHeight = child.getMeasuredHeight() + share;
838 if (childHeight < 0) {
839 childHeight = 0;
840 }
841
842 child.measure(childWidthMeasureSpec,
843 MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
844 } else {
845 // child was skipped in the loop above.
846 // Measure for this first time here
847 child.measure(childWidthMeasureSpec,
848 MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
849 MeasureSpec.EXACTLY));
850 }
Dianne Hackborn189ee182010-12-02 21:48:53 -0800851
852 // Child may now not fit in vertical dimension.
853 childState = combineMeasuredStates(childState, child.getMeasuredState()
854 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
856
857 final int margin = lp.leftMargin + lp.rightMargin;
858 final int measuredWidth = child.getMeasuredWidth() + margin;
859 maxWidth = Math.max(maxWidth, measuredWidth);
860
861 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
Romain Guy980a9382010-01-08 15:06:28 -0800862 lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863
864 alternativeMaxWidth = Math.max(alternativeMaxWidth,
865 matchWidthLocally ? margin : measuredWidth);
866
Romain Guy980a9382010-01-08 15:06:28 -0800867 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868
Romain Guy053b4802010-02-05 15:34:33 -0800869 final int totalLength = mTotalLength;
870 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
871 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 }
873
874 // Add in our padding
Romain Guy053b4802010-02-05 15:34:33 -0800875 mTotalLength += mPaddingTop + mPaddingBottom;
876 // TODO: Should we recompute the heightSpec based on the new total length?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 } else {
878 alternativeMaxWidth = Math.max(alternativeMaxWidth,
879 weightedMaxWidth);
Adam Powellf8ac6b72011-05-23 18:14:09 -0700880
881
882 // We have no limit, so make all weighted views as tall as the largest child.
883 // Children will have already been measured once.
Adam Powelleabc9192012-05-04 14:49:46 -0700884 if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
Adam Powellf8ac6b72011-05-23 18:14:09 -0700885 for (int i = 0; i < count; i++) {
886 final View child = getVirtualChildAt(i);
887
888 if (child == null || child.getVisibility() == View.GONE) {
889 continue;
890 }
891
892 final LinearLayout.LayoutParams lp =
893 (LinearLayout.LayoutParams) child.getLayoutParams();
894
895 float childExtra = lp.weight;
896 if (childExtra > 0) {
897 child.measure(
898 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
899 MeasureSpec.EXACTLY),
900 MeasureSpec.makeMeasureSpec(largestChildHeight,
901 MeasureSpec.EXACTLY));
902 }
903 }
904 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 }
906
907 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
908 maxWidth = alternativeMaxWidth;
909 }
910
911 maxWidth += mPaddingLeft + mPaddingRight;
912
913 // Check against our minimum width
914 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
915
Dianne Hackborn189ee182010-12-02 21:48:53 -0800916 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
917 heightSizeAndState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918
919 if (matchWidth) {
920 forceUniformWidth(count, heightMeasureSpec);
921 }
922 }
923
924 private void forceUniformWidth(int count, int heightMeasureSpec) {
925 // Pretend that the linear layout has an exact size.
926 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
927 MeasureSpec.EXACTLY);
928 for (int i = 0; i< count; ++i) {
929 final View child = getVirtualChildAt(i);
930 if (child.getVisibility() != GONE) {
931 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
932
Romain Guy980a9382010-01-08 15:06:28 -0800933 if (lp.width == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 // Temporarily force children to reuse their old measured height
935 // FIXME: this may not be right for something like wrapping text?
936 int oldHeight = lp.height;
937 lp.height = child.getMeasuredHeight();
938
939 // Remeasue with new dimensions
940 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
941 lp.height = oldHeight;
942 }
943 }
944 }
945 }
946
947 /**
948 * Measures the children when the orientation of this LinearLayout is set
949 * to {@link #HORIZONTAL}.
950 *
951 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
952 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
953 *
954 * @see #getOrientation()
955 * @see #setOrientation(int)
956 * @see #onMeasure(int, int)
957 */
958 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
959 mTotalLength = 0;
960 int maxHeight = 0;
Dianne Hackborn189ee182010-12-02 21:48:53 -0800961 int childState = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 int alternativeMaxHeight = 0;
963 int weightedMaxHeight = 0;
964 boolean allFillParent = true;
965 float totalWeight = 0;
966
967 final int count = getVirtualChildCount();
968
969 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
970 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
971
972 boolean matchHeight = false;
973
974 if (mMaxAscent == null || mMaxDescent == null) {
975 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
976 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
977 }
978
979 final int[] maxAscent = mMaxAscent;
980 final int[] maxDescent = mMaxDescent;
981
982 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
983 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
984
985 final boolean baselineAligned = mBaselineAligned;
Romain Guy5b1b2412010-01-21 19:09:51 -0800986 final boolean useLargestChild = mUseLargestChild;
Romain Guyc3520902010-02-25 11:01:01 -0800987
988 final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
Romain Guy5b1b2412010-01-21 19:09:51 -0800989
990 int largestChildWidth = Integer.MIN_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991
992 // See how wide everyone is. Also remember max height.
993 for (int i = 0; i < count; ++i) {
994 final View child = getVirtualChildAt(i);
995
996 if (child == null) {
997 mTotalLength += measureNullChild(i);
998 continue;
999 }
1000
1001 if (child.getVisibility() == GONE) {
1002 i += getChildrenSkipCount(child, i);
1003 continue;
1004 }
1005
Adam Powell696cba52011-03-29 10:38:16 -07001006 if (hasDividerBeforeChildAt(i)) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001007 mTotalLength += mDividerWidth;
1008 }
1009
Romain Guy5b1b2412010-01-21 19:09:51 -08001010 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1011 child.getLayoutParams();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012
1013 totalWeight += lp.weight;
1014
1015 if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
1016 // Optimization: don't bother measuring children who are going to use
1017 // leftover space. These views will get measured again down below if
1018 // there is any leftover space.
Romain Guyc3520902010-02-25 11:01:01 -08001019 if (isExactly) {
1020 mTotalLength += lp.leftMargin + lp.rightMargin;
1021 } else {
1022 final int totalLength = mTotalLength;
1023 mTotalLength = Math.max(totalLength, totalLength +
1024 lp.leftMargin + lp.rightMargin);
1025 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026
1027 // Baseline alignment requires to measure widgets to obtain the
Romain Guy6c5664a2010-02-24 18:55:22 -08001028 // baseline offset (in particular for TextViews). The following
1029 // defeats the optimization mentioned above. Allow the child to
1030 // use as much space as it wants because we can shrink things
1031 // later (and re-measure).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 if (baselineAligned) {
1033 final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
1034 child.measure(freeSpec, freeSpec);
1035 }
1036 } else {
1037 int oldWidth = Integer.MIN_VALUE;
1038
1039 if (lp.width == 0 && lp.weight > 0) {
Gilles Debunnef5c6eff2010-02-09 19:08:36 -08001040 // widthMode is either UNSPECIFIED or AT_MOST, and this
1041 // child
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 // wanted to stretch to fill available space. Translate that to
1043 // WRAP_CONTENT so that it does not end up with a width of 0
1044 oldWidth = 0;
1045 lp.width = LayoutParams.WRAP_CONTENT;
1046 }
1047
1048 // Determine how big this child would like to be. If this or
1049 // previous children have given a weight, then we allow it to
1050 // use all available space (and we will shrink things later
1051 // if needed).
1052 measureChildBeforeLayout(child, i, widthMeasureSpec,
1053 totalWeight == 0 ? mTotalLength : 0,
1054 heightMeasureSpec, 0);
1055
1056 if (oldWidth != Integer.MIN_VALUE) {
1057 lp.width = oldWidth;
1058 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001059
1060 final int childWidth = child.getMeasuredWidth();
Romain Guyc3520902010-02-25 11:01:01 -08001061 if (isExactly) {
1062 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
1063 getNextLocationOffset(child);
1064 } else {
1065 final int totalLength = mTotalLength;
1066 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
1067 lp.rightMargin + getNextLocationOffset(child));
1068 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001069
1070 if (useLargestChild) {
1071 largestChildWidth = Math.max(childWidth, largestChildWidth);
1072 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 }
1074
1075 boolean matchHeightLocally = false;
Romain Guy980a9382010-01-08 15:06:28 -08001076 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 // The height of the linear layout will scale, and at least one
1078 // child said it wanted to match our height. Set a flag indicating that
1079 // we need to remeasure at least that view when we know our height.
1080 matchHeight = true;
1081 matchHeightLocally = true;
1082 }
1083
1084 final int margin = lp.topMargin + lp.bottomMargin;
1085 final int childHeight = child.getMeasuredHeight() + margin;
Dianne Hackborn189ee182010-12-02 21:48:53 -08001086 childState = combineMeasuredStates(childState, child.getMeasuredState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087
1088 if (baselineAligned) {
1089 final int childBaseline = child.getBaseline();
1090 if (childBaseline != -1) {
1091 // Translates the child's vertical gravity into an index
1092 // in the range 0..VERTICAL_GRAVITY_COUNT
1093 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1094 & Gravity.VERTICAL_GRAVITY_MASK;
1095 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1096 & ~Gravity.AXIS_SPECIFIED) >> 1;
1097
1098 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1099 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
1100 }
1101 }
1102
1103 maxHeight = Math.max(maxHeight, childHeight);
1104
Romain Guy980a9382010-01-08 15:06:28 -08001105 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 if (lp.weight > 0) {
1107 /*
1108 * Heights of weighted Views are bogus if we end up
1109 * remeasuring, so keep them separate.
1110 */
1111 weightedMaxHeight = Math.max(weightedMaxHeight,
1112 matchHeightLocally ? margin : childHeight);
1113 } else {
1114 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1115 matchHeightLocally ? margin : childHeight);
1116 }
1117
1118 i += getChildrenSkipCount(child, i);
1119 }
1120
Adam Powell696cba52011-03-29 10:38:16 -07001121 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001122 mTotalLength += mDividerWidth;
1123 }
1124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1126 // the most common case
1127 if (maxAscent[INDEX_TOP] != -1 ||
1128 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1129 maxAscent[INDEX_BOTTOM] != -1 ||
1130 maxAscent[INDEX_FILL] != -1) {
1131 final int ascent = Math.max(maxAscent[INDEX_FILL],
1132 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1133 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1134 final int descent = Math.max(maxDescent[INDEX_FILL],
1135 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1136 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1137 maxHeight = Math.max(maxHeight, ascent + descent);
1138 }
1139
Adam Powellf8ac6b72011-05-23 18:14:09 -07001140 if (useLargestChild &&
1141 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
Romain Guy5b1b2412010-01-21 19:09:51 -08001142 mTotalLength = 0;
1143
1144 for (int i = 0; i < count; ++i) {
1145 final View child = getVirtualChildAt(i);
1146
1147 if (child == null) {
1148 mTotalLength += measureNullChild(i);
1149 continue;
1150 }
1151
1152 if (child.getVisibility() == GONE) {
1153 i += getChildrenSkipCount(child, i);
1154 continue;
1155 }
1156
1157 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1158 child.getLayoutParams();
Romain Guyc3520902010-02-25 11:01:01 -08001159 if (isExactly) {
1160 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
1161 getNextLocationOffset(child);
1162 } else {
1163 final int totalLength = mTotalLength;
1164 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
1165 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1166 }
Romain Guy5b1b2412010-01-21 19:09:51 -08001167 }
1168 }
1169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 // Add in our padding
1171 mTotalLength += mPaddingLeft + mPaddingRight;
1172
1173 int widthSize = mTotalLength;
1174
1175 // Check against our minimum width
1176 widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
1177
1178 // Reconcile our calculated size with the widthMeasureSpec
Dianne Hackborn189ee182010-12-02 21:48:53 -08001179 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
1180 widthSize = widthSizeAndState & MEASURED_SIZE_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181
1182 // Either expand children with weight to take up available space or
1183 // shrink them if they extend beyond our current bounds
1184 int delta = widthSize - mTotalLength;
1185 if (delta != 0 && totalWeight > 0.0f) {
1186 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
1187
1188 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1189 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1190 maxHeight = -1;
1191
1192 mTotalLength = 0;
1193
1194 for (int i = 0; i < count; ++i) {
1195 final View child = getVirtualChildAt(i);
1196
1197 if (child == null || child.getVisibility() == View.GONE) {
1198 continue;
1199 }
1200
1201 final LinearLayout.LayoutParams lp =
1202 (LinearLayout.LayoutParams) child.getLayoutParams();
1203
1204 float childExtra = lp.weight;
1205 if (childExtra > 0) {
1206 // Child said it could absorb extra space -- give him his share
1207 int share = (int) (childExtra * delta / weightSum);
1208 weightSum -= childExtra;
1209 delta -= share;
1210
1211 final int childHeightMeasureSpec = getChildMeasureSpec(
1212 heightMeasureSpec,
1213 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
1214 lp.height);
1215
1216 // TODO: Use a field like lp.isMeasured to figure out if this
1217 // child has been previously measured
1218 if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
1219 // child was measured once already above ... base new measurement
1220 // on stored values
1221 int childWidth = child.getMeasuredWidth() + share;
1222 if (childWidth < 0) {
1223 childWidth = 0;
1224 }
1225
1226 child.measure(
1227 MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
1228 childHeightMeasureSpec);
1229 } else {
1230 // child was skipped in the loop above. Measure for this first time here
1231 child.measure(MeasureSpec.makeMeasureSpec(
1232 share > 0 ? share : 0, MeasureSpec.EXACTLY),
1233 childHeightMeasureSpec);
1234 }
Dianne Hackborn189ee182010-12-02 21:48:53 -08001235
1236 // Child may now not fit in horizontal dimension.
1237 childState = combineMeasuredStates(childState,
1238 child.getMeasuredState() & MEASURED_STATE_MASK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 }
1240
Romain Guyc3520902010-02-25 11:01:01 -08001241 if (isExactly) {
1242 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
1243 getNextLocationOffset(child);
1244 } else {
1245 final int totalLength = mTotalLength;
1246 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
1247 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1248 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249
1250 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
Romain Guy980a9382010-01-08 15:06:28 -08001251 lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252
1253 final int margin = lp.topMargin + lp .bottomMargin;
1254 int childHeight = child.getMeasuredHeight() + margin;
1255 maxHeight = Math.max(maxHeight, childHeight);
1256 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1257 matchHeightLocally ? margin : childHeight);
1258
Romain Guy980a9382010-01-08 15:06:28 -08001259 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260
1261 if (baselineAligned) {
1262 final int childBaseline = child.getBaseline();
1263 if (childBaseline != -1) {
1264 // Translates the child's vertical gravity into an index in the range 0..2
1265 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1266 & Gravity.VERTICAL_GRAVITY_MASK;
1267 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1268 & ~Gravity.AXIS_SPECIFIED) >> 1;
1269
1270 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1271 maxDescent[index] = Math.max(maxDescent[index],
1272 childHeight - childBaseline);
1273 }
1274 }
1275 }
1276
1277 // Add in our padding
1278 mTotalLength += mPaddingLeft + mPaddingRight;
Romain Guy053b4802010-02-05 15:34:33 -08001279 // TODO: Should we update widthSize with the new total length?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280
1281 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1282 // the most common case
1283 if (maxAscent[INDEX_TOP] != -1 ||
1284 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1285 maxAscent[INDEX_BOTTOM] != -1 ||
1286 maxAscent[INDEX_FILL] != -1) {
1287 final int ascent = Math.max(maxAscent[INDEX_FILL],
1288 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1289 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1290 final int descent = Math.max(maxDescent[INDEX_FILL],
1291 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1292 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1293 maxHeight = Math.max(maxHeight, ascent + descent);
1294 }
1295 } else {
1296 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
Adam Powellf8ac6b72011-05-23 18:14:09 -07001297
1298 // We have no limit, so make all weighted views as wide as the largest child.
1299 // Children will have already been measured once.
Adam Powelleabc9192012-05-04 14:49:46 -07001300 if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
Adam Powellf8ac6b72011-05-23 18:14:09 -07001301 for (int i = 0; i < count; i++) {
1302 final View child = getVirtualChildAt(i);
1303
1304 if (child == null || child.getVisibility() == View.GONE) {
1305 continue;
1306 }
1307
1308 final LinearLayout.LayoutParams lp =
1309 (LinearLayout.LayoutParams) child.getLayoutParams();
1310
1311 float childExtra = lp.weight;
1312 if (childExtra > 0) {
1313 child.measure(
1314 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
1315 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
1316 MeasureSpec.EXACTLY));
1317 }
1318 }
1319 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320 }
1321
1322 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
1323 maxHeight = alternativeMaxHeight;
1324 }
1325
1326 maxHeight += mPaddingTop + mPaddingBottom;
1327
1328 // Check against our minimum height
1329 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
1330
Dianne Hackborn189ee182010-12-02 21:48:53 -08001331 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
1332 resolveSizeAndState(maxHeight, heightMeasureSpec,
1333 (childState<<MEASURED_HEIGHT_STATE_SHIFT)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334
1335 if (matchHeight) {
1336 forceUniformHeight(count, widthMeasureSpec);
1337 }
1338 }
1339
1340 private void forceUniformHeight(int count, int widthMeasureSpec) {
1341 // Pretend that the linear layout has an exact size. This is the measured height of
1342 // ourselves. The measured height should be the max height of the children, changed
Fabrice Di Megliofc53e582012-10-25 11:45:04 -07001343 // to accommodate the heightMeasureSpec from the parent
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
1345 MeasureSpec.EXACTLY);
1346 for (int i = 0; i < count; ++i) {
1347 final View child = getVirtualChildAt(i);
1348 if (child.getVisibility() != GONE) {
1349 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
1350
Romain Guy980a9382010-01-08 15:06:28 -08001351 if (lp.height == LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 // Temporarily force children to reuse their old measured width
1353 // FIXME: this may not be right for something like wrapping text?
1354 int oldWidth = lp.width;
1355 lp.width = child.getMeasuredWidth();
1356
1357 // Remeasure with new dimensions
1358 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
1359 lp.width = oldWidth;
1360 }
1361 }
1362 }
1363 }
1364
1365 /**
1366 * <p>Returns the number of children to skip after measuring/laying out
1367 * the specified child.</p>
1368 *
1369 * @param child the child after which we want to skip children
1370 * @param index the index of the child after which we want to skip children
1371 * @return the number of children to skip, 0 by default
1372 */
1373 int getChildrenSkipCount(View child, int index) {
1374 return 0;
1375 }
1376
1377 /**
1378 * <p>Returns the size (width or height) that should be occupied by a null
1379 * child.</p>
1380 *
1381 * @param childIndex the index of the null child
1382 * @return the width or height of the child depending on the orientation
1383 */
1384 int measureNullChild(int childIndex) {
1385 return 0;
1386 }
1387
1388 /**
1389 * <p>Measure the child according to the parent's measure specs. This
1390 * method should be overriden by subclasses to force the sizing of
1391 * children. This method is called by {@link #measureVertical(int, int)} and
1392 * {@link #measureHorizontal(int, int)}.</p>
1393 *
1394 * @param child the child to measure
1395 * @param childIndex the index of the child in this view
1396 * @param widthMeasureSpec horizontal space requirements as imposed by the parent
1397 * @param totalWidth extra space that has been used up by the parent horizontally
1398 * @param heightMeasureSpec vertical space requirements as imposed by the parent
1399 * @param totalHeight extra space that has been used up by the parent vertically
1400 */
1401 void measureChildBeforeLayout(View child, int childIndex,
1402 int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
1403 int totalHeight) {
1404 measureChildWithMargins(child, widthMeasureSpec, totalWidth,
1405 heightMeasureSpec, totalHeight);
1406 }
1407
1408 /**
1409 * <p>Return the location offset of the specified child. This can be used
1410 * by subclasses to change the location of a given widget.</p>
1411 *
1412 * @param child the child for which to obtain the location offset
1413 * @return the location offset in pixels
1414 */
1415 int getLocationOffset(View child) {
1416 return 0;
1417 }
1418
1419 /**
1420 * <p>Return the size offset of the next sibling of the specified child.
1421 * This can be used by subclasses to change the location of the widget
1422 * following <code>child</code>.</p>
1423 *
1424 * @param child the child whose next sibling will be moved
1425 * @return the location offset of the next child in pixels
1426 */
1427 int getNextLocationOffset(View child) {
1428 return 0;
1429 }
1430
1431 @Override
1432 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1433 if (mOrientation == VERTICAL) {
Philip Milnead365cc2012-09-27 14:38:46 -07001434 layoutVertical(l, t, r, b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 } else {
Philip Milnead365cc2012-09-27 14:38:46 -07001436 layoutHorizontal(l, t, r, b);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437 }
1438 }
1439
1440 /**
1441 * Position the children during a layout pass if the orientation of this
1442 * LinearLayout is set to {@link #VERTICAL}.
1443 *
1444 * @see #getOrientation()
1445 * @see #setOrientation(int)
1446 * @see #onLayout(boolean, int, int, int, int)
Philip Milnead365cc2012-09-27 14:38:46 -07001447 * @param left
1448 * @param top
1449 * @param right
1450 * @param bottom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001451 */
Philip Milnead365cc2012-09-27 14:38:46 -07001452 void layoutVertical(int left, int top, int right, int bottom) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 final int paddingLeft = mPaddingLeft;
1454
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001455 int childTop;
Romain Guy5b1b2412010-01-21 19:09:51 -08001456 int childLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457
1458 // Where right end of child should go
Philip Milnead365cc2012-09-27 14:38:46 -07001459 final int width = right - left;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001460 int childRight = width - mPaddingRight;
1461
1462 // Space available for child
1463 int childSpace = width - paddingLeft - mPaddingRight;
1464
1465 final int count = getVirtualChildCount();
1466
1467 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001468 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001470 switch (majorGravity) {
1471 case Gravity.BOTTOM:
1472 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001473 childTop = mPaddingTop + bottom - top - mTotalLength;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001474 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001476 // mTotalLength contains the padding already
1477 case Gravity.CENTER_VERTICAL:
Philip Milnead365cc2012-09-27 14:38:46 -07001478 childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001479 break;
1480
1481 case Gravity.TOP:
1482 default:
1483 childTop = mPaddingTop;
1484 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 }
Adam Powellfcca00a2010-11-30 21:26:29 -08001486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 for (int i = 0; i < count; i++) {
1488 final View child = getVirtualChildAt(i);
1489 if (child == null) {
1490 childTop += measureNullChild(i);
1491 } else if (child.getVisibility() != GONE) {
1492 final int childWidth = child.getMeasuredWidth();
1493 final int childHeight = child.getMeasuredHeight();
1494
1495 final LinearLayout.LayoutParams lp =
1496 (LinearLayout.LayoutParams) child.getLayoutParams();
1497
1498 int gravity = lp.gravity;
1499 if (gravity < 0) {
1500 gravity = minorGravity;
1501 }
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07001502 final int layoutDirection = getLayoutDirection();
Fabrice Di Meglioc0053222011-06-13 12:16:51 -07001503 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
Fabrice Di Megliode35cee2011-06-01 15:13:50 -07001504 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 case Gravity.CENTER_HORIZONTAL:
1506 childLeft = paddingLeft + ((childSpace - childWidth) / 2)
1507 + lp.leftMargin - lp.rightMargin;
1508 break;
1509
1510 case Gravity.RIGHT:
1511 childLeft = childRight - childWidth - lp.rightMargin;
1512 break;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001513
1514 case Gravity.LEFT:
Romain Guy611cd3f2009-12-15 12:00:37 -08001515 default:
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001516 childLeft = paddingLeft + lp.leftMargin;
Romain Guy611cd3f2009-12-15 12:00:37 -08001517 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 }
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001519
Adam Powell696cba52011-03-29 10:38:16 -07001520 if (hasDividerBeforeChildAt(i)) {
1521 childTop += mDividerHeight;
1522 }
1523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 childTop += lp.topMargin;
1525 setChildFrame(child, childLeft, childTop + getLocationOffset(child),
1526 childWidth, childHeight);
1527 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
1528
1529 i += getChildrenSkipCount(child, i);
1530 }
1531 }
1532 }
1533
1534 /**
1535 * Position the children during a layout pass if the orientation of this
1536 * LinearLayout is set to {@link #HORIZONTAL}.
1537 *
1538 * @see #getOrientation()
1539 * @see #setOrientation(int)
1540 * @see #onLayout(boolean, int, int, int, int)
Philip Milnead365cc2012-09-27 14:38:46 -07001541 * @param left
1542 * @param top
1543 * @param right
1544 * @param bottom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 */
Philip Milnead365cc2012-09-27 14:38:46 -07001546 void layoutHorizontal(int left, int top, int right, int bottom) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001547 final boolean isLayoutRtl = isLayoutRtl();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 final int paddingTop = mPaddingTop;
1549
Romain Guy5b1b2412010-01-21 19:09:51 -08001550 int childTop;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001551 int childLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552
1553 // Where bottom of child should go
Philip Milnead365cc2012-09-27 14:38:46 -07001554 final int height = bottom - top;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 int childBottom = height - mPaddingBottom;
1556
1557 // Space available for child
1558 int childSpace = height - paddingTop - mPaddingBottom;
1559
1560 final int count = getVirtualChildCount();
1561
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001562 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1564
1565 final boolean baselineAligned = mBaselineAligned;
1566
1567 final int[] maxAscent = mMaxAscent;
1568 final int[] maxDescent = mMaxDescent;
1569
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -07001570 final int layoutDirection = getLayoutDirection();
Fabrice Di Meglioc0053222011-06-13 12:16:51 -07001571 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001572 case Gravity.RIGHT:
1573 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001574 childLeft = mPaddingLeft + right - left - mTotalLength;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001575 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001577 case Gravity.CENTER_HORIZONTAL:
1578 // mTotalLength contains the padding already
Philip Milnead365cc2012-09-27 14:38:46 -07001579 childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001580 break;
1581
1582 case Gravity.LEFT:
1583 default:
1584 childLeft = mPaddingLeft;
1585 break;
1586 }
1587
1588 int start = 0;
1589 int dir = 1;
1590 //In case of RTL, start drawing from the last child.
1591 if (isLayoutRtl) {
1592 start = count - 1;
1593 dir = -1;
Adam Powellfcca00a2010-11-30 21:26:29 -08001594 }
1595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 for (int i = 0; i < count; i++) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001597 int childIndex = start + dir * i;
1598 final View child = getVirtualChildAt(childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599
1600 if (child == null) {
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001601 childLeft += measureNullChild(childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 } else if (child.getVisibility() != GONE) {
1603 final int childWidth = child.getMeasuredWidth();
1604 final int childHeight = child.getMeasuredHeight();
1605 int childBaseline = -1;
1606
1607 final LinearLayout.LayoutParams lp =
1608 (LinearLayout.LayoutParams) child.getLayoutParams();
1609
Romain Guy980a9382010-01-08 15:06:28 -08001610 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 childBaseline = child.getBaseline();
1612 }
1613
1614 int gravity = lp.gravity;
1615 if (gravity < 0) {
1616 gravity = minorGravity;
1617 }
1618
1619 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
1620 case Gravity.TOP:
1621 childTop = paddingTop + lp.topMargin;
1622 if (childBaseline != -1) {
1623 childTop += maxAscent[INDEX_TOP] - childBaseline;
1624 }
1625 break;
1626
1627 case Gravity.CENTER_VERTICAL:
Romain Guy5b1b2412010-01-21 19:09:51 -08001628 // Removed support for baseline alignment when layout_gravity or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 // gravity == center_vertical. See bug #1038483.
1630 // Keep the code around if we need to re-enable this feature
1631 // if (childBaseline != -1) {
1632 // // Align baselines vertically only if the child is smaller than us
1633 // if (childSpace - childHeight > 0) {
1634 // childTop = paddingTop + (childSpace / 2) - childBaseline;
1635 // } else {
1636 // childTop = paddingTop + (childSpace - childHeight) / 2;
1637 // }
1638 // } else {
1639 childTop = paddingTop + ((childSpace - childHeight) / 2)
1640 + lp.topMargin - lp.bottomMargin;
1641 break;
1642
1643 case Gravity.BOTTOM:
1644 childTop = childBottom - childHeight - lp.bottomMargin;
1645 if (childBaseline != -1) {
1646 int descent = child.getMeasuredHeight() - childBaseline;
1647 childTop -= (maxDescent[INDEX_BOTTOM] - descent);
1648 }
1649 break;
Romain Guy611cd3f2009-12-15 12:00:37 -08001650 default:
1651 childTop = paddingTop;
1652 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001653 }
1654
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001655 if (hasDividerBeforeChildAt(childIndex)) {
Adam Powell696cba52011-03-29 10:38:16 -07001656 childLeft += mDividerWidth;
1657 }
1658
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 childLeft += lp.leftMargin;
1660 setChildFrame(child, childLeft + getLocationOffset(child), childTop,
1661 childWidth, childHeight);
1662 childLeft += childWidth + lp.rightMargin +
1663 getNextLocationOffset(child);
1664
Fabrice Di Meglio1e4cfbe2010-04-01 15:46:27 -07001665 i += getChildrenSkipCount(child, childIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 }
1667 }
1668 }
1669
1670 private void setChildFrame(View child, int left, int top, int width, int height) {
1671 child.layout(left, top, left + width, top + height);
1672 }
1673
1674 /**
1675 * Should the layout be a column or a row.
1676 * @param orientation Pass HORIZONTAL or VERTICAL. Default
1677 * value is HORIZONTAL.
1678 *
1679 * @attr ref android.R.styleable#LinearLayout_orientation
1680 */
1681 public void setOrientation(int orientation) {
1682 if (mOrientation != orientation) {
1683 mOrientation = orientation;
1684 requestLayout();
1685 }
1686 }
1687
1688 /**
1689 * Returns the current orientation.
1690 *
1691 * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
1692 */
1693 public int getOrientation() {
1694 return mOrientation;
1695 }
1696
1697 /**
1698 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
1699 * this layout has a VERTICAL orientation, this controls where all the child
1700 * views are placed if there is extra vertical space. If this layout has a
1701 * HORIZONTAL orientation, this controls the alignment of the children.
1702 *
1703 * @param gravity See {@link android.view.Gravity}
1704 *
1705 * @attr ref android.R.styleable#LinearLayout_gravity
1706 */
1707 @android.view.RemotableViewMethod
1708 public void setGravity(int gravity) {
1709 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001710 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -07001711 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 }
1713
1714 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
1715 gravity |= Gravity.TOP;
1716 }
1717
1718 mGravity = gravity;
1719 requestLayout();
1720 }
1721 }
1722
1723 @android.view.RemotableViewMethod
1724 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -07001725 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1726 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
1727 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 requestLayout();
1729 }
1730 }
1731
1732 @android.view.RemotableViewMethod
1733 public void setVerticalGravity(int verticalGravity) {
1734 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1735 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1736 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1737 requestLayout();
1738 }
1739 }
1740
1741 @Override
1742 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1743 return new LinearLayout.LayoutParams(getContext(), attrs);
1744 }
1745
1746 /**
1747 * Returns a set of layout parameters with a width of
Romain Guy980a9382010-01-08 15:06:28 -08001748 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001749 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1750 * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1751 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1752 * and the height to {@link LayoutParams#WRAP_CONTENT}.
1753 */
1754 @Override
1755 protected LayoutParams generateDefaultLayoutParams() {
1756 if (mOrientation == HORIZONTAL) {
1757 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1758 } else if (mOrientation == VERTICAL) {
Romain Guy980a9382010-01-08 15:06:28 -08001759 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760 }
1761 return null;
1762 }
1763
1764 @Override
1765 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1766 return new LayoutParams(p);
1767 }
1768
1769
1770 // Override to allow type-checking of LayoutParams.
1771 @Override
1772 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1773 return p instanceof LinearLayout.LayoutParams;
1774 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001775
1776 @Override
1777 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1778 super.onInitializeAccessibilityEvent(event);
1779 event.setClassName(LinearLayout.class.getName());
1780 }
1781
1782 @Override
1783 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1784 super.onInitializeAccessibilityNodeInfo(info);
1785 info.setClassName(LinearLayout.class.getName());
1786 }
1787
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 /**
1789 * Per-child layout information associated with ViewLinearLayout.
1790 *
1791 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
1792 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
1793 */
1794 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1795 /**
1796 * Indicates how much of the extra space in the LinearLayout will be
1797 * allocated to the view associated with these LayoutParams. Specify
1798 * 0 if the view should not be stretched. Otherwise the extra pixels
1799 * will be pro-rated among all views whose weight is greater than 0.
1800 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001801 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 public float weight;
1803
1804 /**
1805 * Gravity for the view associated with these LayoutParams.
1806 *
1807 * @see android.view.Gravity
1808 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001809 @ViewDebug.ExportedProperty(category = "layout", mapping = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 @ViewDebug.IntToString(from = -1, to = "NONE"),
1811 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
1812 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
1813 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
1814 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
1815 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -07001816 @ViewDebug.IntToString(from = Gravity.START, to = "START"),
1817 @ViewDebug.IntToString(from = Gravity.END, to = "END"),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
1819 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
1820 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
1821 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
1822 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
1823 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
1824 })
1825 public int gravity = -1;
1826
1827 /**
1828 * {@inheritDoc}
1829 */
1830 public LayoutParams(Context c, AttributeSet attrs) {
1831 super(c, attrs);
1832 TypedArray a =
1833 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
1834
1835 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
1836 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
1837
1838 a.recycle();
1839 }
1840
1841 /**
1842 * {@inheritDoc}
1843 */
1844 public LayoutParams(int width, int height) {
1845 super(width, height);
1846 weight = 0;
1847 }
1848
1849 /**
1850 * Creates a new set of layout parameters with the specified width, height
1851 * and weight.
1852 *
Romain Guy980a9382010-01-08 15:06:28 -08001853 * @param width the width, either {@link #MATCH_PARENT},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 * {@link #WRAP_CONTENT} or a fixed size in pixels
Romain Guy980a9382010-01-08 15:06:28 -08001855 * @param height the height, either {@link #MATCH_PARENT},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 * {@link #WRAP_CONTENT} or a fixed size in pixels
1857 * @param weight the weight
1858 */
1859 public LayoutParams(int width, int height, float weight) {
1860 super(width, height);
1861 this.weight = weight;
1862 }
1863
1864 /**
1865 * {@inheritDoc}
1866 */
1867 public LayoutParams(ViewGroup.LayoutParams p) {
1868 super(p);
1869 }
1870
1871 /**
1872 * {@inheritDoc}
1873 */
Alan Viverette0a0e1552013-08-07 13:24:09 -07001874 public LayoutParams(ViewGroup.MarginLayoutParams source) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 super(source);
1876 }
1877
Alan Viverette0a0e1552013-08-07 13:24:09 -07001878 /**
1879 * Copy constructor. Clones the width, height, margin values, weight,
1880 * and gravity of the source.
1881 *
1882 * @param source The layout params to copy from.
1883 */
1884 public LayoutParams(LayoutParams source) {
1885 super(source);
1886
1887 this.weight = source.weight;
1888 this.gravity = source.gravity;
1889 }
1890
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001891 @Override
1892 public String debug(String output) {
1893 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
1894 ", height=" + sizeToString(height) + " weight=" + weight + "}";
1895 }
1896 }
1897}