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