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