blob: a796ba545d8f658a58cf8518b1d30f65a7a82f0b [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
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
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.compat.annotation.UnsupportedAppUsage;
23import android.content.Context;
24import android.content.res.TypedArray;
25import android.graphics.Canvas;
26import android.graphics.drawable.Drawable;
27import android.os.Build;
28import android.util.AttributeSet;
29import android.view.Gravity;
30import android.view.View;
31import android.view.ViewDebug;
32import android.view.ViewGroup;
33import android.view.ViewHierarchyEncoder;
34import android.view.inspector.InspectableProperty;
35import android.widget.RemoteViews.RemoteView;
36
37import com.android.internal.R;
38
39import java.lang.annotation.Retention;
40import java.lang.annotation.RetentionPolicy;
41
42
43/**
44 * A layout that arranges other views either horizontally in a single column
45 * or vertically in a single row.
46 *
47 * <p>The following snippet shows how to include a linear layout in your layout XML file:</p>
48 *
49 * <pre>&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
50 * android:layout_width="match_parent"
51 * android:layout_height="match_parent"
52 * android:paddingLeft="16dp"
53 * android:paddingRight="16dp"
54 * android:orientation="horizontal"
55 * android:gravity="center"&gt;
56 *
57 * &lt;!-- Include other widget or layout tags here. These are considered
58 * "child views" or "children" of the linear layout --&gt;
59 *
60 * &lt;/LinearLayout&gt;</pre>
61 *
62 * <p>Set {@link android.R.styleable#LinearLayout_orientation android:orientation} to specify
63 * whether child views are displayed in a row or column.</p>
64 *
65 * <p>To control how linear layout aligns all the views it contains, set a value for
66 * {@link android.R.styleable#LinearLayout_gravity android:gravity}. For example, the
67 * snippet above sets android:gravity to "center". The value you set affects
68 * both horizontal and vertical alignment of all child views within the single row or column.</p>
69 *
70 * <p>You can set
71 * {@link android.R.styleable#LinearLayout_Layout_layout_weight android:layout_weight}
72 * on individual child views to specify how linear layout divides remaining space amongst
73 * the views it contains. See the
74 * <a href="https://developer.android.com/guide/topics/ui/layout/linear.html">Linear Layout</a>
75 * guide for an example.</p>
76 *
77 * <p>See
78 * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}
79 * to learn about other attributes you can set on a child view to affect its
80 * position and size in the containing linear layout.</p>
81 *
82 * @attr ref android.R.styleable#LinearLayout_baselineAligned
83 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
84 * @attr ref android.R.styleable#LinearLayout_gravity
85 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
86 * @attr ref android.R.styleable#LinearLayout_orientation
87 * @attr ref android.R.styleable#LinearLayout_weightSum
88 */
89@RemoteView
90public class LinearLayout extends ViewGroup {
91 /** @hide */
92 @IntDef({HORIZONTAL, VERTICAL})
93 @Retention(RetentionPolicy.SOURCE)
94 public @interface OrientationMode {}
95
96 public static final int HORIZONTAL = 0;
97 public static final int VERTICAL = 1;
98
99 /** @hide */
100 @IntDef(flag = true, prefix = { "SHOW_DIVIDER_" }, value = {
101 SHOW_DIVIDER_NONE,
102 SHOW_DIVIDER_BEGINNING,
103 SHOW_DIVIDER_MIDDLE,
104 SHOW_DIVIDER_END
105 })
106 @Retention(RetentionPolicy.SOURCE)
107 public @interface DividerMode {}
108
109 /**
110 * Don't show any dividers.
111 */
112 public static final int SHOW_DIVIDER_NONE = 0;
113 /**
114 * Show a divider at the beginning of the group.
115 */
116 public static final int SHOW_DIVIDER_BEGINNING = 1;
117 /**
118 * Show dividers between each item in the group.
119 */
120 public static final int SHOW_DIVIDER_MIDDLE = 2;
121 /**
122 * Show a divider at the end of the group.
123 */
124 public static final int SHOW_DIVIDER_END = 4;
125
126 /**
127 * Compatibility check. Old versions of the platform would give different
128 * results from measurement passes using EXACTLY and non-EXACTLY modes,
129 * even when the resulting size was the same.
130 */
131 private final boolean mAllowInconsistentMeasurement;
132
133 /**
134 * Whether the children of this layout are baseline aligned. Only applicable
135 * if {@link #mOrientation} is horizontal.
136 */
137 @ViewDebug.ExportedProperty(category = "layout")
138 private boolean mBaselineAligned = true;
139
140 /**
141 * If this layout is part of another layout that is baseline aligned,
142 * use the child at this index as the baseline.
143 *
144 * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
145 * with whether the children of this layout are baseline aligned.
146 */
147 @ViewDebug.ExportedProperty(category = "layout")
148 private int mBaselineAlignedChildIndex = -1;
149
150 /**
151 * The additional offset to the child's baseline.
152 * We'll calculate the baseline of this layout as we measure vertically; for
153 * horizontal linear layouts, the offset of 0 is appropriate.
154 */
155 @ViewDebug.ExportedProperty(category = "measurement")
156 private int mBaselineChildTop = 0;
157
158 @ViewDebug.ExportedProperty(category = "measurement")
159 private int mOrientation;
160
161 @ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
162 @ViewDebug.FlagToString(mask = -1,
163 equals = -1, name = "NONE"),
164 @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY,
165 equals = Gravity.NO_GRAVITY,name = "NONE"),
166 @ViewDebug.FlagToString(mask = Gravity.TOP,
167 equals = Gravity.TOP, name = "TOP"),
168 @ViewDebug.FlagToString(mask = Gravity.BOTTOM,
169 equals = Gravity.BOTTOM, name = "BOTTOM"),
170 @ViewDebug.FlagToString(mask = Gravity.LEFT,
171 equals = Gravity.LEFT, name = "LEFT"),
172 @ViewDebug.FlagToString(mask = Gravity.RIGHT,
173 equals = Gravity.RIGHT, name = "RIGHT"),
174 @ViewDebug.FlagToString(mask = Gravity.START,
175 equals = Gravity.START, name = "START"),
176 @ViewDebug.FlagToString(mask = Gravity.END,
177 equals = Gravity.END, name = "END"),
178 @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
179 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
180 @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
181 equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"),
182 @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL,
183 equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"),
184 @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL,
185 equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"),
186 @ViewDebug.FlagToString(mask = Gravity.CENTER,
187 equals = Gravity.CENTER, name = "CENTER"),
188 @ViewDebug.FlagToString(mask = Gravity.FILL,
189 equals = Gravity.FILL, name = "FILL"),
190 @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
191 equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
192 }, formatToHexString = true)
193 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
194 private int mGravity = Gravity.START | Gravity.TOP;
195
196 @ViewDebug.ExportedProperty(category = "measurement")
197 @UnsupportedAppUsage
198 private int mTotalLength;
199
200 @ViewDebug.ExportedProperty(category = "layout")
201 private float mWeightSum;
202
203 @ViewDebug.ExportedProperty(category = "layout")
204 @UnsupportedAppUsage
205 private boolean mUseLargestChild;
206
207 @UnsupportedAppUsage
208 private int[] mMaxAscent;
209 @UnsupportedAppUsage
210 private int[] mMaxDescent;
211
212 private static final int VERTICAL_GRAVITY_COUNT = 4;
213
214 private static final int INDEX_CENTER_VERTICAL = 0;
215 @UnsupportedAppUsage
216 private static final int INDEX_TOP = 1;
217 @UnsupportedAppUsage
218 private static final int INDEX_BOTTOM = 2;
219 private static final int INDEX_FILL = 3;
220
221 @UnsupportedAppUsage
222 private Drawable mDivider;
223 private int mDividerWidth;
224 private int mDividerHeight;
225 private int mShowDividers;
226 private int mDividerPadding;
227
228 private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
229
230 /**
231 * Signals that compatibility booleans have been initialized according to
232 * target SDK versions.
233 */
234 private static boolean sCompatibilityDone = false;
235
236 /**
237 * Behavior change in P; always remeasure weighted children, regardless of excess space.
238 */
239 private static boolean sRemeasureWeightedChildren = true;
240
241 public LinearLayout(Context context) {
242 this(context, null);
243 }
244
245 public LinearLayout(Context context, @Nullable AttributeSet attrs) {
246 this(context, attrs, 0);
247 }
248
249 public LinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
250 this(context, attrs, defStyleAttr, 0);
251 }
252
253 public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
254 super(context, attrs, defStyleAttr, defStyleRes);
255
256 if (!sCompatibilityDone && context != null) {
257 final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
258
259 // Older apps only remeasure non-zero children
260 sRemeasureWeightedChildren = targetSdkVersion >= Build.VERSION_CODES.P;
261
262 sCompatibilityDone = true;
263 }
264
265 final TypedArray a = context.obtainStyledAttributes(
266 attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);
267 saveAttributeDataForStyleable(context, com.android.internal.R.styleable.LinearLayout,
268 attrs, a, defStyleAttr, defStyleRes);
269
270 int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);
271 if (index >= 0) {
272 setOrientation(index);
273 }
274
275 index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);
276 if (index >= 0) {
277 setGravity(index);
278 }
279
280 boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);
281 if (!baselineAligned) {
282 setBaselineAligned(baselineAligned);
283 }
284
285 mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);
286
287 mBaselineAlignedChildIndex =
288 a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
289
290 mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
291
292 mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
293 mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
294 setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
295
296 final int version = context.getApplicationInfo().targetSdkVersion;
297 mAllowInconsistentMeasurement = version <= Build.VERSION_CODES.M;
298
299 a.recycle();
300 }
301
302 /**
303 * Returns <code>true</code> if this layout is currently configured to show at least one
304 * divider.
305 */
306 private boolean isShowingDividers() {
307 return (mShowDividers != SHOW_DIVIDER_NONE) && (mDivider != null);
308 }
309
310 /**
311 * Set how dividers should be shown between items in this layout
312 *
313 * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
314 * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}
315 * to show dividers, or {@link #SHOW_DIVIDER_NONE} to show no dividers.
316 */
317 public void setShowDividers(@DividerMode int showDividers) {
318 if (showDividers == mShowDividers) {
319 return;
320 }
321 mShowDividers = showDividers;
322
323 setWillNotDraw(!isShowingDividers());
324 requestLayout();
325 }
326
327 @Override
328 public boolean shouldDelayChildPressedState() {
329 return false;
330 }
331
332 /**
333 * @return A flag set indicating how dividers should be shown around items.
334 * @see #setShowDividers(int)
335 */
336 @DividerMode
337 public int getShowDividers() {
338 return mShowDividers;
339 }
340
341 /**
342 * @return the divider Drawable that will divide each item.
343 *
344 * @see #setDividerDrawable(Drawable)
345 *
346 * @attr ref android.R.styleable#LinearLayout_divider
347 */
348 @InspectableProperty(name = "divider")
349 public Drawable getDividerDrawable() {
350 return mDivider;
351 }
352
353 /**
354 * Set a drawable to be used as a divider between items.
355 *
356 * @param divider Drawable that will divide each item.
357 *
358 * @see #setShowDividers(int)
359 *
360 * @attr ref android.R.styleable#LinearLayout_divider
361 */
362 public void setDividerDrawable(Drawable divider) {
363 if (divider == mDivider) {
364 return;
365 }
366 mDivider = divider;
367 if (divider != null) {
368 mDividerWidth = divider.getIntrinsicWidth();
369 mDividerHeight = divider.getIntrinsicHeight();
370 } else {
371 mDividerWidth = 0;
372 mDividerHeight = 0;
373 }
374
375 setWillNotDraw(!isShowingDividers());
376 requestLayout();
377 }
378
379 /**
380 * Set padding displayed on both ends of dividers. For a vertical layout, the padding is applied
381 * to left and right end of dividers. For a horizontal layout, the padding is applied to top and
382 * bottom end of dividers.
383 *
384 * @param padding Padding value in pixels that will be applied to each end
385 *
386 * @see #setShowDividers(int)
387 * @see #setDividerDrawable(Drawable)
388 * @see #getDividerPadding()
389 */
390 public void setDividerPadding(int padding) {
391 if (padding == mDividerPadding) {
392 return;
393 }
394 mDividerPadding = padding;
395
396 if (isShowingDividers()) {
397 requestLayout();
398 invalidate();
399 }
400 }
401
402 /**
403 * Get the padding size used to inset dividers in pixels
404 *
405 * @see #setShowDividers(int)
406 * @see #setDividerDrawable(Drawable)
407 * @see #setDividerPadding(int)
408 */
409 public int getDividerPadding() {
410 return mDividerPadding;
411 }
412
413 /**
414 * Get the width of the current divider drawable.
415 *
416 * @hide Used internally by framework.
417 */
418 public int getDividerWidth() {
419 return mDividerWidth;
420 }
421
422 @Override
423 protected void onDraw(Canvas canvas) {
424 if (mDivider == null) {
425 return;
426 }
427
428 if (mOrientation == VERTICAL) {
429 drawDividersVertical(canvas);
430 } else {
431 drawDividersHorizontal(canvas);
432 }
433 }
434
435 void drawDividersVertical(Canvas canvas) {
436 final int count = getVirtualChildCount();
437 for (int i = 0; i < count; i++) {
438 final View child = getVirtualChildAt(i);
439 if (child != null && child.getVisibility() != GONE) {
440 if (hasDividerBeforeChildAt(i)) {
441 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
442 final int top = child.getTop() - lp.topMargin - mDividerHeight;
443 drawHorizontalDivider(canvas, top);
444 }
445 }
446 }
447
448 if (hasDividerBeforeChildAt(count)) {
449 final View child = getLastNonGoneChild();
450 int bottom = 0;
451 if (child == null) {
452 bottom = getHeight() - getPaddingBottom() - mDividerHeight;
453 } else {
454 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
455 bottom = child.getBottom() + lp.bottomMargin;
456 }
457 drawHorizontalDivider(canvas, bottom);
458 }
459 }
460
461 /**
462 * Finds the last child that is not gone. The last child will be used as the reference for
463 * where the end divider should be drawn.
464 */
465 private View getLastNonGoneChild() {
466 for (int i = getVirtualChildCount() - 1; i >= 0; i--) {
467 final View child = getVirtualChildAt(i);
468 if (child != null && child.getVisibility() != GONE) {
469 return child;
470 }
471 }
472 return null;
473 }
474
475 void drawDividersHorizontal(Canvas canvas) {
476 final int count = getVirtualChildCount();
477 final boolean isLayoutRtl = isLayoutRtl();
478 for (int i = 0; i < count; i++) {
479 final View child = getVirtualChildAt(i);
480 if (child != null && child.getVisibility() != GONE) {
481 if (hasDividerBeforeChildAt(i)) {
482 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
483 final int position;
484 if (isLayoutRtl) {
485 position = child.getRight() + lp.rightMargin;
486 } else {
487 position = child.getLeft() - lp.leftMargin - mDividerWidth;
488 }
489 drawVerticalDivider(canvas, position);
490 }
491 }
492 }
493
494 if (hasDividerBeforeChildAt(count)) {
495 final View child = getLastNonGoneChild();
496 int position;
497 if (child == null) {
498 if (isLayoutRtl) {
499 position = getPaddingLeft();
500 } else {
501 position = getWidth() - getPaddingRight() - mDividerWidth;
502 }
503 } else {
504 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
505 if (isLayoutRtl) {
506 position = child.getLeft() - lp.leftMargin - mDividerWidth;
507 } else {
508 position = child.getRight() + lp.rightMargin;
509 }
510 }
511 drawVerticalDivider(canvas, position);
512 }
513 }
514
515 void drawHorizontalDivider(Canvas canvas, int top) {
516 mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
517 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
518 mDivider.draw(canvas);
519 }
520
521 void drawVerticalDivider(Canvas canvas, int left) {
522 mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
523 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
524 mDivider.draw(canvas);
525 }
526
527 /**
528 * <p>Indicates whether widgets contained within this layout are aligned
529 * on their baseline or not.</p>
530 *
531 * @return true when widgets are baseline-aligned, false otherwise
532 */
533 @InspectableProperty
534 public boolean isBaselineAligned() {
535 return mBaselineAligned;
536 }
537
538 /**
539 * <p>Defines whether widgets contained in this layout are
540 * baseline-aligned or not.</p>
541 *
542 * @param baselineAligned true to align widgets on their baseline,
543 * false otherwise
544 *
545 * @attr ref android.R.styleable#LinearLayout_baselineAligned
546 */
547 @android.view.RemotableViewMethod
548 public void setBaselineAligned(boolean baselineAligned) {
549 mBaselineAligned = baselineAligned;
550 }
551
552 /**
553 * When true, all children with a weight will be considered having
554 * the minimum size of the largest child. If false, all children are
555 * measured normally.
556 *
557 * @return True to measure children with a weight using the minimum
558 * size of the largest child, false otherwise.
559 *
560 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
561 */
562 @InspectableProperty(name = "measureWithLargestChild")
563 public boolean isMeasureWithLargestChildEnabled() {
564 return mUseLargestChild;
565 }
566
567 /**
568 * When set to true, all children with a weight will be considered having
569 * the minimum size of the largest child. If false, all children are
570 * measured normally.
571 *
572 * Disabled by default.
573 *
574 * @param enabled True to measure children with a weight using the
575 * minimum size of the largest child, false otherwise.
576 *
577 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
578 */
579 @android.view.RemotableViewMethod
580 public void setMeasureWithLargestChildEnabled(boolean enabled) {
581 mUseLargestChild = enabled;
582 }
583
584 @Override
585 public int getBaseline() {
586 if (mBaselineAlignedChildIndex < 0) {
587 return super.getBaseline();
588 }
589
590 if (getChildCount() <= mBaselineAlignedChildIndex) {
591 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
592 + "set to an index that is out of bounds.");
593 }
594
595 final View child = getChildAt(mBaselineAlignedChildIndex);
596 final int childBaseline = child.getBaseline();
597
598 if (childBaseline == -1) {
599 if (mBaselineAlignedChildIndex == 0) {
600 // this is just the default case, safe to return -1
601 return -1;
602 }
603 // the user picked an index that points to something that doesn't
604 // know how to calculate its baseline.
605 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
606 + "points to a View that doesn't know how to get its baseline.");
607 }
608
609 // TODO: This should try to take into account the virtual offsets
610 // (See getNextLocationOffset and getLocationOffset)
611 // We should add to childTop:
612 // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
613 // and also add:
614 // getLocationOffset(child)
615 int childTop = mBaselineChildTop;
616
617 if (mOrientation == VERTICAL) {
618 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
619 if (majorGravity != Gravity.TOP) {
620 switch (majorGravity) {
621 case Gravity.BOTTOM:
622 childTop = mBottom - mTop - mPaddingBottom - mTotalLength;
623 break;
624
625 case Gravity.CENTER_VERTICAL:
626 childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) -
627 mTotalLength) / 2;
628 break;
629 }
630 }
631 }
632
633 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
634 return childTop + lp.topMargin + childBaseline;
635 }
636
637 /**
638 * @return The index of the child that will be used if this layout is
639 * part of a larger layout that is baseline aligned, or -1 if none has
640 * been set.
641 */
642 @InspectableProperty
643 public int getBaselineAlignedChildIndex() {
644 return mBaselineAlignedChildIndex;
645 }
646
647 /**
648 * @param i The index of the child that will be used if this layout is
649 * part of a larger layout that is baseline aligned.
650 *
651 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
652 */
653 @android.view.RemotableViewMethod
654 public void setBaselineAlignedChildIndex(int i) {
655 if ((i < 0) || (i >= getChildCount())) {
656 throw new IllegalArgumentException("base aligned child index out "
657 + "of range (0, " + getChildCount() + ")");
658 }
659 mBaselineAlignedChildIndex = i;
660 }
661
662 /**
663 * <p>Returns the view at the specified index. This method can be overridden
664 * to take into account virtual children. Refer to
665 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
666 * for an example.</p>
667 *
668 * @param index the child's index
669 * @return the child at the specified index, may be {@code null}
670 */
671 @Nullable
672 View getVirtualChildAt(int index) {
673 return getChildAt(index);
674 }
675
676 /**
677 * <p>Returns the virtual number of children. This number might be different
678 * than the actual number of children if the layout can hold virtual
679 * children. Refer to
680 * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
681 * for an example.</p>
682 *
683 * @return the virtual number of children
684 */
685 int getVirtualChildCount() {
686 return getChildCount();
687 }
688
689 /**
690 * Returns the desired weights sum.
691 *
692 * @return A number greater than 0.0f if the weight sum is defined, or
693 * a number lower than or equals to 0.0f if not weight sum is
694 * to be used.
695 */
696 @InspectableProperty
697 public float getWeightSum() {
698 return mWeightSum;
699 }
700
701 /**
702 * Defines the desired weights sum. If unspecified the weights sum is computed
703 * at layout time by adding the layout_weight of each child.
704 *
705 * This can be used for instance to give a single child 50% of the total
706 * available space by giving it a layout_weight of 0.5 and setting the
707 * weightSum to 1.0.
708 *
709 * @param weightSum a number greater than 0.0f, or a number lower than or equals
710 * to 0.0f if the weight sum should be computed from the children's
711 * layout_weight
712 */
713 @android.view.RemotableViewMethod
714 public void setWeightSum(float weightSum) {
715 mWeightSum = Math.max(0.0f, weightSum);
716 }
717
718 @Override
719 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
720 if (mOrientation == VERTICAL) {
721 measureVertical(widthMeasureSpec, heightMeasureSpec);
722 } else {
723 measureHorizontal(widthMeasureSpec, heightMeasureSpec);
724 }
725 }
726
727 /**
728 * Determines where to position dividers between children.
729 *
730 * @param childIndex Index of child to check for preceding divider
731 * @return true if there should be a divider before the child at childIndex
732 * @hide Pending API consideration. Currently only used internally by the system.
733 */
734 protected boolean hasDividerBeforeChildAt(int childIndex) {
735 if (childIndex == getVirtualChildCount()) {
736 // Check whether the end divider should draw.
737 return (mShowDividers & SHOW_DIVIDER_END) != 0;
738 }
739 boolean allViewsAreGoneBefore = allViewsAreGoneBefore(childIndex);
740 if (allViewsAreGoneBefore) {
741 // This is the first view that's not gone, check if beginning divider is enabled.
742 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
743 } else {
744 return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
745 }
746 }
747
748 /**
749 * Checks whether all (virtual) child views before the given index are gone.
750 */
751 private boolean allViewsAreGoneBefore(int childIndex) {
752 for (int i = childIndex - 1; i >= 0; i--) {
753 final View child = getVirtualChildAt(i);
754 if (child != null && child.getVisibility() != GONE) {
755 return false;
756 }
757 }
758 return true;
759 }
760
761 /**
762 * Measures the children when the orientation of this LinearLayout is set
763 * to {@link #VERTICAL}.
764 *
765 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
766 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
767 *
768 * @see #getOrientation()
769 * @see #setOrientation(int)
770 * @see #onMeasure(int, int)
771 */
772 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
773 mTotalLength = 0;
774 int maxWidth = 0;
775 int childState = 0;
776 int alternativeMaxWidth = 0;
777 int weightedMaxWidth = 0;
778 boolean allFillParent = true;
779 float totalWeight = 0;
780
781 final int count = getVirtualChildCount();
782
783 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
784 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
785
786 boolean matchWidth = false;
787 boolean skippedMeasure = false;
788
789 final int baselineChildIndex = mBaselineAlignedChildIndex;
790 final boolean useLargestChild = mUseLargestChild;
791
792 int largestChildHeight = Integer.MIN_VALUE;
793 int consumedExcessSpace = 0;
794
795 int nonSkippedChildCount = 0;
796
797 // See how tall everyone is. Also remember max width.
798 for (int i = 0; i < count; ++i) {
799 final View child = getVirtualChildAt(i);
800 if (child == null) {
801 mTotalLength += measureNullChild(i);
802 continue;
803 }
804
805 if (child.getVisibility() == View.GONE) {
806 i += getChildrenSkipCount(child, i);
807 continue;
808 }
809
810 nonSkippedChildCount++;
811 if (hasDividerBeforeChildAt(i)) {
812 mTotalLength += mDividerHeight;
813 }
814
815 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
816
817 totalWeight += lp.weight;
818
819 final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
820 if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
821 // Optimization: don't bother measuring children who are only
822 // laid out using excess space. These views will get measured
823 // later if we have space to distribute.
824 final int totalLength = mTotalLength;
825 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
826 skippedMeasure = true;
827 } else {
828 if (useExcessSpace) {
829 // The heightMode is either UNSPECIFIED or AT_MOST, and
830 // this child is only laid out using excess space. Measure
831 // using WRAP_CONTENT so that we can find out the view's
832 // optimal height. We'll restore the original height of 0
833 // after measurement.
834 lp.height = LayoutParams.WRAP_CONTENT;
835 }
836
837 // Determine how big this child would like to be. If this or
838 // previous children have given a weight, then we allow it to
839 // use all available space (and we will shrink things later
840 // if needed).
841 final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
842 measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
843 heightMeasureSpec, usedHeight);
844
845 final int childHeight = child.getMeasuredHeight();
846 if (useExcessSpace) {
847 // Restore the original height and record how much space
848 // we've allocated to excess-only children so that we can
849 // match the behavior of EXACTLY measurement.
850 lp.height = 0;
851 consumedExcessSpace += childHeight;
852 }
853
854 final int totalLength = mTotalLength;
855 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
856 lp.bottomMargin + getNextLocationOffset(child));
857
858 if (useLargestChild) {
859 largestChildHeight = Math.max(childHeight, largestChildHeight);
860 }
861 }
862
863 /**
864 * If applicable, compute the additional offset to the child's baseline
865 * we'll need later when asked {@link #getBaseline}.
866 */
867 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
868 mBaselineChildTop = mTotalLength;
869 }
870
871 // if we are trying to use a child index for our baseline, the above
872 // book keeping only works if there are no children above it with
873 // weight. fail fast to aid the developer.
874 if (i < baselineChildIndex && lp.weight > 0) {
875 throw new RuntimeException("A child of LinearLayout with index "
876 + "less than mBaselineAlignedChildIndex has weight > 0, which "
877 + "won't work. Either remove the weight, or don't set "
878 + "mBaselineAlignedChildIndex.");
879 }
880
881 boolean matchWidthLocally = false;
882 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
883 // The width of the linear layout will scale, and at least one
884 // child said it wanted to match our width. Set a flag
885 // indicating that we need to remeasure at least that view when
886 // we know our width.
887 matchWidth = true;
888 matchWidthLocally = true;
889 }
890
891 final int margin = lp.leftMargin + lp.rightMargin;
892 final int measuredWidth = child.getMeasuredWidth() + margin;
893 maxWidth = Math.max(maxWidth, measuredWidth);
894 childState = combineMeasuredStates(childState, child.getMeasuredState());
895
896 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
897 if (lp.weight > 0) {
898 /*
899 * Widths of weighted Views are bogus if we end up
900 * remeasuring, so keep them separate.
901 */
902 weightedMaxWidth = Math.max(weightedMaxWidth,
903 matchWidthLocally ? margin : measuredWidth);
904 } else {
905 alternativeMaxWidth = Math.max(alternativeMaxWidth,
906 matchWidthLocally ? margin : measuredWidth);
907 }
908
909 i += getChildrenSkipCount(child, i);
910 }
911
912 if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
913 mTotalLength += mDividerHeight;
914 }
915
916 if (useLargestChild &&
917 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
918 mTotalLength = 0;
919
920 for (int i = 0; i < count; ++i) {
921 final View child = getVirtualChildAt(i);
922 if (child == null) {
923 mTotalLength += measureNullChild(i);
924 continue;
925 }
926
927 if (child.getVisibility() == GONE) {
928 i += getChildrenSkipCount(child, i);
929 continue;
930 }
931
932 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
933 child.getLayoutParams();
934 // Account for negative margins
935 final int totalLength = mTotalLength;
936 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
937 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
938 }
939 }
940
941 // Add in our padding
942 mTotalLength += mPaddingTop + mPaddingBottom;
943
944 int heightSize = mTotalLength;
945
946 // Check against our minimum height
947 heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
948
949 // Reconcile our calculated size with the heightMeasureSpec
950 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
951 heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
952 // Either expand children with weight to take up available space or
953 // shrink them if they extend beyond our current bounds. If we skipped
954 // measurement on any children, we need to measure them now.
955 int remainingExcess = heightSize - mTotalLength
956 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
957 if (skippedMeasure
958 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
959 float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
960
961 mTotalLength = 0;
962
963 for (int i = 0; i < count; ++i) {
964 final View child = getVirtualChildAt(i);
965 if (child == null || child.getVisibility() == View.GONE) {
966 continue;
967 }
968
969 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
970 final float childWeight = lp.weight;
971 if (childWeight > 0) {
972 final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
973 remainingExcess -= share;
974 remainingWeightSum -= childWeight;
975
976 final int childHeight;
977 if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) {
978 childHeight = largestChildHeight;
979 } else if (lp.height == 0 && (!mAllowInconsistentMeasurement
980 || heightMode == MeasureSpec.EXACTLY)) {
981 // This child needs to be laid out from scratch using
982 // only its share of excess space.
983 childHeight = share;
984 } else {
985 // This child had some intrinsic height to which we
986 // need to add its share of excess space.
987 childHeight = child.getMeasuredHeight() + share;
988 }
989
990 final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
991 Math.max(0, childHeight), MeasureSpec.EXACTLY);
992 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
993 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,
994 lp.width);
995 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
996
997 // Child may now not fit in vertical dimension.
998 childState = combineMeasuredStates(childState, child.getMeasuredState()
999 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
1000 }
1001
1002 final int margin = lp.leftMargin + lp.rightMargin;
1003 final int measuredWidth = child.getMeasuredWidth() + margin;
1004 maxWidth = Math.max(maxWidth, measuredWidth);
1005
1006 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
1007 lp.width == LayoutParams.MATCH_PARENT;
1008
1009 alternativeMaxWidth = Math.max(alternativeMaxWidth,
1010 matchWidthLocally ? margin : measuredWidth);
1011
1012 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
1013
1014 final int totalLength = mTotalLength;
1015 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
1016 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
1017 }
1018
1019 // Add in our padding
1020 mTotalLength += mPaddingTop + mPaddingBottom;
1021 // TODO: Should we recompute the heightSpec based on the new total length?
1022 } else {
1023 alternativeMaxWidth = Math.max(alternativeMaxWidth,
1024 weightedMaxWidth);
1025
1026
1027 // We have no limit, so make all weighted views as tall as the largest child.
1028 // Children will have already been measured once.
1029 if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
1030 for (int i = 0; i < count; i++) {
1031 final View child = getVirtualChildAt(i);
1032 if (child == null || child.getVisibility() == View.GONE) {
1033 continue;
1034 }
1035
1036 final LinearLayout.LayoutParams lp =
1037 (LinearLayout.LayoutParams) child.getLayoutParams();
1038
1039 float childExtra = lp.weight;
1040 if (childExtra > 0) {
1041 child.measure(
1042 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
1043 MeasureSpec.EXACTLY),
1044 MeasureSpec.makeMeasureSpec(largestChildHeight,
1045 MeasureSpec.EXACTLY));
1046 }
1047 }
1048 }
1049 }
1050
1051 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
1052 maxWidth = alternativeMaxWidth;
1053 }
1054
1055 maxWidth += mPaddingLeft + mPaddingRight;
1056
1057 // Check against our minimum width
1058 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
1059
1060 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
1061 heightSizeAndState);
1062
1063 if (matchWidth) {
1064 forceUniformWidth(count, heightMeasureSpec);
1065 }
1066 }
1067
1068 private void forceUniformWidth(int count, int heightMeasureSpec) {
1069 // Pretend that the linear layout has an exact size.
1070 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
1071 MeasureSpec.EXACTLY);
1072 for (int i = 0; i< count; ++i) {
1073 final View child = getVirtualChildAt(i);
1074 if (child != null && child.getVisibility() != GONE) {
1075 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
1076
1077 if (lp.width == LayoutParams.MATCH_PARENT) {
1078 // Temporarily force children to reuse their old measured height
1079 // FIXME: this may not be right for something like wrapping text?
1080 int oldHeight = lp.height;
1081 lp.height = child.getMeasuredHeight();
1082
1083 // Remeasue with new dimensions
1084 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
1085 lp.height = oldHeight;
1086 }
1087 }
1088 }
1089 }
1090
1091 /**
1092 * Measures the children when the orientation of this LinearLayout is set
1093 * to {@link #HORIZONTAL}.
1094 *
1095 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
1096 * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
1097 *
1098 * @see #getOrientation()
1099 * @see #setOrientation(int)
1100 * @see #onMeasure(int, int)
1101 */
1102 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
1103 mTotalLength = 0;
1104 int maxHeight = 0;
1105 int childState = 0;
1106 int alternativeMaxHeight = 0;
1107 int weightedMaxHeight = 0;
1108 boolean allFillParent = true;
1109 float totalWeight = 0;
1110
1111 final int count = getVirtualChildCount();
1112
1113 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
1114 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
1115
1116 boolean matchHeight = false;
1117 boolean skippedMeasure = false;
1118
1119 if (mMaxAscent == null || mMaxDescent == null) {
1120 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
1121 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
1122 }
1123
1124 final int[] maxAscent = mMaxAscent;
1125 final int[] maxDescent = mMaxDescent;
1126
1127 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1128 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1129
1130 final boolean baselineAligned = mBaselineAligned;
1131 final boolean useLargestChild = mUseLargestChild;
1132
1133 final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
1134
1135 int largestChildWidth = Integer.MIN_VALUE;
1136 int usedExcessSpace = 0;
1137
1138 int nonSkippedChildCount = 0;
1139
1140 // See how wide everyone is. Also remember max height.
1141 for (int i = 0; i < count; ++i) {
1142 final View child = getVirtualChildAt(i);
1143 if (child == null) {
1144 mTotalLength += measureNullChild(i);
1145 continue;
1146 }
1147
1148 if (child.getVisibility() == GONE) {
1149 i += getChildrenSkipCount(child, i);
1150 continue;
1151 }
1152
1153 nonSkippedChildCount++;
1154 if (hasDividerBeforeChildAt(i)) {
1155 mTotalLength += mDividerWidth;
1156 }
1157
1158 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1159
1160 totalWeight += lp.weight;
1161
1162 final boolean useExcessSpace = lp.width == 0 && lp.weight > 0;
1163 if (widthMode == MeasureSpec.EXACTLY && useExcessSpace) {
1164 // Optimization: don't bother measuring children who are only
1165 // laid out using excess space. These views will get measured
1166 // later if we have space to distribute.
1167 if (isExactly) {
1168 mTotalLength += lp.leftMargin + lp.rightMargin;
1169 } else {
1170 final int totalLength = mTotalLength;
1171 mTotalLength = Math.max(totalLength, totalLength +
1172 lp.leftMargin + lp.rightMargin);
1173 }
1174
1175 // Baseline alignment requires to measure widgets to obtain the
1176 // baseline offset (in particular for TextViews). The following
1177 // defeats the optimization mentioned above. Allow the child to
1178 // use as much space as it wants because we can shrink things
1179 // later (and re-measure).
1180 if (baselineAligned) {
1181 final int freeWidthSpec = MeasureSpec.makeSafeMeasureSpec(
1182 MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);
1183 final int freeHeightSpec = MeasureSpec.makeSafeMeasureSpec(
1184 MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);
1185 child.measure(freeWidthSpec, freeHeightSpec);
1186 } else {
1187 skippedMeasure = true;
1188 }
1189 } else {
1190 if (useExcessSpace) {
1191 // The widthMode is either UNSPECIFIED or AT_MOST, and
1192 // this child is only laid out using excess space. Measure
1193 // using WRAP_CONTENT so that we can find out the view's
1194 // optimal width. We'll restore the original width of 0
1195 // after measurement.
1196 lp.width = LayoutParams.WRAP_CONTENT;
1197 }
1198
1199 // Determine how big this child would like to be. If this or
1200 // previous children have given a weight, then we allow it to
1201 // use all available space (and we will shrink things later
1202 // if needed).
1203 final int usedWidth = totalWeight == 0 ? mTotalLength : 0;
1204 measureChildBeforeLayout(child, i, widthMeasureSpec, usedWidth,
1205 heightMeasureSpec, 0);
1206
1207 final int childWidth = child.getMeasuredWidth();
1208 if (useExcessSpace) {
1209 // Restore the original width and record how much space
1210 // we've allocated to excess-only children so that we can
1211 // match the behavior of EXACTLY measurement.
1212 lp.width = 0;
1213 usedExcessSpace += childWidth;
1214 }
1215
1216 if (isExactly) {
1217 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin
1218 + getNextLocationOffset(child);
1219 } else {
1220 final int totalLength = mTotalLength;
1221 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin
1222 + lp.rightMargin + getNextLocationOffset(child));
1223 }
1224
1225 if (useLargestChild) {
1226 largestChildWidth = Math.max(childWidth, largestChildWidth);
1227 }
1228 }
1229
1230 boolean matchHeightLocally = false;
1231 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
1232 // The height of the linear layout will scale, and at least one
1233 // child said it wanted to match our height. Set a flag indicating that
1234 // we need to remeasure at least that view when we know our height.
1235 matchHeight = true;
1236 matchHeightLocally = true;
1237 }
1238
1239 final int margin = lp.topMargin + lp.bottomMargin;
1240 final int childHeight = child.getMeasuredHeight() + margin;
1241 childState = combineMeasuredStates(childState, child.getMeasuredState());
1242
1243 if (baselineAligned) {
1244 final int childBaseline = child.getBaseline();
1245 if (childBaseline != -1) {
1246 // Translates the child's vertical gravity into an index
1247 // in the range 0..VERTICAL_GRAVITY_COUNT
1248 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1249 & Gravity.VERTICAL_GRAVITY_MASK;
1250 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1251 & ~Gravity.AXIS_SPECIFIED) >> 1;
1252
1253 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1254 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
1255 }
1256 }
1257
1258 maxHeight = Math.max(maxHeight, childHeight);
1259
1260 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
1261 if (lp.weight > 0) {
1262 /*
1263 * Heights of weighted Views are bogus if we end up
1264 * remeasuring, so keep them separate.
1265 */
1266 weightedMaxHeight = Math.max(weightedMaxHeight,
1267 matchHeightLocally ? margin : childHeight);
1268 } else {
1269 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1270 matchHeightLocally ? margin : childHeight);
1271 }
1272
1273 i += getChildrenSkipCount(child, i);
1274 }
1275
1276 if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
1277 mTotalLength += mDividerWidth;
1278 }
1279
1280 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1281 // the most common case
1282 if (maxAscent[INDEX_TOP] != -1 ||
1283 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1284 maxAscent[INDEX_BOTTOM] != -1 ||
1285 maxAscent[INDEX_FILL] != -1) {
1286 final int ascent = Math.max(maxAscent[INDEX_FILL],
1287 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1288 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1289 final int descent = Math.max(maxDescent[INDEX_FILL],
1290 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1291 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1292 maxHeight = Math.max(maxHeight, ascent + descent);
1293 }
1294
1295 if (useLargestChild &&
1296 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
1297 mTotalLength = 0;
1298
1299 for (int i = 0; i < count; ++i) {
1300 final View child = getVirtualChildAt(i);
1301 if (child == null) {
1302 mTotalLength += measureNullChild(i);
1303 continue;
1304 }
1305
1306 if (child.getVisibility() == GONE) {
1307 i += getChildrenSkipCount(child, i);
1308 continue;
1309 }
1310
1311 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1312 child.getLayoutParams();
1313 if (isExactly) {
1314 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
1315 getNextLocationOffset(child);
1316 } else {
1317 final int totalLength = mTotalLength;
1318 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
1319 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1320 }
1321 }
1322 }
1323
1324 // Add in our padding
1325 mTotalLength += mPaddingLeft + mPaddingRight;
1326
1327 int widthSize = mTotalLength;
1328
1329 // Check against our minimum width
1330 widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
1331
1332 // Reconcile our calculated size with the widthMeasureSpec
1333 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
1334 widthSize = widthSizeAndState & MEASURED_SIZE_MASK;
1335
1336 // Either expand children with weight to take up available space or
1337 // shrink them if they extend beyond our current bounds. If we skipped
1338 // measurement on any children, we need to measure them now.
1339 int remainingExcess = widthSize - mTotalLength
1340 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
1341 if (skippedMeasure
1342 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
1343 float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
1344
1345 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1346 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1347 maxHeight = -1;
1348
1349 mTotalLength = 0;
1350
1351 for (int i = 0; i < count; ++i) {
1352 final View child = getVirtualChildAt(i);
1353 if (child == null || child.getVisibility() == View.GONE) {
1354 continue;
1355 }
1356
1357 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1358 final float childWeight = lp.weight;
1359 if (childWeight > 0) {
1360 final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
1361 remainingExcess -= share;
1362 remainingWeightSum -= childWeight;
1363
1364 final int childWidth;
1365 if (mUseLargestChild && widthMode != MeasureSpec.EXACTLY) {
1366 childWidth = largestChildWidth;
1367 } else if (lp.width == 0 && (!mAllowInconsistentMeasurement
1368 || widthMode == MeasureSpec.EXACTLY)) {
1369 // This child needs to be laid out from scratch using
1370 // only its share of excess space.
1371 childWidth = share;
1372 } else {
1373 // This child had some intrinsic width to which we
1374 // need to add its share of excess space.
1375 childWidth = child.getMeasuredWidth() + share;
1376 }
1377
1378 final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
1379 Math.max(0, childWidth), MeasureSpec.EXACTLY);
1380 final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
1381 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
1382 lp.height);
1383 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
1384
1385 // Child may now not fit in horizontal dimension.
1386 childState = combineMeasuredStates(childState,
1387 child.getMeasuredState() & MEASURED_STATE_MASK);
1388 }
1389
1390 if (isExactly) {
1391 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
1392 getNextLocationOffset(child);
1393 } else {
1394 final int totalLength = mTotalLength;
1395 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
1396 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1397 }
1398
1399 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
1400 lp.height == LayoutParams.MATCH_PARENT;
1401
1402 final int margin = lp.topMargin + lp .bottomMargin;
1403 int childHeight = child.getMeasuredHeight() + margin;
1404 maxHeight = Math.max(maxHeight, childHeight);
1405 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1406 matchHeightLocally ? margin : childHeight);
1407
1408 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
1409
1410 if (baselineAligned) {
1411 final int childBaseline = child.getBaseline();
1412 if (childBaseline != -1) {
1413 // Translates the child's vertical gravity into an index in the range 0..2
1414 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1415 & Gravity.VERTICAL_GRAVITY_MASK;
1416 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1417 & ~Gravity.AXIS_SPECIFIED) >> 1;
1418
1419 maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1420 maxDescent[index] = Math.max(maxDescent[index],
1421 childHeight - childBaseline);
1422 }
1423 }
1424 }
1425
1426 // Add in our padding
1427 mTotalLength += mPaddingLeft + mPaddingRight;
1428 // TODO: Should we update widthSize with the new total length?
1429
1430 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1431 // the most common case
1432 if (maxAscent[INDEX_TOP] != -1 ||
1433 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1434 maxAscent[INDEX_BOTTOM] != -1 ||
1435 maxAscent[INDEX_FILL] != -1) {
1436 final int ascent = Math.max(maxAscent[INDEX_FILL],
1437 Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1438 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1439 final int descent = Math.max(maxDescent[INDEX_FILL],
1440 Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1441 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1442 maxHeight = Math.max(maxHeight, ascent + descent);
1443 }
1444 } else {
1445 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
1446
1447 // We have no limit, so make all weighted views as wide as the largest child.
1448 // Children will have already been measured once.
1449 if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
1450 for (int i = 0; i < count; i++) {
1451 final View child = getVirtualChildAt(i);
1452 if (child == null || child.getVisibility() == View.GONE) {
1453 continue;
1454 }
1455
1456 final LinearLayout.LayoutParams lp =
1457 (LinearLayout.LayoutParams) child.getLayoutParams();
1458
1459 float childExtra = lp.weight;
1460 if (childExtra > 0) {
1461 child.measure(
1462 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
1463 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
1464 MeasureSpec.EXACTLY));
1465 }
1466 }
1467 }
1468 }
1469
1470 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
1471 maxHeight = alternativeMaxHeight;
1472 }
1473
1474 maxHeight += mPaddingTop + mPaddingBottom;
1475
1476 // Check against our minimum height
1477 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
1478
1479 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
1480 resolveSizeAndState(maxHeight, heightMeasureSpec,
1481 (childState<<MEASURED_HEIGHT_STATE_SHIFT)));
1482
1483 if (matchHeight) {
1484 forceUniformHeight(count, widthMeasureSpec);
1485 }
1486 }
1487
1488 private void forceUniformHeight(int count, int widthMeasureSpec) {
1489 // Pretend that the linear layout has an exact size. This is the measured height of
1490 // ourselves. The measured height should be the max height of the children, changed
1491 // to accommodate the heightMeasureSpec from the parent
1492 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
1493 MeasureSpec.EXACTLY);
1494 for (int i = 0; i < count; ++i) {
1495 final View child = getVirtualChildAt(i);
1496 if (child != null && child.getVisibility() != GONE) {
1497 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
1498
1499 if (lp.height == LayoutParams.MATCH_PARENT) {
1500 // Temporarily force children to reuse their old measured width
1501 // FIXME: this may not be right for something like wrapping text?
1502 int oldWidth = lp.width;
1503 lp.width = child.getMeasuredWidth();
1504
1505 // Remeasure with new dimensions
1506 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
1507 lp.width = oldWidth;
1508 }
1509 }
1510 }
1511 }
1512
1513 /**
1514 * <p>Returns the number of children to skip after measuring/laying out
1515 * the specified child.</p>
1516 *
1517 * @param child the child after which we want to skip children
1518 * @param index the index of the child after which we want to skip children
1519 * @return the number of children to skip, 0 by default
1520 */
1521 int getChildrenSkipCount(View child, int index) {
1522 return 0;
1523 }
1524
1525 /**
1526 * <p>Returns the size (width or height) that should be occupied by a null
1527 * child.</p>
1528 *
1529 * @param childIndex the index of the null child
1530 * @return the width or height of the child depending on the orientation
1531 */
1532 int measureNullChild(int childIndex) {
1533 return 0;
1534 }
1535
1536 /**
1537 * <p>Measure the child according to the parent's measure specs. This
1538 * method should be overridden by subclasses to force the sizing of
1539 * children. This method is called by {@link #measureVertical(int, int)} and
1540 * {@link #measureHorizontal(int, int)}.</p>
1541 *
1542 * @param child the child to measure
1543 * @param childIndex the index of the child in this view
1544 * @param widthMeasureSpec horizontal space requirements as imposed by the parent
1545 * @param totalWidth extra space that has been used up by the parent horizontally
1546 * @param heightMeasureSpec vertical space requirements as imposed by the parent
1547 * @param totalHeight extra space that has been used up by the parent vertically
1548 */
1549 void measureChildBeforeLayout(View child, int childIndex,
1550 int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
1551 int totalHeight) {
1552 measureChildWithMargins(child, widthMeasureSpec, totalWidth,
1553 heightMeasureSpec, totalHeight);
1554 }
1555
1556 /**
1557 * <p>Return the location offset of the specified child. This can be used
1558 * by subclasses to change the location of a given widget.</p>
1559 *
1560 * @param child the child for which to obtain the location offset
1561 * @return the location offset in pixels
1562 */
1563 int getLocationOffset(View child) {
1564 return 0;
1565 }
1566
1567 /**
1568 * <p>Return the size offset of the next sibling of the specified child.
1569 * This can be used by subclasses to change the location of the widget
1570 * following <code>child</code>.</p>
1571 *
1572 * @param child the child whose next sibling will be moved
1573 * @return the location offset of the next child in pixels
1574 */
1575 int getNextLocationOffset(View child) {
1576 return 0;
1577 }
1578
1579 @Override
1580 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1581 if (mOrientation == VERTICAL) {
1582 layoutVertical(l, t, r, b);
1583 } else {
1584 layoutHorizontal(l, t, r, b);
1585 }
1586 }
1587
1588 /**
1589 * Position the children during a layout pass if the orientation of this
1590 * LinearLayout is set to {@link #VERTICAL}.
1591 *
1592 * @see #getOrientation()
1593 * @see #setOrientation(int)
1594 * @see #onLayout(boolean, int, int, int, int)
1595 * @param left
1596 * @param top
1597 * @param right
1598 * @param bottom
1599 */
1600 void layoutVertical(int left, int top, int right, int bottom) {
1601 final int paddingLeft = mPaddingLeft;
1602
1603 int childTop;
1604 int childLeft;
1605
1606 // Where right end of child should go
1607 final int width = right - left;
1608 int childRight = width - mPaddingRight;
1609
1610 // Space available for child
1611 int childSpace = width - paddingLeft - mPaddingRight;
1612
1613 final int count = getVirtualChildCount();
1614
1615 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1616 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1617
1618 switch (majorGravity) {
1619 case Gravity.BOTTOM:
1620 // mTotalLength contains the padding already
1621 childTop = mPaddingTop + bottom - top - mTotalLength;
1622 break;
1623
1624 // mTotalLength contains the padding already
1625 case Gravity.CENTER_VERTICAL:
1626 childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
1627 break;
1628
1629 case Gravity.TOP:
1630 default:
1631 childTop = mPaddingTop;
1632 break;
1633 }
1634
1635 for (int i = 0; i < count; i++) {
1636 final View child = getVirtualChildAt(i);
1637 if (child == null) {
1638 childTop += measureNullChild(i);
1639 } else if (child.getVisibility() != GONE) {
1640 final int childWidth = child.getMeasuredWidth();
1641 final int childHeight = child.getMeasuredHeight();
1642
1643 final LinearLayout.LayoutParams lp =
1644 (LinearLayout.LayoutParams) child.getLayoutParams();
1645
1646 int gravity = lp.gravity;
1647 if (gravity < 0) {
1648 gravity = minorGravity;
1649 }
1650 final int layoutDirection = getLayoutDirection();
1651 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
1652 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1653 case Gravity.CENTER_HORIZONTAL:
1654 childLeft = paddingLeft + ((childSpace - childWidth) / 2)
1655 + lp.leftMargin - lp.rightMargin;
1656 break;
1657
1658 case Gravity.RIGHT:
1659 childLeft = childRight - childWidth - lp.rightMargin;
1660 break;
1661
1662 case Gravity.LEFT:
1663 default:
1664 childLeft = paddingLeft + lp.leftMargin;
1665 break;
1666 }
1667
1668 if (hasDividerBeforeChildAt(i)) {
1669 childTop += mDividerHeight;
1670 }
1671
1672 childTop += lp.topMargin;
1673 setChildFrame(child, childLeft, childTop + getLocationOffset(child),
1674 childWidth, childHeight);
1675 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
1676
1677 i += getChildrenSkipCount(child, i);
1678 }
1679 }
1680 }
1681
1682 @Override
1683 public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
1684 super.onRtlPropertiesChanged(layoutDirection);
1685 if (layoutDirection != mLayoutDirection) {
1686 mLayoutDirection = layoutDirection;
1687 if (mOrientation == HORIZONTAL) {
1688 requestLayout();
1689 }
1690 }
1691 }
1692
1693 /**
1694 * Position the children during a layout pass if the orientation of this
1695 * LinearLayout is set to {@link #HORIZONTAL}.
1696 *
1697 * @see #getOrientation()
1698 * @see #setOrientation(int)
1699 * @see #onLayout(boolean, int, int, int, int)
1700 * @param left
1701 * @param top
1702 * @param right
1703 * @param bottom
1704 */
1705 void layoutHorizontal(int left, int top, int right, int bottom) {
1706 final boolean isLayoutRtl = isLayoutRtl();
1707 final int paddingTop = mPaddingTop;
1708
1709 int childTop;
1710 int childLeft;
1711
1712 // Where bottom of child should go
1713 final int height = bottom - top;
1714 int childBottom = height - mPaddingBottom;
1715
1716 // Space available for child
1717 int childSpace = height - paddingTop - mPaddingBottom;
1718
1719 final int count = getVirtualChildCount();
1720
1721 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1722 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1723
1724 final boolean baselineAligned = mBaselineAligned;
1725
1726 final int[] maxAscent = mMaxAscent;
1727 final int[] maxDescent = mMaxDescent;
1728
1729 final int layoutDirection = getLayoutDirection();
1730 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
1731 case Gravity.RIGHT:
1732 // mTotalLength contains the padding already
1733 childLeft = mPaddingLeft + right - left - mTotalLength;
1734 break;
1735
1736 case Gravity.CENTER_HORIZONTAL:
1737 // mTotalLength contains the padding already
1738 childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
1739 break;
1740
1741 case Gravity.LEFT:
1742 default:
1743 childLeft = mPaddingLeft;
1744 break;
1745 }
1746
1747 int start = 0;
1748 int dir = 1;
1749 //In case of RTL, start drawing from the last child.
1750 if (isLayoutRtl) {
1751 start = count - 1;
1752 dir = -1;
1753 }
1754
1755 for (int i = 0; i < count; i++) {
1756 final int childIndex = start + dir * i;
1757 final View child = getVirtualChildAt(childIndex);
1758 if (child == null) {
1759 childLeft += measureNullChild(childIndex);
1760 } else if (child.getVisibility() != GONE) {
1761 final int childWidth = child.getMeasuredWidth();
1762 final int childHeight = child.getMeasuredHeight();
1763 int childBaseline = -1;
1764
1765 final LinearLayout.LayoutParams lp =
1766 (LinearLayout.LayoutParams) child.getLayoutParams();
1767
1768 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
1769 childBaseline = child.getBaseline();
1770 }
1771
1772 int gravity = lp.gravity;
1773 if (gravity < 0) {
1774 gravity = minorGravity;
1775 }
1776
1777 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
1778 case Gravity.TOP:
1779 childTop = paddingTop + lp.topMargin;
1780 if (childBaseline != -1) {
1781 childTop += maxAscent[INDEX_TOP] - childBaseline;
1782 }
1783 break;
1784
1785 case Gravity.CENTER_VERTICAL:
1786 // Removed support for baseline alignment when layout_gravity or
1787 // gravity == center_vertical. See bug #1038483.
1788 // Keep the code around if we need to re-enable this feature
1789 // if (childBaseline != -1) {
1790 // // Align baselines vertically only if the child is smaller than us
1791 // if (childSpace - childHeight > 0) {
1792 // childTop = paddingTop + (childSpace / 2) - childBaseline;
1793 // } else {
1794 // childTop = paddingTop + (childSpace - childHeight) / 2;
1795 // }
1796 // } else {
1797 childTop = paddingTop + ((childSpace - childHeight) / 2)
1798 + lp.topMargin - lp.bottomMargin;
1799 break;
1800
1801 case Gravity.BOTTOM:
1802 childTop = childBottom - childHeight - lp.bottomMargin;
1803 if (childBaseline != -1) {
1804 int descent = child.getMeasuredHeight() - childBaseline;
1805 childTop -= (maxDescent[INDEX_BOTTOM] - descent);
1806 }
1807 break;
1808 default:
1809 childTop = paddingTop;
1810 break;
1811 }
1812
1813 if (hasDividerBeforeChildAt(childIndex)) {
1814 childLeft += mDividerWidth;
1815 }
1816
1817 childLeft += lp.leftMargin;
1818 setChildFrame(child, childLeft + getLocationOffset(child), childTop,
1819 childWidth, childHeight);
1820 childLeft += childWidth + lp.rightMargin +
1821 getNextLocationOffset(child);
1822
1823 i += getChildrenSkipCount(child, childIndex);
1824 }
1825 }
1826 }
1827
1828 private void setChildFrame(View child, int left, int top, int width, int height) {
1829 child.layout(left, top, left + width, top + height);
1830 }
1831
1832 /**
1833 * Should the layout be a column or a row.
1834 * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default
1835 * value is {@link #HORIZONTAL}.
1836 *
1837 * @attr ref android.R.styleable#LinearLayout_orientation
1838 */
1839 public void setOrientation(@OrientationMode int orientation) {
1840 if (mOrientation != orientation) {
1841 mOrientation = orientation;
1842 requestLayout();
1843 }
1844 }
1845
1846 /**
1847 * Returns the current orientation.
1848 *
1849 * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
1850 */
1851 @OrientationMode
1852 @InspectableProperty(enumMapping = {
1853 @InspectableProperty.EnumEntry(value = HORIZONTAL, name = "horizontal"),
1854 @InspectableProperty.EnumEntry(value = VERTICAL, name = "vertical")
1855 })
1856 public int getOrientation() {
1857 return mOrientation;
1858 }
1859
1860 /**
1861 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
1862 * this layout has a VERTICAL orientation, this controls where all the child
1863 * views are placed if there is extra vertical space. If this layout has a
1864 * HORIZONTAL orientation, this controls the alignment of the children.
1865 *
1866 * @param gravity See {@link android.view.Gravity}
1867 *
1868 * @attr ref android.R.styleable#LinearLayout_gravity
1869 */
1870 @android.view.RemotableViewMethod
1871 public void setGravity(int gravity) {
1872 if (mGravity != gravity) {
1873 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
1874 gravity |= Gravity.START;
1875 }
1876
1877 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
1878 gravity |= Gravity.TOP;
1879 }
1880
1881 mGravity = gravity;
1882 requestLayout();
1883 }
1884 }
1885
1886 /**
1887 * Returns the current gravity. See {@link android.view.Gravity}
1888 *
1889 * @return the current gravity.
1890 * @see #setGravity
1891 */
1892 @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
1893 public int getGravity() {
1894 return mGravity;
1895 }
1896
1897 @android.view.RemotableViewMethod
1898 public void setHorizontalGravity(int horizontalGravity) {
1899 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1900 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
1901 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
1902 requestLayout();
1903 }
1904 }
1905
1906 @android.view.RemotableViewMethod
1907 public void setVerticalGravity(int verticalGravity) {
1908 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1909 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1910 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1911 requestLayout();
1912 }
1913 }
1914
1915 @Override
1916 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1917 return new LinearLayout.LayoutParams(getContext(), attrs);
1918 }
1919
1920 /**
1921 * Returns a set of layout parameters with a width of
1922 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
1923 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1924 * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1925 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1926 * and the height to {@link LayoutParams#WRAP_CONTENT}.
1927 */
1928 @Override
1929 protected LayoutParams generateDefaultLayoutParams() {
1930 if (mOrientation == HORIZONTAL) {
1931 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1932 } else if (mOrientation == VERTICAL) {
1933 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1934 }
1935 return null;
1936 }
1937
1938 @Override
1939 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
1940 if (sPreserveMarginParamsInLayoutParamConversion) {
1941 if (lp instanceof LayoutParams) {
1942 return new LayoutParams((LayoutParams) lp);
1943 } else if (lp instanceof MarginLayoutParams) {
1944 return new LayoutParams((MarginLayoutParams) lp);
1945 }
1946 }
1947 return new LayoutParams(lp);
1948 }
1949
1950
1951 // Override to allow type-checking of LayoutParams.
1952 @Override
1953 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1954 return p instanceof LinearLayout.LayoutParams;
1955 }
1956
1957 @Override
1958 public CharSequence getAccessibilityClassName() {
1959 return LinearLayout.class.getName();
1960 }
1961
1962 /** @hide */
1963 @Override
1964 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1965 super.encodeProperties(encoder);
1966 encoder.addProperty("layout:baselineAligned", mBaselineAligned);
1967 encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex);
1968 encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop);
1969 encoder.addProperty("measurement:orientation", mOrientation);
1970 encoder.addProperty("measurement:gravity", mGravity);
1971 encoder.addProperty("measurement:totalLength", mTotalLength);
1972 encoder.addProperty("layout:totalLength", mTotalLength);
1973 encoder.addProperty("layout:useLargestChild", mUseLargestChild);
1974 }
1975
1976 /**
1977 * Per-child layout information associated with ViewLinearLayout.
1978 *
1979 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
1980 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
1981 */
1982 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1983 /**
1984 * Indicates how much of the extra space in the LinearLayout will be
1985 * allocated to the view associated with these LayoutParams. Specify
1986 * 0 if the view should not be stretched. Otherwise the extra pixels
1987 * will be pro-rated among all views whose weight is greater than 0.
1988 */
1989 @ViewDebug.ExportedProperty(category = "layout")
1990 @InspectableProperty(name = "layout_weight")
1991 public float weight;
1992
1993 /**
1994 * Gravity for the view associated with these LayoutParams.
1995 *
1996 * @see android.view.Gravity
1997 */
1998 @ViewDebug.ExportedProperty(category = "layout", mapping = {
1999 @ViewDebug.IntToString(from = -1, to = "NONE"),
2000 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
2001 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
2002 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
2003 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
2004 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
2005 @ViewDebug.IntToString(from = Gravity.START, to = "START"),
2006 @ViewDebug.IntToString(from = Gravity.END, to = "END"),
2007 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
2008 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
2009 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
2010 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
2011 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
2012 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
2013 })
2014 @InspectableProperty(
2015 name = "layout_gravity",
2016 valueType = InspectableProperty.ValueType.GRAVITY)
2017 public int gravity = -1;
2018
2019 /**
2020 * {@inheritDoc}
2021 */
2022 public LayoutParams(Context c, AttributeSet attrs) {
2023 super(c, attrs);
2024 TypedArray a =
2025 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
2026
2027 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
2028 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
2029
2030 a.recycle();
2031 }
2032
2033 /**
2034 * {@inheritDoc}
2035 */
2036 public LayoutParams(int width, int height) {
2037 super(width, height);
2038 weight = 0;
2039 }
2040
2041 /**
2042 * Creates a new set of layout parameters with the specified width, height
2043 * and weight.
2044 *
2045 * @param width the width, either {@link #MATCH_PARENT},
2046 * {@link #WRAP_CONTENT} or a fixed size in pixels
2047 * @param height the height, either {@link #MATCH_PARENT},
2048 * {@link #WRAP_CONTENT} or a fixed size in pixels
2049 * @param weight the weight
2050 */
2051 public LayoutParams(int width, int height, float weight) {
2052 super(width, height);
2053 this.weight = weight;
2054 }
2055
2056 /**
2057 * {@inheritDoc}
2058 */
2059 public LayoutParams(ViewGroup.LayoutParams p) {
2060 super(p);
2061 }
2062
2063 /**
2064 * {@inheritDoc}
2065 */
2066 public LayoutParams(ViewGroup.MarginLayoutParams source) {
2067 super(source);
2068 }
2069
2070 /**
2071 * Copy constructor. Clones the width, height, margin values, weight,
2072 * and gravity of the source.
2073 *
2074 * @param source The layout params to copy from.
2075 */
2076 public LayoutParams(LayoutParams source) {
2077 super(source);
2078
2079 this.weight = source.weight;
2080 this.gravity = source.gravity;
2081 }
2082
2083 @Override
2084 public String debug(String output) {
2085 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
2086 ", height=" + sizeToString(height) + " weight=" + weight + "}";
2087 }
2088
2089 /** @hide */
2090 @Override
2091 @UnsupportedAppUsage
2092 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
2093 super.encodeProperties(encoder);
2094
2095 encoder.addProperty("layout:weight", weight);
2096 encoder.addProperty("layout:gravity", gravity);
2097 }
2098 }
2099}