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