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