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