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(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
216     private static final int INDEX_TOP = 1;
217     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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 (mShowDividers == SHOW_DIVIDER_NONE) {
736             // Short-circuit to save iteration over child views.
737             return false;
738         }
739         if (childIndex == getVirtualChildCount()) {
740             // Check whether the end divider should draw.
741             return (mShowDividers & SHOW_DIVIDER_END) != 0;
742         }
743         boolean allViewsAreGoneBefore = allViewsAreGoneBefore(childIndex);
744         if (allViewsAreGoneBefore) {
745             // This is the first view that's not gone, check if beginning divider is enabled.
746             return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
747         } else {
748             return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
749         }
750     }
751 
752     /**
753      * Determines whether or not there's a divider after a specified child index.
754      *
755      * @param childIndex Index of child to check for following divider
756      * @return true if there should be a divider after the child at childIndex
757      */
hasDividerAfterChildAt(int childIndex)758     private boolean hasDividerAfterChildAt(int childIndex) {
759         if (mShowDividers == SHOW_DIVIDER_NONE) {
760             // Short-circuit to save iteration over child views.
761             return false;
762         }
763         if (allViewsAreGoneAfter(childIndex)) {
764             // This is the last view that's not gone, check if end divider is enabled.
765             return (mShowDividers & SHOW_DIVIDER_END) != 0;
766         }
767         return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
768     }
769 
770     /**
771      * Checks whether all (virtual) child views before the given index are gone.
772      */
allViewsAreGoneBefore(int childIndex)773     private boolean allViewsAreGoneBefore(int childIndex) {
774         for (int i = childIndex - 1; i >= 0; i--) {
775             final View child = getVirtualChildAt(i);
776             if (child != null && child.getVisibility() != GONE) {
777                 return false;
778             }
779         }
780         return true;
781     }
782 
783     /**
784      * Checks whether all (virtual) child views after the given index are gone.
785      */
allViewsAreGoneAfter(int childIndex)786     private boolean allViewsAreGoneAfter(int childIndex) {
787         final int count = getVirtualChildCount();
788         for (int i = childIndex + 1; i < count; i++) {
789             final View child = getVirtualChildAt(i);
790             if (child != null && child.getVisibility() != GONE) {
791                 return false;
792             }
793         }
794         return true;
795     }
796 
797     /**
798      * Measures the children when the orientation of this LinearLayout is set
799      * to {@link #VERTICAL}.
800      *
801      * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
802      * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
803      *
804      * @see #getOrientation()
805      * @see #setOrientation(int)
806      * @see #onMeasure(int, int)
807      */
measureVertical(int widthMeasureSpec, int heightMeasureSpec)808     void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
809         mTotalLength = 0;
810         int maxWidth = 0;
811         int childState = 0;
812         int alternativeMaxWidth = 0;
813         int weightedMaxWidth = 0;
814         boolean allFillParent = true;
815         float totalWeight = 0;
816 
817         final int count = getVirtualChildCount();
818 
819         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
820         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
821 
822         boolean matchWidth = false;
823         boolean skippedMeasure = false;
824 
825         final int baselineChildIndex = mBaselineAlignedChildIndex;
826         final boolean useLargestChild = mUseLargestChild;
827 
828         int largestChildHeight = Integer.MIN_VALUE;
829         int consumedExcessSpace = 0;
830 
831         int nonSkippedChildCount = 0;
832 
833         // See how tall everyone is. Also remember max width.
834         for (int i = 0; i < count; ++i) {
835             final View child = getVirtualChildAt(i);
836             if (child == null) {
837                 mTotalLength += measureNullChild(i);
838                 continue;
839             }
840 
841             if (child.getVisibility() == View.GONE) {
842                i += getChildrenSkipCount(child, i);
843                continue;
844             }
845 
846             nonSkippedChildCount++;
847             if (hasDividerBeforeChildAt(i)) {
848                 mTotalLength += mDividerHeight;
849             }
850 
851             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
852 
853             totalWeight += lp.weight;
854 
855             final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
856             if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
857                 // Optimization: don't bother measuring children who are only
858                 // laid out using excess space. These views will get measured
859                 // later if we have space to distribute.
860                 final int totalLength = mTotalLength;
861                 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
862                 skippedMeasure = true;
863             } else {
864                 if (useExcessSpace) {
865                     // The heightMode is either UNSPECIFIED or AT_MOST, and
866                     // this child is only laid out using excess space. Measure
867                     // using WRAP_CONTENT so that we can find out the view's
868                     // optimal height. We'll restore the original height of 0
869                     // after measurement.
870                     lp.height = LayoutParams.WRAP_CONTENT;
871                 }
872 
873                 // Determine how big this child would like to be. If this or
874                 // previous children have given a weight, then we allow it to
875                 // use all available space (and we will shrink things later
876                 // if needed).
877                 final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
878                 measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
879                         heightMeasureSpec, usedHeight);
880 
881                 final int childHeight = child.getMeasuredHeight();
882                 if (useExcessSpace) {
883                     // Restore the original height and record how much space
884                     // we've allocated to excess-only children so that we can
885                     // match the behavior of EXACTLY measurement.
886                     lp.height = 0;
887                     consumedExcessSpace += childHeight;
888                 }
889 
890                 final int totalLength = mTotalLength;
891                 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
892                        lp.bottomMargin + getNextLocationOffset(child));
893 
894                 if (useLargestChild) {
895                     largestChildHeight = Math.max(childHeight, largestChildHeight);
896                 }
897             }
898 
899             /**
900              * If applicable, compute the additional offset to the child's baseline
901              * we'll need later when asked {@link #getBaseline}.
902              */
903             if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
904                mBaselineChildTop = mTotalLength;
905             }
906 
907             // if we are trying to use a child index for our baseline, the above
908             // book keeping only works if there are no children above it with
909             // weight.  fail fast to aid the developer.
910             if (i < baselineChildIndex && lp.weight > 0) {
911                 throw new RuntimeException("A child of LinearLayout with index "
912                         + "less than mBaselineAlignedChildIndex has weight > 0, which "
913                         + "won't work.  Either remove the weight, or don't set "
914                         + "mBaselineAlignedChildIndex.");
915             }
916 
917             boolean matchWidthLocally = false;
918             if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
919                 // The width of the linear layout will scale, and at least one
920                 // child said it wanted to match our width. Set a flag
921                 // indicating that we need to remeasure at least that view when
922                 // we know our width.
923                 matchWidth = true;
924                 matchWidthLocally = true;
925             }
926 
927             final int margin = lp.leftMargin + lp.rightMargin;
928             final int measuredWidth = child.getMeasuredWidth() + margin;
929             maxWidth = Math.max(maxWidth, measuredWidth);
930             childState = combineMeasuredStates(childState, child.getMeasuredState());
931 
932             allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
933             if (lp.weight > 0) {
934                 /*
935                  * Widths of weighted Views are bogus if we end up
936                  * remeasuring, so keep them separate.
937                  */
938                 weightedMaxWidth = Math.max(weightedMaxWidth,
939                         matchWidthLocally ? margin : measuredWidth);
940             } else {
941                 alternativeMaxWidth = Math.max(alternativeMaxWidth,
942                         matchWidthLocally ? margin : measuredWidth);
943             }
944 
945             i += getChildrenSkipCount(child, i);
946         }
947 
948         if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
949             mTotalLength += mDividerHeight;
950         }
951 
952         if (useLargestChild &&
953                 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
954             mTotalLength = 0;
955 
956             for (int i = 0; i < count; ++i) {
957                 final View child = getVirtualChildAt(i);
958                 if (child == null) {
959                     mTotalLength += measureNullChild(i);
960                     continue;
961                 }
962 
963                 if (child.getVisibility() == GONE) {
964                     i += getChildrenSkipCount(child, i);
965                     continue;
966                 }
967 
968                 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
969                         child.getLayoutParams();
970                 // Account for negative margins
971                 final int totalLength = mTotalLength;
972                 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
973                         lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
974             }
975         }
976 
977         // Add in our padding
978         mTotalLength += mPaddingTop + mPaddingBottom;
979 
980         int heightSize = mTotalLength;
981 
982         // Check against our minimum height
983         heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
984 
985         // Reconcile our calculated size with the heightMeasureSpec
986         int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
987         heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
988         // Either expand children with weight to take up available space or
989         // shrink them if they extend beyond our current bounds. If we skipped
990         // measurement on any children, we need to measure them now.
991         int remainingExcess = heightSize - mTotalLength
992                 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
993         if (skippedMeasure
994                 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
995             float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
996 
997             mTotalLength = 0;
998 
999             for (int i = 0; i < count; ++i) {
1000                 final View child = getVirtualChildAt(i);
1001                 if (child == null || child.getVisibility() == View.GONE) {
1002                     continue;
1003                 }
1004 
1005                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1006                 final float childWeight = lp.weight;
1007                 if (childWeight > 0) {
1008                     final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
1009                     remainingExcess -= share;
1010                     remainingWeightSum -= childWeight;
1011 
1012                     final int childHeight;
1013                     if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) {
1014                         childHeight = largestChildHeight;
1015                     } else if (lp.height == 0 && (!mAllowInconsistentMeasurement
1016                             || heightMode == MeasureSpec.EXACTLY)) {
1017                         // This child needs to be laid out from scratch using
1018                         // only its share of excess space.
1019                         childHeight = share;
1020                     } else {
1021                         // This child had some intrinsic height to which we
1022                         // need to add its share of excess space.
1023                         childHeight = child.getMeasuredHeight() + share;
1024                     }
1025 
1026                     final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
1027                             Math.max(0, childHeight), MeasureSpec.EXACTLY);
1028                     final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
1029                             mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,
1030                             lp.width);
1031                     child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
1032 
1033                     // Child may now not fit in vertical dimension.
1034                     childState = combineMeasuredStates(childState, child.getMeasuredState()
1035                             & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
1036                 }
1037 
1038                 final int margin =  lp.leftMargin + lp.rightMargin;
1039                 final int measuredWidth = child.getMeasuredWidth() + margin;
1040                 maxWidth = Math.max(maxWidth, measuredWidth);
1041 
1042                 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
1043                         lp.width == LayoutParams.MATCH_PARENT;
1044 
1045                 alternativeMaxWidth = Math.max(alternativeMaxWidth,
1046                         matchWidthLocally ? margin : measuredWidth);
1047 
1048                 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
1049 
1050                 final int totalLength = mTotalLength;
1051                 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
1052                         lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
1053             }
1054 
1055             // Add in our padding
1056             mTotalLength += mPaddingTop + mPaddingBottom;
1057             // TODO: Should we recompute the heightSpec based on the new total length?
1058         } else {
1059             alternativeMaxWidth = Math.max(alternativeMaxWidth,
1060                                            weightedMaxWidth);
1061 
1062 
1063             // We have no limit, so make all weighted views as tall as the largest child.
1064             // Children will have already been measured once.
1065             if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
1066                 for (int i = 0; i < count; i++) {
1067                     final View child = getVirtualChildAt(i);
1068                     if (child == null || child.getVisibility() == View.GONE) {
1069                         continue;
1070                     }
1071 
1072                     final LinearLayout.LayoutParams lp =
1073                             (LinearLayout.LayoutParams) child.getLayoutParams();
1074 
1075                     float childExtra = lp.weight;
1076                     if (childExtra > 0) {
1077                         child.measure(
1078                                 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
1079                                         MeasureSpec.EXACTLY),
1080                                 MeasureSpec.makeMeasureSpec(largestChildHeight,
1081                                         MeasureSpec.EXACTLY));
1082                     }
1083                 }
1084             }
1085         }
1086 
1087         if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
1088             maxWidth = alternativeMaxWidth;
1089         }
1090 
1091         maxWidth += mPaddingLeft + mPaddingRight;
1092 
1093         // Check against our minimum width
1094         maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
1095 
1096         setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
1097                 heightSizeAndState);
1098 
1099         if (matchWidth) {
1100             forceUniformWidth(count, heightMeasureSpec);
1101         }
1102     }
1103 
forceUniformWidth(int count, int heightMeasureSpec)1104     private void forceUniformWidth(int count, int heightMeasureSpec) {
1105         // Pretend that the linear layout has an exact size.
1106         int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
1107                 MeasureSpec.EXACTLY);
1108         for (int i = 0; i< count; ++i) {
1109            final View child = getVirtualChildAt(i);
1110            if (child != null && child.getVisibility() != GONE) {
1111                LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
1112 
1113                if (lp.width == LayoutParams.MATCH_PARENT) {
1114                    // Temporarily force children to reuse their old measured height
1115                    // FIXME: this may not be right for something like wrapping text?
1116                    int oldHeight = lp.height;
1117                    lp.height = child.getMeasuredHeight();
1118 
1119                    // Remeasue with new dimensions
1120                    measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
1121                    lp.height = oldHeight;
1122                }
1123            }
1124         }
1125     }
1126 
1127     /**
1128      * Measures the children when the orientation of this LinearLayout is set
1129      * to {@link #HORIZONTAL}.
1130      *
1131      * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
1132      * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
1133      *
1134      * @see #getOrientation()
1135      * @see #setOrientation(int)
1136      * @see #onMeasure(int, int)
1137      */
measureHorizontal(int widthMeasureSpec, int heightMeasureSpec)1138     void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
1139         mTotalLength = 0;
1140         int maxHeight = 0;
1141         int childState = 0;
1142         int alternativeMaxHeight = 0;
1143         int weightedMaxHeight = 0;
1144         boolean allFillParent = true;
1145         float totalWeight = 0;
1146 
1147         final int count = getVirtualChildCount();
1148 
1149         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
1150         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
1151 
1152         boolean matchHeight = false;
1153         boolean skippedMeasure = false;
1154 
1155         if (mMaxAscent == null || mMaxDescent == null) {
1156             mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
1157             mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
1158         }
1159 
1160         final int[] maxAscent = mMaxAscent;
1161         final int[] maxDescent = mMaxDescent;
1162 
1163         maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1164         maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1165 
1166         final boolean baselineAligned = mBaselineAligned;
1167         final boolean useLargestChild = mUseLargestChild;
1168 
1169         final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
1170 
1171         int largestChildWidth = Integer.MIN_VALUE;
1172         int usedExcessSpace = 0;
1173 
1174         int nonSkippedChildCount = 0;
1175 
1176         // See how wide everyone is. Also remember max height.
1177         for (int i = 0; i < count; ++i) {
1178             final View child = getVirtualChildAt(i);
1179             if (child == null) {
1180                 mTotalLength += measureNullChild(i);
1181                 continue;
1182             }
1183 
1184             if (child.getVisibility() == GONE) {
1185                 i += getChildrenSkipCount(child, i);
1186                 continue;
1187             }
1188 
1189             nonSkippedChildCount++;
1190             if (hasDividerBeforeChildAt(i)) {
1191                 mTotalLength += mDividerWidth;
1192             }
1193 
1194             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1195 
1196             totalWeight += lp.weight;
1197 
1198             final boolean useExcessSpace = lp.width == 0 && lp.weight > 0;
1199             if (widthMode == MeasureSpec.EXACTLY && useExcessSpace) {
1200                 // Optimization: don't bother measuring children who are only
1201                 // laid out using excess space. These views will get measured
1202                 // later if we have space to distribute.
1203                 if (isExactly) {
1204                     mTotalLength += lp.leftMargin + lp.rightMargin;
1205                 } else {
1206                     final int totalLength = mTotalLength;
1207                     mTotalLength = Math.max(totalLength, totalLength +
1208                             lp.leftMargin + lp.rightMargin);
1209                 }
1210 
1211                 // Baseline alignment requires to measure widgets to obtain the
1212                 // baseline offset (in particular for TextViews). The following
1213                 // defeats the optimization mentioned above. Allow the child to
1214                 // use as much space as it wants because we can shrink things
1215                 // later (and re-measure).
1216                 if (baselineAligned) {
1217                     final int freeWidthSpec = MeasureSpec.makeSafeMeasureSpec(
1218                             MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED);
1219                     final int freeHeightSpec = MeasureSpec.makeSafeMeasureSpec(
1220                             MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);
1221                     child.measure(freeWidthSpec, freeHeightSpec);
1222                 } else {
1223                     skippedMeasure = true;
1224                 }
1225             } else {
1226                 if (useExcessSpace) {
1227                     // The widthMode is either UNSPECIFIED or AT_MOST, and
1228                     // this child is only laid out using excess space. Measure
1229                     // using WRAP_CONTENT so that we can find out the view's
1230                     // optimal width. We'll restore the original width of 0
1231                     // after measurement.
1232                     lp.width = LayoutParams.WRAP_CONTENT;
1233                 }
1234 
1235                 // Determine how big this child would like to be. If this or
1236                 // previous children have given a weight, then we allow it to
1237                 // use all available space (and we will shrink things later
1238                 // if needed).
1239                 final int usedWidth = totalWeight == 0 ? mTotalLength : 0;
1240                 measureChildBeforeLayout(child, i, widthMeasureSpec, usedWidth,
1241                         heightMeasureSpec, 0);
1242 
1243                 final int childWidth = child.getMeasuredWidth();
1244                 if (useExcessSpace) {
1245                     // Restore the original width and record how much space
1246                     // we've allocated to excess-only children so that we can
1247                     // match the behavior of EXACTLY measurement.
1248                     lp.width = 0;
1249                     usedExcessSpace += childWidth;
1250                 }
1251 
1252                 if (isExactly) {
1253                     mTotalLength += childWidth + lp.leftMargin + lp.rightMargin
1254                             + getNextLocationOffset(child);
1255                 } else {
1256                     final int totalLength = mTotalLength;
1257                     mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin
1258                             + lp.rightMargin + getNextLocationOffset(child));
1259                 }
1260 
1261                 if (useLargestChild) {
1262                     largestChildWidth = Math.max(childWidth, largestChildWidth);
1263                 }
1264             }
1265 
1266             boolean matchHeightLocally = false;
1267             if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
1268                 // The height of the linear layout will scale, and at least one
1269                 // child said it wanted to match our height. Set a flag indicating that
1270                 // we need to remeasure at least that view when we know our height.
1271                 matchHeight = true;
1272                 matchHeightLocally = true;
1273             }
1274 
1275             final int margin = lp.topMargin + lp.bottomMargin;
1276             final int childHeight = child.getMeasuredHeight() + margin;
1277             childState = combineMeasuredStates(childState, child.getMeasuredState());
1278 
1279             if (baselineAligned) {
1280                 final int childBaseline = child.getBaseline();
1281                 if (childBaseline != -1) {
1282                     // Translates the child's vertical gravity into an index
1283                     // in the range 0..VERTICAL_GRAVITY_COUNT
1284                     final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1285                             & Gravity.VERTICAL_GRAVITY_MASK;
1286                     final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1287                             & ~Gravity.AXIS_SPECIFIED) >> 1;
1288 
1289                     maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1290                     maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
1291                 }
1292             }
1293 
1294             maxHeight = Math.max(maxHeight, childHeight);
1295 
1296             allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
1297             if (lp.weight > 0) {
1298                 /*
1299                  * Heights of weighted Views are bogus if we end up
1300                  * remeasuring, so keep them separate.
1301                  */
1302                 weightedMaxHeight = Math.max(weightedMaxHeight,
1303                         matchHeightLocally ? margin : childHeight);
1304             } else {
1305                 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1306                         matchHeightLocally ? margin : childHeight);
1307             }
1308 
1309             i += getChildrenSkipCount(child, i);
1310         }
1311 
1312         if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
1313             mTotalLength += mDividerWidth;
1314         }
1315 
1316         // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1317         // the most common case
1318         if (maxAscent[INDEX_TOP] != -1 ||
1319                 maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1320                 maxAscent[INDEX_BOTTOM] != -1 ||
1321                 maxAscent[INDEX_FILL] != -1) {
1322             final int ascent = Math.max(maxAscent[INDEX_FILL],
1323                     Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1324                     Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1325             final int descent = Math.max(maxDescent[INDEX_FILL],
1326                     Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1327                     Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1328             maxHeight = Math.max(maxHeight, ascent + descent);
1329         }
1330 
1331         if (useLargestChild &&
1332                 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
1333             mTotalLength = 0;
1334             nonSkippedChildCount = 0;
1335 
1336             for (int i = 0; i < count; ++i) {
1337                 final View child = getVirtualChildAt(i);
1338                 if (child == null) {
1339                     mTotalLength += measureNullChild(i);
1340                     continue;
1341                 }
1342 
1343                 if (child.getVisibility() == GONE) {
1344                     i += getChildrenSkipCount(child, i);
1345                     continue;
1346                 }
1347 
1348                 nonSkippedChildCount++;
1349                 if (hasDividerBeforeChildAt(i)) {
1350                     mTotalLength += mDividerWidth;
1351                 }
1352 
1353                 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
1354                         child.getLayoutParams();
1355                 if (isExactly) {
1356                     mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
1357                             getNextLocationOffset(child);
1358                 } else {
1359                     final int totalLength = mTotalLength;
1360                     mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
1361                             lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1362                 }
1363             }
1364 
1365             if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
1366                 mTotalLength += mDividerWidth;
1367             }
1368         }
1369 
1370         // Add in our padding
1371         mTotalLength += mPaddingLeft + mPaddingRight;
1372 
1373         int widthSize = mTotalLength;
1374 
1375         // Check against our minimum width
1376         widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
1377 
1378         // Reconcile our calculated size with the widthMeasureSpec
1379         int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
1380         widthSize = widthSizeAndState & MEASURED_SIZE_MASK;
1381 
1382         // Either expand children with weight to take up available space or
1383         // shrink them if they extend beyond our current bounds. If we skipped
1384         // measurement on any children, we need to measure them now.
1385         int remainingExcess = widthSize - mTotalLength
1386                 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
1387         if (skippedMeasure
1388                 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
1389             float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
1390 
1391             maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
1392             maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
1393             maxHeight = -1;
1394 
1395             mTotalLength = 0;
1396             nonSkippedChildCount = 0;
1397 
1398             for (int i = 0; i < count; ++i) {
1399                 final View child = getVirtualChildAt(i);
1400                 if (child == null || child.getVisibility() == View.GONE) {
1401                     continue;
1402                 }
1403 
1404                 nonSkippedChildCount++;
1405                 if (hasDividerBeforeChildAt(i)) {
1406                     mTotalLength += mDividerWidth;
1407                 }
1408 
1409                 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1410                 final float childWeight = lp.weight;
1411                 if (childWeight > 0) {
1412                     final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
1413                     remainingExcess -= share;
1414                     remainingWeightSum -= childWeight;
1415 
1416                     final int childWidth;
1417                     if (mUseLargestChild && widthMode != MeasureSpec.EXACTLY) {
1418                         childWidth = largestChildWidth;
1419                     } else if (lp.width == 0 && (!mAllowInconsistentMeasurement
1420                             || widthMode == MeasureSpec.EXACTLY)) {
1421                         // This child needs to be laid out from scratch using
1422                         // only its share of excess space.
1423                         childWidth = share;
1424                     } else {
1425                         // This child had some intrinsic width to which we
1426                         // need to add its share of excess space.
1427                         childWidth = child.getMeasuredWidth() + share;
1428                     }
1429 
1430                     final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
1431                             Math.max(0, childWidth), MeasureSpec.EXACTLY);
1432                     final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
1433                             mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
1434                             lp.height);
1435                     child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
1436 
1437                     // Child may now not fit in horizontal dimension.
1438                     childState = combineMeasuredStates(childState,
1439                             child.getMeasuredState() & MEASURED_STATE_MASK);
1440                 }
1441 
1442                 if (isExactly) {
1443                     mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
1444                             getNextLocationOffset(child);
1445                 } else {
1446                     final int totalLength = mTotalLength;
1447                     mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
1448                             lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
1449                 }
1450 
1451                 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
1452                         lp.height == LayoutParams.MATCH_PARENT;
1453 
1454                 final int margin = lp.topMargin + lp .bottomMargin;
1455                 int childHeight = child.getMeasuredHeight() + margin;
1456                 maxHeight = Math.max(maxHeight, childHeight);
1457                 alternativeMaxHeight = Math.max(alternativeMaxHeight,
1458                         matchHeightLocally ? margin : childHeight);
1459 
1460                 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
1461 
1462                 if (baselineAligned) {
1463                     final int childBaseline = child.getBaseline();
1464                     if (childBaseline != -1) {
1465                         // Translates the child's vertical gravity into an index in the range 0..2
1466                         final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
1467                                 & Gravity.VERTICAL_GRAVITY_MASK;
1468                         final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
1469                                 & ~Gravity.AXIS_SPECIFIED) >> 1;
1470 
1471                         maxAscent[index] = Math.max(maxAscent[index], childBaseline);
1472                         maxDescent[index] = Math.max(maxDescent[index],
1473                                 childHeight - childBaseline);
1474                     }
1475                 }
1476             }
1477 
1478             if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
1479                 mTotalLength += mDividerWidth;
1480             }
1481 
1482             // Add in our padding
1483             mTotalLength += mPaddingLeft + mPaddingRight;
1484             // TODO: Should we update widthSize with the new total length?
1485 
1486             // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
1487             // the most common case
1488             if (maxAscent[INDEX_TOP] != -1 ||
1489                     maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
1490                     maxAscent[INDEX_BOTTOM] != -1 ||
1491                     maxAscent[INDEX_FILL] != -1) {
1492                 final int ascent = Math.max(maxAscent[INDEX_FILL],
1493                         Math.max(maxAscent[INDEX_CENTER_VERTICAL],
1494                         Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
1495                 final int descent = Math.max(maxDescent[INDEX_FILL],
1496                         Math.max(maxDescent[INDEX_CENTER_VERTICAL],
1497                         Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
1498                 maxHeight = Math.max(maxHeight, ascent + descent);
1499             }
1500         } else {
1501             alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
1502 
1503             // We have no limit, so make all weighted views as wide as the largest child.
1504             // Children will have already been measured once.
1505             if (useLargestChild && widthMode != MeasureSpec.EXACTLY) {
1506                 for (int i = 0; i < count; i++) {
1507                     final View child = getVirtualChildAt(i);
1508                     if (child == null || child.getVisibility() == View.GONE) {
1509                         continue;
1510                     }
1511 
1512                     final LinearLayout.LayoutParams lp =
1513                             (LinearLayout.LayoutParams) child.getLayoutParams();
1514 
1515                     float childExtra = lp.weight;
1516                     if (childExtra > 0) {
1517                         child.measure(
1518                                 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
1519                                 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
1520                                         MeasureSpec.EXACTLY));
1521                     }
1522                 }
1523             }
1524         }
1525 
1526         if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
1527             maxHeight = alternativeMaxHeight;
1528         }
1529 
1530         maxHeight += mPaddingTop + mPaddingBottom;
1531 
1532         // Check against our minimum height
1533         maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
1534 
1535         setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK),
1536                 resolveSizeAndState(maxHeight, heightMeasureSpec,
1537                         (childState<<MEASURED_HEIGHT_STATE_SHIFT)));
1538 
1539         if (matchHeight) {
1540             forceUniformHeight(count, widthMeasureSpec);
1541         }
1542     }
1543 
forceUniformHeight(int count, int widthMeasureSpec)1544     private void forceUniformHeight(int count, int widthMeasureSpec) {
1545         // Pretend that the linear layout has an exact size. This is the measured height of
1546         // ourselves. The measured height should be the max height of the children, changed
1547         // to accommodate the heightMeasureSpec from the parent
1548         int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
1549                 MeasureSpec.EXACTLY);
1550         for (int i = 0; i < count; ++i) {
1551            final View child = getVirtualChildAt(i);
1552            if (child != null && child.getVisibility() != GONE) {
1553                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
1554 
1555                if (lp.height == LayoutParams.MATCH_PARENT) {
1556                    // Temporarily force children to reuse their old measured width
1557                    // FIXME: this may not be right for something like wrapping text?
1558                    int oldWidth = lp.width;
1559                    lp.width = child.getMeasuredWidth();
1560 
1561                    // Remeasure with new dimensions
1562                    measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
1563                    lp.width = oldWidth;
1564                }
1565            }
1566         }
1567     }
1568 
1569     /**
1570      * <p>Returns the number of children to skip after measuring/laying out
1571      * the specified child.</p>
1572      *
1573      * @param child the child after which we want to skip children
1574      * @param index the index of the child after which we want to skip children
1575      * @return the number of children to skip, 0 by default
1576      */
getChildrenSkipCount(View child, int index)1577     int getChildrenSkipCount(View child, int index) {
1578         return 0;
1579     }
1580 
1581     /**
1582      * <p>Returns the size (width or height) that should be occupied by a null
1583      * child.</p>
1584      *
1585      * @param childIndex the index of the null child
1586      * @return the width or height of the child depending on the orientation
1587      */
measureNullChild(int childIndex)1588     int measureNullChild(int childIndex) {
1589         return 0;
1590     }
1591 
1592     /**
1593      * <p>Measure the child according to the parent's measure specs. This
1594      * method should be overridden by subclasses to force the sizing of
1595      * children. This method is called by {@link #measureVertical(int, int)} and
1596      * {@link #measureHorizontal(int, int)}.</p>
1597      *
1598      * @param child the child to measure
1599      * @param childIndex the index of the child in this view
1600      * @param widthMeasureSpec horizontal space requirements as imposed by the parent
1601      * @param totalWidth extra space that has been used up by the parent horizontally
1602      * @param heightMeasureSpec vertical space requirements as imposed by the parent
1603      * @param totalHeight extra space that has been used up by the parent vertically
1604      */
measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight)1605     void measureChildBeforeLayout(View child, int childIndex,
1606             int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
1607             int totalHeight) {
1608         measureChildWithMargins(child, widthMeasureSpec, totalWidth,
1609                 heightMeasureSpec, totalHeight);
1610     }
1611 
1612     /**
1613      * <p>Return the location offset of the specified child. This can be used
1614      * by subclasses to change the location of a given widget.</p>
1615      *
1616      * @param child the child for which to obtain the location offset
1617      * @return the location offset in pixels
1618      */
getLocationOffset(View child)1619     int getLocationOffset(View child) {
1620         return 0;
1621     }
1622 
1623     /**
1624      * <p>Return the size offset of the next sibling of the specified child.
1625      * This can be used by subclasses to change the location of the widget
1626      * following <code>child</code>.</p>
1627      *
1628      * @param child the child whose next sibling will be moved
1629      * @return the location offset of the next child in pixels
1630      */
getNextLocationOffset(View child)1631     int getNextLocationOffset(View child) {
1632         return 0;
1633     }
1634 
1635     @Override
onLayout(boolean changed, int l, int t, int r, int b)1636     protected void onLayout(boolean changed, int l, int t, int r, int b) {
1637         if (mOrientation == VERTICAL) {
1638             layoutVertical(l, t, r, b);
1639         } else {
1640             layoutHorizontal(l, t, r, b);
1641         }
1642     }
1643 
1644     /**
1645      * Position the children during a layout pass if the orientation of this
1646      * LinearLayout is set to {@link #VERTICAL}.
1647      *
1648      * @see #getOrientation()
1649      * @see #setOrientation(int)
1650      * @see #onLayout(boolean, int, int, int, int)
1651      * @param left
1652      * @param top
1653      * @param right
1654      * @param bottom
1655      */
layoutVertical(int left, int top, int right, int bottom)1656     void layoutVertical(int left, int top, int right, int bottom) {
1657         final int paddingLeft = mPaddingLeft;
1658 
1659         int childTop;
1660         int childLeft;
1661 
1662         // Where right end of child should go
1663         final int width = right - left;
1664         int childRight = width - mPaddingRight;
1665 
1666         // Space available for child
1667         int childSpace = width - paddingLeft - mPaddingRight;
1668 
1669         final int count = getVirtualChildCount();
1670 
1671         final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1672         final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1673 
1674         switch (majorGravity) {
1675            case Gravity.BOTTOM:
1676                // mTotalLength contains the padding already
1677                childTop = mPaddingTop + bottom - top - mTotalLength;
1678                break;
1679 
1680                // mTotalLength contains the padding already
1681            case Gravity.CENTER_VERTICAL:
1682                childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
1683                break;
1684 
1685            case Gravity.TOP:
1686            default:
1687                childTop = mPaddingTop;
1688                break;
1689         }
1690 
1691         for (int i = 0; i < count; i++) {
1692             final View child = getVirtualChildAt(i);
1693             if (child == null) {
1694                 childTop += measureNullChild(i);
1695             } else if (child.getVisibility() != GONE) {
1696                 final int childWidth = child.getMeasuredWidth();
1697                 final int childHeight = child.getMeasuredHeight();
1698 
1699                 final LinearLayout.LayoutParams lp =
1700                         (LinearLayout.LayoutParams) child.getLayoutParams();
1701 
1702                 int gravity = lp.gravity;
1703                 if (gravity < 0) {
1704                     gravity = minorGravity;
1705                 }
1706                 final int layoutDirection = getLayoutDirection();
1707                 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
1708                 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1709                     case Gravity.CENTER_HORIZONTAL:
1710                         childLeft = paddingLeft + ((childSpace - childWidth) / 2)
1711                                 + lp.leftMargin - lp.rightMargin;
1712                         break;
1713 
1714                     case Gravity.RIGHT:
1715                         childLeft = childRight - childWidth - lp.rightMargin;
1716                         break;
1717 
1718                     case Gravity.LEFT:
1719                     default:
1720                         childLeft = paddingLeft + lp.leftMargin;
1721                         break;
1722                 }
1723 
1724                 if (hasDividerBeforeChildAt(i)) {
1725                     childTop += mDividerHeight;
1726                 }
1727 
1728                 childTop += lp.topMargin;
1729                 setChildFrame(child, childLeft, childTop + getLocationOffset(child),
1730                         childWidth, childHeight);
1731                 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
1732 
1733                 i += getChildrenSkipCount(child, i);
1734             }
1735         }
1736     }
1737 
1738     @Override
onRtlPropertiesChanged(@esolvedLayoutDir int layoutDirection)1739     public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
1740         super.onRtlPropertiesChanged(layoutDirection);
1741         if (layoutDirection != mLayoutDirection) {
1742             mLayoutDirection = layoutDirection;
1743             if (mOrientation == HORIZONTAL) {
1744                 requestLayout();
1745             }
1746         }
1747     }
1748 
1749     /**
1750      * Position the children during a layout pass if the orientation of this
1751      * LinearLayout is set to {@link #HORIZONTAL}.
1752      *
1753      * @see #getOrientation()
1754      * @see #setOrientation(int)
1755      * @see #onLayout(boolean, int, int, int, int)
1756      * @param left
1757      * @param top
1758      * @param right
1759      * @param bottom
1760      */
layoutHorizontal(int left, int top, int right, int bottom)1761     void layoutHorizontal(int left, int top, int right, int bottom) {
1762         final boolean isLayoutRtl = isLayoutRtl();
1763         final int paddingTop = mPaddingTop;
1764 
1765         int childTop;
1766         int childLeft;
1767 
1768         // Where bottom of child should go
1769         final int height = bottom - top;
1770         int childBottom = height - mPaddingBottom;
1771 
1772         // Space available for child
1773         int childSpace = height - paddingTop - mPaddingBottom;
1774 
1775         final int count = getVirtualChildCount();
1776 
1777         final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1778         final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1779 
1780         final boolean baselineAligned = mBaselineAligned;
1781 
1782         final int[] maxAscent = mMaxAscent;
1783         final int[] maxDescent = mMaxDescent;
1784 
1785         final int layoutDirection = getLayoutDirection();
1786         switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
1787             case Gravity.RIGHT:
1788                 // mTotalLength contains the padding already
1789                 childLeft = mPaddingLeft + right - left - mTotalLength;
1790                 break;
1791 
1792             case Gravity.CENTER_HORIZONTAL:
1793                 // mTotalLength contains the padding already
1794                 childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
1795                 break;
1796 
1797             case Gravity.LEFT:
1798             default:
1799                 childLeft = mPaddingLeft;
1800                 break;
1801         }
1802 
1803         int start = 0;
1804         int dir = 1;
1805         //In case of RTL, start drawing from the last child.
1806         if (isLayoutRtl) {
1807             start = count - 1;
1808             dir = -1;
1809         }
1810 
1811         for (int i = 0; i < count; i++) {
1812             final int childIndex = start + dir * i;
1813             final View child = getVirtualChildAt(childIndex);
1814             if (child == null) {
1815                 childLeft += measureNullChild(childIndex);
1816             } else if (child.getVisibility() != GONE) {
1817                 final int childWidth = child.getMeasuredWidth();
1818                 final int childHeight = child.getMeasuredHeight();
1819                 int childBaseline = -1;
1820 
1821                 final LinearLayout.LayoutParams lp =
1822                         (LinearLayout.LayoutParams) child.getLayoutParams();
1823 
1824                 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
1825                     childBaseline = child.getBaseline();
1826                 }
1827 
1828                 int gravity = lp.gravity;
1829                 if (gravity < 0) {
1830                     gravity = minorGravity;
1831                 }
1832 
1833                 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
1834                     case Gravity.TOP:
1835                         childTop = paddingTop + lp.topMargin;
1836                         if (childBaseline != -1) {
1837                             childTop += maxAscent[INDEX_TOP] - childBaseline;
1838                         }
1839                         break;
1840 
1841                     case Gravity.CENTER_VERTICAL:
1842                         // Removed support for baseline alignment when layout_gravity or
1843                         // gravity == center_vertical. See bug #1038483.
1844                         // Keep the code around if we need to re-enable this feature
1845                         // if (childBaseline != -1) {
1846                         //     // Align baselines vertically only if the child is smaller than us
1847                         //     if (childSpace - childHeight > 0) {
1848                         //         childTop = paddingTop + (childSpace / 2) - childBaseline;
1849                         //     } else {
1850                         //         childTop = paddingTop + (childSpace - childHeight) / 2;
1851                         //     }
1852                         // } else {
1853                         childTop = paddingTop + ((childSpace - childHeight) / 2)
1854                                 + lp.topMargin - lp.bottomMargin;
1855                         break;
1856 
1857                     case Gravity.BOTTOM:
1858                         childTop = childBottom - childHeight - lp.bottomMargin;
1859                         if (childBaseline != -1) {
1860                             int descent = child.getMeasuredHeight() - childBaseline;
1861                             childTop -= (maxDescent[INDEX_BOTTOM] - descent);
1862                         }
1863                         break;
1864                     default:
1865                         childTop = paddingTop;
1866                         break;
1867                 }
1868 
1869                 if (isLayoutRtl) {
1870                     // Because rtl rendering occurs in the reverse direction, we need to check
1871                     // after the child rather than before (since after=left in this context)
1872                     if (hasDividerAfterChildAt(childIndex)) {
1873                         childLeft += mDividerWidth;
1874                     }
1875                 } else if (hasDividerBeforeChildAt(childIndex)) {
1876                     childLeft += mDividerWidth;
1877                 }
1878 
1879                 childLeft += lp.leftMargin;
1880                 setChildFrame(child, childLeft + getLocationOffset(child), childTop,
1881                         childWidth, childHeight);
1882                 childLeft += childWidth + lp.rightMargin +
1883                         getNextLocationOffset(child);
1884 
1885                 i += getChildrenSkipCount(child, childIndex);
1886             }
1887         }
1888     }
1889 
setChildFrame(View child, int left, int top, int width, int height)1890     private void setChildFrame(View child, int left, int top, int width, int height) {
1891         child.layout(left, top, left + width, top + height);
1892     }
1893 
1894     /**
1895      * Should the layout be a column or a row.
1896      * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default
1897      * value is {@link #HORIZONTAL}.
1898      *
1899      * @attr ref android.R.styleable#LinearLayout_orientation
1900      */
setOrientation(@rientationMode int orientation)1901     public void setOrientation(@OrientationMode int orientation) {
1902         if (mOrientation != orientation) {
1903             mOrientation = orientation;
1904             requestLayout();
1905         }
1906     }
1907 
1908     /**
1909      * Returns the current orientation.
1910      *
1911      * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
1912      */
1913     @OrientationMode
1914     @InspectableProperty(enumMapping = {
1915             @InspectableProperty.EnumEntry(value = HORIZONTAL, name = "horizontal"),
1916             @InspectableProperty.EnumEntry(value = VERTICAL, name = "vertical")
1917     })
getOrientation()1918     public int getOrientation() {
1919         return mOrientation;
1920     }
1921 
1922     /**
1923      * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
1924      * this layout has a VERTICAL orientation, this controls where all the child
1925      * views are placed if there is extra vertical space. If this layout has a
1926      * HORIZONTAL orientation, this controls the alignment of the children.
1927      *
1928      * @param gravity See {@link android.view.Gravity}
1929      *
1930      * @attr ref android.R.styleable#LinearLayout_gravity
1931      */
1932     @android.view.RemotableViewMethod
setGravity(int gravity)1933     public void setGravity(int gravity) {
1934         if (mGravity != gravity) {
1935             if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
1936                 gravity |= Gravity.START;
1937             }
1938 
1939             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
1940                 gravity |= Gravity.TOP;
1941             }
1942 
1943             mGravity = gravity;
1944             requestLayout();
1945         }
1946     }
1947 
1948     /**
1949      * Returns the current gravity. See {@link android.view.Gravity}
1950      *
1951      * @return the current gravity.
1952      * @see #setGravity
1953      */
1954     @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
getGravity()1955     public int getGravity() {
1956         return mGravity;
1957     }
1958 
1959     @android.view.RemotableViewMethod
setHorizontalGravity(int horizontalGravity)1960     public void setHorizontalGravity(int horizontalGravity) {
1961         final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1962         if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
1963             mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
1964             requestLayout();
1965         }
1966     }
1967 
1968     @android.view.RemotableViewMethod
setVerticalGravity(int verticalGravity)1969     public void setVerticalGravity(int verticalGravity) {
1970         final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1971         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1972             mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1973             requestLayout();
1974         }
1975     }
1976 
1977     @Override
generateLayoutParams(AttributeSet attrs)1978     public LayoutParams generateLayoutParams(AttributeSet attrs) {
1979         return new LinearLayout.LayoutParams(getContext(), attrs);
1980     }
1981 
1982     /**
1983      * Returns a set of layout parameters with a width of
1984      * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
1985      * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1986      * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1987      * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1988      * and the height to {@link LayoutParams#WRAP_CONTENT}.
1989      */
1990     @Override
generateDefaultLayoutParams()1991     protected LayoutParams generateDefaultLayoutParams() {
1992         if (mOrientation == HORIZONTAL) {
1993             return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1994         } else if (mOrientation == VERTICAL) {
1995             return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1996         }
1997         return null;
1998     }
1999 
2000     @Override
generateLayoutParams(ViewGroup.LayoutParams lp)2001     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
2002         if (sPreserveMarginParamsInLayoutParamConversion) {
2003             if (lp instanceof LayoutParams) {
2004                 return new LayoutParams((LayoutParams) lp);
2005             } else if (lp instanceof MarginLayoutParams) {
2006                 return new LayoutParams((MarginLayoutParams) lp);
2007             }
2008         }
2009         return new LayoutParams(lp);
2010     }
2011 
2012 
2013     // Override to allow type-checking of LayoutParams.
2014     @Override
checkLayoutParams(ViewGroup.LayoutParams p)2015     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2016         return p instanceof LinearLayout.LayoutParams;
2017     }
2018 
2019     @Override
getAccessibilityClassName()2020     public CharSequence getAccessibilityClassName() {
2021         return LinearLayout.class.getName();
2022     }
2023 
2024     /** @hide */
2025     @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)2026     protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
2027         super.encodeProperties(encoder);
2028         encoder.addProperty("layout:baselineAligned", mBaselineAligned);
2029         encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex);
2030         encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop);
2031         encoder.addProperty("measurement:orientation", mOrientation);
2032         encoder.addProperty("measurement:gravity", mGravity);
2033         encoder.addProperty("measurement:totalLength", mTotalLength);
2034         encoder.addProperty("layout:totalLength", mTotalLength);
2035         encoder.addProperty("layout:useLargestChild", mUseLargestChild);
2036     }
2037 
2038     /**
2039      * Per-child layout information associated with ViewLinearLayout.
2040      *
2041      * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
2042      * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
2043      */
2044     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
2045         /**
2046          * Indicates how much of the extra space in the LinearLayout will be
2047          * allocated to the view associated with these LayoutParams. Specify
2048          * 0 if the view should not be stretched. Otherwise the extra pixels
2049          * will be pro-rated among all views whose weight is greater than 0.
2050          */
2051         @ViewDebug.ExportedProperty(category = "layout")
2052         @InspectableProperty(name = "layout_weight")
2053         public float weight;
2054 
2055         /**
2056          * Gravity for the view associated with these LayoutParams.
2057          *
2058          * @see android.view.Gravity
2059          */
2060         @ViewDebug.ExportedProperty(category = "layout", mapping = {
2061             @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
2062             @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
2063             @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
2064             @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
2065             @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
2066             @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
2067             @ViewDebug.IntToString(from = Gravity.START,             to = "START"),
2068             @ViewDebug.IntToString(from = Gravity.END,               to = "END"),
2069             @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
2070             @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
2071             @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
2072             @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
2073             @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
2074             @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
2075         })
2076         @InspectableProperty(
2077                 name = "layout_gravity",
2078                 valueType = InspectableProperty.ValueType.GRAVITY)
2079         public int gravity = -1;
2080 
2081         /**
2082          * {@inheritDoc}
2083          */
LayoutParams(Context c, AttributeSet attrs)2084         public LayoutParams(Context c, AttributeSet attrs) {
2085             super(c, attrs);
2086             TypedArray a =
2087                     c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
2088 
2089             weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
2090             gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
2091 
2092             a.recycle();
2093         }
2094 
2095         /**
2096          * {@inheritDoc}
2097          */
LayoutParams(int width, int height)2098         public LayoutParams(int width, int height) {
2099             super(width, height);
2100             weight = 0;
2101         }
2102 
2103         /**
2104          * Creates a new set of layout parameters with the specified width, height
2105          * and weight.
2106          *
2107          * @param width the width, either {@link #MATCH_PARENT},
2108          *        {@link #WRAP_CONTENT} or a fixed size in pixels
2109          * @param height the height, either {@link #MATCH_PARENT},
2110          *        {@link #WRAP_CONTENT} or a fixed size in pixels
2111          * @param weight the weight
2112          */
LayoutParams(int width, int height, float weight)2113         public LayoutParams(int width, int height, float weight) {
2114             super(width, height);
2115             this.weight = weight;
2116         }
2117 
2118         /**
2119          * {@inheritDoc}
2120          */
LayoutParams(ViewGroup.LayoutParams p)2121         public LayoutParams(ViewGroup.LayoutParams p) {
2122             super(p);
2123         }
2124 
2125         /**
2126          * {@inheritDoc}
2127          */
LayoutParams(ViewGroup.MarginLayoutParams source)2128         public LayoutParams(ViewGroup.MarginLayoutParams source) {
2129             super(source);
2130         }
2131 
2132         /**
2133          * Copy constructor. Clones the width, height, margin values, weight,
2134          * and gravity of the source.
2135          *
2136          * @param source The layout params to copy from.
2137          */
LayoutParams(LayoutParams source)2138         public LayoutParams(LayoutParams source) {
2139             super(source);
2140 
2141             this.weight = source.weight;
2142             this.gravity = source.gravity;
2143         }
2144 
2145         @Override
debug(String output)2146         public String debug(String output) {
2147             return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
2148                     ", height=" + sizeToString(height) + " weight=" + weight +  "}";
2149         }
2150 
2151         /** @hide */
2152         @Override
2153         @UnsupportedAppUsage
encodeProperties(@onNull ViewHierarchyEncoder encoder)2154         protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
2155             super.encodeProperties(encoder);
2156 
2157             encoder.addProperty("layout:weight", weight);
2158             encoder.addProperty("layout:gravity", gravity);
2159         }
2160     }
2161 }
2162