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