Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2011 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 android.widget;
     18 
     19 import static android.view.Gravity.AXIS_PULL_AFTER;
     20 import static android.view.Gravity.AXIS_PULL_BEFORE;
     21 import static android.view.Gravity.AXIS_SPECIFIED;
     22 import static android.view.Gravity.AXIS_X_SHIFT;
     23 import static android.view.Gravity.AXIS_Y_SHIFT;
     24 import static android.view.Gravity.HORIZONTAL_GRAVITY_MASK;
     25 import static android.view.Gravity.RELATIVE_LAYOUT_DIRECTION;
     26 import static android.view.Gravity.VERTICAL_GRAVITY_MASK;
     27 import static android.view.View.MeasureSpec.EXACTLY;
     28 import static android.view.View.MeasureSpec.makeMeasureSpec;
     29 
     30 import static java.lang.Math.max;
     31 import static java.lang.Math.min;
     32 
     33 import android.annotation.IntDef;
     34 import android.annotation.UnsupportedAppUsage;
     35 import android.content.Context;
     36 import android.content.res.TypedArray;
     37 import android.graphics.Canvas;
     38 import android.graphics.Color;
     39 import android.graphics.Insets;
     40 import android.graphics.Paint;
     41 import android.util.AttributeSet;
     42 import android.util.Log;
     43 import android.util.LogPrinter;
     44 import android.util.Pair;
     45 import android.util.Printer;
     46 import android.view.Gravity;
     47 import android.view.View;
     48 import android.view.ViewGroup;
     49 import android.view.inspector.InspectableProperty;
     50 import android.widget.RemoteViews.RemoteView;
     51 
     52 import com.android.internal.R;
     53 
     54 import java.lang.annotation.Retention;
     55 import java.lang.annotation.RetentionPolicy;
     56 import java.lang.reflect.Array;
     57 import java.util.ArrayList;
     58 import java.util.Arrays;
     59 import java.util.HashMap;
     60 import java.util.List;
     61 import java.util.Map;
     62 
     63 /**
     64  * A layout that places its children in a rectangular <em>grid</em>.
     65  * <p>
     66  * The grid is composed of a set of infinitely thin lines that separate the
     67  * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced
     68  * by grid <em>indices</em>. A grid with {@code N} columns
     69  * has {@code N + 1} grid indices that run from {@code 0}
     70  * through {@code N} inclusive. Regardless of how GridLayout is
     71  * configured, grid index {@code 0} is fixed to the leading edge of the
     72  * container and grid index {@code N} is fixed to its trailing edge
     73  * (after padding is taken into account).
     74  *
     75  * <h4>Row and Column Specs</h4>
     76  *
     77  * Children occupy one or more contiguous cells, as defined
     78  * by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and
     79  * {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters.
     80  * Each spec defines the set of rows or columns that are to be
     81  * occupied; and how children should be aligned within the resulting group of cells.
     82  * Although cells do not normally overlap in a GridLayout, GridLayout does
     83  * not prevent children being defined to occupy the same cell or group of cells.
     84  * In this case however, there is no guarantee that children will not themselves
     85  * overlap after the layout operation completes.
     86  *
     87  * <h4>Default Cell Assignment</h4>
     88  *
     89  * If a child does not specify the row and column indices of the cell it
     90  * wishes to occupy, GridLayout assigns cell locations automatically using its:
     91  * {@link GridLayout#setOrientation(int) orientation},
     92  * {@link GridLayout#setRowCount(int) rowCount} and
     93  * {@link GridLayout#setColumnCount(int) columnCount} properties.
     94  *
     95  * <h4>Space</h4>
     96  *
     97  * Space between children may be specified either by using instances of the
     98  * dedicated {@link Space} view or by setting the
     99  *
    100  * {@link ViewGroup.MarginLayoutParams#leftMargin leftMargin},
    101  * {@link ViewGroup.MarginLayoutParams#topMargin topMargin},
    102  * {@link ViewGroup.MarginLayoutParams#rightMargin rightMargin} and
    103  * {@link ViewGroup.MarginLayoutParams#bottomMargin bottomMargin}
    104  *
    105  * layout parameters. When the
    106  * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins}
    107  * property is set, default margins around children are automatically
    108  * allocated based on the prevailing UI style guide for the platform.
    109  * Each of the margins so defined may be independently overridden by an assignment
    110  * to the appropriate layout parameter.
    111  * Default values will generally produce a reasonable spacing between components
    112  * but values may change between different releases of the platform.
    113  *
    114  * <h4>Excess Space Distribution</h4>
    115  *
    116  * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight.
    117  * In the event that no weights are specified, the previous conventions are respected and
    118  * columns and rows are taken as flexible if their views specify some form of alignment
    119  * within their groups.
    120  * <p>
    121  * The flexibility of a view is therefore influenced by its alignment which is,
    122  * in turn, typically defined by setting the
    123  * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
    124  * If either a weight or alignment were defined along a given axis then the component
    125  * is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
    126  * the component is instead assumed to be <em>inflexible</em>.
    127  * <p>
    128  * Multiple components in the same row or column group are
    129  * considered to act in <em>parallel</em>. Such a
    130  * group is flexible only if <em>all</em> of the components
    131  * within it are flexible. Row and column groups that sit either side of a common boundary
    132  * are instead considered to act in <em>series</em>. The composite group made of these two
    133  * elements is flexible if <em>one</em> of its elements is flexible.
    134  * <p>
    135  * To make a column stretch, make sure all of the components inside it define a
    136  * weight or a gravity. To prevent a column from stretching, ensure that one of the components
    137  * in the column does not define a weight or a gravity.
    138  * <p>
    139  * When the principle of flexibility does not provide complete disambiguation,
    140  * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
    141  * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
    142  * parameters as a constraint in the a set of variables that define the grid-lines along a
    143  * given axis. During layout, GridLayout solves the constraints so as to return the unique
    144  * solution to those constraints for which all variables are less-than-or-equal-to
    145  * the corresponding value in any other valid solution.
    146  *
    147  * <h4>Interpretation of GONE</h4>
    148  *
    149  * For layout purposes, GridLayout treats views whose visibility status is
    150  * {@link View#GONE GONE}, as having zero width and height. This is subtly different from
    151  * the policy of ignoring views that are marked as GONE outright. If, for example, a gone-marked
    152  * view was alone in a column, that column would itself collapse to zero width if and only if
    153  * no gravity was defined on the view. If gravity was defined, then the gone-marked
    154  * view has no effect on the layout and the container should be laid out as if the view
    155  * had never been added to it. GONE views are taken to have zero weight during excess space
    156  * distribution.
    157  * <p>
    158  * These statements apply equally to rows as well as columns, and to groups of rows or columns.
    159  *
    160  * <p>
    161  * See {@link GridLayout.LayoutParams} for a full description of the
    162  * layout parameters used by GridLayout.
    163  *
    164  * @attr ref android.R.styleable#GridLayout_orientation
    165  * @attr ref android.R.styleable#GridLayout_rowCount
    166  * @attr ref android.R.styleable#GridLayout_columnCount
    167  * @attr ref android.R.styleable#GridLayout_useDefaultMargins
    168  * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
    169  * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
    170  */
    171 @RemoteView
    172 public class GridLayout extends ViewGroup {
    173 
    174     // Public constants
    175 
    176     /** @hide */
    177     @IntDef(prefix = { "HORIZONTAL", "VERTICAL" }, value = {
    178             HORIZONTAL,
    179             VERTICAL
    180     })
    181     @Retention(RetentionPolicy.SOURCE)
    182     public @interface Orientation {}
    183 
    184     /**
    185      * The horizontal orientation.
    186      */
    187     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    188 
    189     /**
    190      * The vertical orientation.
    191      */
    192     public static final int VERTICAL = LinearLayout.VERTICAL;
    193 
    194     /**
    195      * The constant used to indicate that a value is undefined.
    196      * Fields can use this value to indicate that their values
    197      * have not yet been set. Similarly, methods can return this value
    198      * to indicate that there is no suitable value that the implementation
    199      * can return.
    200      * The value used for the constant (currently {@link Integer#MIN_VALUE}) is
    201      * intended to avoid confusion between valid values whose sign may not be known.
    202      */
    203     public static final int UNDEFINED = Integer.MIN_VALUE;
    204 
    205     /** @hide */
    206     @IntDef(prefix = { "ALIGN_" }, value = {
    207             ALIGN_BOUNDS,
    208             ALIGN_MARGINS
    209     })
    210     @Retention(RetentionPolicy.SOURCE)
    211     public @interface AlignmentMode {}
    212 
    213     /**
    214      * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
    215      * When the {@code alignmentMode} is set to {@link #ALIGN_BOUNDS}, alignment
    216      * is made between the edges of each component's raw
    217      * view boundary: i.e. the area delimited by the component's:
    218      * {@link android.view.View#getTop() top},
    219      * {@link android.view.View#getLeft() left},
    220      * {@link android.view.View#getBottom() bottom} and
    221      * {@link android.view.View#getRight() right} properties.
    222      * <p>
    223      * For example, when {@code GridLayout} is in {@link #ALIGN_BOUNDS} mode,
    224      * children that belong to a row group that uses {@link #TOP} alignment will
    225      * all return the same value when their {@link android.view.View#getTop()}
    226      * method is called.
    227      *
    228      * @see #setAlignmentMode(int)
    229      */
    230     public static final int ALIGN_BOUNDS = 0;
    231 
    232     /**
    233      * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
    234      * When the {@code alignmentMode} is set to {@link #ALIGN_MARGINS},
    235      * the bounds of each view are extended outwards, according
    236      * to their margins, before the edges of the resulting rectangle are aligned.
    237      * <p>
    238      * For example, when {@code GridLayout} is in {@link #ALIGN_MARGINS} mode,
    239      * the quantity {@code top - layoutParams.topMargin} is the same for all children that
    240      * belong to a row group that uses {@link #TOP} alignment.
    241      *
    242      * @see #setAlignmentMode(int)
    243      */
    244     public static final int ALIGN_MARGINS = 1;
    245 
    246     // Misc constants
    247 
    248     static final int MAX_SIZE = 100000;
    249     static final int DEFAULT_CONTAINER_MARGIN = 0;
    250     static final int UNINITIALIZED_HASH = 0;
    251     static final Printer LOG_PRINTER = new LogPrinter(Log.DEBUG, GridLayout.class.getName());
    252     static final Printer NO_PRINTER = new Printer() {
    253         @Override
    254         public void println(String x) {
    255         }
    256     };
    257 
    258     // Defaults
    259 
    260     private static final int DEFAULT_ORIENTATION = HORIZONTAL;
    261     private static final int DEFAULT_COUNT = UNDEFINED;
    262     private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
    263     private static final boolean DEFAULT_ORDER_PRESERVED = true;
    264     private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
    265 
    266     // TypedArray indices
    267 
    268     private static final int ORIENTATION = R.styleable.GridLayout_orientation;
    269     private static final int ROW_COUNT = R.styleable.GridLayout_rowCount;
    270     private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount;
    271     private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins;
    272     private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode;
    273     private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved;
    274     private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved;
    275 
    276     // Instance variables
    277 
    278     final Axis mHorizontalAxis = new Axis(true);
    279     final Axis mVerticalAxis = new Axis(false);
    280     int mOrientation = DEFAULT_ORIENTATION;
    281     boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
    282     int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
    283     int mDefaultGap;
    284     int mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
    285     Printer mPrinter = LOG_PRINTER;
    286 
    287     // Constructors
    288 
    289     public GridLayout(Context context) {
    290         this(context, null);
    291     }
    292 
    293     public GridLayout(Context context, AttributeSet attrs) {
    294         this(context, attrs, 0);
    295     }
    296 
    297     public GridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    298         this(context, attrs, defStyleAttr, 0);
    299     }
    300 
    301     public GridLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    302         super(context, attrs, defStyleAttr, defStyleRes);
    303         mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
    304         final TypedArray a = context.obtainStyledAttributes(
    305                 attrs, R.styleable.GridLayout, defStyleAttr, defStyleRes);
    306         saveAttributeDataForStyleable(context, R.styleable.GridLayout,
    307                 attrs, a, defStyleAttr, defStyleRes);
    308         try {
    309             setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
    310             setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT));
    311             setOrientation(a.getInt(ORIENTATION, DEFAULT_ORIENTATION));
    312             setUseDefaultMargins(a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS));
    313             setAlignmentMode(a.getInt(ALIGNMENT_MODE, DEFAULT_ALIGNMENT_MODE));
    314             setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
    315             setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
    316         } finally {
    317             a.recycle();
    318         }
    319     }
    320 
    321     // Implementation
    322 
    323     /**
    324      * Returns the current orientation.
    325      *
    326      * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
    327      *
    328      * @see #setOrientation(int)
    329      *
    330      * @attr ref android.R.styleable#GridLayout_orientation
    331      */
    332     @Orientation
    333     @InspectableProperty(enumMapping = {
    334             @InspectableProperty.EnumEntry(value = HORIZONTAL, name = "horizontal"),
    335             @InspectableProperty.EnumEntry(value = VERTICAL, name = "vertical")
    336     })
    337     public int getOrientation() {
    338         return mOrientation;
    339     }
    340 
    341     /**
    342      *
    343      * GridLayout uses the orientation property for two purposes:
    344      * <ul>
    345      *  <li>
    346      *      To control the 'direction' in which default row/column indices are generated
    347      *      when they are not specified in a component's layout parameters.
    348      *  </li>
    349      *  <li>
    350      *      To control which axis should be processed first during the layout operation:
    351      *      when orientation is {@link #HORIZONTAL} the horizontal axis is laid out first.
    352      *  </li>
    353      * </ul>
    354      *
    355      * The order in which axes are laid out is important if, for example, the height of
    356      * one of GridLayout's children is dependent on its width - and its width is, in turn,
    357      * dependent on the widths of other components.
    358      * <p>
    359      * If your layout contains a {@link TextView} (or derivative:
    360      * {@code Button}, {@code EditText}, {@code CheckBox}, etc.) which is
    361      * in multi-line mode (the default) it is normally best to leave GridLayout's
    362      * orientation as {@code HORIZONTAL} - because {@code TextView} is capable of
    363      * deriving its height for a given width, but not the other way around.
    364      * <p>
    365      * Other than the effects above, orientation does not affect the actual layout operation of
    366      * GridLayout, so it's fine to leave GridLayout in {@code HORIZONTAL} mode even if
    367      * the height of the intended layout greatly exceeds its width.
    368      * <p>
    369      * The default value of this property is {@link #HORIZONTAL}.
    370      *
    371      * @param orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
    372      *
    373      * @see #getOrientation()
    374      *
    375      * @attr ref android.R.styleable#GridLayout_orientation
    376      */
    377     public void setOrientation(@Orientation int orientation) {
    378         if (this.mOrientation != orientation) {
    379             this.mOrientation = orientation;
    380             invalidateStructure();
    381             requestLayout();
    382         }
    383     }
    384 
    385     /**
    386      * Returns the current number of rows. This is either the last value that was set
    387      * with {@link #setRowCount(int)} or, if no such value was set, the maximum
    388      * value of each the upper bounds defined in {@link LayoutParams#rowSpec}.
    389      *
    390      * @return the current number of rows
    391      *
    392      * @see #setRowCount(int)
    393      * @see LayoutParams#rowSpec
    394      *
    395      * @attr ref android.R.styleable#GridLayout_rowCount
    396      */
    397     @InspectableProperty
    398     public int getRowCount() {
    399         return mVerticalAxis.getCount();
    400     }
    401 
    402     /**
    403      * RowCount is used only to generate default row/column indices when
    404      * they are not specified by a component's layout parameters.
    405      *
    406      * @param rowCount the number of rows
    407      *
    408      * @see #getRowCount()
    409      * @see LayoutParams#rowSpec
    410      *
    411      * @attr ref android.R.styleable#GridLayout_rowCount
    412      */
    413     public void setRowCount(int rowCount) {
    414         mVerticalAxis.setCount(rowCount);
    415         invalidateStructure();
    416         requestLayout();
    417     }
    418 
    419     /**
    420      * Returns the current number of columns. This is either the last value that was set
    421      * with {@link #setColumnCount(int)} or, if no such value was set, the maximum
    422      * value of each the upper bounds defined in {@link LayoutParams#columnSpec}.
    423      *
    424      * @return the current number of columns
    425      *
    426      * @see #setColumnCount(int)
    427      * @see LayoutParams#columnSpec
    428      *
    429      * @attr ref android.R.styleable#GridLayout_columnCount
    430      */
    431     @InspectableProperty
    432     public int getColumnCount() {
    433         return mHorizontalAxis.getCount();
    434     }
    435 
    436     /**
    437      * ColumnCount is used only to generate default column/column indices when
    438      * they are not specified by a component's layout parameters.
    439      *
    440      * @param columnCount the number of columns.
    441      *
    442      * @see #getColumnCount()
    443      * @see LayoutParams#columnSpec
    444      *
    445      * @attr ref android.R.styleable#GridLayout_columnCount
    446      */
    447     public void setColumnCount(int columnCount) {
    448         mHorizontalAxis.setCount(columnCount);
    449         invalidateStructure();
    450         requestLayout();
    451     }
    452 
    453     /**
    454      * Returns whether or not this GridLayout will allocate default margins when no
    455      * corresponding layout parameters are defined.
    456      *
    457      * @return {@code true} if default margins should be allocated
    458      *
    459      * @see #setUseDefaultMargins(boolean)
    460      *
    461      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
    462      */
    463     @InspectableProperty
    464     public boolean getUseDefaultMargins() {
    465         return mUseDefaultMargins;
    466     }
    467 
    468     /**
    469      * When {@code true}, GridLayout allocates default margins around children
    470      * based on the child's visual characteristics. Each of the
    471      * margins so defined may be independently overridden by an assignment
    472      * to the appropriate layout parameter.
    473      * <p>
    474      * When {@code false}, the default value of all margins is zero.
    475      * <p>
    476      * When setting to {@code true}, consider setting the value of the
    477      * {@link #setAlignmentMode(int) alignmentMode}
    478      * property to {@link #ALIGN_BOUNDS}.
    479      * <p>
    480      * The default value of this property is {@code false}.
    481      *
    482      * @param useDefaultMargins use {@code true} to make GridLayout allocate default margins
    483      *
    484      * @see #getUseDefaultMargins()
    485      * @see #setAlignmentMode(int)
    486      *
    487      * @see ViewGroup.MarginLayoutParams#leftMargin
    488      * @see ViewGroup.MarginLayoutParams#topMargin
    489      * @see ViewGroup.MarginLayoutParams#rightMargin
    490      * @see ViewGroup.MarginLayoutParams#bottomMargin
    491      *
    492      * @attr ref android.R.styleable#GridLayout_useDefaultMargins
    493      */
    494     public void setUseDefaultMargins(boolean useDefaultMargins) {
    495         this.mUseDefaultMargins = useDefaultMargins;
    496         requestLayout();
    497     }
    498 
    499     /**
    500      * Returns the alignment mode.
    501      *
    502      * @return the alignment mode; either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
    503      *
    504      * @see #ALIGN_BOUNDS
    505      * @see #ALIGN_MARGINS
    506      *
    507      * @see #setAlignmentMode(int)
    508      *
    509      * @attr ref android.R.styleable#GridLayout_alignmentMode
    510      */
    511     @AlignmentMode
    512     @InspectableProperty(enumMapping = {
    513             @InspectableProperty.EnumEntry(value = ALIGN_BOUNDS, name = "alignBounds"),
    514             @InspectableProperty.EnumEntry(value = ALIGN_MARGINS, name = "alignMargins"),
    515     })
    516     public int getAlignmentMode() {
    517         return mAlignmentMode;
    518     }
    519 
    520     /**
    521      * Sets the alignment mode to be used for all of the alignments between the
    522      * children of this container.
    523      * <p>
    524      * The default value of this property is {@link #ALIGN_MARGINS}.
    525      *
    526      * @param alignmentMode either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
    527      *
    528      * @see #ALIGN_BOUNDS
    529      * @see #ALIGN_MARGINS
    530      *
    531      * @see #getAlignmentMode()
    532      *
    533      * @attr ref android.R.styleable#GridLayout_alignmentMode
    534      */
    535     public void setAlignmentMode(@AlignmentMode int alignmentMode) {
    536         this.mAlignmentMode = alignmentMode;
    537         requestLayout();
    538     }
    539 
    540     /**
    541      * Returns whether or not row boundaries are ordered by their grid indices.
    542      *
    543      * @return {@code true} if row boundaries must appear in the order of their indices,
    544      *         {@code false} otherwise
    545      *
    546      * @see #setRowOrderPreserved(boolean)
    547      *
    548      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
    549      */
    550     @InspectableProperty
    551     public boolean isRowOrderPreserved() {
    552         return mVerticalAxis.isOrderPreserved();
    553     }
    554 
    555     /**
    556      * When this property is {@code true}, GridLayout is forced to place the row boundaries
    557      * so that their associated grid indices are in ascending order in the view.
    558      * <p>
    559      * When this property is {@code false} GridLayout is at liberty to place the vertical row
    560      * boundaries in whatever order best fits the given constraints.
    561      * <p>
    562      * The default value of this property is {@code true}.
    563 
    564      * @param rowOrderPreserved {@code true} to force GridLayout to respect the order
    565      *        of row boundaries
    566      *
    567      * @see #isRowOrderPreserved()
    568      *
    569      * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
    570      */
    571     public void setRowOrderPreserved(boolean rowOrderPreserved) {
    572         mVerticalAxis.setOrderPreserved(rowOrderPreserved);
    573         invalidateStructure();
    574         requestLayout();
    575     }
    576 
    577     /**
    578      * Returns whether or not column boundaries are ordered by their grid indices.
    579      *
    580      * @return {@code true} if column boundaries must appear in the order of their indices,
    581      *         {@code false} otherwise
    582      *
    583      * @see #setColumnOrderPreserved(boolean)
    584      *
    585      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
    586      */
    587     @InspectableProperty
    588     public boolean isColumnOrderPreserved() {
    589         return mHorizontalAxis.isOrderPreserved();
    590     }
    591 
    592     /**
    593      * When this property is {@code true}, GridLayout is forced to place the column boundaries
    594      * so that their associated grid indices are in ascending order in the view.
    595      * <p>
    596      * When this property is {@code false} GridLayout is at liberty to place the horizontal column
    597      * boundaries in whatever order best fits the given constraints.
    598      * <p>
    599      * The default value of this property is {@code true}.
    600      *
    601      * @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
    602      *        of column boundaries.
    603      *
    604      * @see #isColumnOrderPreserved()
    605      *
    606      * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
    607      */
    608     public void setColumnOrderPreserved(boolean columnOrderPreserved) {
    609         mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
    610         invalidateStructure();
    611         requestLayout();
    612     }
    613 
    614     /**
    615      * Return the printer that will log diagnostics from this layout.
    616      *
    617      * @see #setPrinter(android.util.Printer)
    618      *
    619      * @return the printer associated with this view
    620      *
    621      * @hide
    622      */
    623     public Printer getPrinter() {
    624         return mPrinter;
    625     }
    626 
    627     /**
    628      * Set the printer that will log diagnostics from this layout.
    629      * The default value is created by {@link android.util.LogPrinter}.
    630      *
    631      * @param printer the printer associated with this layout
    632      *
    633      * @see #getPrinter()
    634      *
    635      * @hide
    636      */
    637     public void setPrinter(Printer printer) {
    638         this.mPrinter = (printer == null) ? NO_PRINTER : printer;
    639     }
    640 
    641     // Static utility methods
    642 
    643     static int max2(int[] a, int valueIfEmpty) {
    644         int result = valueIfEmpty;
    645         for (int i = 0, N = a.length; i < N; i++) {
    646             result = Math.max(result, a[i]);
    647         }
    648         return result;
    649     }
    650 
    651     @SuppressWarnings("unchecked")
    652     static <T> T[] append(T[] a, T[] b) {
    653         T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
    654         System.arraycopy(a, 0, result, 0, a.length);
    655         System.arraycopy(b, 0, result, a.length, b.length);
    656         return result;
    657     }
    658 
    659     static Alignment getAlignment(int gravity, boolean horizontal) {
    660         int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
    661         int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
    662         int flags = (gravity & mask) >> shift;
    663         switch (flags) {
    664             case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
    665                 return horizontal ? LEFT : TOP;
    666             case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
    667                 return horizontal ? RIGHT : BOTTOM;
    668             case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
    669                 return FILL;
    670             case AXIS_SPECIFIED:
    671                 return CENTER;
    672             case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | RELATIVE_LAYOUT_DIRECTION):
    673                 return START;
    674             case (AXIS_SPECIFIED | AXIS_PULL_AFTER | RELATIVE_LAYOUT_DIRECTION):
    675                 return END;
    676             default:
    677                 return UNDEFINED_ALIGNMENT;
    678         }
    679     }
    680 
    681     /** @noinspection UnusedParameters*/
    682     private int getDefaultMargin(View c, boolean horizontal, boolean leading) {
    683         if (c.getClass() == Space.class) {
    684             return 0;
    685         }
    686         return mDefaultGap / 2;
    687     }
    688 
    689     private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
    690         return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading);
    691     }
    692 
    693     private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) {
    694         if (!mUseDefaultMargins) {
    695             return 0;
    696         }
    697         Spec spec = horizontal ? p.columnSpec : p.rowSpec;
    698         Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
    699         Interval span = spec.span;
    700         boolean leading1 = (horizontal && isLayoutRtl()) ? !leading : leading;
    701         boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
    702 
    703         return getDefaultMargin(c, isAtEdge, horizontal, leading);
    704     }
    705 
    706     int getMargin1(View view, boolean horizontal, boolean leading) {
    707         LayoutParams lp = getLayoutParams(view);
    708         int margin = horizontal ?
    709                 (leading ? lp.leftMargin : lp.rightMargin) :
    710                 (leading ? lp.topMargin : lp.bottomMargin);
    711         return margin == UNDEFINED ? getDefaultMargin(view, lp, horizontal, leading) : margin;
    712     }
    713 
    714     private int getMargin(View view, boolean horizontal, boolean leading) {
    715         if (mAlignmentMode == ALIGN_MARGINS) {
    716             return getMargin1(view, horizontal, leading);
    717         } else {
    718             Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
    719             int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
    720             LayoutParams lp = getLayoutParams(view);
    721             Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
    722             int index = leading ? spec.span.min : spec.span.max;
    723             return margins[index];
    724         }
    725     }
    726 
    727     private int getTotalMargin(View child, boolean horizontal) {
    728         return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
    729     }
    730 
    731     private static boolean fits(int[] a, int value, int start, int end) {
    732         if (end > a.length) {
    733             return false;
    734         }
    735         for (int i = start; i < end; i++) {
    736             if (a[i] > value) {
    737                 return false;
    738             }
    739         }
    740         return true;
    741     }
    742 
    743     private static void procrusteanFill(int[] a, int start, int end, int value) {
    744         int length = a.length;
    745         Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
    746     }
    747 
    748     private static void setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan) {
    749         lp.setRowSpecSpan(new Interval(row, row + rowSpan));
    750         lp.setColumnSpecSpan(new Interval(col, col + colSpan));
    751     }
    752 
    753     // Logic to avert infinite loops by ensuring that the cells can be placed somewhere.
    754     private static int clip(Interval minorRange, boolean minorWasDefined, int count) {
    755         int size = minorRange.size();
    756         if (count == 0) {
    757             return size;
    758         }
    759         int min = minorWasDefined ? min(minorRange.min, count) : 0;
    760         return min(size, count - min);
    761     }
    762 
    763     // install default indices for cells that don't define them
    764     private void validateLayoutParams() {
    765         final boolean horizontal = (mOrientation == HORIZONTAL);
    766         final Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
    767         final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
    768 
    769         int major = 0;
    770         int minor = 0;
    771         int[] maxSizes = new int[count];
    772 
    773         for (int i = 0, N = getChildCount(); i < N; i++) {
    774             LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
    775 
    776             final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
    777             final Interval majorRange = majorSpec.span;
    778             final boolean majorWasDefined = majorSpec.startDefined;
    779             final int majorSpan = majorRange.size();
    780             if (majorWasDefined) {
    781                 major = majorRange.min;
    782             }
    783 
    784             final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
    785             final Interval minorRange = minorSpec.span;
    786             final boolean minorWasDefined = minorSpec.startDefined;
    787             final int minorSpan = clip(minorRange, minorWasDefined, count);
    788             if (minorWasDefined) {
    789                 minor = minorRange.min;
    790             }
    791 
    792             if (count != 0) {
    793                 // Find suitable row/col values when at least one is undefined.
    794                 if (!majorWasDefined || !minorWasDefined) {
    795                     while (!fits(maxSizes, major, minor, minor + minorSpan)) {
    796                         if (minorWasDefined) {
    797                             major++;
    798                         } else {
    799                             if (minor + minorSpan <= count) {
    800                                 minor++;
    801                             } else {
    802                                 minor = 0;
    803                                 major++;
    804                             }
    805                         }
    806                     }
    807                 }
    808                 procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
    809             }
    810 
    811             if (horizontal) {
    812                 setCellGroup(lp, major, majorSpan, minor, minorSpan);
    813             } else {
    814                 setCellGroup(lp, minor, minorSpan, major, majorSpan);
    815             }
    816 
    817             minor = minor + minorSpan;
    818         }
    819     }
    820 
    821     private void invalidateStructure() {
    822         mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
    823         mHorizontalAxis.invalidateStructure();
    824         mVerticalAxis.invalidateStructure();
    825         // This can end up being done twice. Better twice than not at all.
    826         invalidateValues();
    827     }
    828 
    829     private void invalidateValues() {
    830         // Need null check because requestLayout() is called in View's initializer,
    831         // before we are set up.
    832         if (mHorizontalAxis != null && mVerticalAxis != null) {
    833             mHorizontalAxis.invalidateValues();
    834             mVerticalAxis.invalidateValues();
    835         }
    836     }
    837 
    838     /** @hide */
    839     @Override
    840     protected void onSetLayoutParams(View child, ViewGroup.LayoutParams layoutParams) {
    841         super.onSetLayoutParams(child, layoutParams);
    842 
    843         if (!checkLayoutParams(layoutParams)) {
    844             handleInvalidParams("supplied LayoutParams are of the wrong type");
    845         }
    846 
    847         invalidateStructure();
    848     }
    849 
    850     final LayoutParams getLayoutParams(View c) {
    851         return (LayoutParams) c.getLayoutParams();
    852     }
    853 
    854     private static void handleInvalidParams(String msg) {
    855         throw new IllegalArgumentException(msg + ". ");
    856     }
    857 
    858     private void checkLayoutParams(LayoutParams lp, boolean horizontal) {
    859         String groupName = horizontal ? "column" : "row";
    860         Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
    861         Interval span = spec.span;
    862         if (span.min != UNDEFINED && span.min < 0) {
    863             handleInvalidParams(groupName + " indices must be positive");
    864         }
    865         Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
    866         int count = axis.definedCount;
    867         if (count != UNDEFINED) {
    868             if (span.max > count) {
    869                 handleInvalidParams(groupName +
    870                         " indices (start + span) mustn't exceed the " + groupName + " count");
    871             }
    872             if (span.size() > count) {
    873                 handleInvalidParams(groupName + " span mustn't exceed the " + groupName + " count");
    874             }
    875         }
    876     }
    877 
    878     @Override
    879     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    880         if (!(p instanceof LayoutParams)) {
    881             return false;
    882         }
    883         LayoutParams lp = (LayoutParams) p;
    884 
    885         checkLayoutParams(lp, true);
    886         checkLayoutParams(lp, false);
    887 
    888         return true;
    889     }
    890 
    891     @Override
    892     protected LayoutParams generateDefaultLayoutParams() {
    893         return new LayoutParams();
    894     }
    895 
    896     @Override
    897     public LayoutParams generateLayoutParams(AttributeSet attrs) {
    898         return new LayoutParams(getContext(), attrs);
    899     }
    900 
    901     @Override
    902     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
    903         if (sPreserveMarginParamsInLayoutParamConversion) {
    904             if (lp instanceof LayoutParams) {
    905                 return new LayoutParams((LayoutParams) lp);
    906             } else if (lp instanceof MarginLayoutParams) {
    907                 return new LayoutParams((MarginLayoutParams) lp);
    908             }
    909         }
    910         return new LayoutParams(lp);
    911     }
    912 
    913     // Draw grid
    914 
    915     private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
    916         if (isLayoutRtl()) {
    917             int width = getWidth();
    918             graphics.drawLine(width - x1, y1, width - x2, y2, paint);
    919         } else {
    920             graphics.drawLine(x1, y1, x2, y2, paint);
    921         }
    922     }
    923 
    924     /**
    925      * @hide
    926      */
    927     @Override
    928     protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
    929         // Apply defaults, so as to remove UNDEFINED values
    930         LayoutParams lp = new LayoutParams();
    931         for (int i = 0; i < getChildCount(); i++) {
    932             View c = getChildAt(i);
    933             lp.setMargins(
    934                     getMargin1(c, true, true),
    935                     getMargin1(c, false, true),
    936                     getMargin1(c, true, false),
    937                     getMargin1(c, false, false));
    938             lp.onDebugDraw(c, canvas, paint);
    939         }
    940     }
    941 
    942     /**
    943      * @hide
    944      */
    945     @Override
    946     protected void onDebugDraw(Canvas canvas) {
    947         Paint paint = new Paint();
    948         paint.setStyle(Paint.Style.STROKE);
    949         paint.setColor(Color.argb(50, 255, 255, 255));
    950 
    951         Insets insets = getOpticalInsets();
    952 
    953         int top    =               getPaddingTop()    + insets.top;
    954         int left   =               getPaddingLeft()   + insets.left;
    955         int right  = getWidth()  - getPaddingRight()  - insets.right;
    956         int bottom = getHeight() - getPaddingBottom() - insets.bottom;
    957 
    958         int[] xs = mHorizontalAxis.locations;
    959         if (xs != null) {
    960             for (int i = 0, length = xs.length; i < length; i++) {
    961                 int x = left + xs[i];
    962                 drawLine(canvas, x, top, x, bottom, paint);
    963             }
    964         }
    965 
    966         int[] ys = mVerticalAxis.locations;
    967         if (ys != null) {
    968             for (int i = 0, length = ys.length; i < length; i++) {
    969                 int y = top + ys[i];
    970                 drawLine(canvas, left, y, right, y, paint);
    971             }
    972         }
    973 
    974         super.onDebugDraw(canvas);
    975     }
    976 
    977     @Override
    978     public void onViewAdded(View child) {
    979         super.onViewAdded(child);
    980         invalidateStructure();
    981     }
    982 
    983     @Override
    984     public void onViewRemoved(View child) {
    985         super.onViewRemoved(child);
    986         invalidateStructure();
    987     }
    988 
    989     /**
    990      * We need to call invalidateStructure() when a child's GONE flag changes state.
    991      * This implementation is a catch-all, invalidating on any change in the visibility flags.
    992      *
    993      * @hide
    994      */
    995     @Override
    996     protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
    997         super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
    998         if (oldVisibility == GONE || newVisibility == GONE) {
    999         invalidateStructure();
   1000         }
   1001     }
   1002 
   1003     private int computeLayoutParamsHashCode() {
   1004         int result = 1;
   1005         for (int i = 0, N = getChildCount(); i < N; i++) {
   1006             View c = getChildAt(i);
   1007             if (c.getVisibility() == View.GONE) continue;
   1008             LayoutParams lp = (LayoutParams) c.getLayoutParams();
   1009             result = 31 * result + lp.hashCode();
   1010         }
   1011         return result;
   1012     }
   1013 
   1014     private void consistencyCheck() {
   1015         if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
   1016             validateLayoutParams();
   1017             mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
   1018         } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
   1019             mPrinter.println("The fields of some layout parameters were modified in between "
   1020                     + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
   1021             invalidateStructure();
   1022             consistencyCheck();
   1023         }
   1024     }
   1025 
   1026     // Measurement
   1027 
   1028     // Note: padding has already been removed from the supplied specs
   1029     private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
   1030             int childWidth, int childHeight) {
   1031         int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
   1032                 getTotalMargin(child, true), childWidth);
   1033         int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
   1034                 getTotalMargin(child, false), childHeight);
   1035         child.measure(childWidthSpec, childHeightSpec);
   1036     }
   1037 
   1038     // Note: padding has already been removed from the supplied specs
   1039     private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
   1040         for (int i = 0, N = getChildCount(); i < N; i++) {
   1041             View c = getChildAt(i);
   1042             if (c.getVisibility() == View.GONE) continue;
   1043             LayoutParams lp = getLayoutParams(c);
   1044             if (firstPass) {
   1045                 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
   1046             } else {
   1047                 boolean horizontal = (mOrientation == HORIZONTAL);
   1048                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
   1049                 if (spec.getAbsoluteAlignment(horizontal) == FILL) {
   1050                     Interval span = spec.span;
   1051                     Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
   1052                     int[] locations = axis.getLocations();
   1053                     int cellSize = locations[span.max] - locations[span.min];
   1054                     int viewSize = cellSize - getTotalMargin(c, horizontal);
   1055                     if (horizontal) {
   1056                         measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
   1057                     } else {
   1058                         measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
   1059                     }
   1060                 }
   1061             }
   1062         }
   1063     }
   1064 
   1065     static int adjust(int measureSpec, int delta) {
   1066         return makeMeasureSpec(
   1067                 MeasureSpec.getSize(measureSpec + delta),  MeasureSpec.getMode(measureSpec));
   1068     }
   1069 
   1070     @Override
   1071     protected void onMeasure(int widthSpec, int heightSpec) {
   1072         consistencyCheck();
   1073 
   1074         /** If we have been called by {@link View#measure(int, int)}, one of width or height
   1075          *  is  likely to have changed. We must invalidate if so. */
   1076         invalidateValues();
   1077 
   1078         int hPadding = getPaddingLeft() + getPaddingRight();
   1079         int vPadding = getPaddingTop()  + getPaddingBottom();
   1080 
   1081         int widthSpecSansPadding =  adjust( widthSpec, -hPadding);
   1082         int heightSpecSansPadding = adjust(heightSpec, -vPadding);
   1083 
   1084         measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, true);
   1085 
   1086         int widthSansPadding;
   1087         int heightSansPadding;
   1088 
   1089         // Use the orientation property to decide which axis should be laid out first.
   1090         if (mOrientation == HORIZONTAL) {
   1091             widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
   1092             measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
   1093             heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
   1094         } else {
   1095             heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
   1096             measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
   1097             widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
   1098         }
   1099 
   1100         int measuredWidth  = Math.max(widthSansPadding  + hPadding, getSuggestedMinimumWidth());
   1101         int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());
   1102 
   1103         setMeasuredDimension(
   1104                 resolveSizeAndState(measuredWidth,   widthSpec, 0),
   1105                 resolveSizeAndState(measuredHeight, heightSpec, 0));
   1106     }
   1107 
   1108     private int getMeasurement(View c, boolean horizontal) {
   1109         return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
   1110     }
   1111 
   1112     final int getMeasurementIncludingMargin(View c, boolean horizontal) {
   1113         if (c.getVisibility() == View.GONE) {
   1114             return 0;
   1115         }
   1116         return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
   1117     }
   1118 
   1119     @Override
   1120     public void requestLayout() {
   1121         super.requestLayout();
   1122         invalidateValues();
   1123     }
   1124 
   1125     // Layout container
   1126 
   1127     /**
   1128      * {@inheritDoc}
   1129      */
   1130     /*
   1131      The layout operation is implemented by delegating the heavy lifting to the
   1132      to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
   1133      Together they compute the locations of the vertical and horizontal lines of
   1134      the grid (respectively!).
   1135 
   1136      This method is then left with the simpler task of applying margins, gravity
   1137      and sizing to each child view and then placing it in its cell.
   1138      */
   1139     @Override
   1140     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
   1141         consistencyCheck();
   1142 
   1143         int targetWidth = right - left;
   1144         int targetHeight = bottom - top;
   1145 
   1146         int paddingLeft = getPaddingLeft();
   1147         int paddingTop = getPaddingTop();
   1148         int paddingRight = getPaddingRight();
   1149         int paddingBottom = getPaddingBottom();
   1150 
   1151         mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
   1152         mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
   1153 
   1154         int[] hLocations = mHorizontalAxis.getLocations();
   1155         int[] vLocations = mVerticalAxis.getLocations();
   1156 
   1157         for (int i = 0, N = getChildCount(); i < N; i++) {
   1158             View c = getChildAt(i);
   1159             if (c.getVisibility() == View.GONE) continue;
   1160             LayoutParams lp = getLayoutParams(c);
   1161             Spec columnSpec = lp.columnSpec;
   1162             Spec rowSpec = lp.rowSpec;
   1163 
   1164             Interval colSpan = columnSpec.span;
   1165             Interval rowSpan = rowSpec.span;
   1166 
   1167             int x1 = hLocations[colSpan.min];
   1168             int y1 = vLocations[rowSpan.min];
   1169 
   1170             int x2 = hLocations[colSpan.max];
   1171             int y2 = vLocations[rowSpan.max];
   1172 
   1173             int cellWidth = x2 - x1;
   1174             int cellHeight = y2 - y1;
   1175 
   1176             int pWidth = getMeasurement(c, true);
   1177             int pHeight = getMeasurement(c, false);
   1178 
   1179             Alignment hAlign = columnSpec.getAbsoluteAlignment(true);
   1180             Alignment vAlign = rowSpec.getAbsoluteAlignment(false);
   1181 
   1182             Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
   1183             Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
   1184 
   1185             // Gravity offsets: the location of the alignment group relative to its cell group.
   1186             int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
   1187             int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
   1188 
   1189             int leftMargin = getMargin(c, true, true);
   1190             int topMargin = getMargin(c, false, true);
   1191             int rightMargin = getMargin(c, true, false);
   1192             int bottomMargin = getMargin(c, false, false);
   1193 
   1194             int sumMarginsX = leftMargin + rightMargin;
   1195             int sumMarginsY = topMargin + bottomMargin;
   1196 
   1197             // Alignment offsets: the location of the view relative to its alignment group.
   1198             int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
   1199             int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
   1200 
   1201             int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
   1202             int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
   1203 
   1204             int dx = x1 + gravityOffsetX + alignmentOffsetX;
   1205 
   1206             int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
   1207                     targetWidth - width - paddingRight - rightMargin - dx;
   1208             int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
   1209 
   1210             if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
   1211                 c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
   1212             }
   1213             c.layout(cx, cy, cx + width, cy + height);
   1214         }
   1215     }
   1216 
   1217     @Override
   1218     public CharSequence getAccessibilityClassName() {
   1219         return GridLayout.class.getName();
   1220     }
   1221 
   1222     // Inner classes
   1223 
   1224     /*
   1225      This internal class houses the algorithm for computing the locations of grid lines;
   1226      along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
   1227      distinguished by the "horizontal" flag which is true for the horizontal axis and false
   1228      for the vertical one.
   1229      */
   1230     final class Axis {
   1231         private static final int NEW = 0;
   1232         private static final int PENDING = 1;
   1233         private static final int COMPLETE = 2;
   1234 
   1235         public final boolean horizontal;
   1236 
   1237         public int definedCount = UNDEFINED;
   1238         private int maxIndex = UNDEFINED;
   1239 
   1240         PackedMap<Spec, Bounds> groupBounds;
   1241         public boolean groupBoundsValid = false;
   1242 
   1243         PackedMap<Interval, MutableInt> forwardLinks;
   1244         public boolean forwardLinksValid = false;
   1245 
   1246         PackedMap<Interval, MutableInt> backwardLinks;
   1247         public boolean backwardLinksValid = false;
   1248 
   1249         public int[] leadingMargins;
   1250         public boolean leadingMarginsValid = false;
   1251 
   1252         public int[] trailingMargins;
   1253         public boolean trailingMarginsValid = false;
   1254 
   1255         public Arc[] arcs;
   1256         public boolean arcsValid = false;
   1257 
   1258         public int[] locations;
   1259         public boolean locationsValid = false;
   1260 
   1261         public boolean hasWeights;
   1262         public boolean hasWeightsValid = false;
   1263         public int[] deltas;
   1264 
   1265         boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
   1266 
   1267         private MutableInt parentMin = new MutableInt(0);
   1268         private MutableInt parentMax = new MutableInt(-MAX_SIZE);
   1269 
   1270         private Axis(boolean horizontal) {
   1271             this.horizontal = horizontal;
   1272         }
   1273 
   1274         private int calculateMaxIndex() {
   1275             // the number Integer.MIN_VALUE + 1 comes up in undefined cells
   1276             int result = -1;
   1277             for (int i = 0, N = getChildCount(); i < N; i++) {
   1278                 View c = getChildAt(i);
   1279                 LayoutParams params = getLayoutParams(c);
   1280                 Spec spec = horizontal ? params.columnSpec : params.rowSpec;
   1281                 Interval span = spec.span;
   1282                 result = max(result, span.min);
   1283                 result = max(result, span.max);
   1284                 result = max(result, span.size());
   1285             }
   1286             return result == -1 ? UNDEFINED : result;
   1287         }
   1288 
   1289         private int getMaxIndex() {
   1290             if (maxIndex == UNDEFINED) {
   1291                 maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
   1292             }
   1293             return maxIndex;
   1294         }
   1295 
   1296         public int getCount() {
   1297             return max(definedCount, getMaxIndex());
   1298         }
   1299 
   1300         public void setCount(int count) {
   1301             if (count != UNDEFINED && count < getMaxIndex()) {
   1302                 handleInvalidParams((horizontal ? "column" : "row") +
   1303                         "Count must be greater than or equal to the maximum of all grid indices " +
   1304                         "(and spans) defined in the LayoutParams of each child");
   1305             }
   1306             this.definedCount = count;
   1307         }
   1308 
   1309         public boolean isOrderPreserved() {
   1310             return orderPreserved;
   1311         }
   1312 
   1313         public void setOrderPreserved(boolean orderPreserved) {
   1314             this.orderPreserved = orderPreserved;
   1315             invalidateStructure();
   1316         }
   1317 
   1318         private PackedMap<Spec, Bounds> createGroupBounds() {
   1319             Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
   1320             for (int i = 0, N = getChildCount(); i < N; i++) {
   1321                 View c = getChildAt(i);
   1322                 // we must include views that are GONE here, see introductory javadoc
   1323                 LayoutParams lp = getLayoutParams(c);
   1324                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
   1325                 Bounds bounds = spec.getAbsoluteAlignment(horizontal).getBounds();
   1326                 assoc.put(spec, bounds);
   1327             }
   1328             return assoc.pack();
   1329         }
   1330 
   1331         private void computeGroupBounds() {
   1332             Bounds[] values = groupBounds.values;
   1333             for (int i = 0; i < values.length; i++) {
   1334                 values[i].reset();
   1335             }
   1336             for (int i = 0, N = getChildCount(); i < N; i++) {
   1337                 View c = getChildAt(i);
   1338                 // we must include views that are GONE here, see introductory javadoc
   1339                 LayoutParams lp = getLayoutParams(c);
   1340                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
   1341                 int size = getMeasurementIncludingMargin(c, horizontal) +
   1342                         ((spec.weight == 0) ? 0 : getDeltas()[i]);
   1343                 groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
   1344             }
   1345         }
   1346 
   1347         public PackedMap<Spec, Bounds> getGroupBounds() {
   1348             if (groupBounds == null) {
   1349                 groupBounds = createGroupBounds();
   1350             }
   1351             if (!groupBoundsValid) {
   1352                 computeGroupBounds();
   1353                 groupBoundsValid = true;
   1354             }
   1355             return groupBounds;
   1356         }
   1357 
   1358         // Add values computed by alignment - taking the max of all alignments in each span
   1359         private PackedMap<Interval, MutableInt> createLinks(boolean min) {
   1360             Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
   1361             Spec[] keys = getGroupBounds().keys;
   1362             for (int i = 0, N = keys.length; i < N; i++) {
   1363                 Interval span = min ? keys[i].span : keys[i].span.inverse();
   1364                 result.put(span, new MutableInt());
   1365             }
   1366             return result.pack();
   1367         }
   1368 
   1369         private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
   1370             MutableInt[] spans = links.values;
   1371             for (int i = 0; i < spans.length; i++) {
   1372                 spans[i].reset();
   1373             }
   1374 
   1375             // Use getter to trigger a re-evaluation
   1376             Bounds[] bounds = getGroupBounds().values;
   1377             for (int i = 0; i < bounds.length; i++) {
   1378                 int size = bounds[i].size(min);
   1379                 MutableInt valueHolder = links.getValue(i);
   1380                 // this effectively takes the max() of the minima and the min() of the maxima
   1381                 valueHolder.value = max(valueHolder.value, min ? size : -size);
   1382             }
   1383         }
   1384 
   1385         private PackedMap<Interval, MutableInt> getForwardLinks() {
   1386             if (forwardLinks == null) {
   1387                 forwardLinks = createLinks(true);
   1388             }
   1389             if (!forwardLinksValid) {
   1390                 computeLinks(forwardLinks, true);
   1391                 forwardLinksValid = true;
   1392             }
   1393             return forwardLinks;
   1394         }
   1395 
   1396         private PackedMap<Interval, MutableInt> getBackwardLinks() {
   1397             if (backwardLinks == null) {
   1398                 backwardLinks = createLinks(false);
   1399             }
   1400             if (!backwardLinksValid) {
   1401                 computeLinks(backwardLinks, false);
   1402                 backwardLinksValid = true;
   1403             }
   1404             return backwardLinks;
   1405         }
   1406 
   1407         private void include(List<Arc> arcs, Interval key, MutableInt size,
   1408                 boolean ignoreIfAlreadyPresent) {
   1409             /*
   1410             Remove self referential links.
   1411             These appear:
   1412                 . as parental constraints when GridLayout has no children
   1413                 . when components have been marked as GONE
   1414             */
   1415             if (key.size() == 0) {
   1416                 return;
   1417             }
   1418             // this bit below should really be computed outside here -
   1419             // its just to stop default (row/col > 0) constraints obliterating valid entries
   1420             if (ignoreIfAlreadyPresent) {
   1421                 for (Arc arc : arcs) {
   1422                     Interval span = arc.span;
   1423                     if (span.equals(key)) {
   1424                         return;
   1425                     }
   1426                 }
   1427             }
   1428             arcs.add(new Arc(key, size));
   1429         }
   1430 
   1431         private void include(List<Arc> arcs, Interval key, MutableInt size) {
   1432             include(arcs, key, size, true);
   1433         }
   1434 
   1435         // Group arcs by their first vertex, returning an array of arrays.
   1436         // This is linear in the number of arcs.
   1437         Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
   1438             int N = getCount() + 1; // the number of vertices
   1439             Arc[][] result = new Arc[N][];
   1440             int[] sizes = new int[N];
   1441             for (Arc arc : arcs) {
   1442                 sizes[arc.span.min]++;
   1443             }
   1444             for (int i = 0; i < sizes.length; i++) {
   1445                 result[i] = new Arc[sizes[i]];
   1446             }
   1447             // reuse the sizes array to hold the current last elements as we insert each arc
   1448             Arrays.fill(sizes, 0);
   1449             for (Arc arc : arcs) {
   1450                 int i = arc.span.min;
   1451                 result[i][sizes[i]++] = arc;
   1452             }
   1453 
   1454             return result;
   1455         }
   1456 
   1457         private Arc[] topologicalSort(final Arc[] arcs) {
   1458             return new Object() {
   1459                 Arc[] result = new Arc[arcs.length];
   1460                 int cursor = result.length - 1;
   1461                 Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
   1462                 int[] visited = new int[getCount() + 1];
   1463 
   1464                 void walk(int loc) {
   1465                     switch (visited[loc]) {
   1466                         case NEW: {
   1467                             visited[loc] = PENDING;
   1468                             for (Arc arc : arcsByVertex[loc]) {
   1469                                 walk(arc.span.max);
   1470                                 result[cursor--] = arc;
   1471                             }
   1472                             visited[loc] = COMPLETE;
   1473                             break;
   1474                         }
   1475                         case PENDING: {
   1476                             // le singe est dans l'arbre
   1477                             assert false;
   1478                             break;
   1479                         }
   1480                         case COMPLETE: {
   1481                             break;
   1482                         }
   1483                     }
   1484                 }
   1485 
   1486                 Arc[] sort() {
   1487                     for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
   1488                         walk(loc);
   1489                     }
   1490                     assert cursor == -1;
   1491                     return result;
   1492                 }
   1493             }.sort();
   1494         }
   1495 
   1496         private Arc[] topologicalSort(List<Arc> arcs) {
   1497             return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
   1498         }
   1499 
   1500         private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
   1501             for (int i = 0; i < links.keys.length; i++) {
   1502                 Interval key = links.keys[i];
   1503                 include(result, key, links.values[i], false);
   1504             }
   1505         }
   1506 
   1507         private Arc[] createArcs() {
   1508             List<Arc> mins = new ArrayList<Arc>();
   1509             List<Arc> maxs = new ArrayList<Arc>();
   1510 
   1511             // Add the minimum values from the components.
   1512             addComponentSizes(mins, getForwardLinks());
   1513             // Add the maximum values from the components.
   1514             addComponentSizes(maxs, getBackwardLinks());
   1515 
   1516             // Add ordering constraints to prevent row/col sizes from going negative
   1517             if (orderPreserved) {
   1518                 // Add a constraint for every row/col
   1519                 for (int i = 0; i < getCount(); i++) {
   1520                     include(mins, new Interval(i, i + 1), new MutableInt(0));
   1521                 }
   1522             }
   1523 
   1524             // Add the container constraints. Use the version of include that allows
   1525             // duplicate entries in case a child spans the entire grid.
   1526             int N = getCount();
   1527             include(mins, new Interval(0, N), parentMin, false);
   1528             include(maxs, new Interval(N, 0), parentMax, false);
   1529 
   1530             // Sort
   1531             Arc[] sMins = topologicalSort(mins);
   1532             Arc[] sMaxs = topologicalSort(maxs);
   1533 
   1534             return append(sMins, sMaxs);
   1535         }
   1536 
   1537         private void computeArcs() {
   1538             // getting the links validates the values that are shared by the arc list
   1539             getForwardLinks();
   1540             getBackwardLinks();
   1541         }
   1542 
   1543         public Arc[] getArcs() {
   1544             if (arcs == null) {
   1545                 arcs = createArcs();
   1546             }
   1547             if (!arcsValid) {
   1548                 computeArcs();
   1549                 arcsValid = true;
   1550             }
   1551             return arcs;
   1552         }
   1553 
   1554         private boolean relax(int[] locations, Arc entry) {
   1555             if (!entry.valid) {
   1556                 return false;
   1557             }
   1558             Interval span = entry.span;
   1559             int u = span.min;
   1560             int v = span.max;
   1561             int value = entry.value.value;
   1562             int candidate = locations[u] + value;
   1563             if (candidate > locations[v]) {
   1564                 locations[v] = candidate;
   1565                 return true;
   1566             }
   1567             return false;
   1568         }
   1569 
   1570         private void init(int[] locations) {
   1571             Arrays.fill(locations, 0);
   1572         }
   1573 
   1574         private String arcsToString(List<Arc> arcs) {
   1575             String var = horizontal ? "x" : "y";
   1576             StringBuilder result = new StringBuilder();
   1577             boolean first = true;
   1578             for (Arc arc : arcs) {
   1579                 if (first) {
   1580                     first = false;
   1581                 } else {
   1582                     result = result.append(", ");
   1583                 }
   1584                 int src = arc.span.min;
   1585                 int dst = arc.span.max;
   1586                 int value = arc.value.value;
   1587                 result.append((src < dst) ?
   1588                         var + dst + "-" + var + src + ">=" + value :
   1589                         var + src + "-" + var + dst + "<=" + -value);
   1590 
   1591             }
   1592             return result.toString();
   1593         }
   1594 
   1595         private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
   1596             List<Arc> culprits = new ArrayList<Arc>();
   1597             List<Arc> removed = new ArrayList<Arc>();
   1598             for (int c = 0; c < arcs.length; c++) {
   1599                 Arc arc = arcs[c];
   1600                 if (culprits0[c]) {
   1601                     culprits.add(arc);
   1602                 }
   1603                 if (!arc.valid) {
   1604                     removed.add(arc);
   1605                 }
   1606             }
   1607             mPrinter.println(axisName + " constraints: " + arcsToString(culprits) +
   1608                     " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
   1609         }
   1610 
   1611         /*
   1612         Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
   1613 
   1614         GridLayout converts its requirements into a system of linear constraints of the
   1615         form:
   1616 
   1617         x[i] - x[j] < a[k]
   1618 
   1619         Where the x[i] are variables and the a[k] are constants.
   1620 
   1621         For example, if the variables were instead labeled x, y, z we might have:
   1622 
   1623             x - y < 17
   1624             y - z < 23
   1625             z - x < 42
   1626 
   1627         This is a special case of the Linear Programming problem that is, in turn,
   1628         equivalent to the single-source shortest paths problem on a digraph, for
   1629         which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
   1630         */
   1631         private boolean solve(Arc[] arcs, int[] locations) {
   1632             return solve(arcs, locations, true);
   1633         }
   1634 
   1635         private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
   1636             String axisName = horizontal ? "horizontal" : "vertical";
   1637             int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
   1638             boolean[] originalCulprits = null;
   1639 
   1640             for (int p = 0; p < arcs.length; p++) {
   1641                 init(locations);
   1642 
   1643                 // We take one extra pass over traditional Bellman-Ford (and omit their final step)
   1644                 for (int i = 0; i < N; i++) {
   1645                     boolean changed = false;
   1646                     for (int j = 0, length = arcs.length; j < length; j++) {
   1647                         changed |= relax(locations, arcs[j]);
   1648                     }
   1649                     if (!changed) {
   1650                         if (originalCulprits != null) {
   1651                             logError(axisName, arcs, originalCulprits);
   1652                         }
   1653                         return true;
   1654                     }
   1655                 }
   1656 
   1657                 if (!modifyOnError) {
   1658                     return false; // cannot solve with these constraints
   1659                 }
   1660 
   1661                 boolean[] culprits = new boolean[arcs.length];
   1662                 for (int i = 0; i < N; i++) {
   1663                     for (int j = 0, length = arcs.length; j < length; j++) {
   1664                         culprits[j] |= relax(locations, arcs[j]);
   1665                     }
   1666                 }
   1667 
   1668                 if (p == 0) {
   1669                     originalCulprits = culprits;
   1670                 }
   1671 
   1672                 for (int i = 0; i < arcs.length; i++) {
   1673                     if (culprits[i]) {
   1674                         Arc arc = arcs[i];
   1675                         // Only remove max values, min values alone cannot be inconsistent
   1676                         if (arc.span.min < arc.span.max) {
   1677                             continue;
   1678                         }
   1679                         arc.valid = false;
   1680                         break;
   1681                     }
   1682                 }
   1683             }
   1684             return true;
   1685         }
   1686 
   1687         private void computeMargins(boolean leading) {
   1688             int[] margins = leading ? leadingMargins : trailingMargins;
   1689             for (int i = 0, N = getChildCount(); i < N; i++) {
   1690                 View c = getChildAt(i);
   1691                 if (c.getVisibility() == View.GONE) continue;
   1692                 LayoutParams lp = getLayoutParams(c);
   1693                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
   1694                 Interval span = spec.span;
   1695                 int index = leading ? span.min : span.max;
   1696                 margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
   1697             }
   1698         }
   1699 
   1700         // External entry points
   1701 
   1702         public int[] getLeadingMargins() {
   1703             if (leadingMargins == null) {
   1704                 leadingMargins = new int[getCount() + 1];
   1705             }
   1706             if (!leadingMarginsValid) {
   1707                 computeMargins(true);
   1708                 leadingMarginsValid = true;
   1709             }
   1710             return leadingMargins;
   1711         }
   1712 
   1713         public int[] getTrailingMargins() {
   1714             if (trailingMargins == null) {
   1715                 trailingMargins = new int[getCount() + 1];
   1716             }
   1717             if (!trailingMarginsValid) {
   1718                 computeMargins(false);
   1719                 trailingMarginsValid = true;
   1720             }
   1721             return trailingMargins;
   1722         }
   1723 
   1724         private boolean solve(int[] a) {
   1725             return solve(getArcs(), a);
   1726         }
   1727 
   1728         private boolean computeHasWeights() {
   1729             for (int i = 0, N = getChildCount(); i < N; i++) {
   1730                 final View child = getChildAt(i);
   1731                 if (child.getVisibility() == View.GONE) {
   1732                     continue;
   1733                 }
   1734                 LayoutParams lp = getLayoutParams(child);
   1735                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
   1736                 if (spec.weight != 0) {
   1737                     return true;
   1738                 }
   1739             }
   1740             return false;
   1741         }
   1742 
   1743         private boolean hasWeights() {
   1744             if (!hasWeightsValid) {
   1745                 hasWeights = computeHasWeights();
   1746                 hasWeightsValid = true;
   1747             }
   1748             return hasWeights;
   1749         }
   1750 
   1751         public int[] getDeltas() {
   1752             if (deltas == null) {
   1753                 deltas = new int[getChildCount()];
   1754             }
   1755             return deltas;
   1756         }
   1757 
   1758         private void shareOutDelta(int totalDelta, float totalWeight) {
   1759             Arrays.fill(deltas, 0);
   1760             for (int i = 0, N = getChildCount(); i < N; i++) {
   1761                 final View c = getChildAt(i);
   1762                 if (c.getVisibility() == View.GONE) {
   1763                     continue;
   1764                 }
   1765                 LayoutParams lp = getLayoutParams(c);
   1766                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
   1767                 float weight = spec.weight;
   1768                 if (weight != 0) {
   1769                     int delta = Math.round((weight * totalDelta / totalWeight));
   1770                     deltas[i] = delta;
   1771                     // the two adjustments below are to counter the above rounding and avoid
   1772                     // off-by-ones at the end
   1773                     totalDelta -= delta;
   1774                     totalWeight -= weight;
   1775                 }
   1776             }
   1777         }
   1778 
   1779         private void solveAndDistributeSpace(int[] a) {
   1780             Arrays.fill(getDeltas(), 0);
   1781             solve(a);
   1782             int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
   1783             if (deltaMax < 2) {
   1784                 return; //don't have any delta to distribute
   1785             }
   1786             int deltaMin = 0; //inclusive
   1787 
   1788             float totalWeight = calculateTotalWeight();
   1789 
   1790             int validDelta = -1; //delta for which a solution exists
   1791             boolean validSolution = true;
   1792             // do a binary search to find the max delta that won't conflict with constraints
   1793             while(deltaMin < deltaMax) {
   1794                 // cast to long to prevent overflow.
   1795                 final int delta = (int) (((long) deltaMin + deltaMax) / 2);
   1796                 invalidateValues();
   1797                 shareOutDelta(delta, totalWeight);
   1798                 validSolution = solve(getArcs(), a, false);
   1799                 if (validSolution) {
   1800                     validDelta = delta;
   1801                     deltaMin = delta + 1;
   1802                 } else {
   1803                     deltaMax = delta;
   1804                 }
   1805             }
   1806             if (validDelta > 0 && !validSolution) {
   1807                 // last solution was not successful but we have a successful one. Use it.
   1808                 invalidateValues();
   1809                 shareOutDelta(validDelta, totalWeight);
   1810                 solve(a);
   1811             }
   1812         }
   1813 
   1814         private float calculateTotalWeight() {
   1815             float totalWeight = 0f;
   1816             for (int i = 0, N = getChildCount(); i < N; i++) {
   1817                 View c = getChildAt(i);
   1818                 if (c.getVisibility() == View.GONE) {
   1819                     continue;
   1820                 }
   1821                 LayoutParams lp = getLayoutParams(c);
   1822                 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
   1823                 totalWeight += spec.weight;
   1824             }
   1825             return totalWeight;
   1826         }
   1827 
   1828         private void computeLocations(int[] a) {
   1829             if (!hasWeights()) {
   1830                 solve(a);
   1831             } else {
   1832                 solveAndDistributeSpace(a);
   1833             }
   1834             if (!orderPreserved) {
   1835                 // Solve returns the smallest solution to the constraint system for which all
   1836                 // values are positive. One value is therefore zero - though if the row/col
   1837                 // order is not preserved this may not be the first vertex. For consistency,
   1838                 // translate all the values so that they measure the distance from a[0]; the
   1839                 // leading edge of the parent. After this transformation some values may be
   1840                 // negative.
   1841                 int a0 = a[0];
   1842                 for (int i = 0, N = a.length; i < N; i++) {
   1843                     a[i] = a[i] - a0;
   1844                 }
   1845             }
   1846         }
   1847 
   1848         public int[] getLocations() {
   1849             if (locations == null) {
   1850                 int N = getCount() + 1;
   1851                 locations = new int[N];
   1852             }
   1853             if (!locationsValid) {
   1854                 computeLocations(locations);
   1855                 locationsValid = true;
   1856             }
   1857             return locations;
   1858         }
   1859 
   1860         private int size(int[] locations) {
   1861             // The parental edges are attached to vertices 0 and N - even when order is not
   1862             // being preserved and other vertices fall outside this range. Measure the distance
   1863             // between vertices 0 and N, assuming that locations[0] = 0.
   1864             return locations[getCount()];
   1865         }
   1866 
   1867         private void setParentConstraints(int min, int max) {
   1868             parentMin.value = min;
   1869             parentMax.value = -max;
   1870             locationsValid = false;
   1871         }
   1872 
   1873         private int getMeasure(int min, int max) {
   1874             setParentConstraints(min, max);
   1875             return size(getLocations());
   1876         }
   1877 
   1878         public int getMeasure(int measureSpec) {
   1879             int mode = MeasureSpec.getMode(measureSpec);
   1880             int size = MeasureSpec.getSize(measureSpec);
   1881             switch (mode) {
   1882                 case MeasureSpec.UNSPECIFIED: {
   1883                     return getMeasure(0, MAX_SIZE);
   1884                 }
   1885                 case MeasureSpec.EXACTLY: {
   1886                     return getMeasure(size, size);
   1887                 }
   1888                 case MeasureSpec.AT_MOST: {
   1889                     return getMeasure(0, size);
   1890                 }
   1891                 default: {
   1892                     assert false;
   1893                     return 0;
   1894                 }
   1895             }
   1896         }
   1897 
   1898         public void layout(int size) {
   1899             setParentConstraints(size, size);
   1900             getLocations();
   1901         }
   1902 
   1903         public void invalidateStructure() {
   1904             maxIndex = UNDEFINED;
   1905 
   1906             groupBounds = null;
   1907             forwardLinks = null;
   1908             backwardLinks = null;
   1909 
   1910             leadingMargins = null;
   1911             trailingMargins = null;
   1912             arcs = null;
   1913 
   1914             locations = null;
   1915 
   1916             deltas = null;
   1917             hasWeightsValid = false;
   1918 
   1919             invalidateValues();
   1920         }
   1921 
   1922         public void invalidateValues() {
   1923             groupBoundsValid = false;
   1924             forwardLinksValid = false;
   1925             backwardLinksValid = false;
   1926 
   1927             leadingMarginsValid = false;
   1928             trailingMarginsValid = false;
   1929             arcsValid = false;
   1930 
   1931             locationsValid = false;
   1932         }
   1933     }
   1934 
   1935     /**
   1936      * Layout information associated with each of the children of a GridLayout.
   1937      * <p>
   1938      * GridLayout supports both row and column spanning and arbitrary forms of alignment within
   1939      * each cell group. The fundamental parameters associated with each cell group are
   1940      * gathered into their vertical and horizontal components and stored
   1941      * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
   1942      * {@link GridLayout.Spec Specs} are immutable structures
   1943      * and may be shared between the layout parameters of different children.
   1944      * <p>
   1945      * The row and column specs contain the leading and trailing indices along each axis
   1946      * and together specify the four grid indices that delimit the cells of this cell group.
   1947      * <p>
   1948      * The  alignment properties of the row and column specs together specify
   1949      * both aspects of alignment within the cell group. It is also possible to specify a child's
   1950      * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
   1951      * method.
   1952      * <p>
   1953      * The weight property is also included in Spec and specifies the proportion of any
   1954      * excess space that is due to the associated view.
   1955      *
   1956      * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
   1957      *
   1958      * Because the default values of the {@link #width} and {@link #height}
   1959      * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
   1960      * declared in the layout parameters of GridLayout's children. In addition,
   1961      * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
   1962      * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
   1963      * instead controlled by the principle of <em>flexibility</em>,
   1964      * as discussed in {@link GridLayout}.
   1965      *
   1966      * <h4>Summary</h4>
   1967      *
   1968      * You should not need to use either of the special size values:
   1969      * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
   1970      * a GridLayout.
   1971      *
   1972      * <h4>Default values</h4>
   1973      *
   1974      * <ul>
   1975      *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
   1976      *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
   1977      *     <li>{@link #topMargin} = 0 when
   1978      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
   1979      *          {@code false}; otherwise {@link #UNDEFINED}, to
   1980      *          indicate that a default value should be computed on demand. </li>
   1981      *     <li>{@link #leftMargin} = 0 when
   1982      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
   1983      *          {@code false}; otherwise {@link #UNDEFINED}, to
   1984      *          indicate that a default value should be computed on demand. </li>
   1985      *     <li>{@link #bottomMargin} = 0 when
   1986      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
   1987      *          {@code false}; otherwise {@link #UNDEFINED}, to
   1988      *          indicate that a default value should be computed on demand. </li>
   1989      *     <li>{@link #rightMargin} = 0 when
   1990      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
   1991      *          {@code false}; otherwise {@link #UNDEFINED}, to
   1992      *          indicate that a default value should be computed on demand. </li>
   1993      *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
   1994      *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
   1995      *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
   1996      *     <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
   1997      *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
   1998      *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
   1999      *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
   2000      *     <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
   2001      * </ul>
   2002      *
   2003      * See {@link GridLayout} for a more complete description of the conventions
   2004      * used by GridLayout in the interpretation of the properties of this class.
   2005      *
   2006      * @attr ref android.R.styleable#GridLayout_Layout_layout_row
   2007      * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
   2008      * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
   2009      * @attr ref android.R.styleable#GridLayout_Layout_layout_column
   2010      * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
   2011      * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
   2012      * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
   2013      */
   2014     public static class LayoutParams extends MarginLayoutParams {
   2015 
   2016         // Default values
   2017 
   2018         private static final int DEFAULT_WIDTH = WRAP_CONTENT;
   2019         private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
   2020         private static final int DEFAULT_MARGIN = UNDEFINED;
   2021         private static final int DEFAULT_ROW = UNDEFINED;
   2022         private static final int DEFAULT_COLUMN = UNDEFINED;
   2023         private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
   2024         private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
   2025 
   2026         // TypedArray indices
   2027 
   2028         private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
   2029         private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
   2030         private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
   2031         private static final int RIGHT_MARGIN =
   2032                 R.styleable.ViewGroup_MarginLayout_layout_marginRight;
   2033         private static final int BOTTOM_MARGIN =
   2034                 R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
   2035         private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
   2036         private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
   2037         private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
   2038 
   2039         private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
   2040         private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
   2041         private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
   2042 
   2043         private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
   2044 
   2045         // Instance variables
   2046 
   2047         /**
   2048          * The spec that defines the vertical characteristics of the cell group
   2049          * described by these layout parameters.
   2050          * If an assignment is made to this field after a measurement or layout operation
   2051          * has already taken place, a call to
   2052          * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
   2053          * must be made to notify GridLayout of the change. GridLayout is normally able
   2054          * to detect when code fails to observe this rule, issue a warning and take steps to
   2055          * compensate for the omission. This facility is implemented on a best effort basis
   2056          * and should not be relied upon in production code - so it is best to include the above
   2057          * calls to remove the warnings as soon as it is practical.
   2058          */
   2059         public Spec rowSpec = Spec.UNDEFINED;
   2060 
   2061         /**
   2062          * The spec that defines the horizontal characteristics of the cell group
   2063          * described by these layout parameters.
   2064          * If an assignment is made to this field after a measurement or layout operation
   2065          * has already taken place, a call to
   2066          * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
   2067          * must be made to notify GridLayout of the change. GridLayout is normally able
   2068          * to detect when code fails to observe this rule, issue a warning and take steps to
   2069          * compensate for the omission. This facility is implemented on a best effort basis
   2070          * and should not be relied upon in production code - so it is best to include the above
   2071          * calls to remove the warnings as soon as it is practical.
   2072          */
   2073         public Spec columnSpec = Spec.UNDEFINED;
   2074 
   2075         // Constructors
   2076 
   2077         private LayoutParams(
   2078                 int width, int height,
   2079                 int left, int top, int right, int bottom,
   2080                 Spec rowSpec, Spec columnSpec) {
   2081             super(width, height);
   2082             setMargins(left, top, right, bottom);
   2083             this.rowSpec = rowSpec;
   2084             this.columnSpec = columnSpec;
   2085         }
   2086 
   2087         /**
   2088          * Constructs a new LayoutParams instance for this <code>rowSpec</code>
   2089          * and <code>columnSpec</code>. All other fields are initialized with
   2090          * default values as defined in {@link LayoutParams}.
   2091          *
   2092          * @param rowSpec    the rowSpec
   2093          * @param columnSpec the columnSpec
   2094          */
   2095         public LayoutParams(Spec rowSpec, Spec columnSpec) {
   2096             this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
   2097                     DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
   2098                     rowSpec, columnSpec);
   2099         }
   2100 
   2101         /**
   2102          * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
   2103          */
   2104         public LayoutParams() {
   2105             this(Spec.UNDEFINED, Spec.UNDEFINED);
   2106         }
   2107 
   2108         // Copying constructors
   2109 
   2110         /**
   2111          * {@inheritDoc}
   2112          */
   2113         public LayoutParams(ViewGroup.LayoutParams params) {
   2114             super(params);
   2115         }
   2116 
   2117         /**
   2118          * {@inheritDoc}
   2119          */
   2120         public LayoutParams(MarginLayoutParams params) {
   2121             super(params);
   2122         }
   2123 
   2124         /**
   2125          * Copy constructor. Clones the width, height, margin values, row spec,
   2126          * and column spec of the source.
   2127          *
   2128          * @param source The layout params to copy from.
   2129          */
   2130         public LayoutParams(LayoutParams source) {
   2131             super(source);
   2132 
   2133             this.rowSpec = source.rowSpec;
   2134             this.columnSpec = source.columnSpec;
   2135         }
   2136 
   2137         // AttributeSet constructors
   2138 
   2139         /**
   2140          * {@inheritDoc}
   2141          *
   2142          * Values not defined in the attribute set take the default values
   2143          * defined in {@link LayoutParams}.
   2144          */
   2145         public LayoutParams(Context context, AttributeSet attrs) {
   2146             super(context, attrs);
   2147             reInitSuper(context, attrs);
   2148             init(context, attrs);
   2149         }
   2150 
   2151         // Implementation
   2152 
   2153         // Reinitialise the margins using a different default policy than MarginLayoutParams.
   2154         // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
   2155         // so that a layout manager default can be accessed post set up. We need this as, at the
   2156         // point of installation, we do not know how many rows/cols there are and therefore
   2157         // which elements are positioned next to the container's trailing edges. We need to
   2158         // know this as margins around the container's boundary should have different
   2159         // defaults to those between peers.
   2160 
   2161         // This method could be parametrized and moved into MarginLayout.
   2162         private void reInitSuper(Context context, AttributeSet attrs) {
   2163             TypedArray a =
   2164                     context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
   2165             try {
   2166                 int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
   2167 
   2168                 this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
   2169                 this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
   2170                 this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
   2171                 this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
   2172             } finally {
   2173                 a.recycle();
   2174             }
   2175         }
   2176 
   2177         private void init(Context context, AttributeSet attrs) {
   2178             TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
   2179             try {
   2180                 int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
   2181 
   2182                 int column = a.getInt(COLUMN, DEFAULT_COLUMN);
   2183                 int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
   2184                 float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
   2185                 this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
   2186 
   2187                 int row = a.getInt(ROW, DEFAULT_ROW);
   2188                 int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
   2189                 float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
   2190                 this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
   2191             } finally {
   2192                 a.recycle();
   2193             }
   2194         }
   2195 
   2196         /**
   2197          * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
   2198          * See {@link Gravity}.
   2199          *
   2200          * @param gravity the new gravity value
   2201          *
   2202          * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
   2203          */
   2204         public void setGravity(int gravity) {
   2205             rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
   2206             columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
   2207         }
   2208 
   2209         @Override
   2210         protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
   2211             this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
   2212             this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
   2213         }
   2214 
   2215         final void setRowSpecSpan(Interval span) {
   2216             rowSpec = rowSpec.copyWriteSpan(span);
   2217         }
   2218 
   2219         final void setColumnSpecSpan(Interval span) {
   2220             columnSpec = columnSpec.copyWriteSpan(span);
   2221         }
   2222 
   2223         @Override
   2224         public boolean equals(Object o) {
   2225             if (this == o) return true;
   2226             if (o == null || getClass() != o.getClass()) return false;
   2227 
   2228             LayoutParams that = (LayoutParams) o;
   2229 
   2230             if (!columnSpec.equals(that.columnSpec)) return false;
   2231             if (!rowSpec.equals(that.rowSpec)) return false;
   2232 
   2233             return true;
   2234         }
   2235 
   2236         @Override
   2237         public int hashCode() {
   2238             int result = rowSpec.hashCode();
   2239             result = 31 * result + columnSpec.hashCode();
   2240             return result;
   2241         }
   2242     }
   2243 
   2244     /*
   2245     In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
   2246     Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
   2247      */
   2248     final static class Arc {
   2249         public final Interval span;
   2250         public final MutableInt value;
   2251         public boolean valid = true;
   2252 
   2253         public Arc(Interval span, MutableInt value) {
   2254             this.span = span;
   2255             this.value = value;
   2256         }
   2257 
   2258         @Override
   2259         public String toString() {
   2260             return span + " " + (!valid ? "+>" : "->") + " " + value;
   2261         }
   2262     }
   2263 
   2264     // A mutable Integer - used to avoid heap allocation during the layout operation
   2265 
   2266     final static class MutableInt {
   2267         public int value;
   2268 
   2269         public MutableInt() {
   2270             reset();
   2271         }
   2272 
   2273         public MutableInt(int value) {
   2274             this.value = value;
   2275         }
   2276 
   2277         public void reset() {
   2278             value = Integer.MIN_VALUE;
   2279         }
   2280 
   2281         @Override
   2282         public String toString() {
   2283             return Integer.toString(value);
   2284         }
   2285     }
   2286 
   2287     final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
   2288         private final Class<K> keyType;
   2289         private final Class<V> valueType;
   2290 
   2291         private Assoc(Class<K> keyType, Class<V> valueType) {
   2292             this.keyType = keyType;
   2293             this.valueType = valueType;
   2294         }
   2295 
   2296         public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
   2297             return new Assoc<K, V>(keyType, valueType);
   2298         }
   2299 
   2300         public void put(K key, V value) {
   2301             add(Pair.create(key, value));
   2302         }
   2303 
   2304         @SuppressWarnings(value = "unchecked")
   2305         public PackedMap<K, V> pack() {
   2306             int N = size();
   2307             K[] keys = (K[]) Array.newInstance(keyType, N);
   2308             V[] values = (V[]) Array.newInstance(valueType, N);
   2309             for (int i = 0; i < N; i++) {
   2310                 keys[i] = get(i).first;
   2311                 values[i] = get(i).second;
   2312             }
   2313             return new PackedMap<K, V>(keys, values);
   2314         }
   2315     }
   2316 
   2317     /*
   2318     This data structure is used in place of a Map where we have an index that refers to the order
   2319     in which each key/value pairs were added to the map. In this case we store keys and values
   2320     in arrays of a length that is equal to the number of unique keys. We also maintain an
   2321     array of indexes from insertion order to the compacted arrays of keys and values.
   2322 
   2323     Note that behavior differs from that of a LinkedHashMap in that repeated entries
   2324     *do* get added multiples times. So the length of index is equals to the number of
   2325     items added.
   2326 
   2327     This is useful in the GridLayout class where we can rely on the order of children not
   2328     changing during layout - to use integer-based lookup for our internal structures
   2329     rather than using (and storing) an implementation of Map<Key, ?>.
   2330      */
   2331     @SuppressWarnings(value = "unchecked")
   2332     final static class PackedMap<K, V> {
   2333         public final int[] index;
   2334         public final K[] keys;
   2335         public final V[] values;
   2336 
   2337         private PackedMap(K[] keys, V[] values) {
   2338             this.index = createIndex(keys);
   2339 
   2340             this.keys = compact(keys, index);
   2341             this.values = compact(values, index);
   2342         }
   2343 
   2344         public V getValue(int i) {
   2345             return values[index[i]];
   2346         }
   2347 
   2348         private static <K> int[] createIndex(K[] keys) {
   2349             int size = keys.length;
   2350             int[] result = new int[size];
   2351 
   2352             Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
   2353             for (int i = 0; i < size; i++) {
   2354                 K key = keys[i];
   2355                 Integer index = keyToIndex.get(key);
   2356                 if (index == null) {
   2357                     index = keyToIndex.size();
   2358                     keyToIndex.put(key, index);
   2359                 }
   2360                 result[i] = index;
   2361             }
   2362             return result;
   2363         }
   2364 
   2365         /*
   2366         Create a compact array of keys or values using the supplied index.
   2367          */
   2368         private static <K> K[] compact(K[] a, int[] index) {
   2369             int size = a.length;
   2370             Class<?> componentType = a.getClass().getComponentType();
   2371             K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
   2372 
   2373             // this overwrite duplicates, retaining the last equivalent entry
   2374             for (int i = 0; i < size; i++) {
   2375                 result[index[i]] = a[i];
   2376             }
   2377             return result;
   2378         }
   2379     }
   2380 
   2381     /*
   2382     For each group (with a given alignment) we need to store the amount of space required
   2383     before the alignment point and the amount of space required after it. One side of this
   2384     calculation is always 0 for START and END alignments but we don't make use of this.
   2385     For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
   2386     simple optimisations are possible.
   2387 
   2388     The general algorithm therefore is to create a Map (actually a PackedMap) from
   2389     group to Bounds and to loop through all Views in the group taking the maximum
   2390     of the values for each View.
   2391     */
   2392     static class Bounds {
   2393         public int before;
   2394         public int after;
   2395         public int flexibility; // we're flexible iff all included specs are flexible
   2396 
   2397         private Bounds() {
   2398             reset();
   2399         }
   2400 
   2401         protected void reset() {
   2402             before = Integer.MIN_VALUE;
   2403             after = Integer.MIN_VALUE;
   2404             flexibility = CAN_STRETCH; // from the above, we're flexible when empty
   2405         }
   2406 
   2407         protected void include(int before, int after) {
   2408             this.before = max(this.before, before);
   2409             this.after = max(this.after, after);
   2410         }
   2411 
   2412         protected int size(boolean min) {
   2413             if (!min) {
   2414                 if (canStretch(flexibility)) {
   2415                     return MAX_SIZE;
   2416                 }
   2417             }
   2418             return before + after;
   2419         }
   2420 
   2421         protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
   2422             return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
   2423         }
   2424 
   2425         protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
   2426             this.flexibility &= spec.getFlexibility();
   2427             boolean horizontal = axis.horizontal;
   2428             Alignment alignment = spec.getAbsoluteAlignment(axis.horizontal);
   2429             // todo test this works correctly when the returned value is UNDEFINED
   2430             int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
   2431             include(before, size - before);
   2432         }
   2433 
   2434         @Override
   2435         public String toString() {
   2436             return "Bounds{" +
   2437                     "before=" + before +
   2438                     ", after=" + after +
   2439                     '}';
   2440         }
   2441     }
   2442 
   2443     /**
   2444      * An Interval represents a contiguous range of values that lie between
   2445      * the interval's {@link #min} and {@link #max} values.
   2446      * <p>
   2447      * Intervals are immutable so may be passed as values and used as keys in hash tables.
   2448      * It is not necessary to have multiple instances of Intervals which have the same
   2449      * {@link #min} and {@link #max} values.
   2450      * <p>
   2451      * Intervals are often written as {@code [min, max]} and represent the set of values
   2452      * {@code x} such that {@code min <= x < max}.
   2453      */
   2454     final static class Interval {
   2455         /**
   2456          * The minimum value.
   2457          */
   2458         public final int min;
   2459 
   2460         /**
   2461          * The maximum value.
   2462          */
   2463         public final int max;
   2464 
   2465         /**
   2466          * Construct a new Interval, {@code interval}, where:
   2467          * <ul>
   2468          *     <li> {@code interval.min = min} </li>
   2469          *     <li> {@code interval.max = max} </li>
   2470          * </ul>
   2471          *
   2472          * @param min the minimum value.
   2473          * @param max the maximum value.
   2474          */
   2475         public Interval(int min, int max) {
   2476             this.min = min;
   2477             this.max = max;
   2478         }
   2479 
   2480         int size() {
   2481             return max - min;
   2482         }
   2483 
   2484         Interval inverse() {
   2485             return new Interval(max, min);
   2486         }
   2487 
   2488         /**
   2489          * Returns {@code true} if the {@link #getClass class},
   2490          * {@link #min} and {@link #max} properties of this Interval and the
   2491          * supplied parameter are pairwise equal; {@code false} otherwise.
   2492          *
   2493          * @param that the object to compare this interval with
   2494          *
   2495          * @return {@code true} if the specified object is equal to this
   2496          *         {@code Interval}, {@code false} otherwise.
   2497          */
   2498         @Override
   2499         public boolean equals(Object that) {
   2500             if (this == that) {
   2501                 return true;
   2502             }
   2503             if (that == null || getClass() != that.getClass()) {
   2504                 return false;
   2505             }
   2506 
   2507             Interval interval = (Interval) that;
   2508 
   2509             if (max != interval.max) {
   2510                 return false;
   2511             }
   2512             //noinspection RedundantIfStatement
   2513             if (min != interval.min) {
   2514                 return false;
   2515             }
   2516 
   2517             return true;
   2518         }
   2519 
   2520         @Override
   2521         public int hashCode() {
   2522             int result = min;
   2523             result = 31 * result + max;
   2524             return result;
   2525         }
   2526 
   2527         @Override
   2528         public String toString() {
   2529             return "[" + min + ", " + max + "]";
   2530         }
   2531     }
   2532 
   2533     /**
   2534      * A Spec defines the horizontal or vertical characteristics of a group of
   2535      * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
   2536      * along the appropriate axis.
   2537      * <p>
   2538      * The <em>grid indices</em> are the leading and trailing edges of this cell group.
   2539      * See {@link GridLayout} for a description of the conventions used by GridLayout
   2540      * for grid indices.
   2541      * <p>
   2542      * The <em>alignment</em> property specifies how cells should be aligned in this group.
   2543      * For row groups, this specifies the vertical alignment.
   2544      * For column groups, this specifies the horizontal alignment.
   2545      * <p>
   2546      * Use the following static methods to create specs:
   2547      * <ul>
   2548      *   <li>{@link #spec(int)}</li>
   2549      *   <li>{@link #spec(int, int)}</li>
   2550      *   <li>{@link #spec(int, Alignment)}</li>
   2551      *   <li>{@link #spec(int, int, Alignment)}</li>
   2552      *   <li>{@link #spec(int, float)}</li>
   2553      *   <li>{@link #spec(int, int, float)}</li>
   2554      *   <li>{@link #spec(int, Alignment, float)}</li>
   2555      *   <li>{@link #spec(int, int, Alignment, float)}</li>
   2556      * </ul>
   2557      *
   2558      */
   2559     public static class Spec {
   2560         static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
   2561         static final float DEFAULT_WEIGHT = 0;
   2562 
   2563         final boolean startDefined;
   2564         final Interval span;
   2565         final Alignment alignment;
   2566         final float weight;
   2567 
   2568         private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
   2569             this.startDefined = startDefined;
   2570             this.span = span;
   2571             this.alignment = alignment;
   2572             this.weight = weight;
   2573         }
   2574 
   2575         private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
   2576             this(startDefined, new Interval(start, start + size), alignment, weight);
   2577         }
   2578 
   2579         private Alignment getAbsoluteAlignment(boolean horizontal) {
   2580             if (alignment != UNDEFINED_ALIGNMENT) {
   2581                 return alignment;
   2582             }
   2583             if (weight == 0f) {
   2584                 return horizontal ? START : BASELINE;
   2585             }
   2586             return FILL;
   2587         }
   2588 
   2589         final Spec copyWriteSpan(Interval span) {
   2590             return new Spec(startDefined, span, alignment, weight);
   2591         }
   2592 
   2593         final Spec copyWriteAlignment(Alignment alignment) {
   2594             return new Spec(startDefined, span, alignment, weight);
   2595         }
   2596 
   2597         final int getFlexibility() {
   2598             return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
   2599         }
   2600 
   2601         /**
   2602          * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
   2603          * properties of this Spec and the supplied parameter are pairwise equal,
   2604          * {@code false} otherwise.
   2605          *
   2606          * @param that the object to compare this spec with
   2607          *
   2608          * @return {@code true} if the specified object is equal to this
   2609          *         {@code Spec}; {@code false} otherwise
   2610          */
   2611         @Override
   2612         public boolean equals(Object that) {
   2613             if (this == that) {
   2614                 return true;
   2615             }
   2616             if (that == null || getClass() != that.getClass()) {
   2617                 return false;
   2618             }
   2619 
   2620             Spec spec = (Spec) that;
   2621 
   2622             if (!alignment.equals(spec.alignment)) {
   2623                 return false;
   2624             }
   2625             //noinspection RedundantIfStatement
   2626             if (!span.equals(spec.span)) {
   2627                 return false;
   2628             }
   2629 
   2630             return true;
   2631         }
   2632 
   2633         @Override
   2634         public int hashCode() {
   2635             int result = span.hashCode();
   2636             result = 31 * result + alignment.hashCode();
   2637             return result;
   2638         }
   2639     }
   2640 
   2641     /**
   2642      * Return a Spec, {@code spec}, where:
   2643      * <ul>
   2644      *     <li> {@code spec.span = [start, start + size]} </li>
   2645      *     <li> {@code spec.alignment = alignment} </li>
   2646      *     <li> {@code spec.weight = weight} </li>
   2647      * </ul>
   2648      * <p>
   2649      * To leave the start index undefined, use the value {@link #UNDEFINED}.
   2650      *
   2651      * @param start     the start
   2652      * @param size      the size
   2653      * @param alignment the alignment
   2654      * @param weight    the weight
   2655      */
   2656     public static Spec spec(int start, int size, Alignment alignment, float weight) {
   2657         return new Spec(start != UNDEFINED, start, size, alignment, weight);
   2658     }
   2659 
   2660     /**
   2661      * Equivalent to: {@code spec(start, 1, alignment, weight)}.
   2662      *
   2663      * @param start     the start
   2664      * @param alignment the alignment
   2665      * @param weight    the weight
   2666      */
   2667     public static Spec spec(int start, Alignment alignment, float weight) {
   2668         return spec(start, 1, alignment, weight);
   2669     }
   2670 
   2671     /**
   2672      * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
   2673      * where {@code default_alignment} is specified in
   2674      * {@link android.widget.GridLayout.LayoutParams}.
   2675      *
   2676      * @param start  the start
   2677      * @param size   the size
   2678      * @param weight the weight
   2679      */
   2680     public static Spec spec(int start, int size, float weight) {
   2681         return spec(start, size, UNDEFINED_ALIGNMENT, weight);
   2682     }
   2683 
   2684     /**
   2685      * Equivalent to: {@code spec(start, 1, weight)}.
   2686      *
   2687      * @param start  the start
   2688      * @param weight the weight
   2689      */
   2690     public static Spec spec(int start, float weight) {
   2691         return spec(start, 1, weight);
   2692     }
   2693 
   2694     /**
   2695      * Equivalent to: {@code spec(start, size, alignment, 0f)}.
   2696      *
   2697      * @param start     the start
   2698      * @param size      the size
   2699      * @param alignment the alignment
   2700      */
   2701     public static Spec spec(int start, int size, Alignment alignment) {
   2702         return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
   2703     }
   2704 
   2705     /**
   2706      * Return a Spec, {@code spec}, where:
   2707      * <ul>
   2708      *     <li> {@code spec.span = [start, start + 1]} </li>
   2709      *     <li> {@code spec.alignment = alignment} </li>
   2710      * </ul>
   2711      * <p>
   2712      * To leave the start index undefined, use the value {@link #UNDEFINED}.
   2713      *
   2714      * @param start     the start index
   2715      * @param alignment the alignment
   2716      *
   2717      * @see #spec(int, int, Alignment)
   2718      */
   2719     public static Spec spec(int start, Alignment alignment) {
   2720         return spec(start, 1, alignment);
   2721     }
   2722 
   2723     /**
   2724      * Return a Spec, {@code spec}, where:
   2725      * <ul>
   2726      *     <li> {@code spec.span = [start, start + size]} </li>
   2727      * </ul>
   2728      * <p>
   2729      * To leave the start index undefined, use the value {@link #UNDEFINED}.
   2730      *
   2731      * @param start     the start
   2732      * @param size      the size
   2733      *
   2734      * @see #spec(int, Alignment)
   2735      */
   2736     public static Spec spec(int start, int size) {
   2737         return spec(start, size, UNDEFINED_ALIGNMENT);
   2738     }
   2739 
   2740     /**
   2741      * Return a Spec, {@code spec}, where:
   2742      * <ul>
   2743      *     <li> {@code spec.span = [start, start + 1]} </li>
   2744      * </ul>
   2745      * <p>
   2746      * To leave the start index undefined, use the value {@link #UNDEFINED}.
   2747      *
   2748      * @param start     the start index
   2749      *
   2750      * @see #spec(int, int)
   2751      */
   2752     public static Spec spec(int start) {
   2753         return spec(start, 1);
   2754     }
   2755 
   2756     /**
   2757      * Alignments specify where a view should be placed within a cell group and
   2758      * what size it should be.
   2759      * <p>
   2760      * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
   2761      * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
   2762      * {@code alignment}. Overall placement of the view in the cell
   2763      * group is specified by the two alignments which act along each axis independently.
   2764      * <p>
   2765      *  The GridLayout class defines the most common alignments used in general layout:
   2766      * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
   2767      * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
   2768      */
   2769     /*
   2770      * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
   2771      * to return the appropriate value for the type of alignment being defined.
   2772      * The enclosing algorithms position the children
   2773      * so that the locations defined by the alignment values
   2774      * are the same for all of the views in a group.
   2775      * <p>
   2776      */
   2777     public static abstract class Alignment {
   2778         Alignment() {
   2779         }
   2780 
   2781         abstract int getGravityOffset(View view, int cellDelta);
   2782 
   2783         /**
   2784          * Returns an alignment value. In the case of vertical alignments the value
   2785          * returned should indicate the distance from the top of the view to the
   2786          * alignment location.
   2787          * For horizontal alignments measurement is made from the left edge of the component.
   2788          *
   2789          * @param view              the view to which this alignment should be applied
   2790          * @param viewSize          the measured size of the view
   2791          * @param mode              the basis of alignment: CLIP or OPTICAL
   2792          * @return the alignment value
   2793          */
   2794         abstract int getAlignmentValue(View view, int viewSize, int mode);
   2795 
   2796         /**
   2797          * Returns the size of the view specified by this alignment.
   2798          * In the case of vertical alignments this method should return a height; for
   2799          * horizontal alignments this method should return the width.
   2800          * <p>
   2801          * The default implementation returns {@code viewSize}.
   2802          *
   2803          * @param view              the view to which this alignment should be applied
   2804          * @param viewSize          the measured size of the view
   2805          * @param cellSize          the size of the cell into which this view will be placed
   2806          * @return the aligned size
   2807          */
   2808         int getSizeInCell(View view, int viewSize, int cellSize) {
   2809             return viewSize;
   2810         }
   2811 
   2812         Bounds getBounds() {
   2813             return new Bounds();
   2814         }
   2815     }
   2816 
   2817     @UnsupportedAppUsage
   2818     static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
   2819         @Override
   2820         int getGravityOffset(View view, int cellDelta) {
   2821             return UNDEFINED;
   2822         }
   2823 
   2824         @Override
   2825         public int getAlignmentValue(View view, int viewSize, int mode) {
   2826             return UNDEFINED;
   2827         }
   2828     };
   2829 
   2830     /**
   2831      * Indicates that a view should be aligned with the <em>start</em>
   2832      * edges of the other views in its cell group.
   2833      */
   2834     private static final Alignment LEADING = new Alignment() {
   2835         @Override
   2836         int getGravityOffset(View view, int cellDelta) {
   2837             return 0;
   2838         }
   2839 
   2840         @Override
   2841         public int getAlignmentValue(View view, int viewSize, int mode) {
   2842             return 0;
   2843         }
   2844     };
   2845 
   2846     /**
   2847      * Indicates that a view should be aligned with the <em>end</em>
   2848      * edges of the other views in its cell group.
   2849      */
   2850     private static final Alignment TRAILING = new Alignment() {
   2851         @Override
   2852         int getGravityOffset(View view, int cellDelta) {
   2853             return cellDelta;
   2854         }
   2855 
   2856         @Override
   2857         public int getAlignmentValue(View view, int viewSize, int mode) {
   2858             return viewSize;
   2859         }
   2860     };
   2861 
   2862     /**
   2863      * Indicates that a view should be aligned with the <em>top</em>
   2864      * edges of the other views in its cell group.
   2865      */
   2866     public static final Alignment TOP = LEADING;
   2867 
   2868     /**
   2869      * Indicates that a view should be aligned with the <em>bottom</em>
   2870      * edges of the other views in its cell group.
   2871      */
   2872     public static final Alignment BOTTOM = TRAILING;
   2873 
   2874     /**
   2875      * Indicates that a view should be aligned with the <em>start</em>
   2876      * edges of the other views in its cell group.
   2877      */
   2878     public static final Alignment START = LEADING;
   2879 
   2880     /**
   2881      * Indicates that a view should be aligned with the <em>end</em>
   2882      * edges of the other views in its cell group.
   2883      */
   2884     public static final Alignment END = TRAILING;
   2885 
   2886     private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
   2887         return new Alignment() {
   2888             @Override
   2889             int getGravityOffset(View view, int cellDelta) {
   2890                 return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
   2891             }
   2892 
   2893             @Override
   2894             public int getAlignmentValue(View view, int viewSize, int mode) {
   2895                 return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
   2896             }
   2897         };
   2898     }
   2899 
   2900     /**
   2901      * Indicates that a view should be aligned with the <em>left</em>
   2902      * edges of the other views in its cell group.
   2903      */
   2904     public static final Alignment LEFT = createSwitchingAlignment(START, END);
   2905 
   2906     /**
   2907      * Indicates that a view should be aligned with the <em>right</em>
   2908      * edges of the other views in its cell group.
   2909      */
   2910     public static final Alignment RIGHT = createSwitchingAlignment(END, START);
   2911 
   2912     /**
   2913      * Indicates that a view should be <em>centered</em> with the other views in its cell group.
   2914      * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
   2915      * LayoutParams#columnSpec columnSpecs}.
   2916      */
   2917     public static final Alignment CENTER = new Alignment() {
   2918         @Override
   2919         int getGravityOffset(View view, int cellDelta) {
   2920             return cellDelta >> 1;
   2921         }
   2922 
   2923         @Override
   2924         public int getAlignmentValue(View view, int viewSize, int mode) {
   2925             return viewSize >> 1;
   2926         }
   2927     };
   2928 
   2929     /**
   2930      * Indicates that a view should be aligned with the <em>baselines</em>
   2931      * of the other views in its cell group.
   2932      * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
   2933      *
   2934      * @see View#getBaseline()
   2935      */
   2936     public static final Alignment BASELINE = new Alignment() {
   2937         @Override
   2938         int getGravityOffset(View view, int cellDelta) {
   2939             return 0; // baseline gravity is top
   2940         }
   2941 
   2942         @Override
   2943         public int getAlignmentValue(View view, int viewSize, int mode) {
   2944             if (view.getVisibility() == GONE) {
   2945                 return 0;
   2946             }
   2947             int baseline = view.getBaseline();
   2948             return baseline == -1 ? UNDEFINED : baseline;
   2949         }
   2950 
   2951         @Override
   2952         public Bounds getBounds() {
   2953             return new Bounds() {
   2954                 /*
   2955                 In a baseline aligned row in which some components define a baseline
   2956                 and some don't, we need a third variable to properly account for all
   2957                 the sizes. This tracks the maximum size of all the components -
   2958                 including those that don't define a baseline.
   2959                 */
   2960                 private int size;
   2961 
   2962                 @Override
   2963                 protected void reset() {
   2964                     super.reset();
   2965                     size = Integer.MIN_VALUE;
   2966                 }
   2967 
   2968                 @Override
   2969                 protected void include(int before, int after) {
   2970                     super.include(before, after);
   2971                     size = max(size, before + after);
   2972                 }
   2973 
   2974                 @Override
   2975                 protected int size(boolean min) {
   2976                     return max(super.size(min), size);
   2977                 }
   2978 
   2979                 @Override
   2980                 protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
   2981                     return max(0, super.getOffset(gl, c, a, size, hrz));
   2982                 }
   2983             };
   2984         }
   2985     };
   2986 
   2987     /**
   2988      * Indicates that a view should expanded to fit the boundaries of its cell group.
   2989      * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
   2990      * {@link LayoutParams#columnSpec columnSpecs}.
   2991      */
   2992     public static final Alignment FILL = new Alignment() {
   2993         @Override
   2994         int getGravityOffset(View view, int cellDelta) {
   2995             return 0;
   2996         }
   2997 
   2998         @Override
   2999         public int getAlignmentValue(View view, int viewSize, int mode) {
   3000             return UNDEFINED;
   3001         }
   3002 
   3003         @Override
   3004         public int getSizeInCell(View view, int viewSize, int cellSize) {
   3005             return cellSize;
   3006         }
   3007     };
   3008 
   3009     static boolean canStretch(int flexibility) {
   3010         return (flexibility & CAN_STRETCH) != 0;
   3011     }
   3012 
   3013     private static final int INFLEXIBLE = 0;
   3014     private static final int CAN_STRETCH = 2;
   3015 }
   3016