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