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