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