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