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