GridLayout.java revision 4c8cf4c93314722a77ce69396b9cb201ac007a58
13f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne/*
23f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Copyright (C) 2011 The Android Open Source Project
33f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
43f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Licensed under the Apache License, Version 2.0 (the "License");
53f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * you may not use this file except in compliance with the License.
63f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * You may obtain a copy of the License at
73f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
83f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *      http://www.apache.org/licenses/LICENSE-2.0
93f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Unless required by applicable law or agreed to in writing, software
113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * distributed under the License is distributed on an "AS IS" BASIS,
123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * See the License for the specific language governing permissions and
143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * limitations under the License.
153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne */
163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milnepackage android.widget;
183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.content.Context;
203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.content.res.TypedArray;
213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.graphics.Canvas;
223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.graphics.Color;
233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.graphics.Paint;
243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.util.AttributeSet;
253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.util.Log;
2648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milneimport android.util.Pair;
273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.view.Gravity;
283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.view.View;
293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport android.view.ViewGroup;
30b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milneimport com.android.internal.R;
313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.lang.reflect.Array;
333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.ArrayList;
343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.Arrays;
353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.HashMap;
363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.List;
373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.Map;
383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
395125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milneimport static android.view.Gravity.*;
40899d5922870c78e0e663bc5661849eb468afc984Philip Milneimport static android.view.View.MeasureSpec.EXACTLY;
41899d5922870c78e0e663bc5661849eb468afc984Philip Milneimport static android.view.View.MeasureSpec.makeMeasureSpec;
423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport static java.lang.Math.max;
433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport static java.lang.Math.min;
443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne/**
463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * A layout that places its children in a rectangular <em>grid</em>.
473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <p>
483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * The grid is composed of a set of infinitely thin lines that separate the
493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced
507fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * by grid <em>indices</em>. A grid with {@code N} columns
517fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * has {@code N + 1} grid indices that run from {@code 0}
527fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * through {@code N} inclusive. Regardless of how GridLayout is
537fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * configured, grid index {@code 0} is fixed to the leading edge of the
547fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * container and grid index {@code N} is fixed to its trailing edge
553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * (after padding is taken into account).
563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
5793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * <h4>Row and Column Specs</h4>
583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Children occupy one or more contiguous cells, as defined
6093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and
6193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters.
6293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * Each spec defines the set of rows or columns that are to be
633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * occupied; and how children should be aligned within the resulting group of cells.
643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Although cells do not normally overlap in a GridLayout, GridLayout does
653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * not prevent children being defined to occupy the same cell or group of cells.
663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * In this case however, there is no guarantee that children will not themselves
673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * overlap after the layout operation completes.
683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <h4>Default Cell Assignment</h4>
703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
7148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne * If a child does not specify the row and column indices of the cell it
723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * wishes to occupy, GridLayout assigns cell locations automatically using its:
733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setOrientation(int) orientation},
743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setRowCount(int) rowCount} and
753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setColumnCount(int) columnCount} properties.
763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <h4>Space</h4>
783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Space between children may be specified either by using instances of the
803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * dedicated {@link Space} view or by setting the
813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#leftMargin leftMargin},
833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#topMargin topMargin},
843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#rightMargin rightMargin} and
853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#bottomMargin bottomMargin}
863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * layout parameters. When the
883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins}
893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * property is set, default margins around children are automatically
903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * allocated based on the child's visual characteristics. Each of the
913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * margins so defined may be independently overridden by an assignment
923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * to the appropriate layout parameter.
933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <h4>Excess Space Distribution</h4>
953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
96899d5922870c78e0e663bc5661849eb468afc984Philip Milne * A child's ability to stretch is inferred from the alignment properties of
97899d5922870c78e0e663bc5661849eb468afc984Philip Milne * its row and column groups (which are typically set by setting the
98899d5922870c78e0e663bc5661849eb468afc984Philip Milne * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters).
99899d5922870c78e0e663bc5661849eb468afc984Philip Milne * If alignment was defined along a given axis then the component
100899d5922870c78e0e663bc5661849eb468afc984Philip Milne * is taken as flexible in along that axis. If no alignment was set,
101899d5922870c78e0e663bc5661849eb468afc984Philip Milne * the component is instead assumed to be inflexible. Multiple components in
102899d5922870c78e0e663bc5661849eb468afc984Philip Milne * the same row or column group are considered to act in <em>parallel</em>. Such a
103899d5922870c78e0e663bc5661849eb468afc984Philip Milne * group is flexible only if <em>all</em> of the components
104899d5922870c78e0e663bc5661849eb468afc984Philip Milne * within it are flexible. Row and column groups that sit either side of a common boundary
105899d5922870c78e0e663bc5661849eb468afc984Philip Milne * are instead considered to act in <em>series</em>. The composite group made of these two
106899d5922870c78e0e663bc5661849eb468afc984Philip Milne * elements is flexible if <em>one</em> of its elements is flexible.
107899d5922870c78e0e663bc5661849eb468afc984Philip Milne * <p>
108899d5922870c78e0e663bc5661849eb468afc984Philip Milne * To make a column stretch, make sure all of the components inside it define a
109899d5922870c78e0e663bc5661849eb468afc984Philip Milne * gravity. To prevent a column from stretching, ensure that one of the components
110899d5922870c78e0e663bc5661849eb468afc984Philip Milne * in the column does not define a gravity.
1113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <p>
1123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <p>
1133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * See {@link GridLayout.LayoutParams} for a full description of the
1143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * layout parameters used by GridLayout.
1153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
1163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_orientation
1173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_rowCount
1183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_columnCount
1193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_useDefaultMargins
1203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
1213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
1223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne */
1233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milnepublic class GridLayout extends ViewGroup {
1243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Public constants
1263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
1283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The horizontal orientation.
1293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
1303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
131aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
1333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The vertical orientation.
1343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
1353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final int VERTICAL = LinearLayout.VERTICAL;
1363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
137aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
138aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * The constant used to indicate that a value is undefined.
139aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * Fields can use this value to indicate that their values
140aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * have not yet been set. Similarly, methods can return this value
141aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * to indicate that there is no suitable value that the implementation
142aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * can return.
143aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * The value used for the constant (currently {@link Integer#MIN_VALUE}) is
144aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * intended to avoid confusion between valid values whose sign may not be known.
145aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
146aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    public static final int UNDEFINED = Integer.MIN_VALUE;
147aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1481e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    /**
1491e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
1501e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * When the {@code alignmentMode} is set to {@link #ALIGN_BOUNDS}, alignment
1511e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * is made between the edges of each component's raw
1521e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * view boundary: i.e. the area delimited by the component's:
1531e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getTop() top},
1541e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getLeft() left},
1551e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getBottom() bottom} and
1561e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getRight() right} properties.
1571e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * <p>
1581e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * For example, when {@code GridLayout} is in {@link #ALIGN_BOUNDS} mode,
1591e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * children that belong to a row group that uses {@link #TOP} alignment will
1601e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * all return the same value when their {@link android.view.View#getTop()}
1611e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * method is called.
1621e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
1631e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
1641e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     */
1651e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public static final int ALIGN_BOUNDS = 0;
1661e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne
1671e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    /**
1681e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
1691e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * When the {@code alignmentMode} is set to {@link #ALIGN_MARGINS},
1701e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * the bounds of each view are extended outwards, according
1711e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * to their margins, before the edges of the resulting rectangle are aligned.
1721e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * <p>
1731e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * For example, when {@code GridLayout} is in {@link #ALIGN_MARGINS} mode,
1741e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * the quantity {@code top - layoutParams.topMargin} is the same for all children that
1751e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * belong to a row group that uses {@link #TOP} alignment.
1761e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
1771e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
1781e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     */
1791e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public static final int ALIGN_MARGINS = 1;
1801e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne
1813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Misc constants
1823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final String TAG = GridLayout.class.getName();
1845125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    private static boolean DEBUG = false;
1853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final int PRF = 1;
1863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Defaults
1883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final int DEFAULT_ORIENTATION = HORIZONTAL;
1903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final int DEFAULT_COUNT = UNDEFINED;
1913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
192899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static final boolean DEFAULT_ORDER_PRESERVED = true;
1931e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
19493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    private static final int DEFAULT_CONTAINER_MARGIN = 0;
19548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    private static final int MAX_SIZE = 100000;
1963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // TypedArray indices
1983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
199b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ORIENTATION = R.styleable.GridLayout_orientation;
200b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ROW_COUNT = R.styleable.GridLayout_rowCount;
201b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount;
202b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins;
203b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode;
204b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved;
205b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved;
2063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Instance variables
2083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private final Axis mHorizontalAxis = new Axis(true);
2103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private final Axis mVerticalAxis = new Axis(false);
2113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private boolean mLayoutParamsValid = false;
2123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private int mOrientation = DEFAULT_ORIENTATION;
2133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
2141e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    private int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
215b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private int mDefaultGap;
216aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
2173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Constructors
2183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
2203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * {@inheritDoc}
2213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public GridLayout(Context context, AttributeSet attrs, int defStyle) {
2233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        super(context, attrs, defStyle);
224c655ba5e467090eb4f839f148ac31b50c389ffb2Jim Miller        if (DEBUG) {
225c655ba5e467090eb4f839f148ac31b50c389ffb2Jim Miller            setWillNotDraw(false);
226c655ba5e467090eb4f839f148ac31b50c389ffb2Jim Miller        }
227b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
228b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout);
2293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        try {
2301e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne            setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
2311e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne            setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT));
2325125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            setOrientation(a.getInt(ORIENTATION, DEFAULT_ORIENTATION));
2335125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            setUseDefaultMargins(a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS));
2345125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            setAlignmentMode(a.getInt(ALIGNMENT_MODE, DEFAULT_ALIGNMENT_MODE));
2353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
2363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
2373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        } finally {
2383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            a.recycle();
2393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
2403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
2413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
24248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    /**
24348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     * {@inheritDoc}
24448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     */
24548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    public GridLayout(Context context, AttributeSet attrs) {
24648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        this(context, attrs, 0);
24748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    }
24848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
24948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    /**
25048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     * {@inheritDoc}
25148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     */
25248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    public GridLayout(Context context) {
2534c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        //noinspection NullableProblems
25448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        this(context, null);
25548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    }
25648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
2573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Implementation
2583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
2603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns the current orientation.
2613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2627fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
2633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setOrientation(int)
2653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_orientation
2673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public int getOrientation() {
2693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return mOrientation;
2703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
2713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
2733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The orientation property does not affect layout. Orientation is used
2743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * only to generate default row/column indices when they are not specified
2753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * by a component's layout parameters.
2767fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
2777fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * The default value of this property is {@link #HORIZONTAL}.
2783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2797fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
2803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getOrientation()
2823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_orientation
2843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setOrientation(int orientation) {
2863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        if (mOrientation != orientation) {
2873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            mOrientation = orientation;
2883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            requestLayout();
2893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
2903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
2913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
2933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns the current number of rows. This is either the last value that was set
2943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * with {@link #setRowCount(int)} or, if no such value was set, the maximum
29593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * value of each the upper bounds defined in {@link LayoutParams#rowSpec}.
2963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @return the current number of rows
2983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setRowCount(int)
30093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#rowSpec
3013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowCount
3033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public int getRowCount() {
3053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return mVerticalAxis.getCount();
3063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The rowCount property does not affect layout. RowCount is used
3103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * only to generate default row/column indices when they are not specified
3113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * by a component's layout parameters.
3123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3137fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param rowCount the number of rows
3143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getRowCount()
31693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#rowSpec
3173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowCount
3193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setRowCount(int rowCount) {
3213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mVerticalAxis.setCount(rowCount);
3223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns the current number of columns. This is either the last value that was set
3263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * with {@link #setColumnCount(int)} or, if no such value was set, the maximum
32793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * value of each the upper bounds defined in {@link LayoutParams#columnSpec}.
3283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @return the current number of columns
3303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setColumnCount(int)
33293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#columnSpec
3333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnCount
3353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public int getColumnCount() {
3373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return mHorizontalAxis.getCount();
3383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The columnCount property does not affect layout. ColumnCount is used
3423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * only to generate default column/column indices when they are not specified
3433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * by a component's layout parameters.
3443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @param columnCount the number of columns.
3463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getColumnCount()
34893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#columnSpec
3493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnCount
3513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setColumnCount(int columnCount) {
3533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mHorizontalAxis.setCount(columnCount);
3543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns whether or not this GridLayout will allocate default margins when no
3583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * corresponding layout parameters are defined.
3593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3607fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return {@code true} if default margins should be allocated
3613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setUseDefaultMargins(boolean)
3633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_useDefaultMargins
3653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public boolean getUseDefaultMargins() {
3673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return mUseDefaultMargins;
3683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3717fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When {@code true}, GridLayout allocates default margins around children
3723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * based on the child's visual characteristics. Each of the
3733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * margins so defined may be independently overridden by an assignment
3743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * to the appropriate layout parameter.
3753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
3767fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When {@code false}, the default value of all margins is zero.
377aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * <p>
3787fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When setting to {@code true}, consider setting the value of the
3791e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link #setAlignmentMode(int) alignmentMode}
3801e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * property to {@link #ALIGN_BOUNDS}.
3817fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
3827fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * The default value of this property is {@code false}.
3833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3847fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param useDefaultMargins use {@code true} to make GridLayout allocate default margins
3853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getUseDefaultMargins()
3871e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
3883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#leftMargin
3903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#topMargin
3913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#rightMargin
3923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#bottomMargin
3933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_useDefaultMargins
3953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setUseDefaultMargins(boolean useDefaultMargins) {
3973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mUseDefaultMargins = useDefaultMargins;
39893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        if (useDefaultMargins) {
399b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne            int padding = mDefaultGap;
40093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            setPadding(padding, padding, padding, padding);
40193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        }
402aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
403aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
404aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
405aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
4061e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * Returns the alignment mode.
4071e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
4081e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @return the alignment mode; either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
409aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4101e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_BOUNDS
4111e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_MARGINS
412aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4131e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
414aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4151e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @attr ref android.R.styleable#GridLayout_alignmentMode
416aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
4171e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public int getAlignmentMode() {
4181e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne        return mAlignmentMode;
419aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
420aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
421aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
4221e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * Sets the alignment mode to be used for all of the alignments between the
4231e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * children of this container.
4247fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
4251e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * The default value of this property is {@link #ALIGN_MARGINS}.
4261e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
4271e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @param alignmentMode either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
428aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4291e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_BOUNDS
4301e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_MARGINS
431aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4321e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #getAlignmentMode()
433aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4341e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @attr ref android.R.styleable#GridLayout_alignmentMode
435aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
4361e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public void setAlignmentMode(int alignmentMode) {
4371e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne        mAlignmentMode = alignmentMode;
438aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
4393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
4403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
4413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
4423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns whether or not row boundaries are ordered by their grid indices.
4433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4447fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return {@code true} if row boundaries must appear in the order of their indices,
4457fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *         {@code false} otherwise
4463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setRowOrderPreserved(boolean)
4483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
4503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
4513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public boolean isRowOrderPreserved() {
4523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return mVerticalAxis.isOrderPreserved();
4533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
4543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
4553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
4567fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When this property is {@code true}, GridLayout is forced to place the row boundaries
457aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * so that their associated grid indices are in ascending order in the view.
4583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
459899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * When this property is {@code false} GridLayout is at liberty to place the vertical row
460899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * boundaries in whatever order best fits the given constraints.
4617fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
462899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The default value of this property is {@code true}.
4637fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
4647fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param rowOrderPreserved {@code true} to force GridLayout to respect the order
4657fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *        of row boundaries
4663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #isRowOrderPreserved()
4683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
4703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
4713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setRowOrderPreserved(boolean rowOrderPreserved) {
4723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mVerticalAxis.setOrderPreserved(rowOrderPreserved);
473aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        invalidateStructure();
474aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
4753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
4763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
4773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
4783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns whether or not column boundaries are ordered by their grid indices.
4793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4807fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return {@code true} if column boundaries must appear in the order of their indices,
4817fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *         {@code false} otherwise
4823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setColumnOrderPreserved(boolean)
4843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
4863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
4873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public boolean isColumnOrderPreserved() {
4883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return mHorizontalAxis.isOrderPreserved();
4893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
4903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
4913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
4927fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When this property is {@code true}, GridLayout is forced to place the column boundaries
493aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * so that their associated grid indices are in ascending order in the view.
4943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
495899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * When this property is {@code false} GridLayout is at liberty to place the horizontal column
496899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * boundaries in whatever order best fits the given constraints.
4977fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
498899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The default value of this property is {@code true}.
4993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5007fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
5013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *        of column boundaries.
5023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #isColumnOrderPreserved()
5043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
5063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
5073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setColumnOrderPreserved(boolean columnOrderPreserved) {
5083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
509aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        invalidateStructure();
510aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
5113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5135125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    // Static utility methods
5145125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
51551f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne    private static int max2(int[] a, int valueIfEmpty) {
51651f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        int result = valueIfEmpty;
51751f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        for (int i = 0, N = a.length; i < N; i++) {
51851f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne            result = Math.max(result, a[i]);
51951f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        }
52051f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        return result;
52151f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne    }
52251f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne
523899d5922870c78e0e663bc5661849eb468afc984Philip Milne    @SuppressWarnings("unchecked")
52448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    private static <T> T[] append(T[] a, T[] b) {
52548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
52648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        System.arraycopy(a, 0, result, 0, a.length);
52748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        System.arraycopy(b, 0, result, a.length, b.length);
5283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return result;
5293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5315125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    private static Alignment getAlignment(int gravity, boolean horizontal) {
5325125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
5335125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
5345125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        int flags = (gravity & mask) >> shift;
5355125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        switch (flags) {
5365125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
5375125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return LEADING;
5385125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
5395125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return TRAILING;
5405125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
5415125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return FILL;
5425125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case AXIS_SPECIFIED:
5435125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return CENTER;
5445125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            default:
5455125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return UNDEFINED_ALIGNMENT;
5465125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
5475125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
5485125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
5494c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    /** @noinspection UnusedParameters*/
5501fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getDefaultMargin(View c, boolean horizontal, boolean leading) {
551899d5922870c78e0e663bc5661849eb468afc984Philip Milne        if (c.getClass() == Space.class) {
552899d5922870c78e0e663bc5661849eb468afc984Philip Milne            return 0;
553899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
554b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        return mDefaultGap / 2;
5553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5571fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
5581fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return isAtEdge ? DEFAULT_CONTAINER_MARGIN : getDefaultMargin(c, horizontal, leading);
5593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5611fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getDefaultMarginValue(View c, LayoutParams p, boolean horizontal, boolean leading) {
5623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        if (!mUseDefaultMargins) {
5633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return 0;
5643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
56593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        Spec spec = horizontal ? p.columnSpec : p.rowSpec;
5663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
56793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        Interval span = spec.span;
568aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        boolean isAtEdge = leading ? (span.min == 0) : (span.max == axis.getCount());
5693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5701fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return getDefaultMargin(c, isAtEdge, horizontal, leading);
5713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5734c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    private int getMargin1(View view, boolean horizontal, boolean leading) {
5743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        LayoutParams lp = getLayoutParams(view);
5753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int margin = horizontal ?
576aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                (leading ? lp.leftMargin : lp.rightMargin) :
577aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                (leading ? lp.topMargin : lp.bottomMargin);
5781fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return margin == UNDEFINED ? getDefaultMarginValue(view, lp, horizontal, leading) : margin;
5791fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    }
5801fd16378812792913a6aa6923acbec20037e09ffPhilip Milne
5814c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    private int getMargin(View view, boolean horizontal, boolean leading) {
5824c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        if (mAlignmentMode == ALIGN_MARGINS) {
5834c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return getMargin1(view, horizontal, leading);
5844c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        } else {
5854c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
5864c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
5874c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            LayoutParams lp = getLayoutParams(view);
5884c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
5894c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int index = leading ? spec.span.min : spec.span.max;
5904c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return margins[index];
5914c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        }
5924c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    }
5934c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne
5941fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getTotalMargin(View child, boolean horizontal) {
5951fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
5963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
598f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne    private static int valueIfDefined(int value, int defaultValue) {
599f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne        return (value != UNDEFINED) ? value : defaultValue;
6003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
6013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
602899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static boolean fits(int[] a, int value, int start, int end) {
603899d5922870c78e0e663bc5661849eb468afc984Philip Milne        if (end > a.length) {
604899d5922870c78e0e663bc5661849eb468afc984Philip Milne            return false;
605899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
606899d5922870c78e0e663bc5661849eb468afc984Philip Milne        for (int i = start; i < end; i++) {
607899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (a[i] > value) {
608899d5922870c78e0e663bc5661849eb468afc984Philip Milne                return false;
6093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
610899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
611899d5922870c78e0e663bc5661849eb468afc984Philip Milne        return true;
612899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
6133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
614899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static void procrusteanFill(int[] a, int start, int end, int value) {
615899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int length = a.length;
616899d5922870c78e0e663bc5661849eb468afc984Philip Milne        Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
617899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
6183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
619899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static void setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan) {
620899d5922870c78e0e663bc5661849eb468afc984Philip Milne        lp.setRowSpecSpan(new Interval(row, row + rowSpan));
621899d5922870c78e0e663bc5661849eb468afc984Philip Milne        lp.setColumnSpecSpan(new Interval(col, col + colSpan));
622899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
6233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
624899d5922870c78e0e663bc5661849eb468afc984Philip Milne    // Logic to avert infinite loops by ensuring that the cells can be placed somewhere.
625899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static int clip(Interval minorRange, boolean minorWasDefined, int count) {
626899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int size = minorRange.size();
627899d5922870c78e0e663bc5661849eb468afc984Philip Milne        if (count == 0) {
628899d5922870c78e0e663bc5661849eb468afc984Philip Milne            return size;
629899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
630899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int min = minorWasDefined ? min(minorRange.min, count) : 0;
631899d5922870c78e0e663bc5661849eb468afc984Philip Milne        return min(size, count - min);
632899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
633f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne
634899d5922870c78e0e663bc5661849eb468afc984Philip Milne    // install default indices for cells that don't define them
635899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private void validateLayoutParams() {
636899d5922870c78e0e663bc5661849eb468afc984Philip Milne        final boolean horizontal = (mOrientation == HORIZONTAL);
637899d5922870c78e0e663bc5661849eb468afc984Philip Milne        final int axisCount = horizontal ? mHorizontalAxis.count : mVerticalAxis.count;
638899d5922870c78e0e663bc5661849eb468afc984Philip Milne        final int count = valueIfDefined(axisCount, 0);
639f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne
640899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int major = 0;
641899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int minor = 0;
642899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int[] maxSizes = new int[count];
643f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne
644899d5922870c78e0e663bc5661849eb468afc984Philip Milne        for (int i = 0, N = getChildCount(); i < N; i++) {
645899d5922870c78e0e663bc5661849eb468afc984Philip Milne            LayoutParams lp = getLayoutParams1(getChildAt(i));
646899d5922870c78e0e663bc5661849eb468afc984Philip Milne
647899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final Interval majorRange = (horizontal ? lp.rowSpec : lp.columnSpec).span;
648899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final boolean majorWasDefined = (majorRange.min != UNDEFINED);
649899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final int majorSpan = majorRange.size();
650899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (majorWasDefined) {
651899d5922870c78e0e663bc5661849eb468afc984Philip Milne                major = majorRange.min;
652899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
653899d5922870c78e0e663bc5661849eb468afc984Philip Milne
654899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final Interval minorRange = (horizontal ? lp.columnSpec : lp.rowSpec).span;
655899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final boolean minorWasDefined = (minorRange.min != UNDEFINED);
656899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final int minorSpan = clip(minorRange, minorWasDefined, count);
657899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (minorWasDefined) {
658899d5922870c78e0e663bc5661849eb468afc984Philip Milne                minor = minorRange.min;
659899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
660899d5922870c78e0e663bc5661849eb468afc984Philip Milne
661899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (count != 0) {
662899d5922870c78e0e663bc5661849eb468afc984Philip Milne                // Find suitable row/col values when at least one is undefined.
663899d5922870c78e0e663bc5661849eb468afc984Philip Milne                if (!majorWasDefined || !minorWasDefined) {
664899d5922870c78e0e663bc5661849eb468afc984Philip Milne                    while (!fits(maxSizes, major, minor, minor + minorSpan)) {
665899d5922870c78e0e663bc5661849eb468afc984Philip Milne                        if (minorWasDefined) {
666899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            major++;
667899d5922870c78e0e663bc5661849eb468afc984Philip Milne                        } else {
668899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            if (minor + minorSpan <= count) {
669899d5922870c78e0e663bc5661849eb468afc984Philip Milne                                minor++;
670899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            } else {
671899d5922870c78e0e663bc5661849eb468afc984Philip Milne                                minor = 0;
672899d5922870c78e0e663bc5661849eb468afc984Philip Milne                                major++;
673899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            }
674f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne                        }
675f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne                    }
6763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
677899d5922870c78e0e663bc5661849eb468afc984Philip Milne                procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
6783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
679899d5922870c78e0e663bc5661849eb468afc984Philip Milne
680899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (horizontal) {
681899d5922870c78e0e663bc5661849eb468afc984Philip Milne                setCellGroup(lp, major, majorSpan, minor, minorSpan);
682899d5922870c78e0e663bc5661849eb468afc984Philip Milne            } else {
683899d5922870c78e0e663bc5661849eb468afc984Philip Milne                setCellGroup(lp, minor, minorSpan, major, majorSpan);
684899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
685899d5922870c78e0e663bc5661849eb468afc984Philip Milne
686899d5922870c78e0e663bc5661849eb468afc984Philip Milne            minor = minor + minorSpan;
687899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
688f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne        invalidateStructure();
6893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
6903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
6913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private void invalidateStructure() {
6923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mLayoutParamsValid = false;
6933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mHorizontalAxis.invalidateStructure();
6943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mVerticalAxis.invalidateStructure();
695899d5922870c78e0e663bc5661849eb468afc984Philip Milne        // This can end up being done twice. Better twice than not at all.
6963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        invalidateValues();
6973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
6983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
6993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private void invalidateValues() {
700aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        // Need null check because requestLayout() is called in View's initializer,
701aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        // before we are set up.
702aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        if (mHorizontalAxis != null && mVerticalAxis != null) {
703aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            mHorizontalAxis.invalidateValues();
704aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            mVerticalAxis.invalidateValues();
705aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
7063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private LayoutParams getLayoutParams1(View c) {
7093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return (LayoutParams) c.getLayoutParams();
7103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private LayoutParams getLayoutParams(View c) {
7133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        if (!mLayoutParamsValid) {
7143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            validateLayoutParams();
7153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            mLayoutParamsValid = true;
7163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
7173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return getLayoutParams1(c);
7183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    protected LayoutParams generateDefaultLayoutParams() {
7223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return new LayoutParams();
7233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public LayoutParams generateLayoutParams(AttributeSet attrs) {
7275125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return new LayoutParams(getContext(), attrs);
7283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
7323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return new LayoutParams(p);
7333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Draw grid
7363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
7383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int dx = getPaddingLeft();
7393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int dy = getPaddingTop();
7403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint);
7413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    protected void onDraw(Canvas canvas) {
7453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        super.onDraw(canvas);
7463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        if (DEBUG) {
7483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int height = getHeight() - getPaddingTop() - getPaddingBottom();
7493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int width = getWidth() - getPaddingLeft() - getPaddingRight();
7503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
751b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            Paint paint = new Paint();
7524c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            paint.setStyle(Paint.Style.STROKE);
753b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            paint.setColor(Color.argb(50, 255, 255, 255));
754b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
7553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int[] xs = mHorizontalAxis.locations;
756b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (xs != null) {
757b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                for (int i = 0, length = xs.length; i < length; i++) {
758b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    int x = xs[i];
759b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    drawLine(canvas, x, 0, x, height - 1, paint);
760b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                }
7613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
762b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
7633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int[] ys = mVerticalAxis.locations;
764b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (ys != null) {
765b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                for (int i = 0, length = ys.length; i < length; i++) {
766b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    int y = ys[i];
767b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    drawLine(canvas, 0, y, width - 1, y, paint);
768b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                }
769b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            }
77048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
771b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            // Draw bounds
772b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            paint.setColor(Color.BLUE);
773b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0; i < getChildCount(); i++) {
774b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
7754c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                canvas.drawRect(c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), paint);
776b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            }
777b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
778b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            // Draw margins
77993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            paint.setColor(Color.MAGENTA);
780b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0; i < getChildCount(); i++) {
781b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
7824c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                canvas.drawRect(
7834c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getLeft() - getMargin1(c, true, true),
7844c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getTop() - getMargin1(c, false, true),
7854c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getRight() + getMargin1(c, true, false),
7864c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getBottom() + getMargin1(c, false, false), paint);
7873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
7883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
7893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Add/remove
7923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
793e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne    /**
794e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     * @hide
795e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     */
7963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
797f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne    protected void onViewAdded(View child) {
798f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne        super.onViewAdded(child);
7993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        invalidateStructure();
8003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
8013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
802e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne    /**
803e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     * @hide
804e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     */
8053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
806f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne    protected void onViewRemoved(View child) {
807f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne        super.onViewRemoved(child);
808b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        invalidateStructure();
809350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne    }
810350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne
811350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne    /**
812350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     * We need to call invalidateStructure() when a child's GONE flag changes state.
813350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     * This implementation is a catch-all, invalidating on any change in the visibility flags.
814350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     *
815350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     * @hide
816350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     */
817350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne    @Override
818350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne    protected void onChildVisibilityChanged(View child, int visibility) {
819350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne        super.onChildVisibilityChanged(child, visibility);
820350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne        invalidateStructure();
821b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    }
822b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne
8233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Measurement
8243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
825b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    private boolean isGone(View c) {
826b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        return c.getVisibility() == View.GONE;
827b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    }
828b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
8291fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private void measureChildWithMargins(View child, int widthMeasureSpec, int heightMeasureSpec) {
830b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        LayoutParams lp = getLayoutParams(child);
8311fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
8321fd16378812792913a6aa6923acbec20037e09ffPhilip Milne                mPaddingLeft + mPaddingRight + getTotalMargin(child, true), lp.width);
8331fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
8341fd16378812792913a6aa6923acbec20037e09ffPhilip Milne                mPaddingTop + mPaddingBottom + getTotalMargin(child, false), lp.height);
835b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
836b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    }
837b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
838b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    private void measureChildrenWithMargins(int widthMeasureSpec, int heightMeasureSpec) {
839b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        for (int i = 0, N = getChildCount(); i < N; i++) {
840b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            View c = getChildAt(i);
841b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (isGone(c)) continue;
842b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            measureChildWithMargins(c, widthMeasureSpec, heightMeasureSpec);
843b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        }
844b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    }
845b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
846aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    @Override
847aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    protected void onMeasure(int widthSpec, int heightSpec) {
848b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        measureChildrenWithMargins(widthSpec, heightSpec);
849aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
85048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        int width = getPaddingLeft() + mHorizontalAxis.getMeasure(widthSpec) + getPaddingRight();
85148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        int height = getPaddingTop() + mVerticalAxis.getMeasure(heightSpec) + getPaddingBottom();
8523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
85348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        int measuredWidth = Math.max(width, getSuggestedMinimumWidth());
85448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        int measuredHeight = Math.max(height, getSuggestedMinimumHeight());
85509e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne
8563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        setMeasuredDimension(
85709e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne                resolveSizeAndState(measuredWidth, widthSpec, 0),
85809e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne                resolveSizeAndState(measuredHeight, heightSpec, 0));
8593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
8603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
8613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private int protect(int alignment) {
862aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        return (alignment == UNDEFINED) ? 0 : alignment;
8633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
8643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
86548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    private int getMeasurement(View c, boolean horizontal) {
866aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
8673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
8683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
86948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    private int getMeasurementIncludingMargin(View c, boolean horizontal) {
8705125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        if (isGone(c)) {
8715125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            return 0;
8725125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
8734c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
874aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
8753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
876aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    @Override
877aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    public void requestLayout() {
878aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        super.requestLayout();
879aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        invalidateValues();
880aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
881aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
8825125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    private Alignment getAlignment(Alignment alignment, boolean horizontal) {
8835125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
884899d5922870c78e0e663bc5661849eb468afc984Philip Milne                (horizontal ? LEFT : BASELINE);
8855125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
8865125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
8873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Layout container
8883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
889aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
890aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * {@inheritDoc}
891aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
892aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
893aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     The layout operation is implemented by delegating the heavy lifting to the
894aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
895aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     Together they compute the locations of the vertical and horizontal lines of
896aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     the grid (respectively!).
897aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
898aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     This method is then left with the simpler task of applying margins, gravity
899aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     and sizing to each child view and then placing it in its cell.
900aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
9013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
90209e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
90309e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne        int targetWidth = right - left;
90409e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne        int targetHeight = bottom - top;
9053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingLeft = getPaddingLeft();
9073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingTop = getPaddingTop();
9083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingRight = getPaddingRight();
9093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingBottom = getPaddingBottom();
9103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
9123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
9133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9144c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        int[] hLocations = mHorizontalAxis.getLocations();
9154c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        int[] vLocations = mVerticalAxis.getLocations();
9164c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne
917b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        for (int i = 0, N = getChildCount(); i < N; i++) {
918b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            View c = getChildAt(i);
919b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (isGone(c)) continue;
920b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            LayoutParams lp = getLayoutParams(c);
92193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec columnSpec = lp.columnSpec;
92293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec rowSpec = lp.rowSpec;
923aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
92493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Interval colSpan = columnSpec.span;
92593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Interval rowSpan = rowSpec.span;
9263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9274c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int x1 = hLocations[colSpan.min];
9284c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int y1 = vLocations[rowSpan.min];
9293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9304c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int x2 = hLocations[colSpan.max];
9314c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int y2 = vLocations[rowSpan.max];
9323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int cellWidth = x2 - x1;
9343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int cellHeight = y2 - y1;
9353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
93648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int pWidth = getMeasurement(c, true);
93748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int pHeight = getMeasurement(c, false);
9383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9395125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            Alignment hAlign = getAlignment(columnSpec.alignment, true);
9405125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            Alignment vAlign = getAlignment(rowSpec.alignment, false);
941aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
942aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            int dx, dy;
9433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9447fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            Bounds colBounds = mHorizontalAxis.getGroupBounds().getValue(i);
9457fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i);
9467fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
9477fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            // Gravity offsets: the location of the alignment group relative to its cell group.
9484c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection NullableProblems
94948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int c2ax = protect(hAlign.getAlignmentValue(null, cellWidth - colBounds.size(true)));
9504c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection NullableProblems
95148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int c2ay = protect(vAlign.getAlignmentValue(null, cellHeight - rowBounds.size(true)));
9527fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
9534c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int leftMargin = getMargin(c, true, true);
9544c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int topMargin = getMargin(c, false, true);
9554c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int rightMargin = getMargin(c, true, false);
9564c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int bottomMargin = getMargin(c, false, false);
9577fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
9584c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            // Same calculation as getMeasurementIncludingMargin()
9594c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int mWidth = leftMargin + pWidth + rightMargin;
9604c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int mHeight = topMargin + pHeight + bottomMargin;
9613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9624c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            // Alignment offsets: the location of the view relative to its alignment group.
9634c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int a2vx = colBounds.getOffset(c, hAlign, mWidth);
9644c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int a2vy = rowBounds.getOffset(c, vAlign, mHeight);
9657fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
9664c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            dx = c2ax + a2vx + leftMargin;
9674c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            dy = c2ay + a2vy + topMargin;
9687fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
9694c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            cellWidth -= leftMargin + rightMargin;
9704c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            cellHeight -= topMargin + bottomMargin;
9713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
97248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int type = PRF;
973b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            int width = hAlign.getSizeInCell(c, pWidth, cellWidth, type);
974b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            int height = vAlign.getSizeInCell(c, pHeight, cellHeight, type);
9753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int cx = paddingLeft + x1 + dx;
9773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int cy = paddingTop + y1 + dy;
978899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
979899d5922870c78e0e663bc5661849eb468afc984Philip Milne                c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
980899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
981b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            c.layout(cx, cy, cx + width, cy + height);
9823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
9833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
9843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Inner classes
9863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
987aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
988aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     This internal class houses the algorithm for computing the locations of grid lines;
989aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
990aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     distinguished by the "horizontal" flag which is true for the horizontal axis and false
991aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     for the vertical one.
992aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
9933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private class Axis {
9943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int MIN_VALUE = -1000000;
9953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
99648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private static final int NEW = 0;
9973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int PENDING = 1;
9983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int COMPLETE = 2;
9993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final boolean horizontal;
10013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int count = UNDEFINED;
10033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean countValid = false;
10043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean countWasExplicitySet = false;
10053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
100693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        PackedMap<Spec, Bounds> groupBounds;
10073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean groupBoundsValid = false;
10083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
100948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        PackedMap<Interval, MutableInt> forwardLinks;
101048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean forwardLinksValid = false;
101148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
101248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        PackedMap<Interval, MutableInt> backwardLinks;
101348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean backwardLinksValid = false;
10143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int[] leadingMargins;
1016aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public boolean leadingMarginsValid = false;
1017aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
10183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int[] trailingMargins;
1019aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public boolean trailingMarginsValid = false;
10203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public Arc[] arcs;
10223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean arcsValid = false;
10233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1024aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public int[] locations;
102548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean locationsValid = false;
1026aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1027aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private boolean mOrderPreserved = DEFAULT_ORDER_PRESERVED;
10283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
102948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private MutableInt parentMin = new MutableInt(0);
103048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private MutableInt parentMax = new MutableInt(-MAX_SIZE);
103148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
10323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private Axis(boolean horizontal) {
10333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.horizontal = horizontal;
10343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1036f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne        private int maxIndex() {
10373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            // note the number Integer.MIN_VALUE + 1 comes up in undefined cells
10383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int count = -1;
1039b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
1040b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
1041b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                LayoutParams params = getLayoutParams(c);
104293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec spec = horizontal ? params.columnSpec : params.rowSpec;
104393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                count = max(count, spec.span.min);
104493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                count = max(count, spec.span.max);
10453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
10463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return count == -1 ? UNDEFINED : count;
10473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int getCount() {
1050f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne            if (!countValid) {
1051f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne                count = max(0, maxIndex()); // if there are no cells, the count is zero
10523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                countValid = true;
10533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
10543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return count;
10553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public void setCount(int count) {
10583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.count = count;
10593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.countWasExplicitySet = count != UNDEFINED;
10603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean isOrderPreserved() {
10633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return mOrderPreserved;
10643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public void setOrderPreserved(boolean orderPreserved) {
10673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            mOrderPreserved = orderPreserved;
10683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            invalidateStructure();
10693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
107193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private PackedMap<Spec, Bounds> createGroupBounds() {
107293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
107348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
1074b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
10755125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                LayoutParams lp = getLayoutParams(c);
10765125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
10775125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                Bounds bounds = getAlignment(spec.alignment, horizontal).getBounds();
10785125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                assoc.put(spec, bounds);
10793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
108048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return assoc.pack();
10813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private void computeGroupBounds() {
1084b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            Bounds[] values = groupBounds.values;
1085b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0; i < values.length; i++) {
1086b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                values[i].reset();
10873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
1088aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
10893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                View c = getChildAt(i);
10903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                LayoutParams lp = getLayoutParams(c);
109193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
109293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                groupBounds.getValue(i).include(c, spec, GridLayout.this, this);
10933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
10943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
109693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private PackedMap<Spec, Bounds> getGroupBounds() {
10973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (groupBounds == null) {
10983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                groupBounds = createGroupBounds();
10993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (!groupBoundsValid) {
11013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                computeGroupBounds();
11023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                groupBoundsValid = true;
11033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return groupBounds;
11053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Add values computed by alignment - taking the max of all alignments in each span
110848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private PackedMap<Interval, MutableInt> createLinks(boolean min) {
110948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
111093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec[] keys = getGroupBounds().keys;
111148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0, N = keys.length; i < N; i++) {
111248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Interval span = min ? keys[i].span : keys[i].span.inverse();
111348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                result.put(span, new MutableInt());
11143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
111548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return result.pack();
11163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
111848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
111948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            MutableInt[] spans = links.values;
11203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < spans.length; i++) {
11213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                spans[i].reset();
11223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11245d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            // Use getter to trigger a re-evaluation
112548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Bounds[] bounds = getGroupBounds().values;
11263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < bounds.length; i++) {
112748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                int size = bounds[i].size(min);
112848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                MutableInt valueHolder = links.getValue(i);
11295125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                // this effectively takes the max() of the minima and the min() of the maxima
11305125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                valueHolder.value = max(valueHolder.value, min ? size : -size);
11313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
113448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private PackedMap<Interval, MutableInt> getForwardLinks() {
113548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (forwardLinks == null) {
113648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                forwardLinks = createLinks(true);
11373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
113848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!forwardLinksValid) {
113948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeLinks(forwardLinks, true);
114048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                forwardLinksValid = true;
11413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
114248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return forwardLinks;
11433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
114548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private PackedMap<Interval, MutableInt> getBackwardLinks() {
114648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (backwardLinks == null) {
114748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                backwardLinks = createLinks(false);
11483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
114948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!backwardLinksValid) {
115048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeLinks(backwardLinks, false);
115148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                backwardLinksValid = true;
115248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
115348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return backwardLinks;
11543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
115648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void include(List<Arc> arcs, Interval key, MutableInt size,
115748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                boolean ignoreIfAlreadyPresent) {
115848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            /*
115948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Remove self referential links.
116048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            These appear:
116148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                . as parental constraints when GridLayout has no children
116248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                . when components have been marked as GONE
116348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            */
116448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (key.size() == 0) {
116548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                return;
11663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
116748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // this bit below should really be computed outside here -
116848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // its just to stop default (row/col > 0) constraints obliterating valid entries
116948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (ignoreIfAlreadyPresent) {
117048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                for (Arc arc : arcs) {
117148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    Interval span = arc.span;
117248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    if (span.equals(key)) {
117348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        return;
117448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    }
117548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
117648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
117748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            arcs.add(new Arc(key, size));
11783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
118048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void include(List<Arc> arcs, Interval key, MutableInt size) {
118148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(arcs, key, size, true);
11823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1184aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        // Group arcs by their first vertex, returning an array of arrays.
11853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // This is linear in the number of arcs.
1186aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
118748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int N = getCount() + 1; // the number of vertices
11883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Arc[][] result = new Arc[N][];
11893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int[] sizes = new int[N];
11903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (Arc arc : arcs) {
11913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                sizes[arc.span.min]++;
11925d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            }
11933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < sizes.length; i++) {
11943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                result[i] = new Arc[sizes[i]];
11953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            // reuse the sizes array to hold the current last elements as we insert each arc
11973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Arrays.fill(sizes, 0);
11983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (Arc arc : arcs) {
11993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int i = arc.span.min;
12003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                result[i][sizes[i]++] = arc;
12013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
12023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
12033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
12043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
120648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private Arc[] topologicalSort(final Arc[] arcs) {
120748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return new Object() {
120848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Arc[] result = new Arc[arcs.length];
120948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                int cursor = result.length - 1;
121048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
12113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int[] visited = new int[getCount() + 1];
12123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
121348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                void walk(int loc) {
121448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    switch (visited[loc]) {
121548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        case NEW: {
121648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            visited[loc] = PENDING;
121748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            for (Arc arc : arcsByVertex[loc]) {
121848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                                walk(arc.span.max);
121948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                                result[cursor--] = arc;
12203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                            }
122148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            visited[loc] = COMPLETE;
122248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            break;
122348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        }
122448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        case PENDING: {
122548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            assert false;
122648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            break;
122748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        }
122848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        case COMPLETE: {
122948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            break;
12303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                        }
12313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    }
12323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
123348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
123448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Arc[] sort() {
123548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
123648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        walk(loc);
123748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    }
123848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    assert cursor == -1;
123948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return result;
124048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
124148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }.sort();
124248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
124348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
124448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private Arc[] topologicalSort(List<Arc> arcs) {
124548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
12463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
124848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
124948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0; i < links.keys.length; i++) {
125048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Interval key = links.keys[i];
125148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                include(result, key, links.values[i], false);
125248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
125348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
125448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1255aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private Arc[] createArcs() {
125648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            List<Arc> mins = new ArrayList<Arc>();
125748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            List<Arc> maxs = new ArrayList<Arc>();
12583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
125948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add the minimum values from the components.
126048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            addComponentSizes(mins, getForwardLinks());
126148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add the maximum values from the components.
126248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            addComponentSizes(maxs, getBackwardLinks());
12633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
126448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add ordering constraints to prevent row/col sizes from going negative
12653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (mOrderPreserved) {
126648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                // Add a constraint for every row/col
12673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                for (int i = 0; i < getCount(); i++) {
1268899d5922870c78e0e663bc5661849eb468afc984Philip Milne                    include(mins, new Interval(i, i + 1), new MutableInt(0));
12693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
12703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
127148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
127248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add the container constraints. Use the version of include that allows
127348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // duplicate entries in case a child spans the entire grid.
127448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int N = getCount();
127548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(mins, new Interval(0, N), parentMin, false);
127648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(maxs, new Interval(N, 0), parentMax, false);
127748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
127848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Sort
127948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Arc[] sMins = topologicalSort(mins);
128048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Arc[] sMaxs = topologicalSort(maxs);
128148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
128248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return append(sMins, sMaxs);
128348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
128448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
128548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void computeArcs() {
128648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // getting the links validates the values that are shared by the arc list
128748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            getForwardLinks();
128848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            getBackwardLinks();
12893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1291aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public Arc[] getArcs() {
12923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (arcs == null) {
1293aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                arcs = createArcs();
12943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
12953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (!arcsValid) {
129648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeArcs();
12973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                arcsValid = true;
12983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
12993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return arcs;
13003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
13013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1302aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private boolean relax(int[] locations, Arc entry) {
130348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!entry.valid) {
130448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                return false;
130548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
13063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Interval span = entry.span;
13073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int u = span.min;
13083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int v = span.max;
13093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int value = entry.value.value;
13103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int candidate = locations[u] + value;
1311aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (candidate > locations[v]) {
13123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                locations[v] = candidate;
13133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return true;
13143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
13153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return false;
13163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
13173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1318aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        /*
1319aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1320aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1321aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        GridLayout converts its requirements into a system of linear constraints of the
1322aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        form:
1323aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1324aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        x[i] - x[j] < a[k]
1325aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1326aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Where the x[i] are variables and the a[k] are constants.
1327aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1328aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        For example, if the variables were instead labeled x, y, z we might have:
1329aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1330aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            x - y < 17
1331aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            y - z < 23
1332aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            z - x < 42
1333aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1334aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        This is a special case of the Linear Programming problem that is, in turn,
1335aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        equivalent to the single-source shortest paths problem on a digraph, for
1336aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
1337aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1338aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Other algorithms are faster in the case where no arcs have negative weights
1339aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        but allowing negative weights turns out to be the same as accommodating maximum
1340aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        size requirements as well as minimum ones.
1341aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1342aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Bellman-Ford works by iteratively 'relaxing' constraints over all nodes (an O(N)
1343aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        process) and performing this step N times. Proof of correctness hinges on the
1344aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        fact that there can be no negative weight chains of length > N - unless a
1345aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        'negative weight loop' exists. The algorithm catches this case in a final
1346aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        checking phase that reports failure.
1347aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1348aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        By topologically sorting the nodes and checking this condition at each step
1349aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        typical layout problems complete after the first iteration and the algorithm
1350aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        completes in O(N) steps with very low constants.
1351aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        */
135248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void solve(Arc[] arcs, int[] locations) {
135348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            String axis = horizontal ? "horizontal" : "vertical";
13543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
13553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
13563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            // We take one extra pass over traditional Bellman-Ford (and omit their final step)
13573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < N; i++) {
13584c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                boolean changed = false;
13593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                for (int j = 0, length = arcs.length; j < length; j++) {
136048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    changed |= relax(locations, arcs[j]);
13613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
13623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                if (!changed) {
13633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    if (DEBUG) {
13645125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                        Log.v(TAG, axis + " iteration completed in " + (1 + i) + " steps of " + N);
13653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    }
136648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return;
13673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
13683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
136948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
137048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Log.d(TAG, "The " + axis + " constraints contained a contradiction. Resolving... ");
137148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Log.d(TAG, Arrays.toString(arcs));
137248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
137348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            boolean[] culprits = new boolean[arcs.length];
137448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0; i < N; i++) {
137548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                for (int j = 0, length = arcs.length; j < length; j++) {
137648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    culprits[j] |= relax(locations, arcs[j]);
137748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
13783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
137948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0; i < culprits.length; i++) {
138048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                if (culprits[i]) {
138148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    Arc arc = arcs[i];
138248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    // Only remove max values, min values alone cannot be inconsistent
138348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    if (arc.span.min < arc.span.max) {
138448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        continue;
138548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    }
138648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    Log.d(TAG, "Removing: " + arc);
138748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    arc.valid = false;
138848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    break;
138948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
139048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
139148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            solve1(arcs, locations);
139248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
139348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
139448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void solve1(Arc[] arcs, int[] a) {
139548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Arrays.fill(a, MIN_VALUE);
139648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            a[0] = 0;
139748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            solve(arcs, a);
13983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
13993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1400aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private void computeMargins(boolean leading) {
1401aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            int[] margins = leading ? leadingMargins : trailingMargins;
1402b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
14033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                View c = getChildAt(i);
1404b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                if (isGone(c)) continue;
14053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                LayoutParams lp = getLayoutParams(c);
140693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
140793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Interval span = spec.span;
14083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int index = leading ? span.min : span.max;
14094c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
14103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
14113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
14123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1413aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private int[] getLeadingMargins() {
1414aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (leadingMargins == null) {
1415aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                leadingMargins = new int[getCount() + 1];
1416aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1417aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (!leadingMarginsValid) {
1418aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                computeMargins(true);
1419aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                leadingMarginsValid = true;
1420aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1421aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            return leadingMargins;
1422aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
1423aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1424aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private int[] getTrailingMargins() {
1425aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (trailingMargins == null) {
1426aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                trailingMargins = new int[getCount() + 1];
1427aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1428aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (!trailingMarginsValid) {
1429aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                computeMargins(false);
1430aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                trailingMarginsValid = true;
1431aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1432aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            return trailingMargins;
1433aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
14343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
143548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void computeLocations(int[] a) {
143648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            solve1(getArcs(), a);
14373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
14383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1439aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private int[] getLocations() {
1440aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (locations == null) {
1441aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                int N = getCount() + 1;
1442aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                locations = new int[N];
1443aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
144448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!locationsValid) {
144548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeLocations(locations);
144648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                locationsValid = true;
144748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
1448aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            return locations;
14493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
14503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1451aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        // External entry points
1452aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1453aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private int size(int[] locations) {
145451f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne            return max2(locations, 0) - locations[0];
1455aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
1456aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
145748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void setParentConstraints(int min, int max) {
145848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            parentMin.value = min;
145948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            parentMax.value = -max;
146048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            locationsValid = false;
14613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
14623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
146348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private int getMeasure(int min, int max) {
146448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            setParentConstraints(min, max);
146548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return size(getLocations());
146648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
1467aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
146848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private int getMeasure(int measureSpec) {
146948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int mode = MeasureSpec.getMode(measureSpec);
147048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int size = MeasureSpec.getSize(measureSpec);
147148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            switch (mode) {
147248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                case MeasureSpec.UNSPECIFIED: {
147393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                    return getMeasure(0, MAX_SIZE);
147448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
147548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                case MeasureSpec.EXACTLY: {
147648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return getMeasure(size, size);
147748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
147848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                case MeasureSpec.AT_MOST: {
147948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return getMeasure(0, size);
148048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
148148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                default: {
148248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    assert false;
148348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return 0;
148448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
14853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
148648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
1487aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
148848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void layout(int size) {
148948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            setParentConstraints(size, size);
149048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            getLocations();
14913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
14923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
14933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private void invalidateStructure() {
14943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            countValid = false;
1495aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
14963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            groupBounds = null;
149748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            forwardLinks = null;
149848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            backwardLinks = null;
149948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1500aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            leadingMargins = null;
1501aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            trailingMargins = null;
1502c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne            arcs = null;
150348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1504aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            locations = null;
15053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
15063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            invalidateValues();
15073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
15083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
15093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private void invalidateValues() {
15103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            groupBoundsValid = false;
151148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            forwardLinksValid = false;
151248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            backwardLinksValid = false;
151348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1514aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            leadingMarginsValid = false;
1515aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            trailingMarginsValid = false;
151648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            arcsValid = false;
151748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
151848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            locationsValid = false;
15193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
15203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
15213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
15223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
15233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Layout information associated with each of the children of a GridLayout.
15243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
15253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * GridLayout supports both row and column spanning and arbitrary forms of alignment within
15263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * each cell group. The fundamental parameters associated with each cell group are
15273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * gathered into their vertical and horizontal components and stored
152893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
1529b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne     * {@link android.widget.GridLayout.Spec Specs} are immutable structures
1530b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne     * and may be shared between the layout parameters of different children.
15313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
153293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * The row and column specs contain the leading and trailing indices along each axis
1533aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * and together specify the four grid indices that delimit the cells of this cell group.
15343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
153593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * The  alignment properties of the row and column specs together specify
15363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * both aspects of alignment within the cell group. It is also possible to specify a child's
15373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
15383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * method.
15393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
15403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * See {@link GridLayout} for a description of the conventions used by GridLayout
15413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * in reference to grid indices.
15423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
15433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <h4>Default values</h4>
15443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
15453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <ul>
15463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
15473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
15483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #topMargin} = 0 when
15493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
15507fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
15513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
15523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #leftMargin} = 0 when
15533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
15547fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
15553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
15563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #bottomMargin} = 0 when
15573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
15587fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
15593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
15603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #rightMargin} = 0 when
15613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
15627fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
15633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
156493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li>{@link #rowSpec}{@code .span} = {@code [0, 1]} </li>
156593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li>{@link #rowSpec}{@code .alignment} = {@link #BASELINE} </li>
156693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li>{@link #columnSpec}{@code .span} = {@code [0, 1]} </li>
156793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li>{@link #columnSpec}{@code .alignment} = {@link #LEFT} </li>
15683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * </ul>
15693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
15703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_row
15713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
15723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_column
15733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
15743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
15753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
15763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static class LayoutParams extends MarginLayoutParams {
15773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
15783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Default values
15793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
15803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_WIDTH = WRAP_CONTENT;
15813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
15823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_MARGIN = UNDEFINED;
15833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_ROW = UNDEFINED;
15843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_COLUMN = UNDEFINED;
1585f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne        private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
15863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
15873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
15883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // TypedArray indices
15893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1590b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
1591b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
1592b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
1593b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int RIGHT_MARGIN =
1594b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne                R.styleable.ViewGroup_MarginLayout_layout_marginRight;
15953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int BOTTOM_MARGIN =
1596b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne                R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
15973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1598b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
1599b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
16005d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
1601b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
1602b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
16035d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
1604b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
16053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Instance variables
16073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
160993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * The spec that specifies the vertical characteristics of the cell group
16103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * described by these layout parameters.
16113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
161293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        public Spec rowSpec;
16133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
161493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * The spec that specifies the horizontal characteristics of the cell group
16153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * described by these layout parameters.
16163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
161793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        public Spec columnSpec;
16183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Constructors
16203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private LayoutParams(
16223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int width, int height,
16233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int left, int top, int right, int bottom,
162493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec rowSpec, Spec columnSpec) {
16253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(width, height);
16263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            setMargins(left, top, right, bottom);
162793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            this.rowSpec = rowSpec;
162893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            this.columnSpec = columnSpec;
16293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
163293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * Constructs a new LayoutParams instance for this <code>rowSpec</code>
163393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * and <code>columnSpec</code>. All other fields are initialized with
16343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * default values as defined in {@link LayoutParams}.
16353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
163693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * @param rowSpec    the rowSpec
163793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * @param columnSpec the columnSpec
16383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
163993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        public LayoutParams(Spec rowSpec, Spec columnSpec) {
16403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
16413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
164293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                    rowSpec, columnSpec);
16433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
16463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
16473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
16483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams() {
16495125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            this(spec(UNDEFINED), spec(UNDEFINED));
16503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Copying constructors
16533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
16553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
16563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
16573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(ViewGroup.LayoutParams params) {
16583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(params);
16593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
16623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
16633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
16643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(MarginLayoutParams params) {
16653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(params);
16663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
16693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
16703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
16713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(LayoutParams that) {
16723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(that);
167393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            this.rowSpec = new Spec(that.rowSpec);
16745125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            this.columnSpec = new Spec(that.columnSpec);
16753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // AttributeSet constructors
16783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
16803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
16813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
16823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Values not defined in the attribute set take the default values
16833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * defined in {@link LayoutParams}.
16843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
16853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(Context context, AttributeSet attrs) {
16865125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            super(context, attrs);
16875125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            reInitSuper(context, attrs);
16885125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            init(context, attrs);
16893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Implementation
16923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Reinitialise the margins using a different default policy than MarginLayoutParams.
16943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
16953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // so that a layout manager default can be accessed post set up. We need this as, at the
16963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // point of installation, we do not know how many rows/cols there are and therefore
16973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // which elements are positioned next to the container's trailing edges. We need to
16983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // know this as margins around the container's boundary should have different
16993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // defaults to those between peers.
17003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // This method could be parametrized and moved into MarginLayout.
17023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private void reInitSuper(Context context, AttributeSet attrs) {
1703b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne            TypedArray a =
1704b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne                    context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
17053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            try {
17063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
17073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
17093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
17103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
17113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
17123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            } finally {
17133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                a.recycle();
17143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
17153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17175125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        private void init(Context context, AttributeSet attrs) {
1718b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
17193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            try {
17205125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
17213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17221e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne                int column = a.getInt(COLUMN, DEFAULT_COLUMN);
17235125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
1724899d5922870c78e0e663bc5661849eb468afc984Philip Milne                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
17253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17261e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne                int row = a.getInt(ROW, DEFAULT_ROW);
17271e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne                int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
1728899d5922870c78e0e663bc5661849eb468afc984Philip Milne                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
17293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            } finally {
17303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                a.recycle();
17313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
17323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
17357fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
17367fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * See {@link android.view.Gravity}.
17373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
17387fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * @param gravity the new gravity value
17393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
17403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
17413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
17423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public void setGravity(int gravity) {
17435125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
17445125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
17453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
17483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
17493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
17503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
17513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
175393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private void setRowSpecSpan(Interval span) {
175493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            rowSpec = rowSpec.copyWriteSpan(span);
17553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
175793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private void setColumnSpecSpan(Interval span) {
175893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            columnSpec = columnSpec.copyWriteSpan(span);
17593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
17613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1762aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
1763aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
1764aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
1765aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
17663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static class Arc {
17673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final Interval span;
1768aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public final MutableInt value;
176948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean valid = true;
17703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1771aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public Arc(Interval span, MutableInt value) {
17723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.span = span;
17733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.value = value;
17743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
17773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public String toString() {
177848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return span + " " + (!valid ? "+>" : "->") + " " + value;
17793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
17813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // A mutable Integer - used to avoid heap allocation during the layout operation
17833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1784aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    private static class MutableInt {
17853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int value;
17863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1787aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private MutableInt() {
17883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            reset();
17893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1791aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private MutableInt(int value) {
17923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.value = value;
17933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private void reset() {
17963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            value = Integer.MIN_VALUE;
17973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
179848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
179948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        @Override
180048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public String toString() {
180148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return Integer.toString(value);
180248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
180348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    }
180448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
180548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    private static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
180648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private final Class<K> keyType;
180748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private final Class<V> valueType;
180848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
180948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private Assoc(Class<K> keyType, Class<V> valueType) {
181048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            this.keyType = keyType;
181148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            this.valueType = valueType;
181248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
181348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
181448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
181548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return new Assoc<K, V>(keyType, valueType);
181648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
181748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
181848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public void put(K key, V value) {
181948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            add(Pair.create(key, value));
182048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
182148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
182248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        @SuppressWarnings(value = "unchecked")
182348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public PackedMap<K, V> pack() {
182448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int N = size();
182548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            K[] keys = (K[]) Array.newInstance(keyType, N);
182648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            V[] values = (V[]) Array.newInstance(valueType, N);
182748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0; i < N; i++) {
182848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                keys[i] = get(i).first;
182948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                values[i] = get(i).second;
183048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
183148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return new PackedMap<K, V>(keys, values);
183248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
18333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
18343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1835aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
1836aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    This data structure is used in place of a Map where we have an index that refers to the order
1837aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    in which each key/value pairs were added to the map. In this case we store keys and values
1838aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    in arrays of a length that is equal to the number of unique keys. We also maintain an
1839aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    array of indexes from insertion order to the compacted arrays of keys and values.
1840aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1841aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    Note that behavior differs from that of a LinkedHashMap in that repeated entries
1842aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    *do* get added multiples times. So the length of index is equals to the number of
1843aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    items added.
1844aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1845aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    This is useful in the GridLayout class where we can rely on the order of children not
1846aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    changing during layout - to use integer-based lookup for our internal structures
1847aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    rather than using (and storing) an implementation of Map<Key, ?>.
1848aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
18493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @SuppressWarnings(value = "unchecked")
18503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static class PackedMap<K, V> {
18513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final int[] index;
18523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final K[] keys;
18533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final V[] values;
18543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private PackedMap(K[] keys, V[] values) {
18563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.index = createIndex(keys);
18573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1858aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            this.keys = compact(keys, index);
1859aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            this.values = compact(values, index);
18603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private V getValue(int i) {
18633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return values[index[i]];
18643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static <K> int[] createIndex(K[] keys) {
18673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int size = keys.length;
18683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int[] result = new int[size];
18693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
18713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < size; i++) {
18723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                K key = keys[i];
18733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                Integer index = keyToIndex.get(key);
18743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                if (index == null) {
18753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    index = keyToIndex.size();
18763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    keyToIndex.put(key, index);
18773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
18783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                result[i] = index;
18793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
18803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
18813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1883aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        /*
1884aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Create a compact array of keys or values using the supplied index.
1885aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne         */
1886aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private static <K> K[] compact(K[] a, int[] index) {
1887aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            int size = a.length;
1888aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            Class<?> componentType = a.getClass().getComponentType();
188951f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
18903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            // this overwrite duplicates, retaining the last equivalent entry
18923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < size; i++) {
1893aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                result[index[i]] = a[i];
18943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
18953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
18963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
18983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1899aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
190093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    For each group (with a given alignment) we need to store the amount of space required
19017fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne    before the alignment point and the amount of space required after it. One side of this
1902aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    calculation is always 0 for LEADING and TRAILING alignments but we don't make use of this.
1903aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
1904aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    simple optimisations are possible.
1905aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1906aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    The general algorithm therefore is to create a Map (actually a PackedMap) from
190793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    group to Bounds and to loop through all Views in the group taking the maximum
1908aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    of the values for each View.
1909aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    */
19103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static class Bounds {
19117fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne        public int before;
19127fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne        public int after;
19135125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        public int flexibility; // we're flexible iff all included specs are flexible
19143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
19153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private Bounds() {
19163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            reset();
19173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1919a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        protected void reset() {
19207fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            before = Integer.MIN_VALUE;
19217fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            after = Integer.MIN_VALUE;
19225125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            flexibility = CAN_STRETCH; // from the above, we're flexible when empty
19233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1925a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        protected void include(int before, int after) {
19267fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            this.before = max(this.before, before);
19277fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            this.after = max(this.after, after);
19283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
193048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        protected int size(boolean min) {
19315d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            if (!min) {
19325125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                if (canStretch(flexibility)) {
19335d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne                    return MAX_SIZE;
19345d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne                }
193548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
19367fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            return before + after;
19373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
193948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        protected int getOffset(View c, Alignment alignment, int size) {
194048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return before - alignment.getAlignmentValue(c, size);
194148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
194248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
19435125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        protected final void include(View c, Spec spec, GridLayout gridLayout, Axis axis) {
19445125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            this.flexibility &= spec.getFlexibility();
194548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal);
19465125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            Alignment alignment = gridLayout.getAlignment(spec.alignment, axis.horizontal);
19474c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            // todo test this works correctly when the returned value is UNDEFINED
19485125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            int before = alignment.getAlignmentValue(c, size);
194948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(before, size - before);
1950a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
1951a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
19523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
19533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public String toString() {
19543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return "Bounds{" +
19557fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne                    "before=" + before +
19567fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne                    ", after=" + after +
19573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    '}';
19583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
19603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
19613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
19623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * An Interval represents a contiguous range of values that lie between
19633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * the interval's {@link #min} and {@link #max} values.
19643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
19653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Intervals are immutable so may be passed as values and used as keys in hash tables.
19663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * It is not necessary to have multiple instances of Intervals which have the same
19673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * {@link #min} and {@link #max} values.
19683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
19697fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * Intervals are often written as {@code [min, max]} and represent the set of values
19707fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * {@code x} such that {@code min <= x < max}.
19713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
197248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    static class Interval {
19733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
19743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * The minimum value.
19753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
19763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final int min;
1977aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
19783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
19793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * The maximum value.
19803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
19813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final int max;
19823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
19833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
19847fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * Construct a new Interval, {@code interval}, where:
19853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * <ul>
19867fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         *     <li> {@code interval.min = min} </li>
19877fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         *     <li> {@code interval.max = max} </li>
19883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * </ul>
19893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
19903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @param min the minimum value.
19913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @param max the maximum value.
19923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
19933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public Interval(int min, int max) {
19943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.min = min;
19953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.max = max;
19963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
19983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private int size() {
19993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return max - min;
20003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private Interval inverse() {
20033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return new Interval(max, min);
20043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
20077fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * Returns {@code true} if the {@link #getClass class},
20087fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * {@link #min} and {@link #max} properties of this Interval and the
20097fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * supplied parameter are pairwise equal; {@code false} otherwise.
20103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
20117fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * @param that the object to compare this interval with
20123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
20133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @return {@code true} if the specified object is equal to this
20147fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         *         {@code Interval}, {@code false} otherwise.
20153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
20163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
20173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean equals(Object that) {
20183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (this == that) {
20193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return true;
20203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
20213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (that == null || getClass() != that.getClass()) {
20223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
20233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
20243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Interval interval = (Interval) that;
20263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (max != interval.max) {
20283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
20293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
20304c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection RedundantIfStatement
20313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (min != interval.min) {
20323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
20333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
20343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return true;
20363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
20393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int hashCode() {
20403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int result = min;
20413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            result = 31 * result + max;
20423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
20433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
20463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public String toString() {
20473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return "[" + min + ", " + max + "]";
20483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
20503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2051899d5922870c78e0e663bc5661849eb468afc984Philip Milne    /**
2052899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * A Spec defines the horizontal or vertical characteristics of a group of
2053899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * cells. Each spec. defines the <em>grid indices</em>, <em>alignment</em> and
2054899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * <em>flexibility</em> along the appropriate axis.
2055899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * <p>
2056899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2057899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * See {@link GridLayout} for a description of the conventions used by GridLayout
2058899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * for grid indices.
2059899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * <p>
2060899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The <em>alignment</em> property specifies how cells should be aligned in this group.
2061899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * For row groups, this specifies the vertical alignment.
2062899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * For column groups, this specifies the horizontal alignment.
2063899d5922870c78e0e663bc5661849eb468afc984Philip Milne     */
206493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    public static class Spec {
206548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        final Interval span;
206693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        final Alignment alignment;
20673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20684c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        private Spec(Interval span, Alignment alignment) {
20695d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            this.span = span;
20705d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            this.alignment = alignment;
20715d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne        }
20725d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
20735d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne        /* Copying constructor */
207493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private Spec(Spec that) {
20754c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            this(that.span, that.alignment);
20763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2078899d5922870c78e0e663bc5661849eb468afc984Philip Milne        private Spec(int start, int size, Alignment alignment) {
20794c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            this(new Interval(start, start + size), alignment);
20803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
208293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private Spec copyWriteSpan(Interval span) {
20834c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return new Spec(span, alignment);
20843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
208693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private Spec copyWriteAlignment(Alignment alignment) {
20874c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return new Spec(span, alignment);
20885125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
20895125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
20905125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        int getFlexibility() {
20914c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
20923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
209593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
209693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * properties of this Spec and the supplied parameter are pairwise equal,
20977fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * {@code false} otherwise.
20983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
209993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * @param that the object to compare this spec with
21003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
21013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @return {@code true} if the specified object is equal to this
210293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         *         {@code Spec}; {@code false} otherwise
21033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
21043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
21053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean equals(Object that) {
21063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (this == that) {
21073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return true;
21083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (that == null || getClass() != that.getClass()) {
21103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
21113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
211393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec spec = (Spec) that;
21143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
211593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            if (!alignment.equals(spec.alignment)) {
21163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
21173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21184c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection RedundantIfStatement
211993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            if (!span.equals(spec.span)) {
21203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
21213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return true;
21243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
21253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
21273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int hashCode() {
21283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int result = span.hashCode();
21293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            result = 31 * result + alignment.hashCode();
21303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
21313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
21323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
21333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
213593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * Return a Spec, {@code spec}, where:
213693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * <ul>
213793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.span = [start, start + size]} </li>
213893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.alignment = alignment} </li>
213993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * </ul>
214093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *
214193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param start     the start
214293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param size      the size
214393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param alignment the alignment
214493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     */
214593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    public static Spec spec(int start, int size, Alignment alignment) {
2146899d5922870c78e0e663bc5661849eb468afc984Philip Milne        return new Spec(start, size, alignment);
214793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    }
214893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne
214993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    /**
215093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * Return a Spec, {@code spec}, where:
215193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * <ul>
215293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.span = [start, start + 1]} </li>
215393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.alignment = alignment} </li>
215493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * </ul>
215593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *
215693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param start     the start index
215793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param alignment the alignment
215893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     */
215993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    public static Spec spec(int start, Alignment alignment) {
216093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        return spec(start, 1, alignment);
216193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    }
216293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne
216393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    /**
21645125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * Return a Spec, {@code spec}, where:
21655125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * <ul>
21665125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *     <li> {@code spec.span = [start, start + size]} </li>
21675125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * </ul>
21685125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *
21695125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * @param start     the start
21705125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * @param size      the size
21715125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     */
21725125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    public static Spec spec(int start, int size) {
21735125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return spec(start, size, UNDEFINED_ALIGNMENT);
21745125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
21755125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
21765125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    /**
21775125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * Return a Spec, {@code spec}, where:
21785125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * <ul>
21795125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *     <li> {@code spec.span = [start, start + 1]} </li>
21805125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * </ul>
21815125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *
21825125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * @param start     the start index
21835125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     */
21845125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    public static Spec spec(int start) {
21855125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return spec(start, 1);
21865125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
21875125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
21885125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    /**
21893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Alignments specify where a view should be placed within a cell group and
21903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * what size it should be.
21913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
219293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
219393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
219493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * {@code alignment}. Overall placement of the view in the cell
21953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * group is specified by the two alignments which act along each axis independently.
21963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
2197a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     *  The GridLayout class defines the most common alignments used in general layout:
2198a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #CENTER}, {@link
2199a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     * #BASELINE} and {@link #FILL}.
2200a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     */
2201a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne    /*
2202c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne     * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
22033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * to return the appropriate value for the type of alignment being defined.
22043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The enclosing algorithms position the children
22051e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * so that the locations defined by the alignment values
22063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * are the same for all of the views in a group.
22073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
22083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2209c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    public static abstract class Alignment {
221048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        Alignment() {
2211a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
2212a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
22133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
22143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Returns an alignment value. In the case of vertical alignments the value
22153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * returned should indicate the distance from the top of the view to the
22163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * alignment location.
22173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * For horizontal alignments measurement is made from the left edge of the component.
22183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
2219c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param view              the view to which this alignment should be applied
2220c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param viewSize          the measured size of the view
2221b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne         * @return the alignment value
22223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
222348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        abstract int getAlignmentValue(View view, int viewSize);
22243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
22263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Returns the size of the view specified by this alignment.
22273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * In the case of vertical alignments this method should return a height; for
22283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * horizontal alignments this method should return the width.
2229c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * <p>
2230c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * The default implementation returns {@code viewSize}.
2231c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         *
2232c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param view              the view to which this alignment should be applied
2233c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param viewSize          the measured size of the view
2234c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param cellSize          the size of the cell into which this view will be placed
223509e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne         * @param measurementType   This parameter is currently unused as GridLayout only supports
223609e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne         *                          one type of measurement: {@link View#measure(int, int)}.
22373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
2238b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne         * @return the aligned size
22393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
224048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
22413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return viewSize;
22423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
2243a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
224448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        Bounds getBounds() {
2245a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            return new Bounds();
2246a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
22473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
22483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22495125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    private static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
22505125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        public int getAlignmentValue(View view, int viewSize) {
22515125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            return UNDEFINED;
22525125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
22535125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    };
22545125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
2255c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    private static final Alignment LEADING = new Alignment() {
225648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
22573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return 0;
22583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
22603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2261c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    private static final Alignment TRAILING = new Alignment() {
226248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
22633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return viewSize;
22643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
22663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
22683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>top</em>
22693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
22703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
22713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment TOP = LEADING;
22723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
22743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>bottom</em>
22753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
22763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
22773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment BOTTOM = TRAILING;
22783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
22803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>right</em>
22813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
22823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
22833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment RIGHT = TRAILING;
22843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
22863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>left</em>
22873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
22883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
22893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment LEFT = LEADING;
22903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
22923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be <em>centered</em> with the other views in its cell group.
229393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
229493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * LayoutParams#columnSpec columnSpecs}.
22953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2296c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    public static final Alignment CENTER = new Alignment() {
229748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
22983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return viewSize >> 1;
22993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
23003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
23013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
23023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
23033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>baselines</em>
23043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * of the other views in its cell group.
230593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
23063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
23073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see View#getBaseline()
23083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2309c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    public static final Alignment BASELINE = new Alignment() {
231048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
23113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (view == null) {
23123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return UNDEFINED;
23133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
23143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int baseline = view.getBaseline();
2315a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            return (baseline == -1) ? UNDEFINED : baseline;
2316a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
2317a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2318a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        @Override
2319a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        public Bounds getBounds() {
2320a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            return new Bounds() {
2321a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                /*
2322a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                In a baseline aligned row in which some components define a baseline
2323a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                and some don't, we need a third variable to properly account for all
2324a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                the sizes. This tracks the maximum size of all the components -
2325a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                including those that don't define a baseline.
2326a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                */
2327a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                private int size;
2328a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2329a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
2330a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                protected void reset() {
2331a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                    super.reset();
233248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    size = Integer.MIN_VALUE;
2333a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2334a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2335a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
2336a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                protected void include(int before, int after) {
2337a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                    super.include(before, after);
2338a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                    size = max(size, before + after);
2339a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2340a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2341a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
234248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                protected int size(boolean min) {
234348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return max(super.size(min), size);
2344a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2345a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2346a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
234748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                protected int getOffset(View c, Alignment alignment, int size) {
234848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return max(0, super.getOffset(c, alignment, size));
2349a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2350a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            };
23513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
23523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
23533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
23543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
23553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should expanded to fit the boundaries of its cell group.
235693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
235793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * {@link LayoutParams#columnSpec columnSpecs}.
23583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
23593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment FILL = new Alignment() {
236048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
23613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return UNDEFINED;
23623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
23633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2364c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne        @Override
2365c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne        public int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
23663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return cellSize;
23673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
23683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
236948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
23705d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne    private static boolean canStretch(int flexibility) {
23715d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne        return (flexibility & CAN_STRETCH) != 0;
23725d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne    }
23735d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
23745125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    private static final int INFLEXIBLE = 0;
237548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
23764c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    private static final int CAN_STRETCH = 2;
2377452eec33d667f9e705b57e60948b070536fbc1b4Jim Miller}
2378