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