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
17package android.support.v7.widget;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.graphics.Canvas;
22import android.graphics.drawable.Drawable;
23import android.os.Build;
24import android.support.annotation.IntDef;
25import android.support.annotation.RestrictTo;
26import android.support.v4.view.GravityCompat;
27import android.support.v4.view.ViewCompat;
28import android.support.v7.appcompat.R;
29import android.util.AttributeSet;
30import android.view.Gravity;
31import android.view.View;
32import android.view.ViewGroup;
33import android.view.accessibility.AccessibilityEvent;
34import android.view.accessibility.AccessibilityNodeInfo;
35
36import java.lang.annotation.Retention;
37import java.lang.annotation.RetentionPolicy;
38
39import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
40
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 */
57public class LinearLayoutCompat extends ViewGroup {
58    /** @hide */
59    @RestrictTo(GROUP_ID)
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(GROUP_ID)
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
144    public LinearLayoutCompat(Context context) {
145        this(context, null);
146    }
147
148    public LinearLayoutCompat(Context context, AttributeSet attrs) {
149        this(context, attrs, 0);
150    }
151
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     */
194    public void setShowDividers(@DividerMode int showDividers) {
195        if (showDividers != mShowDividers) {
196            requestLayout();
197        }
198        mShowDividers = showDividers;
199    }
200
201    @Override
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
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     */
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     */
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     */
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     */
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(GROUP_ID)
277    public int getDividerWidth() {
278        return mDividerWidth;
279    }
280
281    @Override
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
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
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
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
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     */
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     */
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     */
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     */
417    public void setMeasureWithLargestChildEnabled(boolean enabled) {
418        mUseLargestChild = enabled;
419    }
420
421    @Override
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     */
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     */
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     */
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     */
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     */
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     */
544    public void setWeightSum(float weightSum) {
545        mWeightSum = Math.max(0.0f, weightSum);
546    }
547
548    @Override
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    protected boolean hasDividerBeforeChildAt(int childIndex) {
565        if (childIndex == 0) {
566            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
567        } else if (childIndex == getChildCount()) {
568            return (mShowDividers & SHOW_DIVIDER_END) != 0;
569        } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
570            boolean hasVisibleViewBefore = false;
571            for (int i = childIndex - 1; i >= 0; i--) {
572                if (getChildAt(i).getVisibility() != GONE) {
573                    hasVisibleViewBefore = true;
574                    break;
575                }
576            }
577            return hasVisibleViewBefore;
578        }
579        return false;
580    }
581
582    /**
583     * Measures the children when the orientation of this LinearLayout is set
584     * to {@link #VERTICAL}.
585     *
586     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
587     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
588     *
589     * @see #getOrientation()
590     * @see #setOrientation(int)
591     * @see #onMeasure(int, int)
592     */
593    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
594        mTotalLength = 0;
595        int maxWidth = 0;
596        int childState = 0;
597        int alternativeMaxWidth = 0;
598        int weightedMaxWidth = 0;
599        boolean allFillParent = true;
600        float totalWeight = 0;
601
602        final int count = getVirtualChildCount();
603
604        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
605        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
606
607        boolean matchWidth = false;
608        boolean skippedMeasure = false;
609
610        final int baselineChildIndex = mBaselineAlignedChildIndex;
611        final boolean useLargestChild = mUseLargestChild;
612
613        int largestChildHeight = Integer.MIN_VALUE;
614
615        // See how tall everyone is. Also remember max width.
616        for (int i = 0; i < count; ++i) {
617            final View child = getVirtualChildAt(i);
618
619            if (child == null) {
620                mTotalLength += measureNullChild(i);
621                continue;
622            }
623
624            if (child.getVisibility() == View.GONE) {
625                i += getChildrenSkipCount(child, i);
626                continue;
627            }
628
629            if (hasDividerBeforeChildAt(i)) {
630                mTotalLength += mDividerHeight;
631            }
632
633            LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
634
635            totalWeight += lp.weight;
636
637            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
638                // Optimization: don't bother measuring children who are going to use
639                // leftover space. These views will get measured again down below if
640                // there is any leftover space.
641                final int totalLength = mTotalLength;
642                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
643                skippedMeasure = true;
644            } else {
645                int oldHeight = Integer.MIN_VALUE;
646
647                if (lp.height == 0 && lp.weight > 0) {
648                    // heightMode is either UNSPECIFIED or AT_MOST, and this
649                    // child wanted to stretch to fill available space.
650                    // Translate that to WRAP_CONTENT so that it does not end up
651                    // with a height of 0
652                    oldHeight = 0;
653                    lp.height = LayoutParams.WRAP_CONTENT;
654                }
655
656                // Determine how big this child would like to be. If this or
657                // previous children have given a weight, then we allow it to
658                // use all available space (and we will shrink things later
659                // if needed).
660                measureChildBeforeLayout(
661                        child, i, widthMeasureSpec, 0, heightMeasureSpec,
662                        totalWeight == 0 ? mTotalLength : 0);
663
664                if (oldHeight != Integer.MIN_VALUE) {
665                    lp.height = oldHeight;
666                }
667
668                final int childHeight = child.getMeasuredHeight();
669                final int totalLength = mTotalLength;
670                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
671                        lp.bottomMargin + getNextLocationOffset(child));
672
673                if (useLargestChild) {
674                    largestChildHeight = Math.max(childHeight, largestChildHeight);
675                }
676            }
677
678            /**
679             * If applicable, compute the additional offset to the child's baseline
680             * we'll need later when asked {@link #getBaseline}.
681             */
682            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
683                mBaselineChildTop = mTotalLength;
684            }
685
686            // if we are trying to use a child index for our baseline, the above
687            // book keeping only works if there are no children above it with
688            // weight.  fail fast to aid the developer.
689            if (i < baselineChildIndex && lp.weight > 0) {
690                throw new RuntimeException("A child of LinearLayout with index "
691                        + "less than mBaselineAlignedChildIndex has weight > 0, which "
692                        + "won't work.  Either remove the weight, or don't set "
693                        + "mBaselineAlignedChildIndex.");
694            }
695
696            boolean matchWidthLocally = false;
697            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
698                // The width of the linear layout will scale, and at least one
699                // child said it wanted to match our width. Set a flag
700                // indicating that we need to remeasure at least that view when
701                // we know our width.
702                matchWidth = true;
703                matchWidthLocally = true;
704            }
705
706            final int margin = lp.leftMargin + lp.rightMargin;
707            final int measuredWidth = child.getMeasuredWidth() + margin;
708            maxWidth = Math.max(maxWidth, measuredWidth);
709            childState = ViewUtils.combineMeasuredStates(childState,
710                    ViewCompat.getMeasuredState(child));
711
712            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
713            if (lp.weight > 0) {
714                /*
715                 * Widths of weighted Views are bogus if we end up
716                 * remeasuring, so keep them separate.
717                 */
718                weightedMaxWidth = Math.max(weightedMaxWidth,
719                        matchWidthLocally ? margin : measuredWidth);
720            } else {
721                alternativeMaxWidth = Math.max(alternativeMaxWidth,
722                        matchWidthLocally ? margin : measuredWidth);
723            }
724
725            i += getChildrenSkipCount(child, i);
726        }
727
728        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
729            mTotalLength += mDividerHeight;
730        }
731
732        if (useLargestChild &&
733                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
734            mTotalLength = 0;
735
736            for (int i = 0; i < count; ++i) {
737                final View child = getVirtualChildAt(i);
738
739                if (child == null) {
740                    mTotalLength += measureNullChild(i);
741                    continue;
742                }
743
744                if (child.getVisibility() == GONE) {
745                    i += getChildrenSkipCount(child, i);
746                    continue;
747                }
748
749                final LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams)
750                        child.getLayoutParams();
751                // Account for negative margins
752                final int totalLength = mTotalLength;
753                mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
754                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
755            }
756        }
757
758        // Add in our padding
759        mTotalLength += getPaddingTop() + getPaddingBottom();
760
761        int heightSize = mTotalLength;
762
763        // Check against our minimum height
764        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
765
766        // Reconcile our calculated size with the heightMeasureSpec
767        int heightSizeAndState = ViewCompat.resolveSizeAndState(heightSize, heightMeasureSpec, 0);
768        heightSize = heightSizeAndState & ViewCompat.MEASURED_SIZE_MASK;
769
770        // Either expand children with weight to take up available space or
771        // shrink them if they extend beyond our current bounds. If we skipped
772        // measurement on any children, we need to measure them now.
773        int delta = heightSize - mTotalLength;
774        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
775            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
776
777            mTotalLength = 0;
778
779            for (int i = 0; i < count; ++i) {
780                final View child = getVirtualChildAt(i);
781
782                if (child.getVisibility() == View.GONE) {
783                    continue;
784                }
785
786                LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
787
788                float childExtra = lp.weight;
789                if (childExtra > 0) {
790                    // Child said it could absorb extra space -- give him his share
791                    int share = (int) (childExtra * delta / weightSum);
792                    weightSum -= childExtra;
793                    delta -= share;
794
795                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
796                            getPaddingLeft() + getPaddingRight() +
797                                    lp.leftMargin + lp.rightMargin, lp.width);
798
799                    // TODO: Use a field like lp.isMeasured to figure out if this
800                    // child has been previously measured
801                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
802                        // child was measured once already above...
803                        // base new measurement on stored values
804                        int childHeight = child.getMeasuredHeight() + share;
805                        if (childHeight < 0) {
806                            childHeight = 0;
807                        }
808
809                        child.measure(childWidthMeasureSpec,
810                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
811                    } else {
812                        // child was skipped in the loop above.
813                        // Measure for this first time here
814                        child.measure(childWidthMeasureSpec,
815                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
816                                        MeasureSpec.EXACTLY));
817                    }
818
819                    // Child may now not fit in vertical dimension.
820                    childState = ViewUtils.combineMeasuredStates(childState,
821                            ViewCompat.getMeasuredState(child) & (ViewCompat.MEASURED_STATE_MASK
822                                    >> ViewCompat.MEASURED_HEIGHT_STATE_SHIFT));
823                }
824
825                final int margin =  lp.leftMargin + lp.rightMargin;
826                final int measuredWidth = child.getMeasuredWidth() + margin;
827                maxWidth = Math.max(maxWidth, measuredWidth);
828
829                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
830                        lp.width == LayoutParams.MATCH_PARENT;
831
832                alternativeMaxWidth = Math.max(alternativeMaxWidth,
833                        matchWidthLocally ? margin : measuredWidth);
834
835                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
836
837                final int totalLength = mTotalLength;
838                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
839                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
840            }
841
842            // Add in our padding
843            mTotalLength += getPaddingTop() + getPaddingBottom();
844            // TODO: Should we recompute the heightSpec based on the new total length?
845        } else {
846            alternativeMaxWidth = Math.max(alternativeMaxWidth,
847                    weightedMaxWidth);
848
849
850            // We have no limit, so make all weighted views as tall as the largest child.
851            // Children will have already been measured once.
852            if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
853                for (int i = 0; i < count; i++) {
854                    final View child = getVirtualChildAt(i);
855
856                    if (child == null || child.getVisibility() == View.GONE) {
857                        continue;
858                    }
859
860                    final LinearLayoutCompat.LayoutParams lp =
861                            (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
862
863                    float childExtra = lp.weight;
864                    if (childExtra > 0) {
865                        child.measure(
866                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
867                                        MeasureSpec.EXACTLY),
868                                MeasureSpec.makeMeasureSpec(largestChildHeight,
869                                        MeasureSpec.EXACTLY));
870                    }
871                }
872            }
873        }
874
875        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
876            maxWidth = alternativeMaxWidth;
877        }
878
879        maxWidth += getPaddingLeft() + getPaddingRight();
880
881        // Check against our minimum width
882        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
883
884        setMeasuredDimension(ViewCompat.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
885                heightSizeAndState);
886
887        if (matchWidth) {
888            forceUniformWidth(count, heightMeasureSpec);
889        }
890    }
891
892    private void forceUniformWidth(int count, int heightMeasureSpec) {
893        // Pretend that the linear layout has an exact size.
894        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
895                MeasureSpec.EXACTLY);
896        for (int i = 0; i< count; ++i) {
897            final View child = getVirtualChildAt(i);
898            if (child.getVisibility() != GONE) {
899                LinearLayoutCompat.LayoutParams lp = ((LinearLayoutCompat.LayoutParams)child.getLayoutParams());
900
901                if (lp.width == LayoutParams.MATCH_PARENT) {
902                    // Temporarily force children to reuse their old measured height
903                    // FIXME: this may not be right for something like wrapping text?
904                    int oldHeight = lp.height;
905                    lp.height = child.getMeasuredHeight();
906
907                    // Remeasue with new dimensions
908                    measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
909                    lp.height = oldHeight;
910                }
911            }
912        }
913    }
914
915    /**
916     * Measures the children when the orientation of this LinearLayout is set
917     * to {@link #HORIZONTAL}.
918     *
919     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
920     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
921     *
922     * @see #getOrientation()
923     * @see #setOrientation(int)
924     * @see #onMeasure(int, int)
925     */
926    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
927        mTotalLength = 0;
928        int maxHeight = 0;
929        int childState = 0;
930        int alternativeMaxHeight = 0;
931        int weightedMaxHeight = 0;
932        boolean allFillParent = true;
933        float totalWeight = 0;
934
935        final int count = getVirtualChildCount();
936
937        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
938        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
939
940        boolean matchHeight = false;
941        boolean skippedMeasure = false;
942
943        if (mMaxAscent == null || mMaxDescent == null) {
944            mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
945            mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
946        }
947
948        final int[] maxAscent = mMaxAscent;
949        final int[] maxDescent = mMaxDescent;
950
951        maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
952        maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
953
954        final boolean baselineAligned = mBaselineAligned;
955        final boolean useLargestChild = mUseLargestChild;
956
957        final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
958
959        int largestChildWidth = Integer.MIN_VALUE;
960
961        // See how wide everyone is. Also remember max height.
962        for (int i = 0; i < count; ++i) {
963            final View child = getVirtualChildAt(i);
964
965            if (child == null) {
966                mTotalLength += measureNullChild(i);
967                continue;
968            }
969
970            if (child.getVisibility() == GONE) {
971                i += getChildrenSkipCount(child, i);
972                continue;
973            }
974
975            if (hasDividerBeforeChildAt(i)) {
976                mTotalLength += mDividerWidth;
977            }
978
979            final LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams)
980                    child.getLayoutParams();
981
982            totalWeight += lp.weight;
983
984            if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
985                // Optimization: don't bother measuring children who are going to use
986                // leftover space. These views will get measured again down below if
987                // there is any leftover space.
988                if (isExactly) {
989                    mTotalLength += lp.leftMargin + lp.rightMargin;
990                } else {
991                    final int totalLength = mTotalLength;
992                    mTotalLength = Math.max(totalLength, totalLength +
993                            lp.leftMargin + lp.rightMargin);
994                }
995
996                // Baseline alignment requires to measure widgets to obtain the
997                // baseline offset (in particular for TextViews). The following
998                // defeats the optimization mentioned above. Allow the child to
999                // use as much space as it wants because we can shrink things
1000                // later (and re-measure).
1001                if (baselineAligned) {
1002                    final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
1003                    child.measure(freeSpec, freeSpec);
1004                } else {
1005                    skippedMeasure = true;
1006                }
1007            } else {
1008                int oldWidth = Integer.MIN_VALUE;
1009
1010                if (lp.width == 0 && lp.weight > 0) {
1011                    // widthMode is either UNSPECIFIED or AT_MOST, and this
1012                    // child
1013                    // wanted to stretch to fill available space. Translate that to
1014                    // WRAP_CONTENT so that it does not end up with a width of 0
1015                    oldWidth = 0;
1016                    lp.width = LayoutParams.WRAP_CONTENT;
1017                }
1018
1019                // Determine how big this child would like to be. If this or
1020                // previous children have given a weight, then we allow it to
1021                // use all available space (and we will shrink things later
1022                // if needed).
1023                measureChildBeforeLayout(child, i, widthMeasureSpec,
1024                        totalWeight == 0 ? mTotalLength : 0,
1025                        heightMeasureSpec, 0);
1026
1027                if (oldWidth != Integer.MIN_VALUE) {
1028                    lp.width = oldWidth;
1029                }
1030
1031                final int childWidth = child.getMeasuredWidth();
1032                if (isExactly) {
1033                    mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
1034                            getNextLocationOffset(child);
1035                } else {
1036                    final int totalLength = mTotalLength;
1037                    mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
1038                            lp.rightMargin + getNextLocationOffset(child));
1039                }
1040
1041                if (useLargestChild) {
1042                    largestChildWidth = Math.max(childWidth, largestChildWidth);
1043                }
1044            }
1045
1046            boolean matchHeightLocally = false;
1047            if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
1048                // The height of the linear layout will scale, and at least one
1049                // child said it wanted to match our height. Set a flag indicating that
1050                // we need to remeasure at least that view when we know our height.
1051                matchHeight = true;
1052                matchHeightLocally = true;
1053            }
1054
1055            final int margin = lp.topMargin + lp.bottomMargin;
1056            final int childHeight = child.getMeasuredHeight() + margin;
1057            childState = ViewUtils.combineMeasuredStates(childState,
1058                    ViewCompat.getMeasuredState(child));
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 = ViewCompat.resolveSizeAndState(widthSize, widthMeasureSpec, 0);
1152        widthSize = widthSizeAndState & ViewCompat.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 = ViewUtils.combineMeasuredStates(childState,
1211                            ViewCompat.getMeasuredState(child) & ViewCompat.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&ViewCompat.MEASURED_STATE_MASK),
1305                ViewCompat.resolveSizeAndState(maxHeight, heightMeasureSpec,
1306                        (childState<<ViewCompat.MEASURED_HEIGHT_STATE_SHIFT)));
1307
1308        if (matchHeight) {
1309            forceUniformHeight(count, widthMeasureSpec);
1310        }
1311    }
1312
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     */
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     */
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     */
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     */
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     */
1400    int getNextLocationOffset(View child) {
1401        return 0;
1402    }
1403
1404    @Override
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     */
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     */
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
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     */
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
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     */
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    public void setHorizontalGravity(int horizontalGravity) {
1694        final int gravity = horizontalGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1695        if ((mGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
1696            mGravity = (mGravity & ~GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
1697            requestLayout();
1698        }
1699    }
1700
1701    public void setVerticalGravity(int verticalGravity) {
1702        final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1703        if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1704            mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1705            requestLayout();
1706        }
1707    }
1708
1709    @Override
1710    public LayoutParams generateLayoutParams(AttributeSet attrs) {
1711        return new LinearLayoutCompat.LayoutParams(getContext(), attrs);
1712    }
1713
1714    /**
1715     * Returns a set of layout parameters with a width of
1716     * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
1717     * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1718     * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1719     * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1720     * and the height to {@link LayoutParams#WRAP_CONTENT}.
1721     */
1722    @Override
1723    protected LayoutParams generateDefaultLayoutParams() {
1724        if (mOrientation == HORIZONTAL) {
1725            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1726        } else if (mOrientation == VERTICAL) {
1727            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1728        }
1729        return null;
1730    }
1731
1732    @Override
1733    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1734        return new LayoutParams(p);
1735    }
1736
1737
1738    // Override to allow type-checking of LayoutParams.
1739    @Override
1740    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1741        return p instanceof LinearLayoutCompat.LayoutParams;
1742    }
1743
1744    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1745        if (Build.VERSION.SDK_INT >= 14) {
1746            super.onInitializeAccessibilityEvent(event);
1747            event.setClassName(LinearLayoutCompat.class.getName());
1748        }
1749    }
1750
1751    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1752        if (Build.VERSION.SDK_INT >= 14) {
1753            super.onInitializeAccessibilityNodeInfo(info);
1754            info.setClassName(LinearLayoutCompat.class.getName());
1755        }
1756    }
1757
1758    /**
1759     * Per-child layout information associated with ViewLinearLayout.
1760     */
1761    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1762        /**
1763         * Indicates how much of the extra space in the LinearLayout will be
1764         * allocated to the view associated with these LayoutParams. Specify
1765         * 0 if the view should not be stretched. Otherwise the extra pixels
1766         * will be pro-rated among all views whose weight is greater than 0.
1767         */
1768        public float weight;
1769
1770        /**
1771         * Gravity for the view associated with these LayoutParams.
1772         *
1773         * @see android.view.Gravity
1774         */
1775        public int gravity = -1;
1776
1777        /**
1778         * {@inheritDoc}
1779         */
1780        public LayoutParams(Context c, AttributeSet attrs) {
1781            super(c, attrs);
1782            TypedArray a =
1783                    c.obtainStyledAttributes(attrs, R.styleable.LinearLayoutCompat_Layout);
1784
1785            weight = a.getFloat(R.styleable.LinearLayoutCompat_Layout_android_layout_weight, 0);
1786            gravity = a.getInt(R.styleable.LinearLayoutCompat_Layout_android_layout_gravity, -1);
1787
1788            a.recycle();
1789        }
1790
1791        /**
1792         * {@inheritDoc}
1793         */
1794        public LayoutParams(int width, int height) {
1795            super(width, height);
1796            weight = 0;
1797        }
1798
1799        /**
1800         * Creates a new set of layout parameters with the specified width, height
1801         * and weight.
1802         *
1803         * @param width the width, either {@link #MATCH_PARENT},
1804         *        {@link #WRAP_CONTENT} or a fixed size in pixels
1805         * @param height the height, either {@link #MATCH_PARENT},
1806         *        {@link #WRAP_CONTENT} or a fixed size in pixels
1807         * @param weight the weight
1808         */
1809        public LayoutParams(int width, int height, float weight) {
1810            super(width, height);
1811            this.weight = weight;
1812        }
1813
1814        /**
1815         * {@inheritDoc}
1816         */
1817        public LayoutParams(ViewGroup.LayoutParams p) {
1818            super(p);
1819        }
1820
1821        /**
1822         * {@inheritDoc}
1823         */
1824        public LayoutParams(ViewGroup.MarginLayoutParams source) {
1825            super(source);
1826        }
1827
1828        /**
1829         * Copy constructor. Clones the width, height, margin values, weight,
1830         * and gravity of the source.
1831         *
1832         * @param source The layout params to copy from.
1833         */
1834        public LayoutParams(LayoutParams source) {
1835            super(source);
1836
1837            this.weight = source.weight;
1838            this.gravity = source.gravity;
1839        }
1840    }
1841}
1842