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