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