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