LinearLayout.java revision 9295ada0ec89fa7a666be4a2f1006a4b722adf4f
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.util.AttributeSet;
24import android.view.Gravity;
25import android.view.View;
26import android.view.ViewDebug;
27import android.view.ViewGroup;
28import android.widget.RemoteViews.RemoteView;
29
30
31/**
32 * A Layout that arranges its children in a single column or a single row. The direction of
33 * the row can be set by calling {@link #setOrientation(int) setOrientation()}.
34 * You can also specify gravity, which specifies the alignment of all the child elements by
35 * calling {@link #setGravity(int) setGravity()} or specify that specific children
36 * grow to fill up any remaining space in the layout by setting the <em>weight</em> member of
37 * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
38 * The default orientation is horizontal.
39 *
40 * <p>
41 * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
42 * for layout attributes </p>
43 *
44 * @attr ref android.R.styleable#LinearLayout_baselineAligned
45 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
46 * @attr ref android.R.styleable#LinearLayout_gravity
47 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
48 * @attr ref android.R.styleable#LinearLayout_orientation
49 * @attr ref android.R.styleable#LinearLayout_weightSum
50 */
51@RemoteView
52public class LinearLayout extends ViewGroup {
53    public static final int HORIZONTAL = 0;
54    public static final int VERTICAL = 1;
55
56    /**
57     * Whether the children of this layout are baseline aligned.  Only applicable
58     * if {@link #mOrientation} is horizontal.
59     */
60    @ViewDebug.ExportedProperty
61    private boolean mBaselineAligned = true;
62
63    /**
64     * If this layout is part of another layout that is baseline aligned,
65     * use the child at this index as the baseline.
66     *
67     * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
68     * with whether the children of this layout are baseline aligned.
69     */
70    @ViewDebug.ExportedProperty
71    private int mBaselineAlignedChildIndex = -1;
72
73    /**
74     * The additional offset to the child's baseline.
75     * We'll calculate the baseline of this layout as we measure vertically; for
76     * horizontal linear layouts, the offset of 0 is appropriate.
77     */
78    @ViewDebug.ExportedProperty
79    private int mBaselineChildTop = 0;
80
81    @ViewDebug.ExportedProperty
82    private int mOrientation;
83    @ViewDebug.ExportedProperty(mapping = {
84            @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
85            @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
86            @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
87            @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
88            @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
89            @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
90            @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
91            @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
92            @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
93            @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
94            @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
95            @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
96        })
97    private int mGravity = Gravity.LEFT | Gravity.TOP;
98    @ViewDebug.ExportedProperty
99    private int mTotalLength;
100
101    @ViewDebug.ExportedProperty
102    private float mWeightSum;
103
104    @ViewDebug.ExportedProperty
105    private boolean mUseLargestChild;
106
107    private int[] mMaxAscent;
108    private int[] mMaxDescent;
109
110    private static final int VERTICAL_GRAVITY_COUNT = 4;
111
112    private static final int INDEX_CENTER_VERTICAL = 0;
113    private static final int INDEX_TOP = 1;
114    private static final int INDEX_BOTTOM = 2;
115    private static final int INDEX_FILL = 3;
116
117    public LinearLayout(Context context) {
118        super(context);
119    }
120
121    public LinearLayout(Context context, AttributeSet attrs) {
122        this(context, attrs, 0);
123    }
124
125    public LinearLayout(Context context, AttributeSet attrs, int defStyle) {
126        super(context, attrs, defStyle);
127
128        TypedArray a =
129            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout);
130
131        int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);
132        if (index >= 0) {
133            setOrientation(index);
134        }
135
136        index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);
137        if (index >= 0) {
138            setGravity(index);
139        }
140
141        boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);
142        if (!baselineAligned) {
143            setBaselineAligned(baselineAligned);
144        }
145
146        mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);
147
148        mBaselineAlignedChildIndex =
149                a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
150
151        mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
152
153        a.recycle();
154    }
155
156    /**
157     * <p>Indicates whether widgets contained within this layout are aligned
158     * on their baseline or not.</p>
159     *
160     * @return true when widgets are baseline-aligned, false otherwise
161     */
162    public boolean isBaselineAligned() {
163        return mBaselineAligned;
164    }
165
166    /**
167     * <p>Defines whether widgets contained in this layout are
168     * baseline-aligned or not.</p>
169     *
170     * @param baselineAligned true to align widgets on their baseline,
171     *         false otherwise
172     *
173     * @attr ref android.R.styleable#LinearLayout_baselineAligned
174     */
175    @android.view.RemotableViewMethod
176    public void setBaselineAligned(boolean baselineAligned) {
177        mBaselineAligned = baselineAligned;
178    }
179
180    /**
181     * When true, all children with a weight will be considered having
182     * the minimum size of the largest child. If false, all children are
183     * measured normally.
184     *
185     * @return True to measure children with a weight using the minimum
186     *         size of the largest child, false otherwise.
187     */
188    public boolean isMeasureWithLargestChildEnabled() {
189        return mUseLargestChild;
190    }
191
192    /**
193     * When set to true, all children with a weight will be considered having
194     * the minimum size of the largest child. If false, all children are
195     * measured normally.
196     *
197     * Disabled by default.
198     *
199     * @param enabled True to measure children with a weight using the
200     *        minimum size of the largest child, false otherwise.
201     */
202    @android.view.RemotableViewMethod
203    public void setMeasureWithLargestChildEnabled(boolean enabled) {
204        mUseLargestChild = enabled;
205    }
206
207    @Override
208    public int getBaseline() {
209        if (mBaselineAlignedChildIndex < 0) {
210            return super.getBaseline();
211        }
212
213        if (getChildCount() <= mBaselineAlignedChildIndex) {
214            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
215                    + "set to an index that is out of bounds.");
216        }
217
218        final View child = getChildAt(mBaselineAlignedChildIndex);
219        final int childBaseline = child.getBaseline();
220
221        if (childBaseline == -1) {
222            if (mBaselineAlignedChildIndex == 0) {
223                // this is just the default case, safe to return -1
224                return -1;
225            }
226            // the user picked an index that points to something that doesn't
227            // know how to calculate its baseline.
228            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
229                    + "points to a View that doesn't know how to get its baseline.");
230        }
231
232        // TODO: This should try to take into account the virtual offsets
233        // (See getNextLocationOffset and getLocationOffset)
234        // We should add to childTop:
235        // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
236        // and also add:
237        // getLocationOffset(child)
238        int childTop = mBaselineChildTop;
239
240        if (mOrientation == VERTICAL) {
241            final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
242            if (majorGravity != Gravity.TOP) {
243               switch (majorGravity) {
244                   case Gravity.BOTTOM:
245                       childTop = mBottom - mTop - mPaddingBottom - mTotalLength;
246                       break;
247
248                   case Gravity.CENTER_VERTICAL:
249                       childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) -
250                               mTotalLength) / 2;
251                       break;
252               }
253            }
254        }
255
256        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
257        return childTop + lp.topMargin + childBaseline;
258    }
259
260    /**
261     * @return The index of the child that will be used if this layout is
262     *   part of a larger layout that is baseline aligned, or -1 if none has
263     *   been set.
264     */
265    public int getBaselineAlignedChildIndex() {
266        return mBaselineAlignedChildIndex;
267    }
268
269    /**
270     * @param i The index of the child that will be used if this layout is
271     *          part of a larger layout that is baseline aligned.
272     *
273     * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
274     */
275    @android.view.RemotableViewMethod
276    public void setBaselineAlignedChildIndex(int i) {
277        if ((i < 0) || (i >= getChildCount())) {
278            throw new IllegalArgumentException("base aligned child index out "
279                    + "of range (0, " + getChildCount() + ")");
280        }
281        mBaselineAlignedChildIndex = i;
282    }
283
284    /**
285     * <p>Returns the view at the specified index. This method can be overriden
286     * to take into account virtual children. Refer to
287     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
288     * for an example.</p>
289     *
290     * @param index the child's index
291     * @return the child at the specified index
292     */
293    View getVirtualChildAt(int index) {
294        return getChildAt(index);
295    }
296
297    /**
298     * <p>Returns the virtual number of children. This number might be different
299     * than the actual number of children if the layout can hold virtual
300     * children. Refer to
301     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
302     * for an example.</p>
303     *
304     * @return the virtual number of children
305     */
306    int getVirtualChildCount() {
307        return getChildCount();
308    }
309
310    /**
311     * Returns the desired weights sum.
312     *
313     * @return A number greater than 0.0f if the weight sum is defined, or
314     *         a number lower than or equals to 0.0f if not weight sum is
315     *         to be used.
316     */
317    public float getWeightSum() {
318        return mWeightSum;
319    }
320
321    /**
322     * Defines the desired weights sum. If unspecified the weights sum is computed
323     * at layout time by adding the layout_weight of each child.
324     *
325     * This can be used for instance to give a single child 50% of the total
326     * available space by giving it a layout_weight of 0.5 and setting the
327     * weightSum to 1.0.
328     *
329     * @param weightSum a number greater than 0.0f, or a number lower than or equals
330     *        to 0.0f if the weight sum should be computed from the children's
331     *        layout_weight
332     */
333    @android.view.RemotableViewMethod
334    public void setWeightSum(float weightSum) {
335        mWeightSum = Math.max(0.0f, weightSum);
336    }
337
338    @Override
339    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
340        if (mOrientation == VERTICAL) {
341            measureVertical(widthMeasureSpec, heightMeasureSpec);
342        } else {
343            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
344        }
345    }
346
347    /**
348     * Measures the children when the orientation of this LinearLayout is set
349     * to {@link #VERTICAL}.
350     *
351     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
352     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
353     *
354     * @see #getOrientation()
355     * @see #setOrientation(int)
356     * @see #onMeasure(int, int)
357     */
358    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
359        mTotalLength = 0;
360        int maxWidth = 0;
361        int alternativeMaxWidth = 0;
362        int weightedMaxWidth = 0;
363        boolean allFillParent = true;
364        float totalWeight = 0;
365
366        final int count = getVirtualChildCount();
367
368        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
369        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
370
371        boolean matchWidth = false;
372
373        final int baselineChildIndex = mBaselineAlignedChildIndex;
374        final boolean useLargestChild = mUseLargestChild;
375
376        int largestChildHeight = Integer.MIN_VALUE;
377
378        // See how tall everyone is. Also remember max width.
379        for (int i = 0; i < count; ++i) {
380            final View child = getVirtualChildAt(i);
381
382            if (child == null) {
383                mTotalLength += measureNullChild(i);
384                continue;
385            }
386
387            if (child.getVisibility() == View.GONE) {
388               i += getChildrenSkipCount(child, i);
389               continue;
390            }
391
392            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
393
394            totalWeight += lp.weight;
395
396            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
397                // Optimization: don't bother measuring children who are going to use
398                // leftover space. These views will get measured again down below if
399                // there is any leftover space.
400                final int totalLength = mTotalLength;
401                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
402            } else {
403                int oldHeight = Integer.MIN_VALUE;
404
405                if (lp.height == 0 && lp.weight > 0) {
406                    // heightMode is either UNSPECIFIED or AT_MOST, and this
407                    // child wanted to stretch to fill available space.
408                    // Translate that to WRAP_CONTENT so that it does not end up
409                    // with a height of 0
410                    oldHeight = 0;
411                    lp.height = LayoutParams.WRAP_CONTENT;
412                }
413
414                // Determine how big this child would like to be. If this or
415                // previous children have given a weight, then we allow it to
416                // use all available space (and we will shrink things later
417                // if needed).
418                measureChildBeforeLayout(
419                       child, i, widthMeasureSpec, 0, heightMeasureSpec,
420                       totalWeight == 0 ? mTotalLength : 0);
421
422                if (oldHeight != Integer.MIN_VALUE) {
423                   lp.height = oldHeight;
424                }
425
426                final int childHeight = child.getMeasuredHeight();
427                final int totalLength = mTotalLength;
428                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
429                       lp.bottomMargin + getNextLocationOffset(child));
430
431                if (useLargestChild) {
432                    largestChildHeight = Math.max(childHeight, largestChildHeight);
433                }
434            }
435
436            /**
437             * If applicable, compute the additional offset to the child's baseline
438             * we'll need later when asked {@link #getBaseline}.
439             */
440            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
441               mBaselineChildTop = mTotalLength;
442            }
443
444            // if we are trying to use a child index for our baseline, the above
445            // book keeping only works if there are no children above it with
446            // weight.  fail fast to aid the developer.
447            if (i < baselineChildIndex && lp.weight > 0) {
448                throw new RuntimeException("A child of LinearLayout with index "
449                        + "less than mBaselineAlignedChildIndex has weight > 0, which "
450                        + "won't work.  Either remove the weight, or don't set "
451                        + "mBaselineAlignedChildIndex.");
452            }
453
454            boolean matchWidthLocally = false;
455            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
456                // The width of the linear layout will scale, and at least one
457                // child said it wanted to match our width. Set a flag
458                // indicating that we need to remeasure at least that view when
459                // we know our width.
460                matchWidth = true;
461                matchWidthLocally = true;
462            }
463
464            final int margin = lp.leftMargin + lp.rightMargin;
465            final int measuredWidth = child.getMeasuredWidth() + margin;
466            maxWidth = Math.max(maxWidth, measuredWidth);
467
468            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
469            if (lp.weight > 0) {
470                /*
471                 * Widths of weighted Views are bogus if we end up
472                 * remeasuring, so keep them separate.
473                 */
474                weightedMaxWidth = Math.max(weightedMaxWidth,
475                        matchWidthLocally ? margin : measuredWidth);
476            } else {
477                alternativeMaxWidth = Math.max(alternativeMaxWidth,
478                        matchWidthLocally ? margin : measuredWidth);
479            }
480
481            i += getChildrenSkipCount(child, i);
482        }
483
484        if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {
485            mTotalLength = 0;
486
487            for (int i = 0; i < count; ++i) {
488                final View child = getVirtualChildAt(i);
489
490                if (child == null) {
491                    mTotalLength += measureNullChild(i);
492                    continue;
493                }
494
495                if (child.getVisibility() == GONE) {
496                    i += getChildrenSkipCount(child, i);
497                    continue;
498                }
499
500                final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
501                        child.getLayoutParams();
502                // Account for negative margins
503                final int totalLength = mTotalLength;
504                mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
505                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
506            }
507        }
508
509        // Add in our padding
510        mTotalLength += mPaddingTop + mPaddingBottom;
511
512        int heightSize = mTotalLength;
513
514        // Check against our minimum height
515        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
516
517        // Reconcile our calculated size with the heightMeasureSpec
518        heightSize = resolveSize(heightSize, heightMeasureSpec);
519
520        // Either expand children with weight to take up available space or
521        // shrink them if they extend beyond our current bounds
522        int delta = heightSize - mTotalLength;
523        if (delta != 0 && totalWeight > 0.0f) {
524            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
525
526            mTotalLength = 0;
527
528            for (int i = 0; i < count; ++i) {
529                final View child = getVirtualChildAt(i);
530
531                if (child.getVisibility() == View.GONE) {
532                    continue;
533                }
534
535                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
536
537                float childExtra = lp.weight;
538                if (childExtra > 0) {
539                    // Child said it could absorb extra space -- give him his share
540                    int share = (int) (childExtra * delta / weightSum);
541                    weightSum -= childExtra;
542                    delta -= share;
543
544                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
545                            mPaddingLeft + mPaddingRight +
546                                    lp.leftMargin + lp.rightMargin, lp.width);
547
548                    // TODO: Use a field like lp.isMeasured to figure out if this
549                    // child has been previously measured
550                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
551                        // child was measured once already above...
552                        // base new measurement on stored values
553                        int childHeight = child.getMeasuredHeight() + share;
554                        if (childHeight < 0) {
555                            childHeight = 0;
556                        }
557
558                        child.measure(childWidthMeasureSpec,
559                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
560                    } else {
561                        // child was skipped in the loop above.
562                        // Measure for this first time here
563                        child.measure(childWidthMeasureSpec,
564                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
565                                        MeasureSpec.EXACTLY));
566                    }
567                }
568
569                final int margin =  lp.leftMargin + lp.rightMargin;
570                final int measuredWidth = child.getMeasuredWidth() + margin;
571                maxWidth = Math.max(maxWidth, measuredWidth);
572
573                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
574                        lp.width == LayoutParams.MATCH_PARENT;
575
576                alternativeMaxWidth = Math.max(alternativeMaxWidth,
577                        matchWidthLocally ? margin : measuredWidth);
578
579                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
580
581                final int totalLength = mTotalLength;
582                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
583                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
584            }
585
586            // Add in our padding
587            mTotalLength += mPaddingTop + mPaddingBottom;
588            // TODO: Should we recompute the heightSpec based on the new total length?
589        } else {
590            alternativeMaxWidth = Math.max(alternativeMaxWidth,
591                                           weightedMaxWidth);
592        }
593
594        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
595            maxWidth = alternativeMaxWidth;
596        }
597
598        maxWidth += mPaddingLeft + mPaddingRight;
599
600        // Check against our minimum width
601        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
602
603        setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize);
604
605        if (matchWidth) {
606            forceUniformWidth(count, heightMeasureSpec);
607        }
608    }
609
610    private void forceUniformWidth(int count, int heightMeasureSpec) {
611        // Pretend that the linear layout has an exact size.
612        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
613                MeasureSpec.EXACTLY);
614        for (int i = 0; i< count; ++i) {
615           final View child = getVirtualChildAt(i);
616           if (child.getVisibility() != GONE) {
617               LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams());
618
619               if (lp.width == LayoutParams.MATCH_PARENT) {
620                   // Temporarily force children to reuse their old measured height
621                   // FIXME: this may not be right for something like wrapping text?
622                   int oldHeight = lp.height;
623                   lp.height = child.getMeasuredHeight();
624
625                   // Remeasue with new dimensions
626                   measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
627                   lp.height = oldHeight;
628               }
629           }
630        }
631    }
632
633    /**
634     * Measures the children when the orientation of this LinearLayout is set
635     * to {@link #HORIZONTAL}.
636     *
637     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
638     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
639     *
640     * @see #getOrientation()
641     * @see #setOrientation(int)
642     * @see #onMeasure(int, int)
643     */
644    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
645        mTotalLength = 0;
646        int maxHeight = 0;
647        int alternativeMaxHeight = 0;
648        int weightedMaxHeight = 0;
649        boolean allFillParent = true;
650        float totalWeight = 0;
651
652        final int count = getVirtualChildCount();
653
654        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
655        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
656
657        boolean matchHeight = false;
658
659        if (mMaxAscent == null || mMaxDescent == null) {
660            mMaxAscent = new int[VERTICAL_GRAVITY_COUNT];
661            mMaxDescent = new int[VERTICAL_GRAVITY_COUNT];
662        }
663
664        final int[] maxAscent = mMaxAscent;
665        final int[] maxDescent = mMaxDescent;
666
667        maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
668        maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
669
670        final boolean baselineAligned = mBaselineAligned;
671        final boolean useLargestChild = mUseLargestChild;
672
673        final boolean isExactly = widthMode == MeasureSpec.EXACTLY;
674
675        int largestChildWidth = Integer.MIN_VALUE;
676
677        // See how wide everyone is. Also remember max height.
678        for (int i = 0; i < count; ++i) {
679            final View child = getVirtualChildAt(i);
680
681            if (child == null) {
682                mTotalLength += measureNullChild(i);
683                continue;
684            }
685
686            if (child.getVisibility() == GONE) {
687                i += getChildrenSkipCount(child, i);
688                continue;
689            }
690
691            final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
692                    child.getLayoutParams();
693
694            totalWeight += lp.weight;
695
696            if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) {
697                // Optimization: don't bother measuring children who are going to use
698                // leftover space. These views will get measured again down below if
699                // there is any leftover space.
700                if (isExactly) {
701                    mTotalLength += lp.leftMargin + lp.rightMargin;
702                } else {
703                    final int totalLength = mTotalLength;
704                    mTotalLength = Math.max(totalLength, totalLength +
705                            lp.leftMargin + lp.rightMargin);
706                }
707
708                // Baseline alignment requires to measure widgets to obtain the
709                // baseline offset (in particular for TextViews). The following
710                // defeats the optimization mentioned above. Allow the child to
711                // use as much space as it wants because we can shrink things
712                // later (and re-measure).
713                if (baselineAligned) {
714                    final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
715                    child.measure(freeSpec, freeSpec);
716                }
717            } else {
718                int oldWidth = Integer.MIN_VALUE;
719
720                if (lp.width == 0 && lp.weight > 0) {
721                    // widthMode is either UNSPECIFIED or AT_MOST, and this
722                    // child
723                    // wanted to stretch to fill available space. Translate that to
724                    // WRAP_CONTENT so that it does not end up with a width of 0
725                    oldWidth = 0;
726                    lp.width = LayoutParams.WRAP_CONTENT;
727                }
728
729                // Determine how big this child would like to be. If this or
730                // previous children have given a weight, then we allow it to
731                // use all available space (and we will shrink things later
732                // if needed).
733                measureChildBeforeLayout(child, i, widthMeasureSpec,
734                        totalWeight == 0 ? mTotalLength : 0,
735                        heightMeasureSpec, 0);
736
737                if (oldWidth != Integer.MIN_VALUE) {
738                    lp.width = oldWidth;
739                }
740
741                final int childWidth = child.getMeasuredWidth();
742                if (isExactly) {
743                    mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
744                            getNextLocationOffset(child);
745                } else {
746                    final int totalLength = mTotalLength;
747                    mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
748                           lp.rightMargin + getNextLocationOffset(child));
749                }
750
751                if (useLargestChild) {
752                    largestChildWidth = Math.max(childWidth, largestChildWidth);
753                }
754            }
755
756            boolean matchHeightLocally = false;
757            if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) {
758                // The height of the linear layout will scale, and at least one
759                // child said it wanted to match our height. Set a flag indicating that
760                // we need to remeasure at least that view when we know our height.
761                matchHeight = true;
762                matchHeightLocally = true;
763            }
764
765            final int margin = lp.topMargin + lp.bottomMargin;
766            final int childHeight = child.getMeasuredHeight() + margin;
767
768            if (baselineAligned) {
769                final int childBaseline = child.getBaseline();
770                if (childBaseline != -1) {
771                    // Translates the child's vertical gravity into an index
772                    // in the range 0..VERTICAL_GRAVITY_COUNT
773                    final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
774                            & Gravity.VERTICAL_GRAVITY_MASK;
775                    final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
776                            & ~Gravity.AXIS_SPECIFIED) >> 1;
777
778                    maxAscent[index] = Math.max(maxAscent[index], childBaseline);
779                    maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline);
780                }
781            }
782
783            maxHeight = Math.max(maxHeight, childHeight);
784
785            allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
786            if (lp.weight > 0) {
787                /*
788                 * Heights of weighted Views are bogus if we end up
789                 * remeasuring, so keep them separate.
790                 */
791                weightedMaxHeight = Math.max(weightedMaxHeight,
792                        matchHeightLocally ? margin : childHeight);
793            } else {
794                alternativeMaxHeight = Math.max(alternativeMaxHeight,
795                        matchHeightLocally ? margin : childHeight);
796            }
797
798            i += getChildrenSkipCount(child, i);
799        }
800
801        // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
802        // the most common case
803        if (maxAscent[INDEX_TOP] != -1 ||
804                maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
805                maxAscent[INDEX_BOTTOM] != -1 ||
806                maxAscent[INDEX_FILL] != -1) {
807            final int ascent = Math.max(maxAscent[INDEX_FILL],
808                    Math.max(maxAscent[INDEX_CENTER_VERTICAL],
809                    Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
810            final int descent = Math.max(maxDescent[INDEX_FILL],
811                    Math.max(maxDescent[INDEX_CENTER_VERTICAL],
812                    Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
813            maxHeight = Math.max(maxHeight, ascent + descent);
814        }
815
816        if (useLargestChild && widthMode == MeasureSpec.AT_MOST) {
817            mTotalLength = 0;
818
819            for (int i = 0; i < count; ++i) {
820                final View child = getVirtualChildAt(i);
821
822                if (child == null) {
823                    mTotalLength += measureNullChild(i);
824                    continue;
825                }
826
827                if (child.getVisibility() == GONE) {
828                    i += getChildrenSkipCount(child, i);
829                    continue;
830                }
831
832                final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
833                        child.getLayoutParams();
834                if (isExactly) {
835                    mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
836                            getNextLocationOffset(child);
837                } else {
838                    final int totalLength = mTotalLength;
839                    mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
840                            lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
841                }
842            }
843        }
844
845        // Add in our padding
846        mTotalLength += mPaddingLeft + mPaddingRight;
847
848        int widthSize = mTotalLength;
849
850        // Check against our minimum width
851        widthSize = Math.max(widthSize, getSuggestedMinimumWidth());
852
853        // Reconcile our calculated size with the widthMeasureSpec
854        widthSize = resolveSize(widthSize, widthMeasureSpec);
855
856        // Either expand children with weight to take up available space or
857        // shrink them if they extend beyond our current bounds
858        int delta = widthSize - mTotalLength;
859        if (delta != 0 && totalWeight > 0.0f) {
860            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
861
862            maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
863            maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
864            maxHeight = -1;
865
866            mTotalLength = 0;
867
868            for (int i = 0; i < count; ++i) {
869                final View child = getVirtualChildAt(i);
870
871                if (child == null || child.getVisibility() == View.GONE) {
872                    continue;
873                }
874
875                final LinearLayout.LayoutParams lp =
876                        (LinearLayout.LayoutParams) child.getLayoutParams();
877
878                float childExtra = lp.weight;
879                if (childExtra > 0) {
880                    // Child said it could absorb extra space -- give him his share
881                    int share = (int) (childExtra * delta / weightSum);
882                    weightSum -= childExtra;
883                    delta -= share;
884
885                    final int childHeightMeasureSpec = getChildMeasureSpec(
886                            heightMeasureSpec,
887                            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin,
888                            lp.height);
889
890                    // TODO: Use a field like lp.isMeasured to figure out if this
891                    // child has been previously measured
892                    if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) {
893                        // child was measured once already above ... base new measurement
894                        // on stored values
895                        int childWidth = child.getMeasuredWidth() + share;
896                        if (childWidth < 0) {
897                            childWidth = 0;
898                        }
899
900                        child.measure(
901                            MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
902                            childHeightMeasureSpec);
903                    } else {
904                        // child was skipped in the loop above. Measure for this first time here
905                        child.measure(MeasureSpec.makeMeasureSpec(
906                                share > 0 ? share : 0, MeasureSpec.EXACTLY),
907                                childHeightMeasureSpec);
908                    }
909                }
910
911                if (isExactly) {
912                    mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
913                            getNextLocationOffset(child);
914                } else {
915                    final int totalLength = mTotalLength;
916                    mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
917                            lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
918                }
919
920                boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
921                        lp.height == LayoutParams.MATCH_PARENT;
922
923                final int margin = lp.topMargin + lp .bottomMargin;
924                int childHeight = child.getMeasuredHeight() + margin;
925                maxHeight = Math.max(maxHeight, childHeight);
926                alternativeMaxHeight = Math.max(alternativeMaxHeight,
927                        matchHeightLocally ? margin : childHeight);
928
929                allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT;
930
931                if (baselineAligned) {
932                    final int childBaseline = child.getBaseline();
933                    if (childBaseline != -1) {
934                        // Translates the child's vertical gravity into an index in the range 0..2
935                        final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity)
936                                & Gravity.VERTICAL_GRAVITY_MASK;
937                        final int index = ((gravity >> Gravity.AXIS_Y_SHIFT)
938                                & ~Gravity.AXIS_SPECIFIED) >> 1;
939
940                        maxAscent[index] = Math.max(maxAscent[index], childBaseline);
941                        maxDescent[index] = Math.max(maxDescent[index],
942                                childHeight - childBaseline);
943                    }
944                }
945            }
946
947            // Add in our padding
948            mTotalLength += mPaddingLeft + mPaddingRight;
949            // TODO: Should we update widthSize with the new total length?
950
951            // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP,
952            // the most common case
953            if (maxAscent[INDEX_TOP] != -1 ||
954                    maxAscent[INDEX_CENTER_VERTICAL] != -1 ||
955                    maxAscent[INDEX_BOTTOM] != -1 ||
956                    maxAscent[INDEX_FILL] != -1) {
957                final int ascent = Math.max(maxAscent[INDEX_FILL],
958                        Math.max(maxAscent[INDEX_CENTER_VERTICAL],
959                        Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM])));
960                final int descent = Math.max(maxDescent[INDEX_FILL],
961                        Math.max(maxDescent[INDEX_CENTER_VERTICAL],
962                        Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM])));
963                maxHeight = Math.max(maxHeight, ascent + descent);
964            }
965        } else {
966            alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
967        }
968
969        if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
970            maxHeight = alternativeMaxHeight;
971        }
972
973        maxHeight += mPaddingTop + mPaddingBottom;
974
975        // Check against our minimum height
976        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
977
978        setMeasuredDimension(widthSize, resolveSize(maxHeight, heightMeasureSpec));
979
980        if (matchHeight) {
981            forceUniformHeight(count, widthMeasureSpec);
982        }
983    }
984
985    private void forceUniformHeight(int count, int widthMeasureSpec) {
986        // Pretend that the linear layout has an exact size. This is the measured height of
987        // ourselves. The measured height should be the max height of the children, changed
988        // to accomodate the heightMesureSpec from the parent
989        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
990                MeasureSpec.EXACTLY);
991        for (int i = 0; i < count; ++i) {
992           final View child = getVirtualChildAt(i);
993           if (child.getVisibility() != GONE) {
994               LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
995
996               if (lp.height == LayoutParams.MATCH_PARENT) {
997                   // Temporarily force children to reuse their old measured width
998                   // FIXME: this may not be right for something like wrapping text?
999                   int oldWidth = lp.width;
1000                   lp.width = child.getMeasuredWidth();
1001
1002                   // Remeasure with new dimensions
1003                   measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
1004                   lp.width = oldWidth;
1005               }
1006           }
1007        }
1008    }
1009
1010    /**
1011     * <p>Returns the number of children to skip after measuring/laying out
1012     * the specified child.</p>
1013     *
1014     * @param child the child after which we want to skip children
1015     * @param index the index of the child after which we want to skip children
1016     * @return the number of children to skip, 0 by default
1017     */
1018    int getChildrenSkipCount(View child, int index) {
1019        return 0;
1020    }
1021
1022    /**
1023     * <p>Returns the size (width or height) that should be occupied by a null
1024     * child.</p>
1025     *
1026     * @param childIndex the index of the null child
1027     * @return the width or height of the child depending on the orientation
1028     */
1029    int measureNullChild(int childIndex) {
1030        return 0;
1031    }
1032
1033    /**
1034     * <p>Measure the child according to the parent's measure specs. This
1035     * method should be overriden by subclasses to force the sizing of
1036     * children. This method is called by {@link #measureVertical(int, int)} and
1037     * {@link #measureHorizontal(int, int)}.</p>
1038     *
1039     * @param child the child to measure
1040     * @param childIndex the index of the child in this view
1041     * @param widthMeasureSpec horizontal space requirements as imposed by the parent
1042     * @param totalWidth extra space that has been used up by the parent horizontally
1043     * @param heightMeasureSpec vertical space requirements as imposed by the parent
1044     * @param totalHeight extra space that has been used up by the parent vertically
1045     */
1046    void measureChildBeforeLayout(View child, int childIndex,
1047            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
1048            int totalHeight) {
1049        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
1050                heightMeasureSpec, totalHeight);
1051    }
1052
1053    /**
1054     * <p>Return the location offset of the specified child. This can be used
1055     * by subclasses to change the location of a given widget.</p>
1056     *
1057     * @param child the child for which to obtain the location offset
1058     * @return the location offset in pixels
1059     */
1060    int getLocationOffset(View child) {
1061        return 0;
1062    }
1063
1064    /**
1065     * <p>Return the size offset of the next sibling of the specified child.
1066     * This can be used by subclasses to change the location of the widget
1067     * following <code>child</code>.</p>
1068     *
1069     * @param child the child whose next sibling will be moved
1070     * @return the location offset of the next child in pixels
1071     */
1072    int getNextLocationOffset(View child) {
1073        return 0;
1074    }
1075
1076    @Override
1077    protected void onLayout(boolean changed, int l, int t, int r, int b) {
1078        if (mOrientation == VERTICAL) {
1079            layoutVertical();
1080        } else {
1081            layoutHorizontal();
1082        }
1083    }
1084
1085    /**
1086     * Position the children during a layout pass if the orientation of this
1087     * LinearLayout is set to {@link #VERTICAL}.
1088     *
1089     * @see #getOrientation()
1090     * @see #setOrientation(int)
1091     * @see #onLayout(boolean, int, int, int, int)
1092     */
1093    void layoutVertical() {
1094        final int paddingLeft = mPaddingLeft;
1095
1096        int childTop = mPaddingTop;
1097        int childLeft;
1098
1099        // Where right end of child should go
1100        final int width = mRight - mLeft;
1101        int childRight = width - mPaddingRight;
1102
1103        // Space available for child
1104        int childSpace = width - paddingLeft - mPaddingRight;
1105
1106        final int count = getVirtualChildCount();
1107
1108        final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1109        final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1110
1111        if (majorGravity != Gravity.TOP) {
1112           switch (majorGravity) {
1113               case Gravity.BOTTOM:
1114                   // mTotalLength contains the padding already, we add the top
1115                   // padding to compensate
1116                   childTop = mBottom - mTop + mPaddingTop - mTotalLength;
1117                   break;
1118
1119               case Gravity.CENTER_VERTICAL:
1120                   childTop += ((mBottom - mTop)  - mTotalLength) / 2;
1121                   break;
1122           }
1123
1124        }
1125
1126        for (int i = 0; i < count; i++) {
1127            final View child = getVirtualChildAt(i);
1128            if (child == null) {
1129                childTop += measureNullChild(i);
1130            } else if (child.getVisibility() != GONE) {
1131                final int childWidth = child.getMeasuredWidth();
1132                final int childHeight = child.getMeasuredHeight();
1133
1134                final LinearLayout.LayoutParams lp =
1135                        (LinearLayout.LayoutParams) child.getLayoutParams();
1136
1137                int gravity = lp.gravity;
1138                if (gravity < 0) {
1139                    gravity = minorGravity;
1140                }
1141
1142                switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1143                    case Gravity.LEFT:
1144                        childLeft = paddingLeft + lp.leftMargin;
1145                        break;
1146
1147                    case Gravity.CENTER_HORIZONTAL:
1148                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
1149                                + lp.leftMargin - lp.rightMargin;
1150                        break;
1151
1152                    case Gravity.RIGHT:
1153                        childLeft = childRight - childWidth - lp.rightMargin;
1154                        break;
1155                    default:
1156                        childLeft = paddingLeft;
1157                        break;
1158                }
1159
1160
1161                childTop += lp.topMargin;
1162                setChildFrame(child, childLeft, childTop + getLocationOffset(child),
1163                        childWidth, childHeight);
1164                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
1165
1166                i += getChildrenSkipCount(child, i);
1167            }
1168        }
1169    }
1170
1171    /**
1172     * Position the children during a layout pass if the orientation of this
1173     * LinearLayout is set to {@link #HORIZONTAL}.
1174     *
1175     * @see #getOrientation()
1176     * @see #setOrientation(int)
1177     * @see #onLayout(boolean, int, int, int, int)
1178     */
1179    void layoutHorizontal() {
1180        final int paddingTop = mPaddingTop;
1181
1182        int childTop;
1183        int childLeft = mPaddingLeft;
1184
1185        // Where bottom of child should go
1186        final int height = mBottom - mTop;
1187        int childBottom = height - mPaddingBottom;
1188
1189        // Space available for child
1190        int childSpace = height - paddingTop - mPaddingBottom;
1191
1192        final int count = getVirtualChildCount();
1193
1194        final int majorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1195        final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1196
1197        final boolean baselineAligned = mBaselineAligned;
1198
1199        final int[] maxAscent = mMaxAscent;
1200        final int[] maxDescent = mMaxDescent;
1201
1202        if (majorGravity != Gravity.LEFT) {
1203            switch (majorGravity) {
1204                case Gravity.RIGHT:
1205                    // mTotalLength contains the padding already, we add the left
1206                    // padding to compensate
1207                    childLeft = mRight - mLeft + mPaddingLeft - mTotalLength;
1208                    break;
1209
1210                case Gravity.CENTER_HORIZONTAL:
1211                    childLeft += ((mRight - mLeft) - mTotalLength) / 2;
1212                    break;
1213            }
1214       }
1215
1216        for (int i = 0; i < count; i++) {
1217            final View child = getVirtualChildAt(i);
1218
1219            if (child == null) {
1220                childLeft += measureNullChild(i);
1221            } else if (child.getVisibility() != GONE) {
1222                final int childWidth = child.getMeasuredWidth();
1223                final int childHeight = child.getMeasuredHeight();
1224                int childBaseline = -1;
1225
1226                final LinearLayout.LayoutParams lp =
1227                        (LinearLayout.LayoutParams) child.getLayoutParams();
1228
1229                if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
1230                    childBaseline = child.getBaseline();
1231                }
1232
1233                int gravity = lp.gravity;
1234                if (gravity < 0) {
1235                    gravity = minorGravity;
1236                }
1237
1238                switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
1239                    case Gravity.TOP:
1240                        childTop = paddingTop + lp.topMargin;
1241                        if (childBaseline != -1) {
1242                            childTop += maxAscent[INDEX_TOP] - childBaseline;
1243                        }
1244                        break;
1245
1246                    case Gravity.CENTER_VERTICAL:
1247                        // Removed support for baseline alignment when layout_gravity or
1248                        // gravity == center_vertical. See bug #1038483.
1249                        // Keep the code around if we need to re-enable this feature
1250                        // if (childBaseline != -1) {
1251                        //     // Align baselines vertically only if the child is smaller than us
1252                        //     if (childSpace - childHeight > 0) {
1253                        //         childTop = paddingTop + (childSpace / 2) - childBaseline;
1254                        //     } else {
1255                        //         childTop = paddingTop + (childSpace - childHeight) / 2;
1256                        //     }
1257                        // } else {
1258                        childTop = paddingTop + ((childSpace - childHeight) / 2)
1259                                + lp.topMargin - lp.bottomMargin;
1260                        break;
1261
1262                    case Gravity.BOTTOM:
1263                        childTop = childBottom - childHeight - lp.bottomMargin;
1264                        if (childBaseline != -1) {
1265                            int descent = child.getMeasuredHeight() - childBaseline;
1266                            childTop -= (maxDescent[INDEX_BOTTOM] - descent);
1267                        }
1268                        break;
1269                    default:
1270                        childTop = paddingTop;
1271                        break;
1272                }
1273
1274                childLeft += lp.leftMargin;
1275                setChildFrame(child, childLeft + getLocationOffset(child), childTop,
1276                        childWidth, childHeight);
1277                childLeft += childWidth + lp.rightMargin +
1278                        getNextLocationOffset(child);
1279
1280                i += getChildrenSkipCount(child, i);
1281            }
1282        }
1283    }
1284
1285    private void setChildFrame(View child, int left, int top, int width, int height) {
1286        child.layout(left, top, left + width, top + height);
1287    }
1288
1289    /**
1290     * Should the layout be a column or a row.
1291     * @param orientation Pass HORIZONTAL or VERTICAL. Default
1292     * value is HORIZONTAL.
1293     *
1294     * @attr ref android.R.styleable#LinearLayout_orientation
1295     */
1296    public void setOrientation(int orientation) {
1297        if (mOrientation != orientation) {
1298            mOrientation = orientation;
1299            requestLayout();
1300        }
1301    }
1302
1303    /**
1304     * Returns the current orientation.
1305     *
1306     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
1307     */
1308    public int getOrientation() {
1309        return mOrientation;
1310    }
1311
1312    /**
1313     * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
1314     * this layout has a VERTICAL orientation, this controls where all the child
1315     * views are placed if there is extra vertical space. If this layout has a
1316     * HORIZONTAL orientation, this controls the alignment of the children.
1317     *
1318     * @param gravity See {@link android.view.Gravity}
1319     *
1320     * @attr ref android.R.styleable#LinearLayout_gravity
1321     */
1322    @android.view.RemotableViewMethod
1323    public void setGravity(int gravity) {
1324        if (mGravity != gravity) {
1325            if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
1326                gravity |= Gravity.LEFT;
1327            }
1328
1329            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
1330                gravity |= Gravity.TOP;
1331            }
1332
1333            mGravity = gravity;
1334            requestLayout();
1335        }
1336    }
1337
1338    @android.view.RemotableViewMethod
1339    public void setHorizontalGravity(int horizontalGravity) {
1340        final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1341        if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) {
1342            mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity;
1343            requestLayout();
1344        }
1345    }
1346
1347    @android.view.RemotableViewMethod
1348    public void setVerticalGravity(int verticalGravity) {
1349        final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1350        if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1351            mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1352            requestLayout();
1353        }
1354    }
1355
1356    @Override
1357    public LayoutParams generateLayoutParams(AttributeSet attrs) {
1358        return new LinearLayout.LayoutParams(getContext(), attrs);
1359    }
1360
1361    /**
1362     * Returns a set of layout parameters with a width of
1363     * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
1364     * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1365     * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1366     * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1367     * and the height to {@link LayoutParams#WRAP_CONTENT}.
1368     */
1369    @Override
1370    protected LayoutParams generateDefaultLayoutParams() {
1371        if (mOrientation == HORIZONTAL) {
1372            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1373        } else if (mOrientation == VERTICAL) {
1374            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1375        }
1376        return null;
1377    }
1378
1379    @Override
1380    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1381        return new LayoutParams(p);
1382    }
1383
1384
1385    // Override to allow type-checking of LayoutParams.
1386    @Override
1387    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1388        return p instanceof LinearLayout.LayoutParams;
1389    }
1390
1391    /**
1392     * Per-child layout information associated with ViewLinearLayout.
1393     *
1394     * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
1395     * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
1396     */
1397    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1398        /**
1399         * Indicates how much of the extra space in the LinearLayout will be
1400         * allocated to the view associated with these LayoutParams. Specify
1401         * 0 if the view should not be stretched. Otherwise the extra pixels
1402         * will be pro-rated among all views whose weight is greater than 0.
1403         */
1404        @ViewDebug.ExportedProperty
1405        public float weight;
1406
1407        /**
1408         * Gravity for the view associated with these LayoutParams.
1409         *
1410         * @see android.view.Gravity
1411         */
1412        @ViewDebug.ExportedProperty(mapping = {
1413            @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
1414            @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
1415            @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
1416            @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
1417            @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
1418            @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
1419            @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
1420            @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
1421            @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
1422            @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
1423            @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
1424            @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
1425        })
1426        public int gravity = -1;
1427
1428        /**
1429         * {@inheritDoc}
1430         */
1431        public LayoutParams(Context c, AttributeSet attrs) {
1432            super(c, attrs);
1433            TypedArray a =
1434                    c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
1435
1436            weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
1437            gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
1438
1439            a.recycle();
1440        }
1441
1442        /**
1443         * {@inheritDoc}
1444         */
1445        public LayoutParams(int width, int height) {
1446            super(width, height);
1447            weight = 0;
1448        }
1449
1450        /**
1451         * Creates a new set of layout parameters with the specified width, height
1452         * and weight.
1453         *
1454         * @param width the width, either {@link #MATCH_PARENT},
1455         *        {@link #WRAP_CONTENT} or a fixed size in pixels
1456         * @param height the height, either {@link #MATCH_PARENT},
1457         *        {@link #WRAP_CONTENT} or a fixed size in pixels
1458         * @param weight the weight
1459         */
1460        public LayoutParams(int width, int height, float weight) {
1461            super(width, height);
1462            this.weight = weight;
1463        }
1464
1465        /**
1466         * {@inheritDoc}
1467         */
1468        public LayoutParams(ViewGroup.LayoutParams p) {
1469            super(p);
1470        }
1471
1472        /**
1473         * {@inheritDoc}
1474         */
1475        public LayoutParams(MarginLayoutParams source) {
1476            super(source);
1477        }
1478
1479        @Override
1480        public String debug(String output) {
1481            return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
1482                    ", height=" + sizeToString(height) + " weight=" + weight +  "}";
1483        }
1484    }
1485}
1486