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