GridLayout.java revision 47d248eb438674ab0ca10154f3ff5e88f4ed70b7
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;
308a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
318a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
328a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
33b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milneimport com.android.internal.R;
343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.lang.reflect.Array;
363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.ArrayList;
373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.Arrays;
383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.HashMap;
393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.List;
403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport java.util.Map;
413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
425125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milneimport static android.view.Gravity.*;
43899d5922870c78e0e663bc5661849eb468afc984Philip Milneimport static android.view.View.MeasureSpec.EXACTLY;
44899d5922870c78e0e663bc5661849eb468afc984Philip Milneimport static android.view.View.MeasureSpec.makeMeasureSpec;
453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport static java.lang.Math.max;
463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milneimport static java.lang.Math.min;
473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne/**
493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * A layout that places its children in a rectangular <em>grid</em>.
503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <p>
513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * The grid is composed of a set of infinitely thin lines that separate the
523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced
537fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * by grid <em>indices</em>. A grid with {@code N} columns
547fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * has {@code N + 1} grid indices that run from {@code 0}
557fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * through {@code N} inclusive. Regardless of how GridLayout is
567fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * configured, grid index {@code 0} is fixed to the leading edge of the
577fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne * container and grid index {@code N} is fixed to its trailing edge
583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * (after padding is taken into account).
593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
6093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * <h4>Row and Column Specs</h4>
613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Children occupy one or more contiguous cells, as defined
6393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and
6493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters.
6593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne * Each spec defines the set of rows or columns that are to be
663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * occupied; and how children should be aligned within the resulting group of cells.
673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Although cells do not normally overlap in a GridLayout, GridLayout does
683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * not prevent children being defined to occupy the same cell or group of cells.
693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * In this case however, there is no guarantee that children will not themselves
703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * overlap after the layout operation completes.
713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <h4>Default Cell Assignment</h4>
733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
7448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne * If a child does not specify the row and column indices of the cell it
753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * wishes to occupy, GridLayout assigns cell locations automatically using its:
763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setOrientation(int) orientation},
773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setRowCount(int) rowCount} and
783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setColumnCount(int) columnCount} properties.
793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <h4>Space</h4>
813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * Space between children may be specified either by using instances of the
833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * dedicated {@link Space} view or by setting the
843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#leftMargin leftMargin},
863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#topMargin topMargin},
873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#rightMargin rightMargin} and
883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link ViewGroup.MarginLayoutParams#bottomMargin bottomMargin}
893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * layout parameters. When the
913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins}
923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * property is set, default margins around children are automatically
93f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * allocated based on the prevailing UI style guide for the platform.
94f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * Each of the margins so defined may be independently overridden by an assignment
953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * to the appropriate layout parameter.
96f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * Default values will generally produce a reasonable spacing between components
97f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * but values may change between different releases of the platform.
983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <h4>Excess Space Distribution</h4>
1003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
101f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * GridLayout's distribution of excess space is based on <em>priority</em>
102f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * rather than <em>weight</em>.
103f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * <p>
104899d5922870c78e0e663bc5661849eb468afc984Philip Milne * A child's ability to stretch is inferred from the alignment properties of
105899d5922870c78e0e663bc5661849eb468afc984Philip Milne * its row and column groups (which are typically set by setting the
106899d5922870c78e0e663bc5661849eb468afc984Philip Milne * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters).
107899d5922870c78e0e663bc5661849eb468afc984Philip Milne * If alignment was defined along a given axis then the component
108f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * is taken as <em>flexible</em> in that direction. If no alignment was set,
109f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * the component is instead assumed to be <em>inflexible</em>.
110f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * <p>
111f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * Multiple components in the same row or column group are
112f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * considered to act in <em>parallel</em>. Such a
113899d5922870c78e0e663bc5661849eb468afc984Philip Milne * group is flexible only if <em>all</em> of the components
114899d5922870c78e0e663bc5661849eb468afc984Philip Milne * within it are flexible. Row and column groups that sit either side of a common boundary
115899d5922870c78e0e663bc5661849eb468afc984Philip Milne * are instead considered to act in <em>series</em>. The composite group made of these two
116899d5922870c78e0e663bc5661849eb468afc984Philip Milne * elements is flexible if <em>one</em> of its elements is flexible.
117899d5922870c78e0e663bc5661849eb468afc984Philip Milne * <p>
118899d5922870c78e0e663bc5661849eb468afc984Philip Milne * To make a column stretch, make sure all of the components inside it define a
119899d5922870c78e0e663bc5661849eb468afc984Philip Milne * gravity. To prevent a column from stretching, ensure that one of the components
120899d5922870c78e0e663bc5661849eb468afc984Philip Milne * in the column does not define a gravity.
1213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <p>
122f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * When the principle of flexibility does not provide complete disambiguation,
123f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
124f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * and <em>bottom</em> edges.
125f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne *
126f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * <h5>Limitations</h5>
127f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne *
128f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * GridLayout does not provide support for the principle of <em>weight</em>, as defined in
129f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible
130f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * to configure a GridLayout to distribute excess space in non-trivial proportions between
131f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * multiple rows or columns.
132f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * <p>
133f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * Some common use-cases may nevertheless be accommodated as follows.
134f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * To place equal amounts of space around a component in a cell group;
135f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * use {@link #CENTER} alignment (or {@link LayoutParams#setGravity(int) gravity}).
136f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * For complete control over excess space distribution in a row or column;
137f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * use a {@link LinearLayout} subview to hold the components in the associated cell group.
138f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne * When using either of these techniques, bear in mind that cell groups may be defined to overlap.
1393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * <p>
1403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * See {@link GridLayout.LayoutParams} for a full description of the
1413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * layout parameters used by GridLayout.
1423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne *
1433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_orientation
1443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_rowCount
1453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_columnCount
1463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_useDefaultMargins
1473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
1483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
1493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne */
1503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milnepublic class GridLayout extends ViewGroup {
1513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Public constants
1533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
1553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The horizontal orientation.
1563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
1573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
158aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
1603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The vertical orientation.
1613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
1623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final int VERTICAL = LinearLayout.VERTICAL;
1633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
164aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
165aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * The constant used to indicate that a value is undefined.
166aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * Fields can use this value to indicate that their values
167aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * have not yet been set. Similarly, methods can return this value
168aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * to indicate that there is no suitable value that the implementation
169aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * can return.
170aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * The value used for the constant (currently {@link Integer#MIN_VALUE}) is
171aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * intended to avoid confusion between valid values whose sign may not be known.
172aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
173aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    public static final int UNDEFINED = Integer.MIN_VALUE;
174aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1751e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    /**
1761e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
1771e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * When the {@code alignmentMode} is set to {@link #ALIGN_BOUNDS}, alignment
1781e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * is made between the edges of each component's raw
1791e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * view boundary: i.e. the area delimited by the component's:
1801e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getTop() top},
1811e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getLeft() left},
1821e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getBottom() bottom} and
1831e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link android.view.View#getRight() right} properties.
1841e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * <p>
1851e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * For example, when {@code GridLayout} is in {@link #ALIGN_BOUNDS} mode,
1861e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * children that belong to a row group that uses {@link #TOP} alignment will
1871e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * all return the same value when their {@link android.view.View#getTop()}
1881e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * method is called.
1891e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
1901e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
1911e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     */
1921e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public static final int ALIGN_BOUNDS = 0;
1931e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne
1941e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    /**
1951e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
1961e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * When the {@code alignmentMode} is set to {@link #ALIGN_MARGINS},
1971e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * the bounds of each view are extended outwards, according
1981e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * to their margins, before the edges of the resulting rectangle are aligned.
1991e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * <p>
2001e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * For example, when {@code GridLayout} is in {@link #ALIGN_MARGINS} mode,
2011e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * the quantity {@code top - layoutParams.topMargin} is the same for all children that
2021e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * belong to a row group that uses {@link #TOP} alignment.
2031e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
2041e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
2051e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     */
2061e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public static final int ALIGN_MARGINS = 1;
2071e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne
2083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Misc constants
2093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
210f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static final String TAG = GridLayout.class.getName();
211f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static final boolean DEBUG = false;
212f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static final int PRF = 1;
213f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static final int MAX_SIZE = 100000;
214f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static final int DEFAULT_CONTAINER_MARGIN = 0;
2153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Defaults
2173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final int DEFAULT_ORIENTATION = HORIZONTAL;
2193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final int DEFAULT_COUNT = UNDEFINED;
2203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
221899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static final boolean DEFAULT_ORDER_PRESERVED = true;
2221e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
2233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // TypedArray indices
2253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
226b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ORIENTATION = R.styleable.GridLayout_orientation;
227b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ROW_COUNT = R.styleable.GridLayout_rowCount;
228b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount;
229b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins;
230b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode;
231b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved;
232b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved;
2333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Instance variables
2353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
236f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final Axis horizontalAxis = new Axis(true);
237f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final Axis verticalAxis = new Axis(false);
238f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    boolean layoutParamsValid = false;
239f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    int orientation = DEFAULT_ORIENTATION;
240f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    boolean useDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
241f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    int alignmentMode = DEFAULT_ALIGNMENT_MODE;
242f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    int defaultGap;
243aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
2443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Constructors
2453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
2473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * {@inheritDoc}
2483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public GridLayout(Context context, AttributeSet attrs, int defStyle) {
2503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        super(context, attrs, defStyle);
251c655ba5e467090eb4f839f148ac31b50c389ffb2Jim Miller        if (DEBUG) {
252c655ba5e467090eb4f839f148ac31b50c389ffb2Jim Miller            setWillNotDraw(false);
253c655ba5e467090eb4f839f148ac31b50c389ffb2Jim Miller        }
254f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        defaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
255b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout);
2563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        try {
2571e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne            setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
2581e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne            setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT));
2595125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            setOrientation(a.getInt(ORIENTATION, DEFAULT_ORIENTATION));
2605125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            setUseDefaultMargins(a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS));
2615125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            setAlignmentMode(a.getInt(ALIGNMENT_MODE, DEFAULT_ALIGNMENT_MODE));
2623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
2633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
2643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        } finally {
2653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            a.recycle();
2663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
2673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
2683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
26948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    /**
27048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     * {@inheritDoc}
27148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     */
27248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    public GridLayout(Context context, AttributeSet attrs) {
27348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        this(context, attrs, 0);
27448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    }
27548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
27648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    /**
27748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     * {@inheritDoc}
27848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne     */
27948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    public GridLayout(Context context) {
2804c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        //noinspection NullableProblems
28148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        this(context, null);
28248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    }
28348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
2843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Implementation
2853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
2873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns the current orientation.
2883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2897fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
2903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setOrientation(int)
2923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
2933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_orientation
2943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public int getOrientation() {
296f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return orientation;
2973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
2983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
300f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * Orientation is used only to generate default row/column indices when
301f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * they are not specified by a component's layout parameters.
3027fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
3037fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * The default value of this property is {@link #HORIZONTAL}.
3043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3057fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
3063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getOrientation()
3083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_orientation
3103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setOrientation(int orientation) {
312f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        if (this.orientation != orientation) {
313f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this.orientation = orientation;
314f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            invalidateStructure();
3153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            requestLayout();
3163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
3173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns the current number of rows. This is either the last value that was set
3213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * with {@link #setRowCount(int)} or, if no such value was set, the maximum
32293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * value of each the upper bounds defined in {@link LayoutParams#rowSpec}.
3233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @return the current number of rows
3253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setRowCount(int)
32793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#rowSpec
3283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowCount
3303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public int getRowCount() {
332f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return verticalAxis.getCount();
3333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
336f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * RowCount is used only to generate default row/column indices when
337f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * they are not specified by a component's layout parameters.
3383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3397fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param rowCount the number of rows
3403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getRowCount()
34293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#rowSpec
3433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowCount
3453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setRowCount(int rowCount) {
347f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        verticalAxis.setCount(rowCount);
348f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        invalidateStructure();
349f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        requestLayout();
3503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns the current number of columns. This is either the last value that was set
3543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * with {@link #setColumnCount(int)} or, if no such value was set, the maximum
35593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * value of each the upper bounds defined in {@link LayoutParams#columnSpec}.
3563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @return the current number of columns
3583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setColumnCount(int)
36093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#columnSpec
3613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnCount
3633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public int getColumnCount() {
365f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return horizontalAxis.getCount();
3663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
369f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * ColumnCount is used only to generate default column/column indices when
370f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * they are not specified by a component's layout parameters.
3713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @param columnCount the number of columns.
3733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getColumnCount()
37593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @see LayoutParams#columnSpec
3763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnCount
3783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setColumnCount(int columnCount) {
380f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        horizontalAxis.setCount(columnCount);
381f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        invalidateStructure();
382f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        requestLayout();
3833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
3863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns whether or not this GridLayout will allocate default margins when no
3873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * corresponding layout parameters are defined.
3883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3897fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return {@code true} if default margins should be allocated
3903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setUseDefaultMargins(boolean)
3923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
3933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_useDefaultMargins
3943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
3953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public boolean getUseDefaultMargins() {
396f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return useDefaultMargins;
3973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
3983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
3993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
4007fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When {@code true}, GridLayout allocates default margins around children
4013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * based on the child's visual characteristics. Each of the
4023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * margins so defined may be independently overridden by an assignment
4033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * to the appropriate layout parameter.
4043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
4057fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When {@code false}, the default value of all margins is zero.
406aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * <p>
4077fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When setting to {@code true}, consider setting the value of the
4081e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * {@link #setAlignmentMode(int) alignmentMode}
4091e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * property to {@link #ALIGN_BOUNDS}.
4107fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
4117fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * The default value of this property is {@code false}.
4123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4137fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param useDefaultMargins use {@code true} to make GridLayout allocate default margins
4143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #getUseDefaultMargins()
4161e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
4173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#leftMargin
4193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#topMargin
4203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#rightMargin
4213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see MarginLayoutParams#bottomMargin
4223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_useDefaultMargins
4243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
4253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setUseDefaultMargins(boolean useDefaultMargins) {
426f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        this.useDefaultMargins = useDefaultMargins;
427aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
428aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
429aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
430aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
4311e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * Returns the alignment mode.
4321e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
4331e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @return the alignment mode; either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
434aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4351e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_BOUNDS
4361e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_MARGINS
437aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4381e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #setAlignmentMode(int)
439aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4401e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @attr ref android.R.styleable#GridLayout_alignmentMode
441aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
4421e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public int getAlignmentMode() {
443f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return alignmentMode;
444aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
445aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
446aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
4471e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * Sets the alignment mode to be used for all of the alignments between the
4481e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * children of this container.
4497fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
4501e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * The default value of this property is {@link #ALIGN_MARGINS}.
4511e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     *
4521e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @param alignmentMode either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
453aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4541e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_BOUNDS
4551e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #ALIGN_MARGINS
456aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4571e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @see #getAlignmentMode()
458aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     *
4591e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * @attr ref android.R.styleable#GridLayout_alignmentMode
460aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
4611e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne    public void setAlignmentMode(int alignmentMode) {
462f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        this.alignmentMode = alignmentMode;
463aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
4643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
4653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
4663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
4673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns whether or not row boundaries are ordered by their grid indices.
4683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4697fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return {@code true} if row boundaries must appear in the order of their indices,
4707fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *         {@code false} otherwise
4713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setRowOrderPreserved(boolean)
4733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
4753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
4763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public boolean isRowOrderPreserved() {
477f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return verticalAxis.isOrderPreserved();
4783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
4793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
4803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
4817fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When this property is {@code true}, GridLayout is forced to place the row boundaries
482aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * so that their associated grid indices are in ascending order in the view.
4833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
484899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * When this property is {@code false} GridLayout is at liberty to place the vertical row
485899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * boundaries in whatever order best fits the given constraints.
4867fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
487899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The default value of this property is {@code true}.
4887fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
4897fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param rowOrderPreserved {@code true} to force GridLayout to respect the order
4907fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *        of row boundaries
4913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #isRowOrderPreserved()
4933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
4943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
4953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
4963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setRowOrderPreserved(boolean rowOrderPreserved) {
497f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        verticalAxis.setOrderPreserved(rowOrderPreserved);
498aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        invalidateStructure();
499aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
5003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
5033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Returns whether or not column boundaries are ordered by their grid indices.
5043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5057fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @return {@code true} if column boundaries must appear in the order of their indices,
5067fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *         {@code false} otherwise
5073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #setColumnOrderPreserved(boolean)
5093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
5113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
5123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public boolean isColumnOrderPreserved() {
513f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return horizontalAxis.isOrderPreserved();
5143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
5177fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * When this property is {@code true}, GridLayout is forced to place the column boundaries
518aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * so that their associated grid indices are in ascending order in the view.
5193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
520899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * When this property is {@code false} GridLayout is at liberty to place the horizontal column
521899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * boundaries in whatever order best fits the given constraints.
5227fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * <p>
523899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The default value of this property is {@code true}.
5243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5257fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
5263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *        of column boundaries.
5273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see #isColumnOrderPreserved()
5293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
5303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
5313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
5323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public void setColumnOrderPreserved(boolean columnOrderPreserved) {
533f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        horizontalAxis.setOrderPreserved(columnOrderPreserved);
534aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        invalidateStructure();
535aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        requestLayout();
5363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5385125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    // Static utility methods
5395125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
540f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static int max2(int[] a, int valueIfEmpty) {
54151f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        int result = valueIfEmpty;
54251f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        for (int i = 0, N = a.length; i < N; i++) {
54351f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne            result = Math.max(result, a[i]);
54451f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        }
54551f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne        return result;
54651f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne    }
54751f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne
548899d5922870c78e0e663bc5661849eb468afc984Philip Milne    @SuppressWarnings("unchecked")
549f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static <T> T[] append(T[] a, T[] b) {
55048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
55148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        System.arraycopy(a, 0, result, 0, a.length);
55248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        System.arraycopy(b, 0, result, a.length, b.length);
5533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return result;
5543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
556f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static Alignment getAlignment(int gravity, boolean horizontal) {
5575125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
5585125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
5595125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        int flags = (gravity & mask) >> shift;
5605125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        switch (flags) {
5615125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
5625125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return LEADING;
5635125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
5645125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return TRAILING;
5655125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
5665125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return FILL;
5675125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            case AXIS_SPECIFIED:
5685125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return CENTER;
56947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | RELATIVE_LAYOUT_DIRECTION):
57047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                return START;
57147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            case (AXIS_SPECIFIED | AXIS_PULL_AFTER | RELATIVE_LAYOUT_DIRECTION):
57247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                return END;
5735125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            default:
5745125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                return UNDEFINED_ALIGNMENT;
5755125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
5765125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
5775125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
5784c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    /** @noinspection UnusedParameters*/
5791fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getDefaultMargin(View c, boolean horizontal, boolean leading) {
580899d5922870c78e0e663bc5661849eb468afc984Philip Milne        if (c.getClass() == Space.class) {
581899d5922870c78e0e663bc5661849eb468afc984Philip Milne            return 0;
582899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
583f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return defaultGap / 2;
5843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5861fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
5871fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return isAtEdge ? DEFAULT_CONTAINER_MARGIN : getDefaultMargin(c, horizontal, leading);
5883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
5893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
5901fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getDefaultMarginValue(View c, LayoutParams p, boolean horizontal, boolean leading) {
591f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        if (!useDefaultMargins) {
5923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return 0;
5933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
59493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        Spec spec = horizontal ? p.columnSpec : p.rowSpec;
595f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        Axis axis = horizontal ? horizontalAxis : verticalAxis;
59693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        Interval span = spec.span;
59747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        boolean leading1 = (horizontal && isLayoutRtl()) ? !leading : leading;
59847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
5993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
6001fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return getDefaultMargin(c, isAtEdge, horizontal, leading);
6013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
6023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
603f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    int getMargin1(View view, boolean horizontal, boolean leading) {
6043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        LayoutParams lp = getLayoutParams(view);
6053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int margin = horizontal ?
606aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                (leading ? lp.leftMargin : lp.rightMargin) :
607aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                (leading ? lp.topMargin : lp.bottomMargin);
6081fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return margin == UNDEFINED ? getDefaultMarginValue(view, lp, horizontal, leading) : margin;
6091fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    }
6101fd16378812792913a6aa6923acbec20037e09ffPhilip Milne
6114c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    private int getMargin(View view, boolean horizontal, boolean leading) {
612f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        if (alignmentMode == ALIGN_MARGINS) {
6134c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return getMargin1(view, horizontal, leading);
6144c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        } else {
615f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            Axis axis = horizontal ? horizontalAxis : verticalAxis;
6164c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
6174c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            LayoutParams lp = getLayoutParams(view);
6184c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
6194c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int index = leading ? spec.span.min : spec.span.max;
6204c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return margins[index];
6214c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        }
6224c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    }
6234c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne
6241fd16378812792913a6aa6923acbec20037e09ffPhilip Milne    private int getTotalMargin(View child, boolean horizontal) {
6251fd16378812792913a6aa6923acbec20037e09ffPhilip Milne        return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
6263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
6273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
628899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static boolean fits(int[] a, int value, int start, int end) {
629899d5922870c78e0e663bc5661849eb468afc984Philip Milne        if (end > a.length) {
630899d5922870c78e0e663bc5661849eb468afc984Philip Milne            return false;
631899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
632899d5922870c78e0e663bc5661849eb468afc984Philip Milne        for (int i = start; i < end; i++) {
633899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (a[i] > value) {
634899d5922870c78e0e663bc5661849eb468afc984Philip Milne                return false;
6353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
636899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
637899d5922870c78e0e663bc5661849eb468afc984Philip Milne        return true;
638899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
6393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
640899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static void procrusteanFill(int[] a, int start, int end, int value) {
641899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int length = a.length;
642899d5922870c78e0e663bc5661849eb468afc984Philip Milne        Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
643899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
6443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
645899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static void setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan) {
646899d5922870c78e0e663bc5661849eb468afc984Philip Milne        lp.setRowSpecSpan(new Interval(row, row + rowSpan));
647899d5922870c78e0e663bc5661849eb468afc984Philip Milne        lp.setColumnSpecSpan(new Interval(col, col + colSpan));
648899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
6493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
650899d5922870c78e0e663bc5661849eb468afc984Philip Milne    // Logic to avert infinite loops by ensuring that the cells can be placed somewhere.
651899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private static int clip(Interval minorRange, boolean minorWasDefined, int count) {
652899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int size = minorRange.size();
653899d5922870c78e0e663bc5661849eb468afc984Philip Milne        if (count == 0) {
654899d5922870c78e0e663bc5661849eb468afc984Philip Milne            return size;
655899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
656899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int min = minorWasDefined ? min(minorRange.min, count) : 0;
657899d5922870c78e0e663bc5661849eb468afc984Philip Milne        return min(size, count - min);
658899d5922870c78e0e663bc5661849eb468afc984Philip Milne    }
659f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne
660899d5922870c78e0e663bc5661849eb468afc984Philip Milne    // install default indices for cells that don't define them
661899d5922870c78e0e663bc5661849eb468afc984Philip Milne    private void validateLayoutParams() {
662f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final boolean horizontal = (orientation == HORIZONTAL);
663f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final Axis axis = horizontal ? horizontalAxis : verticalAxis;
664f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
665f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne
666899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int major = 0;
667899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int minor = 0;
668899d5922870c78e0e663bc5661849eb468afc984Philip Milne        int[] maxSizes = new int[count];
669f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne
670899d5922870c78e0e663bc5661849eb468afc984Philip Milne        for (int i = 0, N = getChildCount(); i < N; i++) {
671899d5922870c78e0e663bc5661849eb468afc984Philip Milne            LayoutParams lp = getLayoutParams1(getChildAt(i));
672899d5922870c78e0e663bc5661849eb468afc984Philip Milne
673f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
674f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            final Interval majorRange = majorSpec.span;
675f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            final boolean majorWasDefined = majorSpec.startDefined;
676899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final int majorSpan = majorRange.size();
677899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (majorWasDefined) {
678899d5922870c78e0e663bc5661849eb468afc984Philip Milne                major = majorRange.min;
679899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
680899d5922870c78e0e663bc5661849eb468afc984Philip Milne
681f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
682f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            final Interval minorRange = minorSpec.span;
683f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            final boolean minorWasDefined = minorSpec.startDefined;
684899d5922870c78e0e663bc5661849eb468afc984Philip Milne            final int minorSpan = clip(minorRange, minorWasDefined, count);
685899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (minorWasDefined) {
686899d5922870c78e0e663bc5661849eb468afc984Philip Milne                minor = minorRange.min;
687899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
688899d5922870c78e0e663bc5661849eb468afc984Philip Milne
689899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (count != 0) {
690899d5922870c78e0e663bc5661849eb468afc984Philip Milne                // Find suitable row/col values when at least one is undefined.
691899d5922870c78e0e663bc5661849eb468afc984Philip Milne                if (!majorWasDefined || !minorWasDefined) {
692899d5922870c78e0e663bc5661849eb468afc984Philip Milne                    while (!fits(maxSizes, major, minor, minor + minorSpan)) {
693899d5922870c78e0e663bc5661849eb468afc984Philip Milne                        if (minorWasDefined) {
694899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            major++;
695899d5922870c78e0e663bc5661849eb468afc984Philip Milne                        } else {
696899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            if (minor + minorSpan <= count) {
697899d5922870c78e0e663bc5661849eb468afc984Philip Milne                                minor++;
698899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            } else {
699899d5922870c78e0e663bc5661849eb468afc984Philip Milne                                minor = 0;
700899d5922870c78e0e663bc5661849eb468afc984Philip Milne                                major++;
701899d5922870c78e0e663bc5661849eb468afc984Philip Milne                            }
702f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne                        }
703f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne                    }
7043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
705899d5922870c78e0e663bc5661849eb468afc984Philip Milne                procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
7063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
707899d5922870c78e0e663bc5661849eb468afc984Philip Milne
708899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (horizontal) {
709899d5922870c78e0e663bc5661849eb468afc984Philip Milne                setCellGroup(lp, major, majorSpan, minor, minorSpan);
710899d5922870c78e0e663bc5661849eb468afc984Philip Milne            } else {
711899d5922870c78e0e663bc5661849eb468afc984Philip Milne                setCellGroup(lp, minor, minorSpan, major, majorSpan);
712899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
713899d5922870c78e0e663bc5661849eb468afc984Philip Milne
714899d5922870c78e0e663bc5661849eb468afc984Philip Milne            minor = minor + minorSpan;
715899d5922870c78e0e663bc5661849eb468afc984Philip Milne        }
716f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne        invalidateStructure();
7173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private void invalidateStructure() {
720f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        layoutParamsValid = false;
721f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        horizontalAxis.invalidateStructure();
722f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        verticalAxis.invalidateStructure();
723899d5922870c78e0e663bc5661849eb468afc984Philip Milne        // This can end up being done twice. Better twice than not at all.
7243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        invalidateValues();
7253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private void invalidateValues() {
728aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        // Need null check because requestLayout() is called in View's initializer,
729aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        // before we are set up.
730f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        if (horizontalAxis != null && verticalAxis != null) {
731f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            horizontalAxis.invalidateValues();
732f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            verticalAxis.invalidateValues();
733aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
7343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private LayoutParams getLayoutParams1(View c) {
7373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return (LayoutParams) c.getLayoutParams();
7383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
740f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final LayoutParams getLayoutParams(View c) {
741f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        if (!layoutParamsValid) {
7423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            validateLayoutParams();
743f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            layoutParamsValid = true;
7443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
7453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return getLayoutParams1(c);
7463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    protected LayoutParams generateDefaultLayoutParams() {
7503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return new LayoutParams();
7513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public LayoutParams generateLayoutParams(AttributeSet attrs) {
7555125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return new LayoutParams(getContext(), attrs);
7563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
7603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        return new LayoutParams(p);
7613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Draw grid
7643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
7663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int dx = getPaddingLeft();
7673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int dy = getPaddingTop();
76847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        if (isLayoutRtl()) {
76947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int width = getWidth();
77047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            graphics.drawLine(width - dx - x1, dy + y1, width - dx - x2, dy + y2, paint);
77147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        } else {
77247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint);
77347d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        }
7743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
7753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
77647d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    private void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint) {
777b559976a50c34848d602cc7138859507a379893cPhilip Milne        canvas.drawRect(x1, y1, x2 - 1, y2 - 1, paint);
778b559976a50c34848d602cc7138859507a379893cPhilip Milne    }
779b559976a50c34848d602cc7138859507a379893cPhilip Milne
7803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
7813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    protected void onDraw(Canvas canvas) {
7823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        super.onDraw(canvas);
7833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
7843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        if (DEBUG) {
7853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int height = getHeight() - getPaddingTop() - getPaddingBottom();
7863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int width = getWidth() - getPaddingLeft() - getPaddingRight();
7873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
788b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            Paint paint = new Paint();
7894c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            paint.setStyle(Paint.Style.STROKE);
790b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            paint.setColor(Color.argb(50, 255, 255, 255));
791b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
792f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            int[] xs = horizontalAxis.locations;
793b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (xs != null) {
794b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                for (int i = 0, length = xs.length; i < length; i++) {
795b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    int x = xs[i];
796b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    drawLine(canvas, x, 0, x, height - 1, paint);
797b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                }
7983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
799b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
800f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            int[] ys = verticalAxis.locations;
801b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (ys != null) {
802b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                for (int i = 0, length = ys.length; i < length; i++) {
803b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    int y = ys[i];
804b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                    drawLine(canvas, 0, y, width - 1, y, paint);
805b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                }
806b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            }
80748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
808b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            // Draw bounds
809b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            paint.setColor(Color.BLUE);
810b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0; i < getChildCount(); i++) {
811b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
812b559976a50c34848d602cc7138859507a379893cPhilip Milne                drawRect(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), paint);
813b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            }
814b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
815b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            // Draw margins
81693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            paint.setColor(Color.MAGENTA);
817b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0; i < getChildCount(); i++) {
818b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
819b559976a50c34848d602cc7138859507a379893cPhilip Milne                drawRect(canvas,
8204c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getLeft() - getMargin1(c, true, true),
8214c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getTop() - getMargin1(c, false, true),
8224c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getRight() + getMargin1(c, true, false),
8234c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                        c.getBottom() + getMargin1(c, false, false), paint);
8243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
8253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
8263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
8273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
8283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Add/remove
8293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
830e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne    /**
831e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     * @hide
832e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     */
8333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
834f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne    protected void onViewAdded(View child) {
835f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne        super.onViewAdded(child);
8363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        invalidateStructure();
8373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
8383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
839e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne    /**
840e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     * @hide
841e7dda0bb3d2b097f36b3e59f34fb5ab70e36b555Philip Milne     */
8423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
843f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne    protected void onViewRemoved(View child) {
844f51d91c3ab232154b6c00d7f71377ff2421f79bfPhilip Milne        super.onViewRemoved(child);
845b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        invalidateStructure();
846350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne    }
847350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne
848350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne    /**
849350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     * We need to call invalidateStructure() when a child's GONE flag changes state.
850350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     * This implementation is a catch-all, invalidating on any change in the visibility flags.
851350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     *
852350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     * @hide
853350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne     */
854350f0a63c9e785304063a95a6df9e128a67ec64fPhilip Milne    @Override
8550d29936ec3b5545a415e8d032150ea987aab36e3Chet Haase    protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
8560d29936ec3b5545a415e8d032150ea987aab36e3Chet Haase        super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
8570d29936ec3b5545a415e8d032150ea987aab36e3Chet Haase        if (oldVisibility == GONE || newVisibility == GONE) {
8580d29936ec3b5545a415e8d032150ea987aab36e3Chet Haase            invalidateStructure();
8590d29936ec3b5545a415e8d032150ea987aab36e3Chet Haase        }
860b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne    }
861b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne
8623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Measurement
8633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
864f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final boolean isGone(View c) {
865b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        return c.getVisibility() == View.GONE;
866b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    }
867b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
8684a145d72622772b920f60195e80942058984259cPhilip Milne    private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
8694a145d72622772b920f60195e80942058984259cPhilip Milne                                          int childWidth, int childHeight) {
8704a145d72622772b920f60195e80942058984259cPhilip Milne        int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
8714a145d72622772b920f60195e80942058984259cPhilip Milne                mPaddingLeft + mPaddingRight + getTotalMargin(child, true), childWidth);
8724a145d72622772b920f60195e80942058984259cPhilip Milne        int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
8734a145d72622772b920f60195e80942058984259cPhilip Milne                mPaddingTop + mPaddingBottom + getTotalMargin(child, false), childHeight);
8744a145d72622772b920f60195e80942058984259cPhilip Milne        child.measure(childWidthSpec, childHeightSpec);
875b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    }
876b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
8774a145d72622772b920f60195e80942058984259cPhilip Milne    private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
878b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        for (int i = 0, N = getChildCount(); i < N; i++) {
879b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            View c = getChildAt(i);
880b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (isGone(c)) continue;
8814a145d72622772b920f60195e80942058984259cPhilip Milne            LayoutParams lp = getLayoutParams(c);
8824a145d72622772b920f60195e80942058984259cPhilip Milne            if (firstPass) {
8834a145d72622772b920f60195e80942058984259cPhilip Milne                measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
8844a145d72622772b920f60195e80942058984259cPhilip Milne            } else {
885ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                boolean horizontal = (orientation == HORIZONTAL);
886ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
8874a145d72622772b920f60195e80942058984259cPhilip Milne                if (spec.alignment == FILL) {
8884a145d72622772b920f60195e80942058984259cPhilip Milne                    Interval span = spec.span;
889ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                    Axis axis = horizontal ? horizontalAxis : verticalAxis;
8904a145d72622772b920f60195e80942058984259cPhilip Milne                    int[] locations = axis.getLocations();
891ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                    int cellSize = locations[span.max] - locations[span.min];
892ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                    int viewSize = cellSize - getTotalMargin(c, horizontal);
893ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                    if (horizontal) {
894ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                        measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
8954a145d72622772b920f60195e80942058984259cPhilip Milne                    } else {
896ecab1178648670f2c72b47faf250040fcded3d13Philip Milne                        measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
8974a145d72622772b920f60195e80942058984259cPhilip Milne                    }
8984a145d72622772b920f60195e80942058984259cPhilip Milne                }
8994a145d72622772b920f60195e80942058984259cPhilip Milne            }
900b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        }
901b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne    }
902b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne
903aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    @Override
904aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    protected void onMeasure(int widthSpec, int heightSpec) {
9054a145d72622772b920f60195e80942058984259cPhilip Milne        /** If we have been called by {@link View#measure(int, int)}, one of width or height
9064a145d72622772b920f60195e80942058984259cPhilip Milne         *  is  likely to have changed. We must invalidate if so. */
9074a145d72622772b920f60195e80942058984259cPhilip Milne        invalidateValues();
9084a145d72622772b920f60195e80942058984259cPhilip Milne
9094a145d72622772b920f60195e80942058984259cPhilip Milne        measureChildrenWithMargins(widthSpec, heightSpec, true);
9104a145d72622772b920f60195e80942058984259cPhilip Milne
9114a145d72622772b920f60195e80942058984259cPhilip Milne        int width, height;
9124a145d72622772b920f60195e80942058984259cPhilip Milne
9134a145d72622772b920f60195e80942058984259cPhilip Milne        // Use the orientation property to decide which axis should be laid out first.
9144a145d72622772b920f60195e80942058984259cPhilip Milne        if (orientation == HORIZONTAL) {
9154a145d72622772b920f60195e80942058984259cPhilip Milne            width = horizontalAxis.getMeasure(widthSpec);
9164a145d72622772b920f60195e80942058984259cPhilip Milne            measureChildrenWithMargins(widthSpec, heightSpec, false);
9174a145d72622772b920f60195e80942058984259cPhilip Milne            height = verticalAxis.getMeasure(heightSpec);
9184a145d72622772b920f60195e80942058984259cPhilip Milne        } else {
9194a145d72622772b920f60195e80942058984259cPhilip Milne            height = verticalAxis.getMeasure(heightSpec);
9204a145d72622772b920f60195e80942058984259cPhilip Milne            measureChildrenWithMargins(widthSpec, heightSpec, false);
9214a145d72622772b920f60195e80942058984259cPhilip Milne            width = horizontalAxis.getMeasure(widthSpec);
9224a145d72622772b920f60195e80942058984259cPhilip Milne        }
923aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
9244a145d72622772b920f60195e80942058984259cPhilip Milne        int hPadding = getPaddingLeft() + getPaddingRight();
9254a145d72622772b920f60195e80942058984259cPhilip Milne        int vPadding = getPaddingTop() + getPaddingBottom();
9263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9274a145d72622772b920f60195e80942058984259cPhilip Milne        int measuredWidth = Math.max(hPadding + width, getSuggestedMinimumWidth());
9284a145d72622772b920f60195e80942058984259cPhilip Milne        int measuredHeight = Math.max(vPadding + height, getSuggestedMinimumHeight());
92909e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne
9303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        setMeasuredDimension(
93109e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne                resolveSizeAndState(measuredWidth, widthSpec, 0),
93209e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne                resolveSizeAndState(measuredHeight, heightSpec, 0));
9333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
9343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    private int protect(int alignment) {
936aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        return (alignment == UNDEFINED) ? 0 : alignment;
9373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
9383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
93948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    private int getMeasurement(View c, boolean horizontal) {
940aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
9413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
9423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
943f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final int getMeasurementIncludingMargin(View c, boolean horizontal) {
9445125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        if (isGone(c)) {
9455125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            return 0;
9465125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
9474c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne        return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
948aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
9493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
950aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    @Override
951aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    public void requestLayout() {
952aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        super.requestLayout();
953aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        invalidateValues();
954aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    }
955aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
956f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final Alignment getAlignment(Alignment alignment, boolean horizontal) {
9575125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return (alignment != UNDEFINED_ALIGNMENT) ? alignment :
95847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                (horizontal ? START : BASELINE);
9595125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
9605125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
9613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Layout container
9623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
963aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /**
964aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * {@inheritDoc}
965aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
966aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
967aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     The layout operation is implemented by delegating the heavy lifting to the
968aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
969aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     Together they compute the locations of the vertical and horizontal lines of
970aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     the grid (respectively!).
971aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
972aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     This method is then left with the simpler task of applying margins, gravity
973aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     and sizing to each child view and then placing it in its cell.
974aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
9753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @Override
97609e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
97709e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne        int targetWidth = right - left;
97809e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne        int targetHeight = bottom - top;
9793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
9803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingLeft = getPaddingLeft();
9813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingTop = getPaddingTop();
9823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingRight = getPaddingRight();
9833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        int paddingBottom = getPaddingBottom();
9843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
985f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        horizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
986f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        verticalAxis.layout(targetHeight - paddingTop - paddingBottom);
9873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
988f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        int[] hLocations = horizontalAxis.getLocations();
989f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        int[] vLocations = verticalAxis.getLocations();
9904c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne
991b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne        for (int i = 0, N = getChildCount(); i < N; i++) {
992b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            View c = getChildAt(i);
993b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            if (isGone(c)) continue;
994b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            LayoutParams lp = getLayoutParams(c);
99593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec columnSpec = lp.columnSpec;
99693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec rowSpec = lp.rowSpec;
997aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
99893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Interval colSpan = columnSpec.span;
99993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Interval rowSpan = rowSpec.span;
10003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10014c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int x1 = hLocations[colSpan.min];
10024c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int y1 = vLocations[rowSpan.min];
10033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10044c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int x2 = hLocations[colSpan.max];
10054c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int y2 = vLocations[rowSpan.max];
10063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int cellWidth = x2 - x1;
10083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int cellHeight = y2 - y1;
10093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
101048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int pWidth = getMeasurement(c, true);
101148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int pHeight = getMeasurement(c, false);
10123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10135125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            Alignment hAlign = getAlignment(columnSpec.alignment, true);
10145125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            Alignment vAlign = getAlignment(rowSpec.alignment, false);
1015aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1016f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            Bounds colBounds = horizontalAxis.getGroupBounds().getValue(i);
1017f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            Bounds rowBounds = verticalAxis.getGroupBounds().getValue(i);
10187fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
10197fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            // Gravity offsets: the location of the alignment group relative to its cell group.
10204c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection NullableProblems
102147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int gravityOffsetX = protect(hAlign.getAlignmentValue(null,
102247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                    cellWidth - colBounds.size(true)));
10234c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection NullableProblems
102447d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int gravityOffsetY = protect(vAlign.getAlignmentValue(null,
102547d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                    cellHeight - rowBounds.size(true)));
10267fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
102747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            boolean rtl = isLayoutRtl();
102847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int startMargin = getMargin(c, true, !rtl);
10294c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int topMargin = getMargin(c, false, true);
103047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int endMargin = getMargin(c, true, rtl);
10314c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int bottomMargin = getMargin(c, false, false);
10327fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
10334c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            // Same calculation as getMeasurementIncludingMargin()
103447d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int mWidth = startMargin + pWidth + endMargin;
10354c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            int mHeight = topMargin + pHeight + bottomMargin;
10363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10374c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            // Alignment offsets: the location of the view relative to its alignment group.
103847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int alignmentOffsetX = colBounds.getOffset(c, hAlign, mWidth);
103947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int alignmentOffsetY = rowBounds.getOffset(c, vAlign, mHeight);
10407fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
104147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int dx = gravityOffsetX + alignmentOffsetX + startMargin;
104247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int dy = gravityOffsetY + alignmentOffsetY + topMargin;
10437fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne
104447d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            cellWidth -= startMargin + endMargin;
10454c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            cellHeight -= topMargin + bottomMargin;
10463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
104747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int width = hAlign.getSizeInCell(c, pWidth, cellWidth, PRF);
104847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int height = vAlign.getSizeInCell(c, pHeight, cellHeight, PRF);
10493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
105047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            int cx = rtl ? targetWidth - paddingRight - x1 - dx - width : paddingLeft + x1 + dx;
10513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int cy = paddingTop + y1 + dy;
1052899d5922870c78e0e663bc5661849eb468afc984Philip Milne            if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
1053899d5922870c78e0e663bc5661849eb468afc984Philip Milne                c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
1054899d5922870c78e0e663bc5661849eb468afc984Philip Milne            }
1055b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            c.layout(cx, cy, cx + width, cy + height);
10563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
10573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
10583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10598a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
10608a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
10618a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
10628a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(GridLayout.class.getName());
10638a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
10648a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
10658a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
10668a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
10678a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityNodeInfo(info);
10688a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        info.setClassName(GridLayout.class.getName());
10698a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
10708a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
10713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // Inner classes
10723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1073aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
1074aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     This internal class houses the algorithm for computing the locations of grid lines;
1075aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
1076aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     distinguished by the "horizontal" flag which is true for the horizontal axis and false
1077aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     for the vertical one.
1078aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
1079f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final class Axis {
108048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private static final int NEW = 0;
10813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int PENDING = 1;
10823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int COMPLETE = 2;
10833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final boolean horizontal;
10853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1086f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public int definedCount = UNDEFINED;
10874a145d72622772b920f60195e80942058984259cPhilip Milne        private int maxIndex = UNDEFINED;
10883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
108993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        PackedMap<Spec, Bounds> groupBounds;
10903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean groupBoundsValid = false;
10913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
109248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        PackedMap<Interval, MutableInt> forwardLinks;
109348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean forwardLinksValid = false;
109448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
109548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        PackedMap<Interval, MutableInt> backwardLinks;
109648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean backwardLinksValid = false;
10973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
10983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int[] leadingMargins;
1099aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public boolean leadingMarginsValid = false;
1100aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
11013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int[] trailingMargins;
1102aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public boolean trailingMarginsValid = false;
11033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public Arc[] arcs;
11053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean arcsValid = false;
11063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1107aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public int[] locations;
110848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean locationsValid = false;
1109aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1110f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
11113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
111248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private MutableInt parentMin = new MutableInt(0);
111348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private MutableInt parentMax = new MutableInt(-MAX_SIZE);
111448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
11153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private Axis(boolean horizontal) {
11163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.horizontal = horizontal;
11173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11194a145d72622772b920f60195e80942058984259cPhilip Milne        private int calculateMaxIndex() {
11204a145d72622772b920f60195e80942058984259cPhilip Milne            // the number Integer.MIN_VALUE + 1 comes up in undefined cells
11214a145d72622772b920f60195e80942058984259cPhilip Milne            int result = -1;
1122b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
1123b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
1124b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                LayoutParams params = getLayoutParams(c);
112593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec spec = horizontal ? params.columnSpec : params.rowSpec;
11264a145d72622772b920f60195e80942058984259cPhilip Milne                Interval span = spec.span;
11274a145d72622772b920f60195e80942058984259cPhilip Milne                result = max(result, span.min);
11284a145d72622772b920f60195e80942058984259cPhilip Milne                result = max(result, span.max);
11293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11304a145d72622772b920f60195e80942058984259cPhilip Milne            return result == -1 ? UNDEFINED : result;
11313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11334a145d72622772b920f60195e80942058984259cPhilip Milne        private int getMaxIndex() {
11344a145d72622772b920f60195e80942058984259cPhilip Milne            if (maxIndex == UNDEFINED) {
11354a145d72622772b920f60195e80942058984259cPhilip Milne                maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
11363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11374a145d72622772b920f60195e80942058984259cPhilip Milne            return maxIndex;
1138f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        }
1139f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1140f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public int getCount() {
11414a145d72622772b920f60195e80942058984259cPhilip Milne            return max(definedCount, getMaxIndex());
11423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public void setCount(int count) {
1145f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this.definedCount = count;
11463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean isOrderPreserved() {
1149f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            return orderPreserved;
11503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public void setOrderPreserved(boolean orderPreserved) {
1153f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this.orderPreserved = orderPreserved;
11543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            invalidateStructure();
11553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
115793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        private PackedMap<Spec, Bounds> createGroupBounds() {
115893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
115948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
1160b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                View c = getChildAt(i);
11615125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                LayoutParams lp = getLayoutParams(c);
11625125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
11635125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                Bounds bounds = getAlignment(spec.alignment, horizontal).getBounds();
11645125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                assoc.put(spec, bounds);
11653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
116648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return assoc.pack();
11673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private void computeGroupBounds() {
1170b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            Bounds[] values = groupBounds.values;
1171b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0; i < values.length; i++) {
1172b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                values[i].reset();
11733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
1174aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
11753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                View c = getChildAt(i);
11763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                LayoutParams lp = getLayoutParams(c);
117793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
117893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                groupBounds.getValue(i).include(c, spec, GridLayout.this, this);
11793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1182f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public PackedMap<Spec, Bounds> getGroupBounds() {
11833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (groupBounds == null) {
11843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                groupBounds = createGroupBounds();
11853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (!groupBoundsValid) {
11873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                computeGroupBounds();
11883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                groupBoundsValid = true;
11893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
11903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return groupBounds;
11913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
11923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
11933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Add values computed by alignment - taking the max of all alignments in each span
119448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private PackedMap<Interval, MutableInt> createLinks(boolean min) {
119548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
119693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec[] keys = getGroupBounds().keys;
119748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0, N = keys.length; i < N; i++) {
119848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Interval span = min ? keys[i].span : keys[i].span.inverse();
119948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                result.put(span, new MutableInt());
12003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
120148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return result.pack();
12023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
120448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
120548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            MutableInt[] spans = links.values;
12063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < spans.length; i++) {
12073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                spans[i].reset();
12083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
12093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
12105d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            // Use getter to trigger a re-evaluation
121148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Bounds[] bounds = getGroupBounds().values;
12123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < bounds.length; i++) {
121348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                int size = bounds[i].size(min);
121448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                MutableInt valueHolder = links.getValue(i);
12155125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                // this effectively takes the max() of the minima and the min() of the maxima
12165125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                valueHolder.value = max(valueHolder.value, min ? size : -size);
12173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
12183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
122048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private PackedMap<Interval, MutableInt> getForwardLinks() {
122148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (forwardLinks == null) {
122248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                forwardLinks = createLinks(true);
12233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
122448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!forwardLinksValid) {
122548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeLinks(forwardLinks, true);
122648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                forwardLinksValid = true;
12273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
122848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return forwardLinks;
12293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
123148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private PackedMap<Interval, MutableInt> getBackwardLinks() {
123248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (backwardLinks == null) {
123348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                backwardLinks = createLinks(false);
12343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
123548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!backwardLinksValid) {
123648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeLinks(backwardLinks, false);
123748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                backwardLinksValid = true;
123848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
123948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return backwardLinks;
12403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
124248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void include(List<Arc> arcs, Interval key, MutableInt size,
12434a145d72622772b920f60195e80942058984259cPhilip Milne                             boolean ignoreIfAlreadyPresent) {
124448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            /*
124548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Remove self referential links.
124648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            These appear:
124748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                . as parental constraints when GridLayout has no children
124848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                . when components have been marked as GONE
124948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            */
125048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (key.size() == 0) {
125148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                return;
12523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
125348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // this bit below should really be computed outside here -
125448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // its just to stop default (row/col > 0) constraints obliterating valid entries
125548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (ignoreIfAlreadyPresent) {
125648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                for (Arc arc : arcs) {
125748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    Interval span = arc.span;
125848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    if (span.equals(key)) {
125948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        return;
126048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    }
126148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
126248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
126348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            arcs.add(new Arc(key, size));
12643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
126648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void include(List<Arc> arcs, Interval key, MutableInt size) {
126748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(arcs, key, size, true);
12683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1270aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        // Group arcs by their first vertex, returning an array of arrays.
12713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // This is linear in the number of arcs.
1272f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
127348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int N = getCount() + 1; // the number of vertices
12743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Arc[][] result = new Arc[N][];
12753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int[] sizes = new int[N];
12763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (Arc arc : arcs) {
12773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                sizes[arc.span.min]++;
12785d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            }
12793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < sizes.length; i++) {
12803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                result[i] = new Arc[sizes[i]];
12813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
12823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            // reuse the sizes array to hold the current last elements as we insert each arc
12833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Arrays.fill(sizes, 0);
12843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (Arc arc : arcs) {
12853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int i = arc.span.min;
12863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                result[i][sizes[i]++] = arc;
12873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
12883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
12893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
12903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
12913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
129248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private Arc[] topologicalSort(final Arc[] arcs) {
129348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return new Object() {
129448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Arc[] result = new Arc[arcs.length];
129548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                int cursor = result.length - 1;
129648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
12973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int[] visited = new int[getCount() + 1];
12983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
129948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                void walk(int loc) {
130048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    switch (visited[loc]) {
130148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        case NEW: {
130248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            visited[loc] = PENDING;
130348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            for (Arc arc : arcsByVertex[loc]) {
130448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                                walk(arc.span.max);
130548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                                result[cursor--] = arc;
13063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                            }
130748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            visited[loc] = COMPLETE;
130848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            break;
130948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        }
131048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        case PENDING: {
131148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            assert false;
131248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            break;
131348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        }
131448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        case COMPLETE: {
131548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                            break;
13163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                        }
13173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    }
13183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
131948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
132048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Arc[] sort() {
132148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
132248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                        walk(loc);
132348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    }
132448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    assert cursor == -1;
132548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return result;
132648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
132748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }.sort();
132848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
132948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
133048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private Arc[] topologicalSort(List<Arc> arcs) {
133148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
13323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
13333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
133448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
133548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0; i < links.keys.length; i++) {
133648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                Interval key = links.keys[i];
133748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                include(result, key, links.values[i], false);
133848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
133948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
134048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1341aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private Arc[] createArcs() {
134248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            List<Arc> mins = new ArrayList<Arc>();
134348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            List<Arc> maxs = new ArrayList<Arc>();
13443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
134548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add the minimum values from the components.
134648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            addComponentSizes(mins, getForwardLinks());
134748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add the maximum values from the components.
134848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            addComponentSizes(maxs, getBackwardLinks());
13493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
135048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add ordering constraints to prevent row/col sizes from going negative
1351f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            if (orderPreserved) {
135248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                // Add a constraint for every row/col
13533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                for (int i = 0; i < getCount(); i++) {
1354899d5922870c78e0e663bc5661849eb468afc984Philip Milne                    include(mins, new Interval(i, i + 1), new MutableInt(0));
13553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
13563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
135748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
135848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Add the container constraints. Use the version of include that allows
135948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // duplicate entries in case a child spans the entire grid.
136048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int N = getCount();
136148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(mins, new Interval(0, N), parentMin, false);
136248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(maxs, new Interval(N, 0), parentMax, false);
136348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
136448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // Sort
136548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Arc[] sMins = topologicalSort(mins);
136648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            Arc[] sMaxs = topologicalSort(maxs);
136748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
136848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return append(sMins, sMaxs);
136948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
137048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
137148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void computeArcs() {
137248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            // getting the links validates the values that are shared by the arc list
137348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            getForwardLinks();
137448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            getBackwardLinks();
13753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
13763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1377aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public Arc[] getArcs() {
13783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (arcs == null) {
1379aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                arcs = createArcs();
13803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
13813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (!arcsValid) {
138248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeArcs();
13833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                arcsValid = true;
13843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
13853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return arcs;
13863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
13873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1388aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private boolean relax(int[] locations, Arc entry) {
138948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!entry.valid) {
139048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                return false;
139148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
13923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Interval span = entry.span;
13933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int u = span.min;
13943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int v = span.max;
13953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int value = entry.value.value;
13963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int candidate = locations[u] + value;
1397aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (candidate > locations[v]) {
13983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                locations[v] = candidate;
13993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return true;
14003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
14013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return false;
14023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
14033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1404f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        private void init(int[] locations) {
14054a145d72622772b920f60195e80942058984259cPhilip Milne            Arrays.fill(locations, 0);
1406f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        }
1407f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1408f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        private String arcsToString(List<Arc> arcs) {
14094a145d72622772b920f60195e80942058984259cPhilip Milne            String var = horizontal ? "x" : "y";
1410f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            StringBuilder result = new StringBuilder();
14114a145d72622772b920f60195e80942058984259cPhilip Milne            boolean first = true;
14124a145d72622772b920f60195e80942058984259cPhilip Milne            for (Arc arc : arcs) {
14134a145d72622772b920f60195e80942058984259cPhilip Milne                if (first) {
14144a145d72622772b920f60195e80942058984259cPhilip Milne                    first = false;
1415f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                } else {
14164a145d72622772b920f60195e80942058984259cPhilip Milne                    result = result.append(", ");
1417f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                }
1418f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                int src = arc.span.min;
1419f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                int dst = arc.span.max;
1420f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                int value = arc.value.value;
1421f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                result.append((src < dst) ?
1422f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        var + dst + " - " + var + src + " > " + value :
1423f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        var + src + " - " + var + dst + " < " + -value);
1424f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1425f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            }
1426f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            return result.toString();
1427f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        }
1428f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1429f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
1430f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            List<Arc> culprits = new ArrayList<Arc>();
1431f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            List<Arc> removed = new ArrayList<Arc>();
1432f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            for (int c = 0; c < arcs.length; c++) {
1433f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                Arc arc = arcs[c];
1434f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                if (culprits0[c]) {
1435f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    culprits.add(arc);
1436f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                }
1437f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                if (!arc.valid) {
1438f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    removed.add(arc);
1439f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                }
1440f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            }
1441f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            Log.d(TAG, axisName + " constraints: " + arcsToString(culprits) + " are inconsistent; "
1442f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    + "permanently removing: " + arcsToString(removed) + ". ");
1443f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        }
1444f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1445aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        /*
1446aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1447aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1448aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        GridLayout converts its requirements into a system of linear constraints of the
1449aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        form:
1450aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1451aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        x[i] - x[j] < a[k]
1452aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1453aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Where the x[i] are variables and the a[k] are constants.
1454aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1455aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        For example, if the variables were instead labeled x, y, z we might have:
1456aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1457aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            x - y < 17
1458aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            y - z < 23
1459aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            z - x < 42
1460aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1461aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        This is a special case of the Linear Programming problem that is, in turn,
1462aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        equivalent to the single-source shortest paths problem on a digraph, for
1463aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
1464aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1465aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Other algorithms are faster in the case where no arcs have negative weights
1466aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        but allowing negative weights turns out to be the same as accommodating maximum
1467aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        size requirements as well as minimum ones.
1468aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1469aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Bellman-Ford works by iteratively 'relaxing' constraints over all nodes (an O(N)
1470aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        process) and performing this step N times. Proof of correctness hinges on the
1471aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        fact that there can be no negative weight chains of length > N - unless a
1472aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        'negative weight loop' exists. The algorithm catches this case in a final
1473aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        checking phase that reports failure.
1474aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1475aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        By topologically sorting the nodes and checking this condition at each step
1476aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        typical layout problems complete after the first iteration and the algorithm
1477aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        completes in O(N) steps with very low constants.
1478aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        */
147948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void solve(Arc[] arcs, int[] locations) {
1480f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            String axisName = horizontal ? "horizontal" : "vertical";
14813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
1482f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            boolean[] originalCulprits = null;
14833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1484f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            for (int p = 0; p < arcs.length; p++) {
1485f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                init(locations);
1486f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1487f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1488f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                for (int i = 0; i < N; i++) {
1489f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    boolean changed = false;
1490f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    for (int j = 0, length = arcs.length; j < length; j++) {
1491f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        changed |= relax(locations, arcs[j]);
1492f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    }
1493f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    if (!changed) {
1494f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        if (originalCulprits != null) {
1495f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                            logError(axisName, arcs, originalCulprits);
1496f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        }
1497f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        return;
14983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    }
14993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
150048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1501f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                boolean[] culprits = new boolean[arcs.length];
1502f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                for (int i = 0; i < N; i++) {
1503f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    for (int j = 0, length = arcs.length; j < length; j++) {
1504f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        culprits[j] |= relax(locations, arcs[j]);
1505f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    }
1506f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                }
150748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1508f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                if (p == 0) {
1509f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    originalCulprits = culprits;
151048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
1511f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1512f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                for (int i = 0; i < arcs.length; i++) {
1513f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                    if (culprits[i]) {
1514f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        Arc arc = arcs[i];
1515f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        // Only remove max values, min values alone cannot be inconsistent
1516f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        if (arc.span.min < arc.span.max) {
1517f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                            continue;
1518f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        }
1519f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        arc.valid = false;
1520f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne                        break;
152148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    }
152248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
152348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
15243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
15253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1526aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private void computeMargins(boolean leading) {
1527aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            int[] margins = leading ? leadingMargins : trailingMargins;
1528b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne            for (int i = 0, N = getChildCount(); i < N; i++) {
15293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                View c = getChildAt(i);
1530b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne                if (isGone(c)) continue;
15313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                LayoutParams lp = getLayoutParams(c);
153293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
153393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Interval span = spec.span;
15343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int index = leading ? span.min : span.max;
15354c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne                margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
15363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
15373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
15383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1539f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        // External entry points
1540f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
1541f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public int[] getLeadingMargins() {
1542aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (leadingMargins == null) {
1543aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                leadingMargins = new int[getCount() + 1];
1544aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1545aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (!leadingMarginsValid) {
1546aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                computeMargins(true);
1547aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                leadingMarginsValid = true;
1548aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1549aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            return leadingMargins;
1550aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
1551aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1552f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public int[] getTrailingMargins() {
1553aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (trailingMargins == null) {
1554aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                trailingMargins = new int[getCount() + 1];
1555aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1556aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (!trailingMarginsValid) {
1557aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                computeMargins(false);
1558aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                trailingMarginsValid = true;
1559aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
1560aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            return trailingMargins;
1561aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
15623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
156348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void computeLocations(int[] a) {
1564f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            solve(getArcs(), a);
15654a145d72622772b920f60195e80942058984259cPhilip Milne            if (!orderPreserved) {
15664a145d72622772b920f60195e80942058984259cPhilip Milne                // Solve returns the smallest solution to the constraint system for which all
15674a145d72622772b920f60195e80942058984259cPhilip Milne                // values are positive. One value is therefore zero - though if the row/col
15684a145d72622772b920f60195e80942058984259cPhilip Milne                // order is not preserved this may not be the first vertex. For consistency,
15694a145d72622772b920f60195e80942058984259cPhilip Milne                // translate all the values so that they measure the distance from a[0]; the
15704a145d72622772b920f60195e80942058984259cPhilip Milne                // leading edge of the parent. After this transformation some values may be
15714a145d72622772b920f60195e80942058984259cPhilip Milne                // negative.
15724a145d72622772b920f60195e80942058984259cPhilip Milne                int a0 = a[0];
15734a145d72622772b920f60195e80942058984259cPhilip Milne                for (int i = 0, N = a.length; i < N; i++) {
15744a145d72622772b920f60195e80942058984259cPhilip Milne                    a[i] = a[i] - a0;
15754a145d72622772b920f60195e80942058984259cPhilip Milne                }
15764a145d72622772b920f60195e80942058984259cPhilip Milne            }
15773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
15783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1579f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public int[] getLocations() {
1580aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            if (locations == null) {
1581aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                int N = getCount() + 1;
1582aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                locations = new int[N];
1583aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            }
158448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            if (!locationsValid) {
158548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                computeLocations(locations);
158648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                locationsValid = true;
158748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
1588aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            return locations;
15893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
15903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1591aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private int size(int[] locations) {
15924a145d72622772b920f60195e80942058984259cPhilip Milne            // The parental edges are attached to vertices 0 and N - even when order is not
15934a145d72622772b920f60195e80942058984259cPhilip Milne            // being preserved and other vertices fall outside this range. Measure the distance
15944a145d72622772b920f60195e80942058984259cPhilip Milne            // between vertices 0 and N, assuming that locations[0] = 0.
15954a145d72622772b920f60195e80942058984259cPhilip Milne            return locations[getCount()];
1596aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        }
1597aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
159848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private void setParentConstraints(int min, int max) {
159948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            parentMin.value = min;
160048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            parentMax.value = -max;
160148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            locationsValid = false;
16023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
160448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private int getMeasure(int min, int max) {
160548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            setParentConstraints(min, max);
160648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return size(getLocations());
160748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
1608aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1609f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public int getMeasure(int measureSpec) {
161048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int mode = MeasureSpec.getMode(measureSpec);
161148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int size = MeasureSpec.getSize(measureSpec);
161248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            switch (mode) {
161348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                case MeasureSpec.UNSPECIFIED: {
161493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                    return getMeasure(0, MAX_SIZE);
161548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
161648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                case MeasureSpec.EXACTLY: {
161748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return getMeasure(size, size);
161848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
161948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                case MeasureSpec.AT_MOST: {
162048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return getMeasure(0, size);
162148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
162248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                default: {
162348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    assert false;
162448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return 0;
162548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                }
16263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
162748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
1628aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
1629f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public void layout(int size) {
163048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            setParentConstraints(size, size);
163148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            getLocations();
16323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1634f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public void invalidateStructure() {
16354a145d72622772b920f60195e80942058984259cPhilip Milne            maxIndex = UNDEFINED;
1636aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
16373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            groupBounds = null;
163848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            forwardLinks = null;
163948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            backwardLinks = null;
164048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1641aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            leadingMargins = null;
1642aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            trailingMargins = null;
1643c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne            arcs = null;
164448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1645aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            locations = null;
16463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            invalidateValues();
16483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1650f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public void invalidateValues() {
16513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            groupBoundsValid = false;
165248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            forwardLinksValid = false;
165348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            backwardLinksValid = false;
165448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1655aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            leadingMarginsValid = false;
1656aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            trailingMarginsValid = false;
165748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            arcsValid = false;
165848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
165948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            locationsValid = false;
16603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
16613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
16623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
16633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
16643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Layout information associated with each of the children of a GridLayout.
16653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
16663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * GridLayout supports both row and column spanning and arbitrary forms of alignment within
16673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * each cell group. The fundamental parameters associated with each cell group are
16683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * gathered into their vertical and horizontal components and stored
166993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
1670b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne     * {@link android.widget.GridLayout.Spec Specs} are immutable structures
1671b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne     * and may be shared between the layout parameters of different children.
16723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
167393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * The row and column specs contain the leading and trailing indices along each axis
1674aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     * and together specify the four grid indices that delimit the cells of this cell group.
16753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
167693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * The  alignment properties of the row and column specs together specify
16773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * both aspects of alignment within the cell group. It is also possible to specify a child's
16783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
16793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * method.
1680f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *
1681f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
1682f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *
1683f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * Because the default values of the {@link #width} and {@link #height}
1684f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
1685f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * declared in the layout parameters of GridLayout's children. In addition,
1686f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
1687f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
1688f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * instead controlled by the principle of <em>flexibility</em>,
1689f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * as discussed in {@link GridLayout}.
1690f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *
1691f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * <h4>Summary</h4>
1692f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *
1693f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * You should not need to use either of the special size values:
1694f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
1695f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * a GridLayout.
16963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
16973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <h4>Default values</h4>
16983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
16993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <ul>
17003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #width} = {@link #WRAP_CONTENT}</li>
17013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
17023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #topMargin} = 0 when
17033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
17047fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
17053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
17063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #leftMargin} = 0 when
17073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
17087fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
17093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
17103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #bottomMargin} = 0 when
17113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
17127fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
17133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
17143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *     <li>{@link #rightMargin} = 0 when
17153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
17167fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     *          {@code false}; otherwise {@link #UNDEFINED}, to
17173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *          indicate that a default value should be computed on demand. </li>
1718f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *     <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
1719f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *     <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
1720f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *     <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
1721f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *     <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
1722f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *     <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
1723f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *     <li>{@link #columnSpec}<code>.alignment</code> = {@link #LEFT} </li>
17243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * </ul>
17253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
1726f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * See {@link GridLayout} for a more complete description of the conventions
1727f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     * used by GridLayout in the interpretation of the properties of this class.
1728f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne     *
17293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_row
17303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
17313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_column
17323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
17333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
17343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
17353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static class LayoutParams extends MarginLayoutParams {
17363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Default values
17383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_WIDTH = WRAP_CONTENT;
17403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
17413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_MARGIN = UNDEFINED;
17423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_ROW = UNDEFINED;
17433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_COLUMN = UNDEFINED;
1744f474870fe1189f73cf8ffbaba9e524ef194b5043Philip Milne        private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
17453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
17463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // TypedArray indices
17483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1749b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
1750b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
1751b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
1752b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int RIGHT_MARGIN =
1753b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne                R.styleable.ViewGroup_MarginLayout_layout_marginRight;
17543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static final int BOTTOM_MARGIN =
1755b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne                R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
17563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1757b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
1758b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
17595d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
1760b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
1761b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
17625d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
1763b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne        private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
17643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Instance variables
17663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
1768f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne         * The spec that defines the vertical characteristics of the cell group
17693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * described by these layout parameters.
17703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
1771f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public Spec rowSpec = Spec.UNDEFINED;
1772f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
17733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
1774f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne         * The spec that defines the horizontal characteristics of the cell group
17753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * described by these layout parameters.
17763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
1777f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public Spec columnSpec = Spec.UNDEFINED;
17783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Constructors
17803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private LayoutParams(
17823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int width, int height,
17833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int left, int top, int right, int bottom,
178493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                Spec rowSpec, Spec columnSpec) {
17853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(width, height);
17863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            setMargins(left, top, right, bottom);
178793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            this.rowSpec = rowSpec;
178893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            this.columnSpec = columnSpec;
17893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
17903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
17913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
179293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * Constructs a new LayoutParams instance for this <code>rowSpec</code>
179393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * and <code>columnSpec</code>. All other fields are initialized with
17943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * default values as defined in {@link LayoutParams}.
17953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
179693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * @param rowSpec    the rowSpec
179793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * @param columnSpec the columnSpec
17983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
179993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        public LayoutParams(Spec rowSpec, Spec columnSpec) {
18003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
18013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
180293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne                    rowSpec, columnSpec);
18033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
18063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
18073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
18083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams() {
1809f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this(Spec.UNDEFINED, Spec.UNDEFINED);
18103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Copying constructors
18133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
18153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
18163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
18173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(ViewGroup.LayoutParams params) {
18183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(params);
18193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
18223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
18233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
18243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(MarginLayoutParams params) {
18253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(params);
18263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
18293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
18303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
18313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(LayoutParams that) {
18323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            super(that);
1833f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this.rowSpec = that.rowSpec;
1834f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this.columnSpec = that.columnSpec;
18353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // AttributeSet constructors
18383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
18403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * {@inheritDoc}
18413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
18423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Values not defined in the attribute set take the default values
18433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * defined in {@link LayoutParams}.
18443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
18453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public LayoutParams(Context context, AttributeSet attrs) {
18465125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            super(context, attrs);
18475125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            reInitSuper(context, attrs);
18485125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            init(context, attrs);
18493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Implementation
18523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Reinitialise the margins using a different default policy than MarginLayoutParams.
18543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
18553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // so that a layout manager default can be accessed post set up. We need this as, at the
18563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // point of installation, we do not know how many rows/cols there are and therefore
18573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // which elements are positioned next to the container's trailing edges. We need to
18583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // know this as margins around the container's boundary should have different
18593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // defaults to those between peers.
18603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        // This method could be parametrized and moved into MarginLayout.
18623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private void reInitSuper(Context context, AttributeSet attrs) {
1863b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne            TypedArray a =
1864b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne                    context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
18653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            try {
18663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
18673f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
18693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
18703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
18713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
18723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            } finally {
18733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                a.recycle();
18743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
18753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18775125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        private void init(Context context, AttributeSet attrs) {
1878b0ce49b5ad53631ff0c3cdd8266e82f3c20c65dcPhilip Milne            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
18793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            try {
18805125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
18813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18821e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne                int column = a.getInt(COLUMN, DEFAULT_COLUMN);
18835125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
1884899d5922870c78e0e663bc5661849eb468afc984Philip Milne                this.columnSpec = spec(column, colSpan, getAlignment(gravity, true));
18853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18861e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne                int row = a.getInt(ROW, DEFAULT_ROW);
18871e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne                int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
1888899d5922870c78e0e663bc5661849eb468afc984Philip Milne                this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false));
18893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            } finally {
18903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                a.recycle();
18913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
18923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
18933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
18943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
18957fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
18967fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * See {@link android.view.Gravity}.
18973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
18987fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * @param gravity the new gravity value
18993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
19003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
19013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
19023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public void setGravity(int gravity) {
19035125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
19045125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
19053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
19073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
19083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
19093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
19103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
19113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1913f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final void setRowSpecSpan(Interval span) {
191493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            rowSpec = rowSpec.copyWriteSpan(span);
19153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1917f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final void setColumnSpecSpan(Interval span) {
191893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            columnSpec = columnSpec.copyWriteSpan(span);
19193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
19213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1922aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
1923aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
1924aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
1925aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
1926f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final static class Arc {
19273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final Interval span;
1928aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public final MutableInt value;
192948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public boolean valid = true;
19303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1931aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        public Arc(Interval span, MutableInt value) {
19323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.span = span;
19333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.value = value;
19343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
19363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
19373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public String toString() {
193848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return span + " " + (!valid ? "+>" : "->") + " " + value;
19393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
19413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
19423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    // A mutable Integer - used to avoid heap allocation during the layout operation
19433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1944f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final static class MutableInt {
19453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int value;
19463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1947f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public MutableInt() {
19483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            reset();
19493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1951f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public MutableInt(int value) {
19523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.value = value;
19533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
19543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1955f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public void reset() {
19563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            value = Integer.MIN_VALUE;
19573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
195848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
195948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        @Override
196048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public String toString() {
196148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return Integer.toString(value);
196248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
196348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne    }
196448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1965f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
196648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private final Class<K> keyType;
196748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private final Class<V> valueType;
196848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
196948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        private Assoc(Class<K> keyType, Class<V> valueType) {
197048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            this.keyType = keyType;
197148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            this.valueType = valueType;
197248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
197348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
1974f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
197548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return new Assoc<K, V>(keyType, valueType);
197648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
197748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
197848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public void put(K key, V value) {
197948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            add(Pair.create(key, value));
198048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
198148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
198248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        @SuppressWarnings(value = "unchecked")
198348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public PackedMap<K, V> pack() {
198448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int N = size();
198548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            K[] keys = (K[]) Array.newInstance(keyType, N);
198648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            V[] values = (V[]) Array.newInstance(valueType, N);
198748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            for (int i = 0; i < N; i++) {
198848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                keys[i] = get(i).first;
198948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                values[i] = get(i).second;
199048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
199148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return new PackedMap<K, V>(keys, values);
199248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
19933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
19943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
1995aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
1996aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    This data structure is used in place of a Map where we have an index that refers to the order
1997aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    in which each key/value pairs were added to the map. In this case we store keys and values
1998aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    in arrays of a length that is equal to the number of unique keys. We also maintain an
1999aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    array of indexes from insertion order to the compacted arrays of keys and values.
2000aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
2001aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    Note that behavior differs from that of a LinkedHashMap in that repeated entries
2002aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    *do* get added multiples times. So the length of index is equals to the number of
2003aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    items added.
2004aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
2005aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    This is useful in the GridLayout class where we can rely on the order of children not
2006aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    changing during layout - to use integer-based lookup for our internal structures
2007aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    rather than using (and storing) an implementation of Map<Key, ?>.
2008aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne     */
20093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    @SuppressWarnings(value = "unchecked")
2010f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final static class PackedMap<K, V> {
20113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final int[] index;
20123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final K[] keys;
20133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final V[] values;
20143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private PackedMap(K[] keys, V[] values) {
20163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.index = createIndex(keys);
20173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2018aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            this.keys = compact(keys, index);
2019aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            this.values = compact(values, index);
20203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2022f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        public V getValue(int i) {
20233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return values[index[i]];
20243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private static <K> int[] createIndex(K[] keys) {
20273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int size = keys.length;
20283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int[] result = new int[size];
20293f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20303f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
20313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < size; i++) {
20323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                K key = keys[i];
20333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                Integer index = keyToIndex.get(key);
20343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                if (index == null) {
20353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    index = keyToIndex.size();
20363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    keyToIndex.put(key, index);
20373f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                }
20383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                result[i] = index;
20393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
20403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
20413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2043aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        /*
2044aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        Create a compact array of keys or values using the supplied index.
2045aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne         */
2046aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne        private static <K> K[] compact(K[] a, int[] index) {
2047aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            int size = a.length;
2048aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne            Class<?> componentType = a.getClass().getComponentType();
204951f17d54613c33638c8a2da8affcd9ba35994cb3Philip Milne            K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
20503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            // this overwrite duplicates, retaining the last equivalent entry
20523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            for (int i = 0; i < size; i++) {
2053aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne                result[index[i]] = a[i];
20543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
20553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
20563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
20583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2059aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    /*
206093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    For each group (with a given alignment) we need to store the amount of space required
20617fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne    before the alignment point and the amount of space required after it. One side of this
206247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    calculation is always 0 for START and END alignments but we don't make use of this.
2063aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
2064aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    simple optimisations are possible.
2065aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
2066aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    The general algorithm therefore is to create a Map (actually a PackedMap) from
206793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    group to Bounds and to loop through all Views in the group taking the maximum
2068aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    of the values for each View.
2069aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne    */
2070f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static class Bounds {
20717fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne        public int before;
20727fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne        public int after;
20735125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        public int flexibility; // we're flexible iff all included specs are flexible
20743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
20753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        private Bounds() {
20763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            reset();
20773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2079a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        protected void reset() {
20807fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            before = Integer.MIN_VALUE;
20817fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            after = Integer.MIN_VALUE;
20825125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            flexibility = CAN_STRETCH; // from the above, we're flexible when empty
20833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2085a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        protected void include(int before, int after) {
20867fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            this.before = max(this.before, before);
20877fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            this.after = max(this.after, after);
20883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
209048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        protected int size(boolean min) {
20915d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            if (!min) {
20925125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne                if (canStretch(flexibility)) {
20935d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne                    return MAX_SIZE;
20945d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne                }
209548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            }
20967fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne            return before + after;
20973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
20983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
209948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        protected int getOffset(View c, Alignment alignment, int size) {
210048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            return before - alignment.getAlignmentValue(c, size);
210148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        }
210248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
21035125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        protected final void include(View c, Spec spec, GridLayout gridLayout, Axis axis) {
21045125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            this.flexibility &= spec.getFlexibility();
210548b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal);
21065125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            Alignment alignment = gridLayout.getAlignment(spec.alignment, axis.horizontal);
21074c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            // todo test this works correctly when the returned value is UNDEFINED
21085125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            int before = alignment.getAlignmentValue(c, size);
210948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne            include(before, size - before);
2110a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
2111a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
21123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
21133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public String toString() {
21143f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return "Bounds{" +
21157fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne                    "before=" + before +
21167fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne                    ", after=" + after +
21173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                    '}';
21183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
21193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
21203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21213f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
21223f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * An Interval represents a contiguous range of values that lie between
21233f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * the interval's {@link #min} and {@link #max} values.
21243f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
21253f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Intervals are immutable so may be passed as values and used as keys in hash tables.
21263f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * It is not necessary to have multiple instances of Intervals which have the same
21273f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * {@link #min} and {@link #max} values.
21283f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
21297fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * Intervals are often written as {@code [min, max]} and represent the set of values
21307fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne     * {@code x} such that {@code min <= x < max}.
21313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2132f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    final static class Interval {
21333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
21343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * The minimum value.
21353f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
21363f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final int min;
2137aa616f31fe7c0c8e3657bb9a5889ec5e56ee5232Philip Milne
21383f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
21393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * The maximum value.
21403f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
21413f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public final int max;
21423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
21447fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * Construct a new Interval, {@code interval}, where:
21453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * <ul>
21467fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         *     <li> {@code interval.min = min} </li>
21477fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         *     <li> {@code interval.max = max} </li>
21483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * </ul>
21493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
21503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @param min the minimum value.
21513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @param max the maximum value.
21523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
21533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public Interval(int min, int max) {
21543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.min = min;
21553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            this.max = max;
21563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
21573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2158f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        int size() {
21593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return max - min;
21603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
21613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2162f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        Interval inverse() {
21633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return new Interval(max, min);
21643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
21653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
21677fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * Returns {@code true} if the {@link #getClass class},
21687fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * {@link #min} and {@link #max} properties of this Interval and the
21697fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * supplied parameter are pairwise equal; {@code false} otherwise.
21703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
21717fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * @param that the object to compare this interval with
21723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
21733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @return {@code true} if the specified object is equal to this
21747fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         *         {@code Interval}, {@code false} otherwise.
21753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
21763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
21773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean equals(Object that) {
21783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (this == that) {
21793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return true;
21803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (that == null || getClass() != that.getClass()) {
21823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
21833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            Interval interval = (Interval) that;
21863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21873f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (max != interval.max) {
21883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
21893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21904c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection RedundantIfStatement
21913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (min != interval.min) {
21923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
21933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
21943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return true;
21963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
21973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
21983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
21993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int hashCode() {
22003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int result = min;
22013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            result = 31 * result + max;
22023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
22033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
22063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public String toString() {
22073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return "[" + min + ", " + max + "]";
22083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
22103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2211899d5922870c78e0e663bc5661849eb468afc984Philip Milne    /**
2212899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * A Spec defines the horizontal or vertical characteristics of a group of
22134a145d72622772b920f60195e80942058984259cPhilip Milne     * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
22144a145d72622772b920f60195e80942058984259cPhilip Milne     * along the appropriate axis.
2215899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * <p>
2216899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2217899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * See {@link GridLayout} for a description of the conventions used by GridLayout
2218899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * for grid indices.
2219899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * <p>
2220899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * The <em>alignment</em> property specifies how cells should be aligned in this group.
2221899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * For row groups, this specifies the vertical alignment.
2222899d5922870c78e0e663bc5661849eb468afc984Philip Milne     * For column groups, this specifies the horizontal alignment.
22234a145d72622772b920f60195e80942058984259cPhilip Milne     * <p>
22244a145d72622772b920f60195e80942058984259cPhilip Milne     * Use the following static methods to create specs:
22254a145d72622772b920f60195e80942058984259cPhilip Milne     * <ul>
22264a145d72622772b920f60195e80942058984259cPhilip Milne     *   <li>{@link #spec(int)}</li>
22274a145d72622772b920f60195e80942058984259cPhilip Milne     *   <li>{@link #spec(int, int)}</li>
22284a145d72622772b920f60195e80942058984259cPhilip Milne     *   <li>{@link #spec(int, Alignment)}</li>
22294a145d72622772b920f60195e80942058984259cPhilip Milne     *   <li>{@link #spec(int, int, Alignment)}</li>
22304a145d72622772b920f60195e80942058984259cPhilip Milne     * </ul>
22314a145d72622772b920f60195e80942058984259cPhilip Milne     *
2232899d5922870c78e0e663bc5661849eb468afc984Philip Milne     */
223393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    public static class Spec {
2234f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
2235f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne
2236f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final boolean startDefined;
223748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        final Interval span;
223893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        final Alignment alignment;
22393f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2240f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        private Spec(boolean startDefined, Interval span, Alignment alignment) {
2241f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this.startDefined = startDefined;
22425d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            this.span = span;
22435d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne            this.alignment = alignment;
22445d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne        }
22455d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
2246f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        private Spec(boolean startDefined, int start, int size, Alignment alignment) {
2247f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            this(startDefined, new Interval(start, start + size), alignment);
22483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2250f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final Spec copyWriteSpan(Interval span) {
2251f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            return new Spec(startDefined, span, alignment);
22523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2254f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final Spec copyWriteAlignment(Alignment alignment) {
2255f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne            return new Spec(startDefined, span, alignment);
22565125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
22575125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
2258f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        final int getFlexibility() {
22594c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            return (alignment == UNDEFINED_ALIGNMENT) ? INFLEXIBLE : CAN_STRETCH;
22603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22623f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
226393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
226493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * properties of this Spec and the supplied parameter are pairwise equal,
22657fd948756947506db62a2bafca2ed45ff53ac0a0Philip Milne         * {@code false} otherwise.
22663f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
226793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         * @param that the object to compare this spec with
22683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
22693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * @return {@code true} if the specified object is equal to this
227093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne         *         {@code Spec}; {@code false} otherwise
22713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
22723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
22733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public boolean equals(Object that) {
22743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (this == that) {
22753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return true;
22763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
22773f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (that == null || getClass() != that.getClass()) {
22783f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
22793f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
22803f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
228193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            Spec spec = (Spec) that;
22823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
228393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            if (!alignment.equals(spec.alignment)) {
22843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
22853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
22864c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne            //noinspection RedundantIfStatement
228793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne            if (!span.equals(spec.span)) {
22883f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return false;
22893f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
22903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22913f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return true;
22923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
22933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
22943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        @Override
22953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        public int hashCode() {
22963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int result = span.hashCode();
22973f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            result = 31 * result + alignment.hashCode();
22983f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return result;
22993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
23003f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
23013f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
23023f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
230393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * Return a Spec, {@code spec}, where:
230493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * <ul>
230593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.span = [start, start + size]} </li>
230693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.alignment = alignment} </li>
230793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * </ul>
230893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *
230993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param start     the start
231093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param size      the size
231193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param alignment the alignment
231293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     */
231393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    public static Spec spec(int start, int size, Alignment alignment) {
2314f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne        return new Spec(start != UNDEFINED, start, size, alignment);
231593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    }
231693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne
231793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    /**
231893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * Return a Spec, {@code spec}, where:
231993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * <ul>
232093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.span = [start, start + 1]} </li>
232193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *     <li> {@code spec.alignment = alignment} </li>
232293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * </ul>
232393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     *
232493cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param start     the start index
232593cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * @param alignment the alignment
232693cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     */
232793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    public static Spec spec(int start, Alignment alignment) {
232893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne        return spec(start, 1, alignment);
232993cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    }
233093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne
233193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne    /**
23325125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * Return a Spec, {@code spec}, where:
23335125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * <ul>
23345125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *     <li> {@code spec.span = [start, start + size]} </li>
23355125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * </ul>
23365125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *
23375125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * @param start     the start
23385125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * @param size      the size
23395125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     */
23405125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    public static Spec spec(int start, int size) {
23415125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return spec(start, size, UNDEFINED_ALIGNMENT);
23425125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
23435125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
23445125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    /**
23455125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * Return a Spec, {@code spec}, where:
23465125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * <ul>
23475125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *     <li> {@code spec.span = [start, start + 1]} </li>
23485125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * </ul>
23495125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     *
23505125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     * @param start     the start index
23515125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne     */
23525125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    public static Spec spec(int start) {
23535125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        return spec(start, 1);
23545125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    }
23555125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
23565125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    /**
23573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Alignments specify where a view should be placed within a cell group and
23583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * what size it should be.
23593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
236093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
236193cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
236293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * {@code alignment}. Overall placement of the view in the cell
23633f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * group is specified by the two alignments which act along each axis independently.
23643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
2365a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     *  The GridLayout class defines the most common alignments used in general layout:
2366a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #CENTER}, {@link
2367a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     * #BASELINE} and {@link #FILL}.
2368a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne     */
2369a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne    /*
2370c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne     * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
23713f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * to return the appropriate value for the type of alignment being defined.
23723f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * The enclosing algorithms position the children
23731e54825135a7ccde421aa7fc400ab69e9348b5d6Philip Milne     * so that the locations defined by the alignment values
23743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * are the same for all of the views in a group.
23753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * <p>
23763f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2377c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    public static abstract class Alignment {
237848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        Alignment() {
2379a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
2380a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
23813f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
23823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Returns an alignment value. In the case of vertical alignments the value
23833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * returned should indicate the distance from the top of the view to the
23843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * alignment location.
23853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * For horizontal alignments measurement is made from the left edge of the component.
23863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
2387c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param view              the view to which this alignment should be applied
2388c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param viewSize          the measured size of the view
2389b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne         * @return the alignment value
23903f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
239148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        abstract int getAlignmentValue(View view, int viewSize);
23923f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
23933f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        /**
23943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * Returns the size of the view specified by this alignment.
23953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * In the case of vertical alignments this method should return a height; for
23963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         * horizontal alignments this method should return the width.
2397c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * <p>
2398c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * The default implementation returns {@code viewSize}.
2399c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         *
2400c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param view              the view to which this alignment should be applied
2401c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param viewSize          the measured size of the view
2402c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne         * @param cellSize          the size of the cell into which this view will be placed
240309e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne         * @param measurementType   This parameter is currently unused as GridLayout only supports
240409e2d4d0d5101e2fc44909b83965465a7b90afabPhilip Milne         *                          one type of measurement: {@link View#measure(int, int)}.
24053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         *
2406b3a8c54389c9be2b37c5524ad8eb3112054221a7Philip Milne         * @return the aligned size
24073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne         */
240848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
24093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return viewSize;
24103f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
2411a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
241248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        Bounds getBounds() {
2413a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            return new Bounds();
2414a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
24153f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    }
24163f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2417f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
241847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        @Override
24195125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        public int getAlignmentValue(View view, int viewSize) {
24205125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne            return UNDEFINED;
24215125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne        }
24225125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    };
24235125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne
242447d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    /**
242547d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * Indicates that a view should be aligned with the <em>start</em>
242647d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * edges of the other views in its cell group.
242747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     */
2428c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    private static final Alignment LEADING = new Alignment() {
242947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        @Override
243048b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
24313f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return 0;
24323f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
24333f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
24343f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
243547d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    /**
243647d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * Indicates that a view should be aligned with the <em>end</em>
243747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * edges of the other views in its cell group.
243847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     */
2439c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    private static final Alignment TRAILING = new Alignment() {
244047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        @Override
244148b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
24423f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return viewSize;
24433f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
24443f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
24453f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
24463f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
24473f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>top</em>
24483f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
24493f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
24503f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment TOP = LEADING;
24513f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
24523f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
24533f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>bottom</em>
24543f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
24553f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
24563f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment BOTTOM = TRAILING;
24573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
24583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
245947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * Indicates that a view should be aligned with the <em>start</em>
24603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
24613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
246247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    public static final Alignment START = LEADING;
246347d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio
246447d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    /**
246547d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * Indicates that a view should be aligned with the <em>end</em>
246647d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * edges of the other views in its cell group.
246747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     */
246847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    public static final Alignment END = TRAILING;
246947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio
247047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    private static Alignment getAbsoluteAlignment(final Alignment a1, final Alignment a2) {
247147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        return new Alignment() {
247247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            @Override
247347d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            public int getAlignmentValue(View view, int viewSize) {
247447d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                if (view == null) {
247547d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                    return UNDEFINED;
247647d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                }
247747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                Alignment alignment = view.isLayoutRtl() ? a2 : a1;
247847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio                return alignment.getAlignmentValue(view, viewSize);
247947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio            }
248047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        };
248147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    }
24823f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
24833f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
24843f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>left</em>
24853f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * edges of the other views in its cell group.
24863f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
248747d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    public static final Alignment LEFT = getAbsoluteAlignment(START, END);
248847d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio
248947d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    /**
249047d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * Indicates that a view should be aligned with the <em>right</em>
249147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     * edges of the other views in its cell group.
249247d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio     */
249347d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio    public static final Alignment RIGHT = getAbsoluteAlignment(END, START);
24943f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
24953f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
24963f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be <em>centered</em> with the other views in its cell group.
249793cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
249893cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * LayoutParams#columnSpec columnSpecs}.
24993f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2500c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    public static final Alignment CENTER = new Alignment() {
250147d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        @Override
250248b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
25033f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return viewSize >> 1;
25043f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
25053f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
25063f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
25073f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
25083f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should be aligned with the <em>baselines</em>
25093f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * of the other views in its cell group.
251093cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
25113f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     *
25123f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * @see View#getBaseline()
25133f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
2514c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne    public static final Alignment BASELINE = new Alignment() {
251547d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        @Override
251648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
25173f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            if (view == null) {
25183f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne                return UNDEFINED;
25193f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            }
25203f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            int baseline = view.getBaseline();
2521a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            return (baseline == -1) ? UNDEFINED : baseline;
2522a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        }
2523a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2524a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        @Override
2525a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne        public Bounds getBounds() {
2526a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            return new Bounds() {
2527a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                /*
2528a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                In a baseline aligned row in which some components define a baseline
2529a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                and some don't, we need a third variable to properly account for all
2530a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                the sizes. This tracks the maximum size of all the components -
2531a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                including those that don't define a baseline.
2532a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                */
2533a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                private int size;
2534a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2535a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
2536a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                protected void reset() {
2537a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                    super.reset();
253848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    size = Integer.MIN_VALUE;
2539a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2540a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2541a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
2542a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                protected void include(int before, int after) {
2543a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                    super.include(before, after);
2544a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                    size = max(size, before + after);
2545a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2546a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2547a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
254848b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                protected int size(boolean min) {
254948b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return max(super.size(min), size);
2550a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2551a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne
2552a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                @Override
255348b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                protected int getOffset(View c, Alignment alignment, int size) {
255448b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne                    return max(0, super.getOffset(c, alignment, size));
2555a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne                }
2556a1f7b10f7299b40ee3a4e5e309882ea1a931cd5ePhilip Milne            };
25573f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
25583f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
25593f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
25603f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    /**
25613f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     * Indicates that a view should expanded to fit the boundaries of its cell group.
256293cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
256393cd6a6c78683643de51f9e698b38847bd1f1155Philip Milne     * {@link LayoutParams#columnSpec columnSpecs}.
25643f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne     */
25653f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    public static final Alignment FILL = new Alignment() {
256647d248eb438674ab0ca10154f3ff5e88f4ed70b7Fabrice Di Meglio        @Override
256748b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne        public int getAlignmentValue(View view, int viewSize) {
25683f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return UNDEFINED;
25693f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
25703f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne
2571c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne        @Override
2572c9885f6557dc1c96e2cc2c1a86fba359f00f131cPhilip Milne        public int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) {
25733f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne            return cellSize;
25743f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne        }
25753f8956d82bb40b15acee26017db0d13ddf43c80aPhilip Milne    };
257648b55244d286b6d4e3699a5d9e938a9c87aaae75Philip Milne
2577f6679c88d12e9f7e10e6884d4a8487673e53c097Philip Milne    static boolean canStretch(int flexibility) {
25785d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne        return (flexibility & CAN_STRETCH) != 0;
25795d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne    }
25805d1a9840aaf57ae90716f0ac34abdcd09f7f4ed6Philip Milne
25815125e21bc0bbe5b9718d0f03b26cdafc67a7c726Philip Milne    private static final int INFLEXIBLE = 0;
25824c8cf4c93314722a77ce69396b9cb201ac007a58Philip Milne    private static final int CAN_STRETCH = 2;
2583452eec33d667f9e705b57e60948b070536fbc1b4Jim Miller}
2584