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