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