blob: cbd1e0ad0998255ba4c541526870bcb880005fec [file] [log] [blame]
Philip Milne3f8956d2011-05-13 17:29:00 +01001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Aurimas Liutikas99441c52016-10-11 16:48:32 -070019import static android.view.Gravity.AXIS_PULL_AFTER;
20import static android.view.Gravity.AXIS_PULL_BEFORE;
21import static android.view.Gravity.AXIS_SPECIFIED;
22import static android.view.Gravity.AXIS_X_SHIFT;
23import static android.view.Gravity.AXIS_Y_SHIFT;
24import static android.view.Gravity.HORIZONTAL_GRAVITY_MASK;
25import static android.view.Gravity.RELATIVE_LAYOUT_DIRECTION;
26import static android.view.Gravity.VERTICAL_GRAVITY_MASK;
27import static android.view.View.MeasureSpec.EXACTLY;
28import static android.view.View.MeasureSpec.makeMeasureSpec;
29
30import static java.lang.Math.max;
31import static java.lang.Math.min;
32
Tor Norbyed9273d62013-05-30 15:59:53 -070033import android.annotation.IntDef;
Philip Milne3f8956d2011-05-13 17:29:00 +010034import android.content.Context;
35import android.content.res.TypedArray;
36import android.graphics.Canvas;
37import android.graphics.Color;
Philip Milne1557fd72012-04-04 23:41:34 -070038import android.graphics.Insets;
Philip Milne3f8956d2011-05-13 17:29:00 +010039import android.graphics.Paint;
Philip Milne3f8956d2011-05-13 17:29:00 +010040import android.util.AttributeSet;
41import android.util.Log;
Philip Milne211d0332012-04-24 14:45:14 -070042import android.util.LogPrinter;
Philip Milne48b55242011-06-29 11:09:45 -070043import android.util.Pair;
Philip Milne211d0332012-04-24 14:45:14 -070044import android.util.Printer;
Philip Milne3f8956d2011-05-13 17:29:00 +010045import android.view.Gravity;
46import android.view.View;
47import android.view.ViewGroup;
Philip Milneedd69512012-03-14 17:21:33 -070048import android.widget.RemoteViews.RemoteView;
Aurimas Liutikas99441c52016-10-11 16:48:32 -070049
Philip Milne10ca24a2012-04-23 15:38:27 -070050import com.android.internal.R;
Philip Milne3f8956d2011-05-13 17:29:00 +010051
Tor Norbyed9273d62013-05-30 15:59:53 -070052import java.lang.annotation.Retention;
53import java.lang.annotation.RetentionPolicy;
Philip Milne3f8956d2011-05-13 17:29:00 +010054import java.lang.reflect.Array;
55import java.util.ArrayList;
56import java.util.Arrays;
Philip Milne3f8956d2011-05-13 17:29:00 +010057import java.util.HashMap;
58import java.util.List;
59import java.util.Map;
60
Philip Milne3f8956d2011-05-13 17:29:00 +010061/**
62 * A layout that places its children in a rectangular <em>grid</em>.
63 * <p>
64 * The grid is composed of a set of infinitely thin lines that separate the
65 * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced
Philip Milne7fd94872011-06-07 20:14:17 -070066 * by grid <em>indices</em>. A grid with {@code N} columns
67 * has {@code N + 1} grid indices that run from {@code 0}
68 * through {@code N} inclusive. Regardless of how GridLayout is
69 * configured, grid index {@code 0} is fixed to the leading edge of the
70 * container and grid index {@code N} is fixed to its trailing edge
Philip Milne3f8956d2011-05-13 17:29:00 +010071 * (after padding is taken into account).
72 *
Philip Milne93cd6a62011-07-12 14:49:45 -070073 * <h4>Row and Column Specs</h4>
Philip Milne3f8956d2011-05-13 17:29:00 +010074 *
75 * Children occupy one or more contiguous cells, as defined
Philip Milne93cd6a62011-07-12 14:49:45 -070076 * by their {@link GridLayout.LayoutParams#rowSpec rowSpec} and
77 * {@link GridLayout.LayoutParams#columnSpec columnSpec} layout parameters.
78 * Each spec defines the set of rows or columns that are to be
Philip Milne3f8956d2011-05-13 17:29:00 +010079 * occupied; and how children should be aligned within the resulting group of cells.
80 * Although cells do not normally overlap in a GridLayout, GridLayout does
81 * not prevent children being defined to occupy the same cell or group of cells.
82 * In this case however, there is no guarantee that children will not themselves
83 * overlap after the layout operation completes.
84 *
85 * <h4>Default Cell Assignment</h4>
86 *
Philip Milne48b55242011-06-29 11:09:45 -070087 * If a child does not specify the row and column indices of the cell it
Philip Milne3f8956d2011-05-13 17:29:00 +010088 * wishes to occupy, GridLayout assigns cell locations automatically using its:
89 * {@link GridLayout#setOrientation(int) orientation},
90 * {@link GridLayout#setRowCount(int) rowCount} and
91 * {@link GridLayout#setColumnCount(int) columnCount} properties.
92 *
93 * <h4>Space</h4>
94 *
95 * Space between children may be specified either by using instances of the
96 * dedicated {@link Space} view or by setting the
97 *
98 * {@link ViewGroup.MarginLayoutParams#leftMargin leftMargin},
99 * {@link ViewGroup.MarginLayoutParams#topMargin topMargin},
100 * {@link ViewGroup.MarginLayoutParams#rightMargin rightMargin} and
101 * {@link ViewGroup.MarginLayoutParams#bottomMargin bottomMargin}
102 *
103 * layout parameters. When the
104 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins}
105 * property is set, default margins around children are automatically
Philip Milnef6679c82011-09-18 11:36:57 -0700106 * allocated based on the prevailing UI style guide for the platform.
107 * Each of the margins so defined may be independently overridden by an assignment
Philip Milne3f8956d2011-05-13 17:29:00 +0100108 * to the appropriate layout parameter.
Philip Milnef6679c82011-09-18 11:36:57 -0700109 * Default values will generally produce a reasonable spacing between components
110 * but values may change between different releases of the platform.
Philip Milne3f8956d2011-05-13 17:29:00 +0100111 *
112 * <h4>Excess Space Distribution</h4>
113 *
Philip Milne87260842014-05-22 13:56:03 -0700114 * As of API 21, GridLayout's distribution of excess space accomodates the principle of weight.
115 * In the event that no weights are specified, the previous conventions are respected and
116 * columns and rows are taken as flexible if their views specify some form of alignment
117 * within their groups.
Philip Milnef6679c82011-09-18 11:36:57 -0700118 * <p>
Philip Milne87260842014-05-22 13:56:03 -0700119 * The flexibility of a view is therefore influenced by its alignment which is,
120 * in turn, typically defined by setting the
121 * {@link LayoutParams#setGravity(int) gravity} property of the child's layout parameters.
122 * If either a weight or alignment were defined along a given axis then the component
123 * is taken as <em>flexible</em> in that direction. If no weight or alignment was set,
Philip Milnef6679c82011-09-18 11:36:57 -0700124 * the component is instead assumed to be <em>inflexible</em>.
125 * <p>
126 * Multiple components in the same row or column group are
127 * considered to act in <em>parallel</em>. Such a
Philip Milne899d5922011-07-21 11:39:37 -0700128 * group is flexible only if <em>all</em> of the components
129 * within it are flexible. Row and column groups that sit either side of a common boundary
130 * are instead considered to act in <em>series</em>. The composite group made of these two
131 * elements is flexible if <em>one</em> of its elements is flexible.
132 * <p>
133 * To make a column stretch, make sure all of the components inside it define a
Philip Milne87260842014-05-22 13:56:03 -0700134 * weight or a gravity. To prevent a column from stretching, ensure that one of the components
135 * in the column does not define a weight or a gravity.
Philip Milne3f8956d2011-05-13 17:29:00 +0100136 * <p>
Philip Milnef6679c82011-09-18 11:36:57 -0700137 * When the principle of flexibility does not provide complete disambiguation,
138 * GridLayout's algorithms favour rows and columns that are closer to its <em>right</em>
Philip Milne87260842014-05-22 13:56:03 -0700139 * and <em>bottom</em> edges. To be more precise, GridLayout treats each of its layout
140 * parameters as a constraint in the a set of variables that define the grid-lines along a
141 * given axis. During layout, GridLayout solves the constraints so as to return the unique
142 * solution to those constraints for which all variables are less-than-or-equal-to
143 * the corresponding value in any other valid solution.
Philip Milnef6679c82011-09-18 11:36:57 -0700144 *
Philip Milnea8416442013-02-25 10:49:39 -0800145 * <h4>Interpretation of GONE</h4>
146 *
147 * For layout purposes, GridLayout treats views whose visibility status is
148 * {@link View#GONE GONE}, as having zero width and height. This is subtly different from
149 * the policy of ignoring views that are marked as GONE outright. If, for example, a gone-marked
150 * view was alone in a column, that column would itself collapse to zero width if and only if
151 * no gravity was defined on the view. If gravity was defined, then the gone-marked
152 * view has no effect on the layout and the container should be laid out as if the view
Yigit Boyar6dafd872015-03-05 13:59:56 -0800153 * had never been added to it. GONE views are taken to have zero weight during excess space
154 * distribution.
155 * <p>
Philip Milnea8416442013-02-25 10:49:39 -0800156 * These statements apply equally to rows as well as columns, and to groups of rows or columns.
157 *
Philip Milne3f8956d2011-05-13 17:29:00 +0100158 * <p>
159 * See {@link GridLayout.LayoutParams} for a full description of the
160 * layout parameters used by GridLayout.
161 *
162 * @attr ref android.R.styleable#GridLayout_orientation
163 * @attr ref android.R.styleable#GridLayout_rowCount
164 * @attr ref android.R.styleable#GridLayout_columnCount
165 * @attr ref android.R.styleable#GridLayout_useDefaultMargins
166 * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
167 * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
168 */
Philip Milneedd69512012-03-14 17:21:33 -0700169@RemoteView
Philip Milne3f8956d2011-05-13 17:29:00 +0100170public class GridLayout extends ViewGroup {
171
172 // Public constants
173
Tor Norbyed9273d62013-05-30 15:59:53 -0700174 /** @hide */
175 @IntDef({HORIZONTAL, VERTICAL})
176 @Retention(RetentionPolicy.SOURCE)
177 public @interface Orientation {}
178
Philip Milne3f8956d2011-05-13 17:29:00 +0100179 /**
180 * The horizontal orientation.
181 */
182 public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
Philip Milneaa616f32011-05-27 18:38:01 -0700183
Philip Milne3f8956d2011-05-13 17:29:00 +0100184 /**
185 * The vertical orientation.
186 */
187 public static final int VERTICAL = LinearLayout.VERTICAL;
188
Philip Milneaa616f32011-05-27 18:38:01 -0700189 /**
190 * The constant used to indicate that a value is undefined.
191 * Fields can use this value to indicate that their values
192 * have not yet been set. Similarly, methods can return this value
193 * to indicate that there is no suitable value that the implementation
194 * can return.
195 * The value used for the constant (currently {@link Integer#MIN_VALUE}) is
196 * intended to avoid confusion between valid values whose sign may not be known.
197 */
198 public static final int UNDEFINED = Integer.MIN_VALUE;
199
Tor Norbyed9273d62013-05-30 15:59:53 -0700200 /** @hide */
201 @IntDef({ALIGN_BOUNDS, ALIGN_MARGINS})
202 @Retention(RetentionPolicy.SOURCE)
203 public @interface AlignmentMode {}
204
Philip Milne1e548252011-06-16 19:02:33 -0700205 /**
206 * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
207 * When the {@code alignmentMode} is set to {@link #ALIGN_BOUNDS}, alignment
208 * is made between the edges of each component's raw
209 * view boundary: i.e. the area delimited by the component's:
210 * {@link android.view.View#getTop() top},
211 * {@link android.view.View#getLeft() left},
212 * {@link android.view.View#getBottom() bottom} and
213 * {@link android.view.View#getRight() right} properties.
214 * <p>
215 * For example, when {@code GridLayout} is in {@link #ALIGN_BOUNDS} mode,
216 * children that belong to a row group that uses {@link #TOP} alignment will
217 * all return the same value when their {@link android.view.View#getTop()}
218 * method is called.
219 *
220 * @see #setAlignmentMode(int)
221 */
222 public static final int ALIGN_BOUNDS = 0;
223
224 /**
225 * This constant is an {@link #setAlignmentMode(int) alignmentMode}.
226 * When the {@code alignmentMode} is set to {@link #ALIGN_MARGINS},
227 * the bounds of each view are extended outwards, according
228 * to their margins, before the edges of the resulting rectangle are aligned.
229 * <p>
230 * For example, when {@code GridLayout} is in {@link #ALIGN_MARGINS} mode,
231 * the quantity {@code top - layoutParams.topMargin} is the same for all children that
232 * belong to a row group that uses {@link #TOP} alignment.
233 *
234 * @see #setAlignmentMode(int)
235 */
236 public static final int ALIGN_MARGINS = 1;
237
Philip Milne3f8956d2011-05-13 17:29:00 +0100238 // Misc constants
239
Philip Milnef6679c82011-09-18 11:36:57 -0700240 static final int MAX_SIZE = 100000;
241 static final int DEFAULT_CONTAINER_MARGIN = 0;
Philip Milned7dd8902012-01-26 16:55:30 -0800242 static final int UNINITIALIZED_HASH = 0;
Philip Milnea2353622013-03-05 12:19:15 -0800243 static final Printer LOG_PRINTER = new LogPrinter(Log.DEBUG, GridLayout.class.getName());
244 static final Printer NO_PRINTER = new Printer() {
245 @Override
246 public void println(String x) {
247 }
248 };
Philip Milne3f8956d2011-05-13 17:29:00 +0100249
250 // Defaults
251
252 private static final int DEFAULT_ORIENTATION = HORIZONTAL;
253 private static final int DEFAULT_COUNT = UNDEFINED;
254 private static final boolean DEFAULT_USE_DEFAULT_MARGINS = false;
Philip Milne899d5922011-07-21 11:39:37 -0700255 private static final boolean DEFAULT_ORDER_PRESERVED = true;
Philip Milne1e548252011-06-16 19:02:33 -0700256 private static final int DEFAULT_ALIGNMENT_MODE = ALIGN_MARGINS;
Philip Milne3f8956d2011-05-13 17:29:00 +0100257
258 // TypedArray indices
259
Philip Milneb0ce49b2011-07-15 15:39:07 -0700260 private static final int ORIENTATION = R.styleable.GridLayout_orientation;
261 private static final int ROW_COUNT = R.styleable.GridLayout_rowCount;
262 private static final int COLUMN_COUNT = R.styleable.GridLayout_columnCount;
263 private static final int USE_DEFAULT_MARGINS = R.styleable.GridLayout_useDefaultMargins;
264 private static final int ALIGNMENT_MODE = R.styleable.GridLayout_alignmentMode;
265 private static final int ROW_ORDER_PRESERVED = R.styleable.GridLayout_rowOrderPreserved;
266 private static final int COLUMN_ORDER_PRESERVED = R.styleable.GridLayout_columnOrderPreserved;
Philip Milne3f8956d2011-05-13 17:29:00 +0100267
268 // Instance variables
269
Adam Powell465ea742013-08-29 14:56:51 -0700270 final Axis mHorizontalAxis = new Axis(true);
271 final Axis mVerticalAxis = new Axis(false);
272 int mOrientation = DEFAULT_ORIENTATION;
273 boolean mUseDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
274 int mAlignmentMode = DEFAULT_ALIGNMENT_MODE;
275 int mDefaultGap;
276 int mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
277 Printer mPrinter = LOG_PRINTER;
Philip Milneaa616f32011-05-27 18:38:01 -0700278
Philip Milne3f8956d2011-05-13 17:29:00 +0100279 // Constructors
280
Alan Viveretted6479ec2013-09-10 17:03:02 -0700281 public GridLayout(Context context) {
282 this(context, null);
283 }
284
285 public GridLayout(Context context, AttributeSet attrs) {
286 this(context, attrs, 0);
287 }
288
Alan Viverette617feb92013-09-09 18:09:13 -0700289 public GridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
290 this(context, attrs, defStyleAttr, 0);
291 }
292
293 public GridLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
294 super(context, attrs, defStyleAttr, defStyleRes);
Adam Powell465ea742013-08-29 14:56:51 -0700295 mDefaultGap = context.getResources().getDimensionPixelOffset(R.dimen.default_gap);
Alan Viverette617feb92013-09-09 18:09:13 -0700296 final TypedArray a = context.obtainStyledAttributes(
297 attrs, R.styleable.GridLayout, defStyleAttr, defStyleRes);
Philip Milne3f8956d2011-05-13 17:29:00 +0100298 try {
Philip Milne1e548252011-06-16 19:02:33 -0700299 setRowCount(a.getInt(ROW_COUNT, DEFAULT_COUNT));
300 setColumnCount(a.getInt(COLUMN_COUNT, DEFAULT_COUNT));
Philip Milne5125e212011-07-21 11:39:37 -0700301 setOrientation(a.getInt(ORIENTATION, DEFAULT_ORIENTATION));
302 setUseDefaultMargins(a.getBoolean(USE_DEFAULT_MARGINS, DEFAULT_USE_DEFAULT_MARGINS));
303 setAlignmentMode(a.getInt(ALIGNMENT_MODE, DEFAULT_ALIGNMENT_MODE));
Philip Milne3f8956d2011-05-13 17:29:00 +0100304 setRowOrderPreserved(a.getBoolean(ROW_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
305 setColumnOrderPreserved(a.getBoolean(COLUMN_ORDER_PRESERVED, DEFAULT_ORDER_PRESERVED));
306 } finally {
307 a.recycle();
308 }
309 }
310
311 // Implementation
312
313 /**
314 * Returns the current orientation.
315 *
Philip Milne7fd94872011-06-07 20:14:17 -0700316 * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
Philip Milne3f8956d2011-05-13 17:29:00 +0100317 *
318 * @see #setOrientation(int)
319 *
320 * @attr ref android.R.styleable#GridLayout_orientation
321 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700322 @Orientation
Philip Milne3f8956d2011-05-13 17:29:00 +0100323 public int getOrientation() {
Adam Powell465ea742013-08-29 14:56:51 -0700324 return mOrientation;
Philip Milne3f8956d2011-05-13 17:29:00 +0100325 }
326
327 /**
Philip Milneb65408f2012-05-21 10:44:46 -0700328 *
329 * GridLayout uses the orientation property for two purposes:
330 * <ul>
331 * <li>
332 * To control the 'direction' in which default row/column indices are generated
333 * when they are not specified in a component's layout parameters.
334 * </li>
335 * <li>
336 * To control which axis should be processed first during the layout operation:
337 * when orientation is {@link #HORIZONTAL} the horizontal axis is laid out first.
338 * </li>
339 * </ul>
340 *
341 * The order in which axes are laid out is important if, for example, the height of
342 * one of GridLayout's children is dependent on its width - and its width is, in turn,
343 * dependent on the widths of other components.
344 * <p>
345 * If your layout contains a {@link TextView} (or derivative:
346 * {@code Button}, {@code EditText}, {@code CheckBox}, etc.) which is
347 * in multi-line mode (the default) it is normally best to leave GridLayout's
348 * orientation as {@code HORIZONTAL} - because {@code TextView} is capable of
349 * deriving its height for a given width, but not the other way around.
350 * <p>
351 * Other than the effects above, orientation does not affect the actual layout operation of
352 * GridLayout, so it's fine to leave GridLayout in {@code HORIZONTAL} mode even if
353 * the height of the intended layout greatly exceeds its width.
Philip Milne7fd94872011-06-07 20:14:17 -0700354 * <p>
355 * The default value of this property is {@link #HORIZONTAL}.
Philip Milne3f8956d2011-05-13 17:29:00 +0100356 *
Philip Milne7fd94872011-06-07 20:14:17 -0700357 * @param orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
Philip Milne3f8956d2011-05-13 17:29:00 +0100358 *
359 * @see #getOrientation()
360 *
361 * @attr ref android.R.styleable#GridLayout_orientation
362 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700363 public void setOrientation(@Orientation int orientation) {
Adam Powell465ea742013-08-29 14:56:51 -0700364 if (this.mOrientation != orientation) {
365 this.mOrientation = orientation;
Philip Milnef6679c82011-09-18 11:36:57 -0700366 invalidateStructure();
Philip Milne3f8956d2011-05-13 17:29:00 +0100367 requestLayout();
368 }
369 }
370
371 /**
372 * Returns the current number of rows. This is either the last value that was set
373 * with {@link #setRowCount(int)} or, if no such value was set, the maximum
Philip Milne93cd6a62011-07-12 14:49:45 -0700374 * value of each the upper bounds defined in {@link LayoutParams#rowSpec}.
Philip Milne3f8956d2011-05-13 17:29:00 +0100375 *
376 * @return the current number of rows
377 *
378 * @see #setRowCount(int)
Philip Milne93cd6a62011-07-12 14:49:45 -0700379 * @see LayoutParams#rowSpec
Philip Milne3f8956d2011-05-13 17:29:00 +0100380 *
381 * @attr ref android.R.styleable#GridLayout_rowCount
382 */
383 public int getRowCount() {
Adam Powell465ea742013-08-29 14:56:51 -0700384 return mVerticalAxis.getCount();
Philip Milne3f8956d2011-05-13 17:29:00 +0100385 }
386
387 /**
Philip Milnef6679c82011-09-18 11:36:57 -0700388 * RowCount is used only to generate default row/column indices when
389 * they are not specified by a component's layout parameters.
Philip Milne3f8956d2011-05-13 17:29:00 +0100390 *
Philip Milne7fd94872011-06-07 20:14:17 -0700391 * @param rowCount the number of rows
Philip Milne3f8956d2011-05-13 17:29:00 +0100392 *
393 * @see #getRowCount()
Philip Milne93cd6a62011-07-12 14:49:45 -0700394 * @see LayoutParams#rowSpec
Philip Milne3f8956d2011-05-13 17:29:00 +0100395 *
396 * @attr ref android.R.styleable#GridLayout_rowCount
397 */
398 public void setRowCount(int rowCount) {
Adam Powell465ea742013-08-29 14:56:51 -0700399 mVerticalAxis.setCount(rowCount);
Philip Milnef6679c82011-09-18 11:36:57 -0700400 invalidateStructure();
401 requestLayout();
Philip Milne3f8956d2011-05-13 17:29:00 +0100402 }
403
404 /**
405 * Returns the current number of columns. This is either the last value that was set
406 * with {@link #setColumnCount(int)} or, if no such value was set, the maximum
Philip Milne93cd6a62011-07-12 14:49:45 -0700407 * value of each the upper bounds defined in {@link LayoutParams#columnSpec}.
Philip Milne3f8956d2011-05-13 17:29:00 +0100408 *
409 * @return the current number of columns
410 *
411 * @see #setColumnCount(int)
Philip Milne93cd6a62011-07-12 14:49:45 -0700412 * @see LayoutParams#columnSpec
Philip Milne3f8956d2011-05-13 17:29:00 +0100413 *
414 * @attr ref android.R.styleable#GridLayout_columnCount
415 */
416 public int getColumnCount() {
Adam Powell465ea742013-08-29 14:56:51 -0700417 return mHorizontalAxis.getCount();
Philip Milne3f8956d2011-05-13 17:29:00 +0100418 }
419
420 /**
Philip Milnef6679c82011-09-18 11:36:57 -0700421 * ColumnCount is used only to generate default column/column indices when
422 * they are not specified by a component's layout parameters.
Philip Milne3f8956d2011-05-13 17:29:00 +0100423 *
424 * @param columnCount the number of columns.
425 *
426 * @see #getColumnCount()
Philip Milne93cd6a62011-07-12 14:49:45 -0700427 * @see LayoutParams#columnSpec
Philip Milne3f8956d2011-05-13 17:29:00 +0100428 *
429 * @attr ref android.R.styleable#GridLayout_columnCount
430 */
431 public void setColumnCount(int columnCount) {
Adam Powell465ea742013-08-29 14:56:51 -0700432 mHorizontalAxis.setCount(columnCount);
Philip Milnef6679c82011-09-18 11:36:57 -0700433 invalidateStructure();
434 requestLayout();
Philip Milne3f8956d2011-05-13 17:29:00 +0100435 }
436
437 /**
438 * Returns whether or not this GridLayout will allocate default margins when no
439 * corresponding layout parameters are defined.
440 *
Philip Milne7fd94872011-06-07 20:14:17 -0700441 * @return {@code true} if default margins should be allocated
Philip Milne3f8956d2011-05-13 17:29:00 +0100442 *
443 * @see #setUseDefaultMargins(boolean)
444 *
445 * @attr ref android.R.styleable#GridLayout_useDefaultMargins
446 */
447 public boolean getUseDefaultMargins() {
Adam Powell465ea742013-08-29 14:56:51 -0700448 return mUseDefaultMargins;
Philip Milne3f8956d2011-05-13 17:29:00 +0100449 }
450
451 /**
Philip Milne7fd94872011-06-07 20:14:17 -0700452 * When {@code true}, GridLayout allocates default margins around children
Philip Milne3f8956d2011-05-13 17:29:00 +0100453 * based on the child's visual characteristics. Each of the
454 * margins so defined may be independently overridden by an assignment
455 * to the appropriate layout parameter.
456 * <p>
Philip Milne7fd94872011-06-07 20:14:17 -0700457 * When {@code false}, the default value of all margins is zero.
Philip Milneaa616f32011-05-27 18:38:01 -0700458 * <p>
Philip Milne7fd94872011-06-07 20:14:17 -0700459 * When setting to {@code true}, consider setting the value of the
Philip Milne1e548252011-06-16 19:02:33 -0700460 * {@link #setAlignmentMode(int) alignmentMode}
461 * property to {@link #ALIGN_BOUNDS}.
Philip Milne7fd94872011-06-07 20:14:17 -0700462 * <p>
463 * The default value of this property is {@code false}.
Philip Milne3f8956d2011-05-13 17:29:00 +0100464 *
Philip Milne7fd94872011-06-07 20:14:17 -0700465 * @param useDefaultMargins use {@code true} to make GridLayout allocate default margins
Philip Milne3f8956d2011-05-13 17:29:00 +0100466 *
467 * @see #getUseDefaultMargins()
Philip Milne1e548252011-06-16 19:02:33 -0700468 * @see #setAlignmentMode(int)
Philip Milne3f8956d2011-05-13 17:29:00 +0100469 *
470 * @see MarginLayoutParams#leftMargin
471 * @see MarginLayoutParams#topMargin
472 * @see MarginLayoutParams#rightMargin
473 * @see MarginLayoutParams#bottomMargin
474 *
475 * @attr ref android.R.styleable#GridLayout_useDefaultMargins
476 */
477 public void setUseDefaultMargins(boolean useDefaultMargins) {
Adam Powell465ea742013-08-29 14:56:51 -0700478 this.mUseDefaultMargins = useDefaultMargins;
Philip Milneaa616f32011-05-27 18:38:01 -0700479 requestLayout();
480 }
481
482 /**
Philip Milne1e548252011-06-16 19:02:33 -0700483 * Returns the alignment mode.
Philip Milneaa616f32011-05-27 18:38:01 -0700484 *
Philip Milne1e548252011-06-16 19:02:33 -0700485 * @return the alignment mode; either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
Philip Milneaa616f32011-05-27 18:38:01 -0700486 *
Philip Milne1e548252011-06-16 19:02:33 -0700487 * @see #ALIGN_BOUNDS
488 * @see #ALIGN_MARGINS
Philip Milneaa616f32011-05-27 18:38:01 -0700489 *
Philip Milne1e548252011-06-16 19:02:33 -0700490 * @see #setAlignmentMode(int)
491 *
492 * @attr ref android.R.styleable#GridLayout_alignmentMode
Philip Milneaa616f32011-05-27 18:38:01 -0700493 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700494 @AlignmentMode
Philip Milne1e548252011-06-16 19:02:33 -0700495 public int getAlignmentMode() {
Adam Powell465ea742013-08-29 14:56:51 -0700496 return mAlignmentMode;
Philip Milneaa616f32011-05-27 18:38:01 -0700497 }
498
499 /**
Philip Milne1e548252011-06-16 19:02:33 -0700500 * Sets the alignment mode to be used for all of the alignments between the
501 * children of this container.
Philip Milne7fd94872011-06-07 20:14:17 -0700502 * <p>
Philip Milne1e548252011-06-16 19:02:33 -0700503 * The default value of this property is {@link #ALIGN_MARGINS}.
Philip Milneaa616f32011-05-27 18:38:01 -0700504 *
Philip Milne1e548252011-06-16 19:02:33 -0700505 * @param alignmentMode either {@link #ALIGN_BOUNDS} or {@link #ALIGN_MARGINS}
Philip Milneaa616f32011-05-27 18:38:01 -0700506 *
Philip Milne1e548252011-06-16 19:02:33 -0700507 * @see #ALIGN_BOUNDS
508 * @see #ALIGN_MARGINS
Philip Milneaa616f32011-05-27 18:38:01 -0700509 *
Philip Milne1e548252011-06-16 19:02:33 -0700510 * @see #getAlignmentMode()
511 *
512 * @attr ref android.R.styleable#GridLayout_alignmentMode
Philip Milneaa616f32011-05-27 18:38:01 -0700513 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700514 public void setAlignmentMode(@AlignmentMode int alignmentMode) {
Adam Powell465ea742013-08-29 14:56:51 -0700515 this.mAlignmentMode = alignmentMode;
Philip Milneaa616f32011-05-27 18:38:01 -0700516 requestLayout();
Philip Milne3f8956d2011-05-13 17:29:00 +0100517 }
518
519 /**
520 * Returns whether or not row boundaries are ordered by their grid indices.
521 *
Philip Milne7fd94872011-06-07 20:14:17 -0700522 * @return {@code true} if row boundaries must appear in the order of their indices,
523 * {@code false} otherwise
Philip Milne3f8956d2011-05-13 17:29:00 +0100524 *
525 * @see #setRowOrderPreserved(boolean)
526 *
527 * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
528 */
529 public boolean isRowOrderPreserved() {
Adam Powell465ea742013-08-29 14:56:51 -0700530 return mVerticalAxis.isOrderPreserved();
Philip Milne3f8956d2011-05-13 17:29:00 +0100531 }
532
533 /**
Philip Milne7fd94872011-06-07 20:14:17 -0700534 * When this property is {@code true}, GridLayout is forced to place the row boundaries
Philip Milneaa616f32011-05-27 18:38:01 -0700535 * so that their associated grid indices are in ascending order in the view.
Philip Milne3f8956d2011-05-13 17:29:00 +0100536 * <p>
Philip Milne899d5922011-07-21 11:39:37 -0700537 * When this property is {@code false} GridLayout is at liberty to place the vertical row
538 * boundaries in whatever order best fits the given constraints.
Philip Milne7fd94872011-06-07 20:14:17 -0700539 * <p>
Philip Milne899d5922011-07-21 11:39:37 -0700540 * The default value of this property is {@code true}.
Philip Milne7fd94872011-06-07 20:14:17 -0700541
542 * @param rowOrderPreserved {@code true} to force GridLayout to respect the order
543 * of row boundaries
Philip Milne3f8956d2011-05-13 17:29:00 +0100544 *
545 * @see #isRowOrderPreserved()
546 *
547 * @attr ref android.R.styleable#GridLayout_rowOrderPreserved
548 */
549 public void setRowOrderPreserved(boolean rowOrderPreserved) {
Adam Powell465ea742013-08-29 14:56:51 -0700550 mVerticalAxis.setOrderPreserved(rowOrderPreserved);
Philip Milneaa616f32011-05-27 18:38:01 -0700551 invalidateStructure();
552 requestLayout();
Philip Milne3f8956d2011-05-13 17:29:00 +0100553 }
554
555 /**
556 * Returns whether or not column boundaries are ordered by their grid indices.
557 *
Philip Milne7fd94872011-06-07 20:14:17 -0700558 * @return {@code true} if column boundaries must appear in the order of their indices,
559 * {@code false} otherwise
Philip Milne3f8956d2011-05-13 17:29:00 +0100560 *
561 * @see #setColumnOrderPreserved(boolean)
562 *
563 * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
564 */
565 public boolean isColumnOrderPreserved() {
Adam Powell465ea742013-08-29 14:56:51 -0700566 return mHorizontalAxis.isOrderPreserved();
Philip Milne3f8956d2011-05-13 17:29:00 +0100567 }
568
569 /**
Philip Milne7fd94872011-06-07 20:14:17 -0700570 * When this property is {@code true}, GridLayout is forced to place the column boundaries
Philip Milneaa616f32011-05-27 18:38:01 -0700571 * so that their associated grid indices are in ascending order in the view.
Philip Milne3f8956d2011-05-13 17:29:00 +0100572 * <p>
Philip Milne899d5922011-07-21 11:39:37 -0700573 * When this property is {@code false} GridLayout is at liberty to place the horizontal column
574 * boundaries in whatever order best fits the given constraints.
Philip Milne7fd94872011-06-07 20:14:17 -0700575 * <p>
Philip Milne899d5922011-07-21 11:39:37 -0700576 * The default value of this property is {@code true}.
Philip Milne3f8956d2011-05-13 17:29:00 +0100577 *
Philip Milne7fd94872011-06-07 20:14:17 -0700578 * @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
Philip Milne3f8956d2011-05-13 17:29:00 +0100579 * of column boundaries.
580 *
581 * @see #isColumnOrderPreserved()
582 *
583 * @attr ref android.R.styleable#GridLayout_columnOrderPreserved
584 */
585 public void setColumnOrderPreserved(boolean columnOrderPreserved) {
Adam Powell465ea742013-08-29 14:56:51 -0700586 mHorizontalAxis.setOrderPreserved(columnOrderPreserved);
Philip Milneaa616f32011-05-27 18:38:01 -0700587 invalidateStructure();
588 requestLayout();
Philip Milne3f8956d2011-05-13 17:29:00 +0100589 }
590
Philip Milne211d0332012-04-24 14:45:14 -0700591 /**
592 * Return the printer that will log diagnostics from this layout.
593 *
594 * @see #setPrinter(android.util.Printer)
595 *
596 * @return the printer associated with this view
Adam Powell465ea742013-08-29 14:56:51 -0700597 *
598 * @hide
Philip Milne211d0332012-04-24 14:45:14 -0700599 */
600 public Printer getPrinter() {
Adam Powell465ea742013-08-29 14:56:51 -0700601 return mPrinter;
Philip Milne211d0332012-04-24 14:45:14 -0700602 }
603
604 /**
605 * Set the printer that will log diagnostics from this layout.
606 * The default value is created by {@link android.util.LogPrinter}.
607 *
608 * @param printer the printer associated with this layout
609 *
610 * @see #getPrinter()
Adam Powell465ea742013-08-29 14:56:51 -0700611 *
612 * @hide
Philip Milne211d0332012-04-24 14:45:14 -0700613 */
614 public void setPrinter(Printer printer) {
Adam Powell465ea742013-08-29 14:56:51 -0700615 this.mPrinter = (printer == null) ? NO_PRINTER : printer;
Philip Milne211d0332012-04-24 14:45:14 -0700616 }
617
Philip Milne5125e212011-07-21 11:39:37 -0700618 // Static utility methods
619
Philip Milnef6679c82011-09-18 11:36:57 -0700620 static int max2(int[] a, int valueIfEmpty) {
Philip Milne51f17d52011-06-13 10:44:49 -0700621 int result = valueIfEmpty;
622 for (int i = 0, N = a.length; i < N; i++) {
623 result = Math.max(result, a[i]);
624 }
625 return result;
626 }
627
Philip Milne899d5922011-07-21 11:39:37 -0700628 @SuppressWarnings("unchecked")
Philip Milnef6679c82011-09-18 11:36:57 -0700629 static <T> T[] append(T[] a, T[] b) {
Philip Milne48b55242011-06-29 11:09:45 -0700630 T[] result = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length + b.length);
631 System.arraycopy(a, 0, result, 0, a.length);
632 System.arraycopy(b, 0, result, a.length, b.length);
Philip Milne3f8956d2011-05-13 17:29:00 +0100633 return result;
634 }
635
Philip Milnef6679c82011-09-18 11:36:57 -0700636 static Alignment getAlignment(int gravity, boolean horizontal) {
Philip Milne5125e212011-07-21 11:39:37 -0700637 int mask = horizontal ? HORIZONTAL_GRAVITY_MASK : VERTICAL_GRAVITY_MASK;
638 int shift = horizontal ? AXIS_X_SHIFT : AXIS_Y_SHIFT;
639 int flags = (gravity & mask) >> shift;
640 switch (flags) {
641 case (AXIS_SPECIFIED | AXIS_PULL_BEFORE):
Philip Milne1557fd72012-04-04 23:41:34 -0700642 return horizontal ? LEFT : TOP;
Philip Milne5125e212011-07-21 11:39:37 -0700643 case (AXIS_SPECIFIED | AXIS_PULL_AFTER):
Philip Milne1557fd72012-04-04 23:41:34 -0700644 return horizontal ? RIGHT : BOTTOM;
Philip Milne5125e212011-07-21 11:39:37 -0700645 case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER):
646 return FILL;
647 case AXIS_SPECIFIED:
648 return CENTER;
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -0800649 case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | RELATIVE_LAYOUT_DIRECTION):
650 return START;
651 case (AXIS_SPECIFIED | AXIS_PULL_AFTER | RELATIVE_LAYOUT_DIRECTION):
652 return END;
Philip Milne5125e212011-07-21 11:39:37 -0700653 default:
654 return UNDEFINED_ALIGNMENT;
655 }
656 }
657
Philip Milne4c8cf4c2011-08-03 11:50:50 -0700658 /** @noinspection UnusedParameters*/
Philip Milne1fd16372011-06-21 14:57:47 -0700659 private int getDefaultMargin(View c, boolean horizontal, boolean leading) {
Philip Milne899d5922011-07-21 11:39:37 -0700660 if (c.getClass() == Space.class) {
661 return 0;
662 }
Adam Powell465ea742013-08-29 14:56:51 -0700663 return mDefaultGap / 2;
Philip Milne3f8956d2011-05-13 17:29:00 +0100664 }
665
Philip Milne1fd16372011-06-21 14:57:47 -0700666 private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
Philip Milne7b757812012-09-19 18:13:44 -0700667 return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading);
Philip Milne3f8956d2011-05-13 17:29:00 +0100668 }
669
Philip Milne7a23b492012-04-24 22:12:36 -0700670 private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) {
Adam Powell465ea742013-08-29 14:56:51 -0700671 if (!mUseDefaultMargins) {
Philip Milne3f8956d2011-05-13 17:29:00 +0100672 return 0;
673 }
Philip Milne93cd6a62011-07-12 14:49:45 -0700674 Spec spec = horizontal ? p.columnSpec : p.rowSpec;
Adam Powell465ea742013-08-29 14:56:51 -0700675 Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
Philip Milne93cd6a62011-07-12 14:49:45 -0700676 Interval span = spec.span;
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -0800677 boolean leading1 = (horizontal && isLayoutRtl()) ? !leading : leading;
678 boolean isAtEdge = leading1 ? (span.min == 0) : (span.max == axis.getCount());
Philip Milne3f8956d2011-05-13 17:29:00 +0100679
Philip Milne1fd16372011-06-21 14:57:47 -0700680 return getDefaultMargin(c, isAtEdge, horizontal, leading);
Philip Milne3f8956d2011-05-13 17:29:00 +0100681 }
682
Philip Milnef6679c82011-09-18 11:36:57 -0700683 int getMargin1(View view, boolean horizontal, boolean leading) {
Philip Milne3f8956d2011-05-13 17:29:00 +0100684 LayoutParams lp = getLayoutParams(view);
685 int margin = horizontal ?
Philip Milneaa616f32011-05-27 18:38:01 -0700686 (leading ? lp.leftMargin : lp.rightMargin) :
687 (leading ? lp.topMargin : lp.bottomMargin);
Philip Milne7a23b492012-04-24 22:12:36 -0700688 return margin == UNDEFINED ? getDefaultMargin(view, lp, horizontal, leading) : margin;
Philip Milne1fd16372011-06-21 14:57:47 -0700689 }
690
Philip Milne4c8cf4c2011-08-03 11:50:50 -0700691 private int getMargin(View view, boolean horizontal, boolean leading) {
Adam Powell465ea742013-08-29 14:56:51 -0700692 if (mAlignmentMode == ALIGN_MARGINS) {
Philip Milne4c8cf4c2011-08-03 11:50:50 -0700693 return getMargin1(view, horizontal, leading);
694 } else {
Adam Powell465ea742013-08-29 14:56:51 -0700695 Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
Philip Milne4c8cf4c2011-08-03 11:50:50 -0700696 int[] margins = leading ? axis.getLeadingMargins() : axis.getTrailingMargins();
697 LayoutParams lp = getLayoutParams(view);
698 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
699 int index = leading ? spec.span.min : spec.span.max;
700 return margins[index];
701 }
702 }
703
Philip Milne1fd16372011-06-21 14:57:47 -0700704 private int getTotalMargin(View child, boolean horizontal) {
705 return getMargin(child, horizontal, true) + getMargin(child, horizontal, false);
Philip Milne3f8956d2011-05-13 17:29:00 +0100706 }
707
Philip Milne899d5922011-07-21 11:39:37 -0700708 private static boolean fits(int[] a, int value, int start, int end) {
709 if (end > a.length) {
710 return false;
711 }
712 for (int i = start; i < end; i++) {
713 if (a[i] > value) {
714 return false;
715 }
716 }
717 return true;
718 }
719
720 private static void procrusteanFill(int[] a, int start, int end, int value) {
721 int length = a.length;
722 Arrays.fill(a, Math.min(start, length), Math.min(end, length), value);
723 }
724
725 private static void setCellGroup(LayoutParams lp, int row, int rowSpan, int col, int colSpan) {
726 lp.setRowSpecSpan(new Interval(row, row + rowSpan));
727 lp.setColumnSpecSpan(new Interval(col, col + colSpan));
728 }
729
730 // Logic to avert infinite loops by ensuring that the cells can be placed somewhere.
731 private static int clip(Interval minorRange, boolean minorWasDefined, int count) {
732 int size = minorRange.size();
733 if (count == 0) {
734 return size;
735 }
736 int min = minorWasDefined ? min(minorRange.min, count) : 0;
737 return min(size, count - min);
738 }
739
Philip Milnef4748702011-06-09 18:30:32 -0700740 // install default indices for cells that don't define them
Philip Milne3f8956d2011-05-13 17:29:00 +0100741 private void validateLayoutParams() {
Adam Powell465ea742013-08-29 14:56:51 -0700742 final boolean horizontal = (mOrientation == HORIZONTAL);
743 final Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
Jim Miller782c04b2012-05-02 15:45:23 -0700744 final int count = (axis.definedCount != UNDEFINED) ? axis.definedCount : 0;
Philip Milne3f8956d2011-05-13 17:29:00 +0100745
Philip Milne899d5922011-07-21 11:39:37 -0700746 int major = 0;
747 int minor = 0;
748 int[] maxSizes = new int[count];
749
750 for (int i = 0, N = getChildCount(); i < N; i++) {
Philip Milned7dd8902012-01-26 16:55:30 -0800751 LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
Philip Milne899d5922011-07-21 11:39:37 -0700752
Philip Milnef6679c82011-09-18 11:36:57 -0700753 final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
754 final Interval majorRange = majorSpec.span;
755 final boolean majorWasDefined = majorSpec.startDefined;
Philip Milne899d5922011-07-21 11:39:37 -0700756 final int majorSpan = majorRange.size();
757 if (majorWasDefined) {
758 major = majorRange.min;
Philip Milnef4748702011-06-09 18:30:32 -0700759 }
760
Philip Milnef6679c82011-09-18 11:36:57 -0700761 final Spec minorSpec = horizontal ? lp.columnSpec : lp.rowSpec;
762 final Interval minorRange = minorSpec.span;
763 final boolean minorWasDefined = minorSpec.startDefined;
Philip Milne899d5922011-07-21 11:39:37 -0700764 final int minorSpan = clip(minorRange, minorWasDefined, count);
765 if (minorWasDefined) {
766 minor = minorRange.min;
767 }
Philip Milnef4748702011-06-09 18:30:32 -0700768
Philip Milne899d5922011-07-21 11:39:37 -0700769 if (count != 0) {
770 // Find suitable row/col values when at least one is undefined.
771 if (!majorWasDefined || !minorWasDefined) {
772 while (!fits(maxSizes, major, minor, minor + minorSpan)) {
773 if (minorWasDefined) {
774 major++;
775 } else {
776 if (minor + minorSpan <= count) {
777 minor++;
778 } else {
779 minor = 0;
780 major++;
781 }
Philip Milnef4748702011-06-09 18:30:32 -0700782 }
Philip Milne3f8956d2011-05-13 17:29:00 +0100783 }
784 }
Philip Milne899d5922011-07-21 11:39:37 -0700785 procrusteanFill(maxSizes, minor, minor + minorSpan, major + majorSpan);
Philip Milne3f8956d2011-05-13 17:29:00 +0100786 }
Philip Milne899d5922011-07-21 11:39:37 -0700787
788 if (horizontal) {
789 setCellGroup(lp, major, majorSpan, minor, minorSpan);
790 } else {
791 setCellGroup(lp, minor, minorSpan, major, majorSpan);
792 }
793
794 minor = minor + minorSpan;
795 }
Philip Milne3f8956d2011-05-13 17:29:00 +0100796 }
797
798 private void invalidateStructure() {
Adam Powell465ea742013-08-29 14:56:51 -0700799 mLastLayoutParamsHashCode = UNINITIALIZED_HASH;
800 mHorizontalAxis.invalidateStructure();
801 mVerticalAxis.invalidateStructure();
Philip Milne899d5922011-07-21 11:39:37 -0700802 // This can end up being done twice. Better twice than not at all.
Philip Milne3f8956d2011-05-13 17:29:00 +0100803 invalidateValues();
804 }
805
806 private void invalidateValues() {
Philip Milneaa616f32011-05-27 18:38:01 -0700807 // Need null check because requestLayout() is called in View's initializer,
808 // before we are set up.
Adam Powell465ea742013-08-29 14:56:51 -0700809 if (mHorizontalAxis != null && mVerticalAxis != null) {
810 mHorizontalAxis.invalidateValues();
811 mVerticalAxis.invalidateValues();
Philip Milneaa616f32011-05-27 18:38:01 -0700812 }
Philip Milne3f8956d2011-05-13 17:29:00 +0100813 }
814
Philip Milned7dd8902012-01-26 16:55:30 -0800815 /** @hide */
816 @Override
817 protected void onSetLayoutParams(View child, ViewGroup.LayoutParams layoutParams) {
818 super.onSetLayoutParams(child, layoutParams);
Philip Milne0f57cea2012-05-12 09:34:25 -0700819
820 if (!checkLayoutParams(layoutParams)) {
821 handleInvalidParams("supplied LayoutParams are of the wrong type");
822 }
823
Philip Milned7dd8902012-01-26 16:55:30 -0800824 invalidateStructure();
Philip Milne3f8956d2011-05-13 17:29:00 +0100825 }
826
Philip Milnef6679c82011-09-18 11:36:57 -0700827 final LayoutParams getLayoutParams(View c) {
Philip Milned7dd8902012-01-26 16:55:30 -0800828 return (LayoutParams) c.getLayoutParams();
Philip Milne3f8956d2011-05-13 17:29:00 +0100829 }
830
Philip Milne0f57cea2012-05-12 09:34:25 -0700831 private static void handleInvalidParams(String msg) {
832 throw new IllegalArgumentException(msg + ". ");
833 }
834
835 private void checkLayoutParams(LayoutParams lp, boolean horizontal) {
836 String groupName = horizontal ? "column" : "row";
837 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
838 Interval span = spec.span;
839 if (span.min != UNDEFINED && span.min < 0) {
840 handleInvalidParams(groupName + " indices must be positive");
841 }
Adam Powell465ea742013-08-29 14:56:51 -0700842 Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
Philip Milne0f57cea2012-05-12 09:34:25 -0700843 int count = axis.definedCount;
844 if (count != UNDEFINED) {
845 if (span.max > count) {
846 handleInvalidParams(groupName +
847 " indices (start + span) mustn't exceed the " + groupName + " count");
848 }
849 if (span.size() > count) {
850 handleInvalidParams(groupName + " span mustn't exceed the " + groupName + " count");
851 }
852 }
853 }
854
855 @Override
856 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
857 if (!(p instanceof LayoutParams)) {
858 return false;
859 }
860 LayoutParams lp = (LayoutParams) p;
861
862 checkLayoutParams(lp, true);
863 checkLayoutParams(lp, false);
864
865 return true;
866 }
867
Philip Milne3f8956d2011-05-13 17:29:00 +0100868 @Override
869 protected LayoutParams generateDefaultLayoutParams() {
870 return new LayoutParams();
871 }
872
873 @Override
874 public LayoutParams generateLayoutParams(AttributeSet attrs) {
Philip Milne5125e212011-07-21 11:39:37 -0700875 return new LayoutParams(getContext(), attrs);
Philip Milne3f8956d2011-05-13 17:29:00 +0100876 }
877
878 @Override
Yigit Boyar885c50b2016-03-22 16:53:42 -0700879 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
Yigit Boyar2dd20a62016-08-01 17:42:28 -0700880 if (sPreserveMarginParamsInLayoutParamConversion) {
881 if (lp instanceof LayoutParams) {
882 return new LayoutParams((LayoutParams) lp);
883 } else if (lp instanceof MarginLayoutParams) {
884 return new LayoutParams((MarginLayoutParams) lp);
885 }
Yigit Boyar885c50b2016-03-22 16:53:42 -0700886 }
Yigit Boyar2dd20a62016-08-01 17:42:28 -0700887 return new LayoutParams(lp);
Philip Milne3f8956d2011-05-13 17:29:00 +0100888 }
889
890 // Draw grid
891
892 private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -0800893 if (isLayoutRtl()) {
894 int width = getWidth();
Philip Milne7b757812012-09-19 18:13:44 -0700895 graphics.drawLine(width - x1, y1, width - x2, y2, paint);
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -0800896 } else {
Philip Milne7b757812012-09-19 18:13:44 -0700897 graphics.drawLine(x1, y1, x2, y2, paint);
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -0800898 }
Philip Milne3f8956d2011-05-13 17:29:00 +0100899 }
900
Philip Milne10ca24a2012-04-23 15:38:27 -0700901 @Override
Philip Milne7b757812012-09-19 18:13:44 -0700902 protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
Philip Milne10ca24a2012-04-23 15:38:27 -0700903 // Apply defaults, so as to remove UNDEFINED values
904 LayoutParams lp = new LayoutParams();
905 for (int i = 0; i < getChildCount(); i++) {
906 View c = getChildAt(i);
907 lp.setMargins(
Philip Milne7b757812012-09-19 18:13:44 -0700908 getMargin1(c, true, true),
909 getMargin1(c, false, true),
910 getMargin1(c, true, false),
911 getMargin1(c, false, false));
912 lp.onDebugDraw(c, canvas, paint);
Philip Milne10ca24a2012-04-23 15:38:27 -0700913 }
Philip Milneb5599762011-08-05 11:04:36 -0700914 }
915
Philip Milne3f8956d2011-05-13 17:29:00 +0100916 @Override
Philip Milne10ca24a2012-04-23 15:38:27 -0700917 protected void onDebugDraw(Canvas canvas) {
Philip Milne10ca24a2012-04-23 15:38:27 -0700918 Paint paint = new Paint();
919 paint.setStyle(Paint.Style.STROKE);
920 paint.setColor(Color.argb(50, 255, 255, 255));
Philip Milne3f8956d2011-05-13 17:29:00 +0100921
Philip Milne7b757812012-09-19 18:13:44 -0700922 Insets insets = getOpticalInsets();
923
924 int top = getPaddingTop() + insets.top;
925 int left = getPaddingLeft() + insets.left;
926 int right = getWidth() - getPaddingRight() - insets.right;
927 int bottom = getHeight() - getPaddingBottom() - insets.bottom;
928
Adam Powell465ea742013-08-29 14:56:51 -0700929 int[] xs = mHorizontalAxis.locations;
Philip Milne10ca24a2012-04-23 15:38:27 -0700930 if (xs != null) {
931 for (int i = 0, length = xs.length; i < length; i++) {
Philip Milne7b757812012-09-19 18:13:44 -0700932 int x = left + xs[i];
933 drawLine(canvas, x, top, x, bottom, paint);
Philip Milne3f8956d2011-05-13 17:29:00 +0100934 }
935 }
Philip Milne10ca24a2012-04-23 15:38:27 -0700936
Adam Powell465ea742013-08-29 14:56:51 -0700937 int[] ys = mVerticalAxis.locations;
Philip Milne10ca24a2012-04-23 15:38:27 -0700938 if (ys != null) {
939 for (int i = 0, length = ys.length; i < length; i++) {
Philip Milne7b757812012-09-19 18:13:44 -0700940 int y = top + ys[i];
941 drawLine(canvas, left, y, right, y, paint);
Philip Milne10ca24a2012-04-23 15:38:27 -0700942 }
943 }
944
945 super.onDebugDraw(canvas);
Philip Milne3f8956d2011-05-13 17:29:00 +0100946 }
947
Philip Milne3f8956d2011-05-13 17:29:00 +0100948 @Override
Adam Powell6690d012015-06-17 16:41:56 -0700949 public void onViewAdded(View child) {
Philip Milnef51d91c2011-07-18 16:12:19 -0700950 super.onViewAdded(child);
Philip Milne3f8956d2011-05-13 17:29:00 +0100951 invalidateStructure();
952 }
953
954 @Override
Adam Powell6690d012015-06-17 16:41:56 -0700955 public void onViewRemoved(View child) {
Philip Milnef51d91c2011-07-18 16:12:19 -0700956 super.onViewRemoved(child);
Philip Milneb0ce49b2011-07-15 15:39:07 -0700957 invalidateStructure();
958 }
959
Philip Milne350f0a62011-07-19 14:00:56 -0700960 /**
961 * We need to call invalidateStructure() when a child's GONE flag changes state.
962 * This implementation is a catch-all, invalidating on any change in the visibility flags.
963 *
964 * @hide
965 */
966 @Override
Chet Haase0d299362012-01-26 10:51:48 -0800967 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
968 super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
969 if (oldVisibility == GONE || newVisibility == GONE) {
Philip Milnea8416442013-02-25 10:49:39 -0800970 invalidateStructure();
Chet Haase0d299362012-01-26 10:51:48 -0800971 }
Philip Milne350f0a62011-07-19 14:00:56 -0700972 }
973
Philip Milned7dd8902012-01-26 16:55:30 -0800974 private int computeLayoutParamsHashCode() {
975 int result = 1;
976 for (int i = 0, N = getChildCount(); i < N; i++) {
977 View c = getChildAt(i);
978 if (c.getVisibility() == View.GONE) continue;
979 LayoutParams lp = (LayoutParams) c.getLayoutParams();
980 result = 31 * result + lp.hashCode();
981 }
982 return result;
Philip Milneb3a8c542011-06-20 16:02:59 -0700983 }
984
Philip Milneedd69512012-03-14 17:21:33 -0700985 private void consistencyCheck() {
Adam Powell465ea742013-08-29 14:56:51 -0700986 if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
Philip Milneedd69512012-03-14 17:21:33 -0700987 validateLayoutParams();
Adam Powell465ea742013-08-29 14:56:51 -0700988 mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
989 } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
990 mPrinter.println("The fields of some layout parameters were modified in between "
Philip Milne211d0332012-04-24 14:45:14 -0700991 + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
Philip Milneedd69512012-03-14 17:21:33 -0700992 invalidateStructure();
993 consistencyCheck();
Philip Milned7dd8902012-01-26 16:55:30 -0800994 }
995 }
996
997 // Measurement
998
Philip Milnee0b85cd2013-04-12 14:38:34 -0700999 // Note: padding has already been removed from the supplied specs
Philip Milne4a145d72011-09-29 14:14:25 -07001000 private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
Philip Milneedd69512012-03-14 17:21:33 -07001001 int childWidth, int childHeight) {
Philip Milne4a145d72011-09-29 14:14:25 -07001002 int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
Philip Milnee0b85cd2013-04-12 14:38:34 -07001003 getTotalMargin(child, true), childWidth);
Philip Milne4a145d72011-09-29 14:14:25 -07001004 int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
Philip Milnee0b85cd2013-04-12 14:38:34 -07001005 getTotalMargin(child, false), childHeight);
Philip Milne4a145d72011-09-29 14:14:25 -07001006 child.measure(childWidthSpec, childHeightSpec);
Philip Milneb3a8c542011-06-20 16:02:59 -07001007 }
1008
Philip Milnee0b85cd2013-04-12 14:38:34 -07001009 // Note: padding has already been removed from the supplied specs
Philip Milne4a145d72011-09-29 14:14:25 -07001010 private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
Philip Milneb3a8c542011-06-20 16:02:59 -07001011 for (int i = 0, N = getChildCount(); i < N; i++) {
1012 View c = getChildAt(i);
Philip Milned7dd8902012-01-26 16:55:30 -08001013 if (c.getVisibility() == View.GONE) continue;
Philip Milne4a145d72011-09-29 14:14:25 -07001014 LayoutParams lp = getLayoutParams(c);
1015 if (firstPass) {
1016 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
1017 } else {
Adam Powell465ea742013-08-29 14:56:51 -07001018 boolean horizontal = (mOrientation == HORIZONTAL);
Philip Milneecab1172011-10-25 15:07:19 -07001019 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Yigit Boyar6dafd872015-03-05 13:59:56 -08001020 if (spec.getAbsoluteAlignment(horizontal) == FILL) {
Philip Milne4a145d72011-09-29 14:14:25 -07001021 Interval span = spec.span;
Adam Powell465ea742013-08-29 14:56:51 -07001022 Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
Philip Milne4a145d72011-09-29 14:14:25 -07001023 int[] locations = axis.getLocations();
Philip Milneecab1172011-10-25 15:07:19 -07001024 int cellSize = locations[span.max] - locations[span.min];
1025 int viewSize = cellSize - getTotalMargin(c, horizontal);
1026 if (horizontal) {
1027 measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
Philip Milne4a145d72011-09-29 14:14:25 -07001028 } else {
Philip Milneecab1172011-10-25 15:07:19 -07001029 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
Philip Milne4a145d72011-09-29 14:14:25 -07001030 }
1031 }
1032 }
Philip Milneb3a8c542011-06-20 16:02:59 -07001033 }
1034 }
1035
Philip Milnee0b85cd2013-04-12 14:38:34 -07001036 static int adjust(int measureSpec, int delta) {
1037 return makeMeasureSpec(
1038 MeasureSpec.getSize(measureSpec + delta), MeasureSpec.getMode(measureSpec));
1039 }
1040
Philip Milne3f8956d2011-05-13 17:29:00 +01001041 @Override
1042 protected void onMeasure(int widthSpec, int heightSpec) {
Philip Milneedd69512012-03-14 17:21:33 -07001043 consistencyCheck();
Philip Milned7dd8902012-01-26 16:55:30 -08001044
Philip Milne4a145d72011-09-29 14:14:25 -07001045 /** If we have been called by {@link View#measure(int, int)}, one of width or height
1046 * is likely to have changed. We must invalidate if so. */
1047 invalidateValues();
Philip Milne3f8956d2011-05-13 17:29:00 +01001048
Philip Milnee0b85cd2013-04-12 14:38:34 -07001049 int hPadding = getPaddingLeft() + getPaddingRight();
1050 int vPadding = getPaddingTop() + getPaddingBottom();
Philip Milne3f8956d2011-05-13 17:29:00 +01001051
Philip Milnee0b85cd2013-04-12 14:38:34 -07001052 int widthSpecSansPadding = adjust( widthSpec, -hPadding);
1053 int heightSpecSansPadding = adjust(heightSpec, -vPadding);
1054
1055 measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, true);
1056
1057 int widthSansPadding;
1058 int heightSansPadding;
Philip Milne4a145d72011-09-29 14:14:25 -07001059
1060 // Use the orientation property to decide which axis should be laid out first.
Adam Powell465ea742013-08-29 14:56:51 -07001061 if (mOrientation == HORIZONTAL) {
1062 widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
Philip Milnee0b85cd2013-04-12 14:38:34 -07001063 measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
Adam Powell465ea742013-08-29 14:56:51 -07001064 heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
Philip Milne4a145d72011-09-29 14:14:25 -07001065 } else {
Adam Powell465ea742013-08-29 14:56:51 -07001066 heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
Philip Milnee0b85cd2013-04-12 14:38:34 -07001067 measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
Adam Powell465ea742013-08-29 14:56:51 -07001068 widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
Philip Milne4a145d72011-09-29 14:14:25 -07001069 }
1070
Philip Milnee0b85cd2013-04-12 14:38:34 -07001071 int measuredWidth = Math.max(widthSansPadding + hPadding, getSuggestedMinimumWidth());
1072 int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());
Philip Milne09e2d4d2011-06-20 10:28:18 -07001073
Philip Milne3f8956d2011-05-13 17:29:00 +01001074 setMeasuredDimension(
Philip Milnee0b85cd2013-04-12 14:38:34 -07001075 resolveSizeAndState(measuredWidth, widthSpec, 0),
Philip Milne09e2d4d2011-06-20 10:28:18 -07001076 resolveSizeAndState(measuredHeight, heightSpec, 0));
Philip Milne3f8956d2011-05-13 17:29:00 +01001077 }
1078
Philip Milne48b55242011-06-29 11:09:45 -07001079 private int getMeasurement(View c, boolean horizontal) {
Philip Milne7b757812012-09-19 18:13:44 -07001080 return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
Philip Milneaa616f32011-05-27 18:38:01 -07001081 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001082
Philip Milnef6679c82011-09-18 11:36:57 -07001083 final int getMeasurementIncludingMargin(View c, boolean horizontal) {
Philip Milned7dd8902012-01-26 16:55:30 -08001084 if (c.getVisibility() == View.GONE) {
Philip Milne5125e212011-07-21 11:39:37 -07001085 return 0;
1086 }
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001087 return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
Philip Milne3f8956d2011-05-13 17:29:00 +01001088 }
1089
Philip Milne3f8956d2011-05-13 17:29:00 +01001090 @Override
Philip Milneaa616f32011-05-27 18:38:01 -07001091 public void requestLayout() {
1092 super.requestLayout();
Philip Milne3f8956d2011-05-13 17:29:00 +01001093 invalidateValues();
Philip Milneaa616f32011-05-27 18:38:01 -07001094 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001095
Philip Milneaa616f32011-05-27 18:38:01 -07001096 // Layout container
1097
1098 /**
1099 * {@inheritDoc}
1100 */
1101 /*
1102 The layout operation is implemented by delegating the heavy lifting to the
1103 to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
1104 Together they compute the locations of the vertical and horizontal lines of
1105 the grid (respectively!).
1106
1107 This method is then left with the simpler task of applying margins, gravity
1108 and sizing to each child view and then placing it in its cell.
1109 */
1110 @Override
Philip Milne09e2d4d2011-06-20 10:28:18 -07001111 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Philip Milneedd69512012-03-14 17:21:33 -07001112 consistencyCheck();
Philip Milned7dd8902012-01-26 16:55:30 -08001113
Philip Milne09e2d4d2011-06-20 10:28:18 -07001114 int targetWidth = right - left;
1115 int targetHeight = bottom - top;
Philip Milne3f8956d2011-05-13 17:29:00 +01001116
1117 int paddingLeft = getPaddingLeft();
1118 int paddingTop = getPaddingTop();
1119 int paddingRight = getPaddingRight();
1120 int paddingBottom = getPaddingBottom();
1121
Adam Powell465ea742013-08-29 14:56:51 -07001122 mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
1123 mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
Philip Milne3f8956d2011-05-13 17:29:00 +01001124
Adam Powell465ea742013-08-29 14:56:51 -07001125 int[] hLocations = mHorizontalAxis.getLocations();
1126 int[] vLocations = mVerticalAxis.getLocations();
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001127
Philip Milneb3a8c542011-06-20 16:02:59 -07001128 for (int i = 0, N = getChildCount(); i < N; i++) {
1129 View c = getChildAt(i);
Philip Milned7dd8902012-01-26 16:55:30 -08001130 if (c.getVisibility() == View.GONE) continue;
Philip Milneb3a8c542011-06-20 16:02:59 -07001131 LayoutParams lp = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001132 Spec columnSpec = lp.columnSpec;
1133 Spec rowSpec = lp.rowSpec;
Philip Milne3f8956d2011-05-13 17:29:00 +01001134
Philip Milne93cd6a62011-07-12 14:49:45 -07001135 Interval colSpan = columnSpec.span;
1136 Interval rowSpan = rowSpec.span;
Philip Milne3f8956d2011-05-13 17:29:00 +01001137
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001138 int x1 = hLocations[colSpan.min];
1139 int y1 = vLocations[rowSpan.min];
Philip Milneaa616f32011-05-27 18:38:01 -07001140
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001141 int x2 = hLocations[colSpan.max];
1142 int y2 = vLocations[rowSpan.max];
Philip Milne3f8956d2011-05-13 17:29:00 +01001143
1144 int cellWidth = x2 - x1;
1145 int cellHeight = y2 - y1;
1146
Philip Milne48b55242011-06-29 11:09:45 -07001147 int pWidth = getMeasurement(c, true);
1148 int pHeight = getMeasurement(c, false);
Philip Milne3f8956d2011-05-13 17:29:00 +01001149
Yigit Boyar6dafd872015-03-05 13:59:56 -08001150 Alignment hAlign = columnSpec.getAbsoluteAlignment(true);
1151 Alignment vAlign = rowSpec.getAbsoluteAlignment(false);
Philip Milne3f8956d2011-05-13 17:29:00 +01001152
Adam Powell465ea742013-08-29 14:56:51 -07001153 Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
1154 Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
Philip Milne7fd94872011-06-07 20:14:17 -07001155
1156 // Gravity offsets: the location of the alignment group relative to its cell group.
Philip Milne6216e872012-02-16 17:15:50 -08001157 int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
1158 int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
Philip Milne7fd94872011-06-07 20:14:17 -07001159
Philip Milne6216e872012-02-16 17:15:50 -08001160 int leftMargin = getMargin(c, true, true);
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001161 int topMargin = getMargin(c, false, true);
Philip Milne6216e872012-02-16 17:15:50 -08001162 int rightMargin = getMargin(c, true, false);
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001163 int bottomMargin = getMargin(c, false, false);
Philip Milne7fd94872011-06-07 20:14:17 -07001164
Philip Milne1557fd72012-04-04 23:41:34 -07001165 int sumMarginsX = leftMargin + rightMargin;
1166 int sumMarginsY = topMargin + bottomMargin;
Philip Milne7fd94872011-06-07 20:14:17 -07001167
Philip Milne1557fd72012-04-04 23:41:34 -07001168 // Alignment offsets: the location of the view relative to its alignment group.
1169 int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
1170 int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
1171
1172 int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
1173 int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
Philip Milne7fd94872011-06-07 20:14:17 -07001174
Philip Milne6216e872012-02-16 17:15:50 -08001175 int dx = x1 + gravityOffsetX + alignmentOffsetX;
Philip Milne3f8956d2011-05-13 17:29:00 +01001176
Philip Milne6216e872012-02-16 17:15:50 -08001177 int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
1178 targetWidth - width - paddingRight - rightMargin - dx;
1179 int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
Philip Milne3f8956d2011-05-13 17:29:00 +01001180
Philip Milne899d5922011-07-21 11:39:37 -07001181 if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
1182 c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
1183 }
Philip Milneb3a8c542011-06-20 16:02:59 -07001184 c.layout(cx, cy, cx + width, cy + height);
Philip Milne3f8956d2011-05-13 17:29:00 +01001185 }
1186 }
1187
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001188 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -08001189 public CharSequence getAccessibilityClassName() {
1190 return GridLayout.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001191 }
1192
Philip Milne3f8956d2011-05-13 17:29:00 +01001193 // Inner classes
1194
Philip Milneaa616f32011-05-27 18:38:01 -07001195 /*
1196 This internal class houses the algorithm for computing the locations of grid lines;
1197 along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
1198 distinguished by the "horizontal" flag which is true for the horizontal axis and false
1199 for the vertical one.
1200 */
Philip Milnef6679c82011-09-18 11:36:57 -07001201 final class Axis {
Philip Milne48b55242011-06-29 11:09:45 -07001202 private static final int NEW = 0;
Philip Milne3f8956d2011-05-13 17:29:00 +01001203 private static final int PENDING = 1;
1204 private static final int COMPLETE = 2;
1205
1206 public final boolean horizontal;
1207
Philip Milnef6679c82011-09-18 11:36:57 -07001208 public int definedCount = UNDEFINED;
Philip Milne4a145d72011-09-29 14:14:25 -07001209 private int maxIndex = UNDEFINED;
Philip Milne3f8956d2011-05-13 17:29:00 +01001210
Philip Milne93cd6a62011-07-12 14:49:45 -07001211 PackedMap<Spec, Bounds> groupBounds;
Philip Milne3f8956d2011-05-13 17:29:00 +01001212 public boolean groupBoundsValid = false;
1213
Philip Milne48b55242011-06-29 11:09:45 -07001214 PackedMap<Interval, MutableInt> forwardLinks;
1215 public boolean forwardLinksValid = false;
1216
1217 PackedMap<Interval, MutableInt> backwardLinks;
1218 public boolean backwardLinksValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001219
Philip Milne3f8956d2011-05-13 17:29:00 +01001220 public int[] leadingMargins;
Philip Milneaa616f32011-05-27 18:38:01 -07001221 public boolean leadingMarginsValid = false;
1222
Philip Milne3f8956d2011-05-13 17:29:00 +01001223 public int[] trailingMargins;
Philip Milneaa616f32011-05-27 18:38:01 -07001224 public boolean trailingMarginsValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001225
1226 public Arc[] arcs;
1227 public boolean arcsValid = false;
1228
Philip Milneaa616f32011-05-27 18:38:01 -07001229 public int[] locations;
Philip Milne48b55242011-06-29 11:09:45 -07001230 public boolean locationsValid = false;
Philip Milneaa616f32011-05-27 18:38:01 -07001231
Philip Milne95054802014-05-23 19:50:40 -07001232 public boolean hasWeights;
1233 public boolean hasWeightsValid = false;
Philip Milne95054802014-05-23 19:50:40 -07001234 public int[] deltas;
1235
Philip Milnef6679c82011-09-18 11:36:57 -07001236 boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
Philip Milne3f8956d2011-05-13 17:29:00 +01001237
Philip Milne48b55242011-06-29 11:09:45 -07001238 private MutableInt parentMin = new MutableInt(0);
1239 private MutableInt parentMax = new MutableInt(-MAX_SIZE);
1240
Philip Milne3f8956d2011-05-13 17:29:00 +01001241 private Axis(boolean horizontal) {
1242 this.horizontal = horizontal;
1243 }
1244
Philip Milne4a145d72011-09-29 14:14:25 -07001245 private int calculateMaxIndex() {
1246 // the number Integer.MIN_VALUE + 1 comes up in undefined cells
1247 int result = -1;
Philip Milneb3a8c542011-06-20 16:02:59 -07001248 for (int i = 0, N = getChildCount(); i < N; i++) {
1249 View c = getChildAt(i);
Philip Milneb3a8c542011-06-20 16:02:59 -07001250 LayoutParams params = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001251 Spec spec = horizontal ? params.columnSpec : params.rowSpec;
Philip Milne4a145d72011-09-29 14:14:25 -07001252 Interval span = spec.span;
1253 result = max(result, span.min);
1254 result = max(result, span.max);
Philip Milne0f57cea2012-05-12 09:34:25 -07001255 result = max(result, span.size());
Philip Milne3f8956d2011-05-13 17:29:00 +01001256 }
Philip Milne4a145d72011-09-29 14:14:25 -07001257 return result == -1 ? UNDEFINED : result;
Philip Milne3f8956d2011-05-13 17:29:00 +01001258 }
1259
Philip Milne4a145d72011-09-29 14:14:25 -07001260 private int getMaxIndex() {
1261 if (maxIndex == UNDEFINED) {
1262 maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
Philip Milne3f8956d2011-05-13 17:29:00 +01001263 }
Philip Milne4a145d72011-09-29 14:14:25 -07001264 return maxIndex;
Philip Milnef6679c82011-09-18 11:36:57 -07001265 }
1266
1267 public int getCount() {
Philip Milne4a145d72011-09-29 14:14:25 -07001268 return max(definedCount, getMaxIndex());
Philip Milne3f8956d2011-05-13 17:29:00 +01001269 }
1270
1271 public void setCount(int count) {
Philip Milne0f57cea2012-05-12 09:34:25 -07001272 if (count != UNDEFINED && count < getMaxIndex()) {
1273 handleInvalidParams((horizontal ? "column" : "row") +
1274 "Count must be greater than or equal to the maximum of all grid indices " +
1275 "(and spans) defined in the LayoutParams of each child");
1276 }
Philip Milnef6679c82011-09-18 11:36:57 -07001277 this.definedCount = count;
Philip Milne3f8956d2011-05-13 17:29:00 +01001278 }
1279
1280 public boolean isOrderPreserved() {
Philip Milnef6679c82011-09-18 11:36:57 -07001281 return orderPreserved;
Philip Milne3f8956d2011-05-13 17:29:00 +01001282 }
1283
1284 public void setOrderPreserved(boolean orderPreserved) {
Philip Milnef6679c82011-09-18 11:36:57 -07001285 this.orderPreserved = orderPreserved;
Philip Milne3f8956d2011-05-13 17:29:00 +01001286 invalidateStructure();
1287 }
1288
Philip Milne93cd6a62011-07-12 14:49:45 -07001289 private PackedMap<Spec, Bounds> createGroupBounds() {
1290 Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
Philip Milne48b55242011-06-29 11:09:45 -07001291 for (int i = 0, N = getChildCount(); i < N; i++) {
Philip Milneb3a8c542011-06-20 16:02:59 -07001292 View c = getChildAt(i);
Philip Milnea8416442013-02-25 10:49:39 -08001293 // we must include views that are GONE here, see introductory javadoc
Philip Milne5125e212011-07-21 11:39:37 -07001294 LayoutParams lp = getLayoutParams(c);
1295 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Yigit Boyar6dafd872015-03-05 13:59:56 -08001296 Bounds bounds = spec.getAbsoluteAlignment(horizontal).getBounds();
Philip Milne5125e212011-07-21 11:39:37 -07001297 assoc.put(spec, bounds);
Philip Milne3f8956d2011-05-13 17:29:00 +01001298 }
Philip Milne48b55242011-06-29 11:09:45 -07001299 return assoc.pack();
Philip Milne3f8956d2011-05-13 17:29:00 +01001300 }
1301
1302 private void computeGroupBounds() {
Philip Milneb3a8c542011-06-20 16:02:59 -07001303 Bounds[] values = groupBounds.values;
1304 for (int i = 0; i < values.length; i++) {
1305 values[i].reset();
Philip Milne3f8956d2011-05-13 17:29:00 +01001306 }
Philip Milneaa616f32011-05-27 18:38:01 -07001307 for (int i = 0, N = getChildCount(); i < N; i++) {
Philip Milne3f8956d2011-05-13 17:29:00 +01001308 View c = getChildAt(i);
Philip Milnea8416442013-02-25 10:49:39 -08001309 // we must include views that are GONE here, see introductory javadoc
Philip Milne3f8956d2011-05-13 17:29:00 +01001310 LayoutParams lp = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001311 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Yigit Boyarfbd99972015-03-05 14:17:19 -08001312 int size = getMeasurementIncludingMargin(c, horizontal) +
1313 ((spec.weight == 0) ? 0 : getDeltas()[i]);
Philip Milne95054802014-05-23 19:50:40 -07001314 groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
Philip Milne3f8956d2011-05-13 17:29:00 +01001315 }
1316 }
1317
Philip Milnef6679c82011-09-18 11:36:57 -07001318 public PackedMap<Spec, Bounds> getGroupBounds() {
Philip Milne3f8956d2011-05-13 17:29:00 +01001319 if (groupBounds == null) {
1320 groupBounds = createGroupBounds();
1321 }
1322 if (!groupBoundsValid) {
1323 computeGroupBounds();
1324 groupBoundsValid = true;
1325 }
1326 return groupBounds;
1327 }
1328
1329 // Add values computed by alignment - taking the max of all alignments in each span
Philip Milne48b55242011-06-29 11:09:45 -07001330 private PackedMap<Interval, MutableInt> createLinks(boolean min) {
1331 Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
Philip Milne93cd6a62011-07-12 14:49:45 -07001332 Spec[] keys = getGroupBounds().keys;
Philip Milne48b55242011-06-29 11:09:45 -07001333 for (int i = 0, N = keys.length; i < N; i++) {
1334 Interval span = min ? keys[i].span : keys[i].span.inverse();
1335 result.put(span, new MutableInt());
Philip Milne3f8956d2011-05-13 17:29:00 +01001336 }
Philip Milne48b55242011-06-29 11:09:45 -07001337 return result.pack();
Philip Milne3f8956d2011-05-13 17:29:00 +01001338 }
1339
Philip Milne48b55242011-06-29 11:09:45 -07001340 private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
1341 MutableInt[] spans = links.values;
Philip Milne3f8956d2011-05-13 17:29:00 +01001342 for (int i = 0; i < spans.length; i++) {
1343 spans[i].reset();
1344 }
1345
Philip Milne5d1a9842011-07-07 11:47:08 -07001346 // Use getter to trigger a re-evaluation
Philip Milne48b55242011-06-29 11:09:45 -07001347 Bounds[] bounds = getGroupBounds().values;
Philip Milne3f8956d2011-05-13 17:29:00 +01001348 for (int i = 0; i < bounds.length; i++) {
Philip Milne48b55242011-06-29 11:09:45 -07001349 int size = bounds[i].size(min);
Philip Milne48b55242011-06-29 11:09:45 -07001350 MutableInt valueHolder = links.getValue(i);
Philip Milne5125e212011-07-21 11:39:37 -07001351 // this effectively takes the max() of the minima and the min() of the maxima
1352 valueHolder.value = max(valueHolder.value, min ? size : -size);
Philip Milne3f8956d2011-05-13 17:29:00 +01001353 }
1354 }
1355
Philip Milne48b55242011-06-29 11:09:45 -07001356 private PackedMap<Interval, MutableInt> getForwardLinks() {
1357 if (forwardLinks == null) {
1358 forwardLinks = createLinks(true);
Philip Milne3f8956d2011-05-13 17:29:00 +01001359 }
Philip Milne48b55242011-06-29 11:09:45 -07001360 if (!forwardLinksValid) {
1361 computeLinks(forwardLinks, true);
1362 forwardLinksValid = true;
Philip Milne3f8956d2011-05-13 17:29:00 +01001363 }
Philip Milne48b55242011-06-29 11:09:45 -07001364 return forwardLinks;
Philip Milne3f8956d2011-05-13 17:29:00 +01001365 }
1366
Philip Milne48b55242011-06-29 11:09:45 -07001367 private PackedMap<Interval, MutableInt> getBackwardLinks() {
1368 if (backwardLinks == null) {
1369 backwardLinks = createLinks(false);
1370 }
1371 if (!backwardLinksValid) {
1372 computeLinks(backwardLinks, false);
1373 backwardLinksValid = true;
1374 }
1375 return backwardLinks;
1376 }
1377
1378 private void include(List<Arc> arcs, Interval key, MutableInt size,
Philip Milneedd69512012-03-14 17:21:33 -07001379 boolean ignoreIfAlreadyPresent) {
Philip Milne48b55242011-06-29 11:09:45 -07001380 /*
1381 Remove self referential links.
1382 These appear:
1383 . as parental constraints when GridLayout has no children
1384 . when components have been marked as GONE
1385 */
1386 if (key.size() == 0) {
1387 return;
1388 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001389 // this bit below should really be computed outside here -
Philip Milne48b55242011-06-29 11:09:45 -07001390 // its just to stop default (row/col > 0) constraints obliterating valid entries
1391 if (ignoreIfAlreadyPresent) {
1392 for (Arc arc : arcs) {
1393 Interval span = arc.span;
1394 if (span.equals(key)) {
1395 return;
1396 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001397 }
1398 }
1399 arcs.add(new Arc(key, size));
1400 }
1401
Philip Milne48b55242011-06-29 11:09:45 -07001402 private void include(List<Arc> arcs, Interval key, MutableInt size) {
1403 include(arcs, key, size, true);
Philip Milne3f8956d2011-05-13 17:29:00 +01001404 }
1405
Philip Milneaa616f32011-05-27 18:38:01 -07001406 // Group arcs by their first vertex, returning an array of arrays.
Philip Milne3f8956d2011-05-13 17:29:00 +01001407 // This is linear in the number of arcs.
Philip Milnef6679c82011-09-18 11:36:57 -07001408 Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
Philip Milne48b55242011-06-29 11:09:45 -07001409 int N = getCount() + 1; // the number of vertices
Philip Milne3f8956d2011-05-13 17:29:00 +01001410 Arc[][] result = new Arc[N][];
1411 int[] sizes = new int[N];
1412 for (Arc arc : arcs) {
1413 sizes[arc.span.min]++;
Philip Milne5d1a9842011-07-07 11:47:08 -07001414 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001415 for (int i = 0; i < sizes.length; i++) {
1416 result[i] = new Arc[sizes[i]];
1417 }
1418 // reuse the sizes array to hold the current last elements as we insert each arc
1419 Arrays.fill(sizes, 0);
1420 for (Arc arc : arcs) {
1421 int i = arc.span.min;
1422 result[i][sizes[i]++] = arc;
1423 }
1424
1425 return result;
1426 }
1427
Philip Milne48b55242011-06-29 11:09:45 -07001428 private Arc[] topologicalSort(final Arc[] arcs) {
1429 return new Object() {
1430 Arc[] result = new Arc[arcs.length];
1431 int cursor = result.length - 1;
1432 Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
Philip Milne3f8956d2011-05-13 17:29:00 +01001433 int[] visited = new int[getCount() + 1];
1434
Philip Milne48b55242011-06-29 11:09:45 -07001435 void walk(int loc) {
1436 switch (visited[loc]) {
1437 case NEW: {
1438 visited[loc] = PENDING;
1439 for (Arc arc : arcsByVertex[loc]) {
1440 walk(arc.span.max);
1441 result[cursor--] = arc;
Philip Milne3f8956d2011-05-13 17:29:00 +01001442 }
Philip Milne48b55242011-06-29 11:09:45 -07001443 visited[loc] = COMPLETE;
1444 break;
Philip Milne3f8956d2011-05-13 17:29:00 +01001445 }
Philip Milne48b55242011-06-29 11:09:45 -07001446 case PENDING: {
Philip Milneb65408f2012-05-21 10:44:46 -07001447 // le singe est dans l'arbre
Philip Milne48b55242011-06-29 11:09:45 -07001448 assert false;
1449 break;
1450 }
1451 case COMPLETE: {
1452 break;
1453 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001454 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001455 }
Philip Milne48b55242011-06-29 11:09:45 -07001456
1457 Arc[] sort() {
1458 for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
1459 walk(loc);
1460 }
1461 assert cursor == -1;
1462 return result;
1463 }
1464 }.sort();
1465 }
1466
1467 private Arc[] topologicalSort(List<Arc> arcs) {
1468 return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
Philip Milne3f8956d2011-05-13 17:29:00 +01001469 }
1470
Philip Milne48b55242011-06-29 11:09:45 -07001471 private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
1472 for (int i = 0; i < links.keys.length; i++) {
1473 Interval key = links.keys[i];
1474 include(result, key, links.values[i], false);
Philip Milne3f8956d2011-05-13 17:29:00 +01001475 }
Philip Milne48b55242011-06-29 11:09:45 -07001476 }
1477
1478 private Arc[] createArcs() {
1479 List<Arc> mins = new ArrayList<Arc>();
1480 List<Arc> maxs = new ArrayList<Arc>();
1481
1482 // Add the minimum values from the components.
1483 addComponentSizes(mins, getForwardLinks());
1484 // Add the maximum values from the components.
1485 addComponentSizes(maxs, getBackwardLinks());
Philip Milne3f8956d2011-05-13 17:29:00 +01001486
Philip Milne48b55242011-06-29 11:09:45 -07001487 // Add ordering constraints to prevent row/col sizes from going negative
Philip Milnef6679c82011-09-18 11:36:57 -07001488 if (orderPreserved) {
Philip Milne48b55242011-06-29 11:09:45 -07001489 // Add a constraint for every row/col
Philip Milne3f8956d2011-05-13 17:29:00 +01001490 for (int i = 0; i < getCount(); i++) {
Philip Milne899d5922011-07-21 11:39:37 -07001491 include(mins, new Interval(i, i + 1), new MutableInt(0));
Philip Milne3f8956d2011-05-13 17:29:00 +01001492 }
1493 }
Philip Milne48b55242011-06-29 11:09:45 -07001494
1495 // Add the container constraints. Use the version of include that allows
1496 // duplicate entries in case a child spans the entire grid.
1497 int N = getCount();
1498 include(mins, new Interval(0, N), parentMin, false);
1499 include(maxs, new Interval(N, 0), parentMax, false);
1500
1501 // Sort
1502 Arc[] sMins = topologicalSort(mins);
1503 Arc[] sMaxs = topologicalSort(maxs);
1504
1505 return append(sMins, sMaxs);
1506 }
1507
1508 private void computeArcs() {
1509 // getting the links validates the values that are shared by the arc list
1510 getForwardLinks();
1511 getBackwardLinks();
Philip Milne3f8956d2011-05-13 17:29:00 +01001512 }
1513
Philip Milneaa616f32011-05-27 18:38:01 -07001514 public Arc[] getArcs() {
Philip Milne3f8956d2011-05-13 17:29:00 +01001515 if (arcs == null) {
Philip Milneaa616f32011-05-27 18:38:01 -07001516 arcs = createArcs();
Philip Milne3f8956d2011-05-13 17:29:00 +01001517 }
1518 if (!arcsValid) {
Philip Milne48b55242011-06-29 11:09:45 -07001519 computeArcs();
Philip Milne3f8956d2011-05-13 17:29:00 +01001520 arcsValid = true;
1521 }
1522 return arcs;
1523 }
1524
Philip Milneaa616f32011-05-27 18:38:01 -07001525 private boolean relax(int[] locations, Arc entry) {
Philip Milne48b55242011-06-29 11:09:45 -07001526 if (!entry.valid) {
1527 return false;
1528 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001529 Interval span = entry.span;
1530 int u = span.min;
1531 int v = span.max;
1532 int value = entry.value.value;
1533 int candidate = locations[u] + value;
Philip Milneaa616f32011-05-27 18:38:01 -07001534 if (candidate > locations[v]) {
Philip Milne3f8956d2011-05-13 17:29:00 +01001535 locations[v] = candidate;
1536 return true;
1537 }
1538 return false;
1539 }
1540
Philip Milnef6679c82011-09-18 11:36:57 -07001541 private void init(int[] locations) {
Philip Milne4a145d72011-09-29 14:14:25 -07001542 Arrays.fill(locations, 0);
Philip Milnef6679c82011-09-18 11:36:57 -07001543 }
1544
1545 private String arcsToString(List<Arc> arcs) {
Philip Milne4a145d72011-09-29 14:14:25 -07001546 String var = horizontal ? "x" : "y";
Philip Milnef6679c82011-09-18 11:36:57 -07001547 StringBuilder result = new StringBuilder();
Philip Milne4a145d72011-09-29 14:14:25 -07001548 boolean first = true;
1549 for (Arc arc : arcs) {
1550 if (first) {
1551 first = false;
Philip Milnef6679c82011-09-18 11:36:57 -07001552 } else {
Philip Milne4a145d72011-09-29 14:14:25 -07001553 result = result.append(", ");
Philip Milnef6679c82011-09-18 11:36:57 -07001554 }
1555 int src = arc.span.min;
1556 int dst = arc.span.max;
1557 int value = arc.value.value;
1558 result.append((src < dst) ?
Philip Milneedd69512012-03-14 17:21:33 -07001559 var + dst + "-" + var + src + ">=" + value :
1560 var + src + "-" + var + dst + "<=" + -value);
Philip Milnef6679c82011-09-18 11:36:57 -07001561
1562 }
1563 return result.toString();
1564 }
1565
1566 private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
1567 List<Arc> culprits = new ArrayList<Arc>();
1568 List<Arc> removed = new ArrayList<Arc>();
1569 for (int c = 0; c < arcs.length; c++) {
1570 Arc arc = arcs[c];
1571 if (culprits0[c]) {
1572 culprits.add(arc);
1573 }
1574 if (!arc.valid) {
1575 removed.add(arc);
1576 }
1577 }
Adam Powell465ea742013-08-29 14:56:51 -07001578 mPrinter.println(axisName + " constraints: " + arcsToString(culprits) +
Philip Milne211d0332012-04-24 14:45:14 -07001579 " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
Philip Milnef6679c82011-09-18 11:36:57 -07001580 }
1581
Philip Milneaa616f32011-05-27 18:38:01 -07001582 /*
1583 Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1584
1585 GridLayout converts its requirements into a system of linear constraints of the
1586 form:
1587
1588 x[i] - x[j] < a[k]
1589
1590 Where the x[i] are variables and the a[k] are constants.
1591
1592 For example, if the variables were instead labeled x, y, z we might have:
1593
1594 x - y < 17
1595 y - z < 23
1596 z - x < 42
1597
1598 This is a special case of the Linear Programming problem that is, in turn,
1599 equivalent to the single-source shortest paths problem on a digraph, for
1600 which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
Philip Milneaa616f32011-05-27 18:38:01 -07001601 */
Yigit Boyar98d5f042014-11-18 15:47:56 -08001602 private boolean solve(Arc[] arcs, int[] locations) {
1603 return solve(arcs, locations, true);
1604 }
1605
1606 private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
Philip Milnef6679c82011-09-18 11:36:57 -07001607 String axisName = horizontal ? "horizontal" : "vertical";
Philip Milne3f8956d2011-05-13 17:29:00 +01001608 int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
Philip Milnef6679c82011-09-18 11:36:57 -07001609 boolean[] originalCulprits = null;
Philip Milne3f8956d2011-05-13 17:29:00 +01001610
Philip Milnef6679c82011-09-18 11:36:57 -07001611 for (int p = 0; p < arcs.length; p++) {
1612 init(locations);
1613
1614 // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1615 for (int i = 0; i < N; i++) {
1616 boolean changed = false;
1617 for (int j = 0, length = arcs.length; j < length; j++) {
1618 changed |= relax(locations, arcs[j]);
Philip Milne3f8956d2011-05-13 17:29:00 +01001619 }
Philip Milnef6679c82011-09-18 11:36:57 -07001620 if (!changed) {
1621 if (originalCulprits != null) {
1622 logError(axisName, arcs, originalCulprits);
1623 }
Yigit Boyar98d5f042014-11-18 15:47:56 -08001624 return true;
Philip Milne48b55242011-06-29 11:09:45 -07001625 }
Philip Milnef6679c82011-09-18 11:36:57 -07001626 }
1627
Yigit Boyar98d5f042014-11-18 15:47:56 -08001628 if (!modifyOnError) {
1629 return false; // cannot solve with these constraints
1630 }
1631
Philip Milnef6679c82011-09-18 11:36:57 -07001632 boolean[] culprits = new boolean[arcs.length];
1633 for (int i = 0; i < N; i++) {
1634 for (int j = 0, length = arcs.length; j < length; j++) {
1635 culprits[j] |= relax(locations, arcs[j]);
1636 }
1637 }
1638
1639 if (p == 0) {
1640 originalCulprits = culprits;
1641 }
1642
1643 for (int i = 0; i < arcs.length; i++) {
1644 if (culprits[i]) {
1645 Arc arc = arcs[i];
1646 // Only remove max values, min values alone cannot be inconsistent
1647 if (arc.span.min < arc.span.max) {
1648 continue;
1649 }
1650 arc.valid = false;
1651 break;
1652 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001653 }
1654 }
Yigit Boyar98d5f042014-11-18 15:47:56 -08001655 return true;
Philip Milne3f8956d2011-05-13 17:29:00 +01001656 }
1657
Philip Milneaa616f32011-05-27 18:38:01 -07001658 private void computeMargins(boolean leading) {
1659 int[] margins = leading ? leadingMargins : trailingMargins;
Philip Milneb3a8c542011-06-20 16:02:59 -07001660 for (int i = 0, N = getChildCount(); i < N; i++) {
Philip Milne3f8956d2011-05-13 17:29:00 +01001661 View c = getChildAt(i);
Philip Milned7dd8902012-01-26 16:55:30 -08001662 if (c.getVisibility() == View.GONE) continue;
Philip Milne3f8956d2011-05-13 17:29:00 +01001663 LayoutParams lp = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001664 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1665 Interval span = spec.span;
Philip Milne3f8956d2011-05-13 17:29:00 +01001666 int index = leading ? span.min : span.max;
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001667 margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
Philip Milne3f8956d2011-05-13 17:29:00 +01001668 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001669 }
1670
Philip Milnef6679c82011-09-18 11:36:57 -07001671 // External entry points
1672
1673 public int[] getLeadingMargins() {
Philip Milneaa616f32011-05-27 18:38:01 -07001674 if (leadingMargins == null) {
1675 leadingMargins = new int[getCount() + 1];
1676 }
1677 if (!leadingMarginsValid) {
1678 computeMargins(true);
1679 leadingMarginsValid = true;
1680 }
1681 return leadingMargins;
1682 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001683
Philip Milnef6679c82011-09-18 11:36:57 -07001684 public int[] getTrailingMargins() {
Philip Milneaa616f32011-05-27 18:38:01 -07001685 if (trailingMargins == null) {
1686 trailingMargins = new int[getCount() + 1];
1687 }
1688 if (!trailingMarginsValid) {
1689 computeMargins(false);
1690 trailingMarginsValid = true;
1691 }
1692 return trailingMargins;
1693 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001694
Yigit Boyar98d5f042014-11-18 15:47:56 -08001695 private boolean solve(int[] a) {
1696 return solve(getArcs(), a);
Philip Milne87260842014-05-22 13:56:03 -07001697 }
1698
Philip Milne95054802014-05-23 19:50:40 -07001699 private boolean computeHasWeights() {
1700 for (int i = 0, N = getChildCount(); i < N; i++) {
Yigit Boyar6dafd872015-03-05 13:59:56 -08001701 final View child = getChildAt(i);
1702 if (child.getVisibility() == View.GONE) {
1703 continue;
1704 }
1705 LayoutParams lp = getLayoutParams(child);
Philip Milne95054802014-05-23 19:50:40 -07001706 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1707 if (spec.weight != 0) {
Philip Milne87260842014-05-22 13:56:03 -07001708 return true;
1709 }
1710 }
1711 return false;
1712 }
1713
Philip Milne95054802014-05-23 19:50:40 -07001714 private boolean hasWeights() {
1715 if (!hasWeightsValid) {
1716 hasWeights = computeHasWeights();
1717 hasWeightsValid = true;
1718 }
1719 return hasWeights;
1720 }
1721
Philip Milne95054802014-05-23 19:50:40 -07001722 public int[] getDeltas() {
1723 if (deltas == null) {
1724 deltas = new int[getChildCount()];
1725 }
1726 return deltas;
1727 }
1728
Yigit Boyar98d5f042014-11-18 15:47:56 -08001729 private void shareOutDelta(int totalDelta, float totalWeight) {
1730 Arrays.fill(deltas, 0);
Philip Milne95054802014-05-23 19:50:40 -07001731 for (int i = 0, N = getChildCount(); i < N; i++) {
Yigit Boyar6dafd872015-03-05 13:59:56 -08001732 final View c = getChildAt(i);
1733 if (c.getVisibility() == View.GONE) {
1734 continue;
1735 }
Philip Milne95054802014-05-23 19:50:40 -07001736 LayoutParams lp = getLayoutParams(c);
1737 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1738 float weight = spec.weight;
1739 if (weight != 0) {
Philip Milne87260842014-05-22 13:56:03 -07001740 int delta = Math.round((weight * totalDelta / totalWeight));
Philip Milne95054802014-05-23 19:50:40 -07001741 deltas[i] = delta;
Yigit Boyar98d5f042014-11-18 15:47:56 -08001742 // the two adjustments below are to counter the above rounding and avoid
1743 // off-by-ones at the end
Philip Milne87260842014-05-22 13:56:03 -07001744 totalDelta -= delta;
1745 totalWeight -= weight;
1746 }
1747 }
1748 }
1749
1750 private void solveAndDistributeSpace(int[] a) {
Philip Milne95054802014-05-23 19:50:40 -07001751 Arrays.fill(getDeltas(), 0);
Philip Milne87260842014-05-22 13:56:03 -07001752 solve(a);
Yigit Boyar98d5f042014-11-18 15:47:56 -08001753 int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
1754 if (deltaMax < 2) {
1755 return; //don't have any delta to distribute
1756 }
1757 int deltaMin = 0; //inclusive
1758
1759 float totalWeight = calculateTotalWeight();
1760
1761 int validDelta = -1; //delta for which a solution exists
1762 boolean validSolution = true;
1763 // do a binary search to find the max delta that won't conflict with constraints
1764 while(deltaMin < deltaMax) {
Deepanshu Gupta7a8fb262016-01-07 13:29:04 -08001765 // cast to long to prevent overflow.
1766 final int delta = (int) (((long) deltaMin + deltaMax) / 2);
Yigit Boyar98d5f042014-11-18 15:47:56 -08001767 invalidateValues();
1768 shareOutDelta(delta, totalWeight);
1769 validSolution = solve(getArcs(), a, false);
1770 if (validSolution) {
1771 validDelta = delta;
1772 deltaMin = delta + 1;
1773 } else {
1774 deltaMax = delta;
1775 }
1776 }
1777 if (validDelta > 0 && !validSolution) {
1778 // last solution was not successful but we have a successful one. Use it.
1779 invalidateValues();
1780 shareOutDelta(validDelta, totalWeight);
1781 solve(a);
1782 }
1783 }
1784
1785 private float calculateTotalWeight() {
1786 float totalWeight = 0f;
1787 for (int i = 0, N = getChildCount(); i < N; i++) {
1788 View c = getChildAt(i);
Yigit Boyar6dafd872015-03-05 13:59:56 -08001789 if (c.getVisibility() == View.GONE) {
1790 continue;
1791 }
Yigit Boyar98d5f042014-11-18 15:47:56 -08001792 LayoutParams lp = getLayoutParams(c);
1793 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1794 totalWeight += spec.weight;
1795 }
1796 return totalWeight;
Philip Milne87260842014-05-22 13:56:03 -07001797 }
1798
1799 private void computeLocations(int[] a) {
Philip Milne95054802014-05-23 19:50:40 -07001800 if (!hasWeights()) {
1801 solve(a);
1802 } else {
1803 solveAndDistributeSpace(a);
1804 }
Philip Milne4a145d72011-09-29 14:14:25 -07001805 if (!orderPreserved) {
1806 // Solve returns the smallest solution to the constraint system for which all
1807 // values are positive. One value is therefore zero - though if the row/col
1808 // order is not preserved this may not be the first vertex. For consistency,
1809 // translate all the values so that they measure the distance from a[0]; the
1810 // leading edge of the parent. After this transformation some values may be
1811 // negative.
1812 int a0 = a[0];
1813 for (int i = 0, N = a.length; i < N; i++) {
1814 a[i] = a[i] - a0;
1815 }
1816 }
Philip Milneaa616f32011-05-27 18:38:01 -07001817 }
1818
Philip Milnef6679c82011-09-18 11:36:57 -07001819 public int[] getLocations() {
Philip Milneaa616f32011-05-27 18:38:01 -07001820 if (locations == null) {
1821 int N = getCount() + 1;
1822 locations = new int[N];
1823 }
Philip Milne48b55242011-06-29 11:09:45 -07001824 if (!locationsValid) {
1825 computeLocations(locations);
1826 locationsValid = true;
1827 }
Philip Milneaa616f32011-05-27 18:38:01 -07001828 return locations;
1829 }
1830
Philip Milne3f8956d2011-05-13 17:29:00 +01001831 private int size(int[] locations) {
Philip Milne4a145d72011-09-29 14:14:25 -07001832 // The parental edges are attached to vertices 0 and N - even when order is not
1833 // being preserved and other vertices fall outside this range. Measure the distance
1834 // between vertices 0 and N, assuming that locations[0] = 0.
1835 return locations[getCount()];
Philip Milne3f8956d2011-05-13 17:29:00 +01001836 }
1837
Philip Milne48b55242011-06-29 11:09:45 -07001838 private void setParentConstraints(int min, int max) {
1839 parentMin.value = min;
1840 parentMax.value = -max;
1841 locationsValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001842 }
1843
Philip Milne48b55242011-06-29 11:09:45 -07001844 private int getMeasure(int min, int max) {
1845 setParentConstraints(min, max);
1846 return size(getLocations());
1847 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001848
Philip Milnef6679c82011-09-18 11:36:57 -07001849 public int getMeasure(int measureSpec) {
Philip Milne48b55242011-06-29 11:09:45 -07001850 int mode = MeasureSpec.getMode(measureSpec);
1851 int size = MeasureSpec.getSize(measureSpec);
1852 switch (mode) {
1853 case MeasureSpec.UNSPECIFIED: {
Philip Milne93cd6a62011-07-12 14:49:45 -07001854 return getMeasure(0, MAX_SIZE);
Philip Milne48b55242011-06-29 11:09:45 -07001855 }
1856 case MeasureSpec.EXACTLY: {
1857 return getMeasure(size, size);
1858 }
1859 case MeasureSpec.AT_MOST: {
1860 return getMeasure(0, size);
1861 }
1862 default: {
1863 assert false;
1864 return 0;
1865 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001866 }
Philip Milne48b55242011-06-29 11:09:45 -07001867 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001868
Philip Milnef6679c82011-09-18 11:36:57 -07001869 public void layout(int size) {
Philip Milne48b55242011-06-29 11:09:45 -07001870 setParentConstraints(size, size);
1871 getLocations();
Philip Milne3f8956d2011-05-13 17:29:00 +01001872 }
1873
Philip Milnef6679c82011-09-18 11:36:57 -07001874 public void invalidateStructure() {
Philip Milne4a145d72011-09-29 14:14:25 -07001875 maxIndex = UNDEFINED;
Philip Milneaa616f32011-05-27 18:38:01 -07001876
Philip Milne3f8956d2011-05-13 17:29:00 +01001877 groupBounds = null;
Philip Milne48b55242011-06-29 11:09:45 -07001878 forwardLinks = null;
1879 backwardLinks = null;
1880
Philip Milneaa616f32011-05-27 18:38:01 -07001881 leadingMargins = null;
1882 trailingMargins = null;
Philip Milnec9885f62011-06-15 17:07:35 -07001883 arcs = null;
Philip Milne48b55242011-06-29 11:09:45 -07001884
Philip Milneaa616f32011-05-27 18:38:01 -07001885 locations = null;
Philip Milne3f8956d2011-05-13 17:29:00 +01001886
Philip Milne95054802014-05-23 19:50:40 -07001887 deltas = null;
1888 hasWeightsValid = false;
1889
Philip Milne3f8956d2011-05-13 17:29:00 +01001890 invalidateValues();
1891 }
1892
Philip Milnef6679c82011-09-18 11:36:57 -07001893 public void invalidateValues() {
Philip Milne3f8956d2011-05-13 17:29:00 +01001894 groupBoundsValid = false;
Philip Milne48b55242011-06-29 11:09:45 -07001895 forwardLinksValid = false;
1896 backwardLinksValid = false;
1897
Philip Milneaa616f32011-05-27 18:38:01 -07001898 leadingMarginsValid = false;
1899 trailingMarginsValid = false;
Philip Milne48b55242011-06-29 11:09:45 -07001900 arcsValid = false;
1901
1902 locationsValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001903 }
1904 }
1905
1906 /**
1907 * Layout information associated with each of the children of a GridLayout.
1908 * <p>
1909 * GridLayout supports both row and column spanning and arbitrary forms of alignment within
1910 * each cell group. The fundamental parameters associated with each cell group are
1911 * gathered into their vertical and horizontal components and stored
Philip Milne93cd6a62011-07-12 14:49:45 -07001912 * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
Philip Milne6216e872012-02-16 17:15:50 -08001913 * {@link GridLayout.Spec Specs} are immutable structures
Philip Milneb0ce49b2011-07-15 15:39:07 -07001914 * and may be shared between the layout parameters of different children.
Philip Milne3f8956d2011-05-13 17:29:00 +01001915 * <p>
Philip Milne93cd6a62011-07-12 14:49:45 -07001916 * The row and column specs contain the leading and trailing indices along each axis
Philip Milneaa616f32011-05-27 18:38:01 -07001917 * and together specify the four grid indices that delimit the cells of this cell group.
Philip Milne3f8956d2011-05-13 17:29:00 +01001918 * <p>
Philip Milne93cd6a62011-07-12 14:49:45 -07001919 * The alignment properties of the row and column specs together specify
Philip Milne3f8956d2011-05-13 17:29:00 +01001920 * both aspects of alignment within the cell group. It is also possible to specify a child's
1921 * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
1922 * method.
Philip Milne87260842014-05-22 13:56:03 -07001923 * <p>
1924 * The weight property is also included in Spec and specifies the proportion of any
Philip Milne95054802014-05-23 19:50:40 -07001925 * excess space that is due to the associated view.
Philip Milnef6679c82011-09-18 11:36:57 -07001926 *
1927 * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
1928 *
1929 * Because the default values of the {@link #width} and {@link #height}
1930 * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
1931 * declared in the layout parameters of GridLayout's children. In addition,
1932 * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
1933 * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
1934 * instead controlled by the principle of <em>flexibility</em>,
1935 * as discussed in {@link GridLayout}.
1936 *
1937 * <h4>Summary</h4>
1938 *
1939 * You should not need to use either of the special size values:
1940 * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
1941 * a GridLayout.
Philip Milne3f8956d2011-05-13 17:29:00 +01001942 *
1943 * <h4>Default values</h4>
1944 *
1945 * <ul>
1946 * <li>{@link #width} = {@link #WRAP_CONTENT}</li>
1947 * <li>{@link #height} = {@link #WRAP_CONTENT}</li>
1948 * <li>{@link #topMargin} = 0 when
1949 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001950 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001951 * indicate that a default value should be computed on demand. </li>
1952 * <li>{@link #leftMargin} = 0 when
1953 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001954 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001955 * indicate that a default value should be computed on demand. </li>
1956 * <li>{@link #bottomMargin} = 0 when
1957 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001958 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001959 * indicate that a default value should be computed on demand. </li>
1960 * <li>{@link #rightMargin} = 0 when
1961 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001962 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001963 * indicate that a default value should be computed on demand. </li>
Philip Milnef6679c82011-09-18 11:36:57 -07001964 * <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
1965 * <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
1966 * <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
Philip Milne87260842014-05-22 13:56:03 -07001967 * <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
Philip Milnef6679c82011-09-18 11:36:57 -07001968 * <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
1969 * <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
Philip Milne6216e872012-02-16 17:15:50 -08001970 * <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
Philip Milne87260842014-05-22 13:56:03 -07001971 * <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
Philip Milne3f8956d2011-05-13 17:29:00 +01001972 * </ul>
1973 *
Philip Milnef6679c82011-09-18 11:36:57 -07001974 * See {@link GridLayout} for a more complete description of the conventions
1975 * used by GridLayout in the interpretation of the properties of this class.
1976 *
Philip Milne3f8956d2011-05-13 17:29:00 +01001977 * @attr ref android.R.styleable#GridLayout_Layout_layout_row
1978 * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
Philip Milne87260842014-05-22 13:56:03 -07001979 * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
Philip Milne3f8956d2011-05-13 17:29:00 +01001980 * @attr ref android.R.styleable#GridLayout_Layout_layout_column
1981 * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
Philip Milne87260842014-05-22 13:56:03 -07001982 * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
Philip Milne3f8956d2011-05-13 17:29:00 +01001983 * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1984 */
1985 public static class LayoutParams extends MarginLayoutParams {
1986
1987 // Default values
1988
1989 private static final int DEFAULT_WIDTH = WRAP_CONTENT;
1990 private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
1991 private static final int DEFAULT_MARGIN = UNDEFINED;
1992 private static final int DEFAULT_ROW = UNDEFINED;
1993 private static final int DEFAULT_COLUMN = UNDEFINED;
Philip Milnef4748702011-06-09 18:30:32 -07001994 private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
Philip Milne3f8956d2011-05-13 17:29:00 +01001995 private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
Philip Milne3f8956d2011-05-13 17:29:00 +01001996
1997 // TypedArray indices
1998
Philip Milneb0ce49b2011-07-15 15:39:07 -07001999 private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
2000 private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
2001 private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
2002 private static final int RIGHT_MARGIN =
2003 R.styleable.ViewGroup_MarginLayout_layout_marginRight;
Philip Milne3f8956d2011-05-13 17:29:00 +01002004 private static final int BOTTOM_MARGIN =
Philip Milneb0ce49b2011-07-15 15:39:07 -07002005 R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
Philip Milneb0ce49b2011-07-15 15:39:07 -07002006 private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
2007 private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
Philip Milne87260842014-05-22 13:56:03 -07002008 private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
Philip Milne5d1a9842011-07-07 11:47:08 -07002009
Philip Milneb0ce49b2011-07-15 15:39:07 -07002010 private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
2011 private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
Philip Milne87260842014-05-22 13:56:03 -07002012 private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
Philip Milne5d1a9842011-07-07 11:47:08 -07002013
Philip Milneb0ce49b2011-07-15 15:39:07 -07002014 private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
Philip Milne3f8956d2011-05-13 17:29:00 +01002015
2016 // Instance variables
2017
2018 /**
Philip Milnef6679c82011-09-18 11:36:57 -07002019 * The spec that defines the vertical characteristics of the cell group
Philip Milne3f8956d2011-05-13 17:29:00 +01002020 * described by these layout parameters.
Philip Milned7dd8902012-01-26 16:55:30 -08002021 * If an assignment is made to this field after a measurement or layout operation
2022 * has already taken place, a call to
2023 * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2024 * must be made to notify GridLayout of the change. GridLayout is normally able
2025 * to detect when code fails to observe this rule, issue a warning and take steps to
2026 * compensate for the omission. This facility is implemented on a best effort basis
2027 * and should not be relied upon in production code - so it is best to include the above
2028 * calls to remove the warnings as soon as it is practical.
Philip Milne3f8956d2011-05-13 17:29:00 +01002029 */
Philip Milnef6679c82011-09-18 11:36:57 -07002030 public Spec rowSpec = Spec.UNDEFINED;
2031
Philip Milne3f8956d2011-05-13 17:29:00 +01002032 /**
Philip Milnef6679c82011-09-18 11:36:57 -07002033 * The spec that defines the horizontal characteristics of the cell group
Philip Milne3f8956d2011-05-13 17:29:00 +01002034 * described by these layout parameters.
Philip Milned7dd8902012-01-26 16:55:30 -08002035 * If an assignment is made to this field after a measurement or layout operation
2036 * has already taken place, a call to
2037 * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2038 * must be made to notify GridLayout of the change. GridLayout is normally able
2039 * to detect when code fails to observe this rule, issue a warning and take steps to
2040 * compensate for the omission. This facility is implemented on a best effort basis
2041 * and should not be relied upon in production code - so it is best to include the above
2042 * calls to remove the warnings as soon as it is practical.
Philip Milne3f8956d2011-05-13 17:29:00 +01002043 */
Philip Milnef6679c82011-09-18 11:36:57 -07002044 public Spec columnSpec = Spec.UNDEFINED;
Philip Milne3f8956d2011-05-13 17:29:00 +01002045
2046 // Constructors
2047
2048 private LayoutParams(
2049 int width, int height,
2050 int left, int top, int right, int bottom,
Philip Milne93cd6a62011-07-12 14:49:45 -07002051 Spec rowSpec, Spec columnSpec) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002052 super(width, height);
2053 setMargins(left, top, right, bottom);
Philip Milne93cd6a62011-07-12 14:49:45 -07002054 this.rowSpec = rowSpec;
2055 this.columnSpec = columnSpec;
Philip Milne3f8956d2011-05-13 17:29:00 +01002056 }
2057
2058 /**
Philip Milne93cd6a62011-07-12 14:49:45 -07002059 * Constructs a new LayoutParams instance for this <code>rowSpec</code>
2060 * and <code>columnSpec</code>. All other fields are initialized with
Philip Milne3f8956d2011-05-13 17:29:00 +01002061 * default values as defined in {@link LayoutParams}.
2062 *
Philip Milne93cd6a62011-07-12 14:49:45 -07002063 * @param rowSpec the rowSpec
2064 * @param columnSpec the columnSpec
Philip Milne3f8956d2011-05-13 17:29:00 +01002065 */
Philip Milne93cd6a62011-07-12 14:49:45 -07002066 public LayoutParams(Spec rowSpec, Spec columnSpec) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002067 this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
2068 DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
Philip Milne93cd6a62011-07-12 14:49:45 -07002069 rowSpec, columnSpec);
Philip Milne3f8956d2011-05-13 17:29:00 +01002070 }
2071
2072 /**
2073 * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
2074 */
2075 public LayoutParams() {
Philip Milnef6679c82011-09-18 11:36:57 -07002076 this(Spec.UNDEFINED, Spec.UNDEFINED);
Philip Milne3f8956d2011-05-13 17:29:00 +01002077 }
2078
2079 // Copying constructors
2080
2081 /**
2082 * {@inheritDoc}
2083 */
2084 public LayoutParams(ViewGroup.LayoutParams params) {
2085 super(params);
2086 }
2087
2088 /**
2089 * {@inheritDoc}
2090 */
2091 public LayoutParams(MarginLayoutParams params) {
2092 super(params);
2093 }
2094
2095 /**
Alan Viverette0a0e1552013-08-07 13:24:09 -07002096 * Copy constructor. Clones the width, height, margin values, row spec,
2097 * and column spec of the source.
2098 *
2099 * @param source The layout params to copy from.
Philip Milne3f8956d2011-05-13 17:29:00 +01002100 */
Alan Viverette0a0e1552013-08-07 13:24:09 -07002101 public LayoutParams(LayoutParams source) {
2102 super(source);
2103
2104 this.rowSpec = source.rowSpec;
2105 this.columnSpec = source.columnSpec;
Philip Milne3f8956d2011-05-13 17:29:00 +01002106 }
2107
2108 // AttributeSet constructors
2109
Philip Milne3f8956d2011-05-13 17:29:00 +01002110 /**
2111 * {@inheritDoc}
2112 *
2113 * Values not defined in the attribute set take the default values
2114 * defined in {@link LayoutParams}.
2115 */
2116 public LayoutParams(Context context, AttributeSet attrs) {
Philip Milne5125e212011-07-21 11:39:37 -07002117 super(context, attrs);
2118 reInitSuper(context, attrs);
2119 init(context, attrs);
Philip Milne3f8956d2011-05-13 17:29:00 +01002120 }
2121
2122 // Implementation
2123
Philip Milne3f8956d2011-05-13 17:29:00 +01002124 // Reinitialise the margins using a different default policy than MarginLayoutParams.
2125 // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
2126 // so that a layout manager default can be accessed post set up. We need this as, at the
2127 // point of installation, we do not know how many rows/cols there are and therefore
2128 // which elements are positioned next to the container's trailing edges. We need to
2129 // know this as margins around the container's boundary should have different
2130 // defaults to those between peers.
2131
2132 // This method could be parametrized and moved into MarginLayout.
2133 private void reInitSuper(Context context, AttributeSet attrs) {
Philip Milneb0ce49b2011-07-15 15:39:07 -07002134 TypedArray a =
2135 context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
Philip Milne3f8956d2011-05-13 17:29:00 +01002136 try {
2137 int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
2138
2139 this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
2140 this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
2141 this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
2142 this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
2143 } finally {
2144 a.recycle();
2145 }
2146 }
2147
Philip Milne5125e212011-07-21 11:39:37 -07002148 private void init(Context context, AttributeSet attrs) {
Philip Milneb0ce49b2011-07-15 15:39:07 -07002149 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
Philip Milne3f8956d2011-05-13 17:29:00 +01002150 try {
Philip Milne5125e212011-07-21 11:39:37 -07002151 int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
Philip Milne3f8956d2011-05-13 17:29:00 +01002152
Philip Milne1e548252011-06-16 19:02:33 -07002153 int column = a.getInt(COLUMN, DEFAULT_COLUMN);
Philip Milne5125e212011-07-21 11:39:37 -07002154 int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
Philip Milne87260842014-05-22 13:56:03 -07002155 float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
2156 this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
Philip Milne3f8956d2011-05-13 17:29:00 +01002157
Philip Milne1e548252011-06-16 19:02:33 -07002158 int row = a.getInt(ROW, DEFAULT_ROW);
2159 int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
Philip Milne87260842014-05-22 13:56:03 -07002160 float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
2161 this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
Philip Milne3f8956d2011-05-13 17:29:00 +01002162 } finally {
2163 a.recycle();
2164 }
2165 }
2166
2167 /**
Philip Milne7fd94872011-06-07 20:14:17 -07002168 * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
Philip Milne6216e872012-02-16 17:15:50 -08002169 * See {@link Gravity}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002170 *
Philip Milne7fd94872011-06-07 20:14:17 -07002171 * @param gravity the new gravity value
Philip Milne3f8956d2011-05-13 17:29:00 +01002172 *
2173 * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
2174 */
2175 public void setGravity(int gravity) {
Philip Milne5125e212011-07-21 11:39:37 -07002176 rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
2177 columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
Philip Milne3f8956d2011-05-13 17:29:00 +01002178 }
2179
2180 @Override
2181 protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
2182 this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
2183 this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
2184 }
2185
Philip Milnef6679c82011-09-18 11:36:57 -07002186 final void setRowSpecSpan(Interval span) {
Philip Milne93cd6a62011-07-12 14:49:45 -07002187 rowSpec = rowSpec.copyWriteSpan(span);
Philip Milne3f8956d2011-05-13 17:29:00 +01002188 }
2189
Philip Milnef6679c82011-09-18 11:36:57 -07002190 final void setColumnSpecSpan(Interval span) {
Philip Milne93cd6a62011-07-12 14:49:45 -07002191 columnSpec = columnSpec.copyWriteSpan(span);
Philip Milne3f8956d2011-05-13 17:29:00 +01002192 }
Philip Milned7dd8902012-01-26 16:55:30 -08002193
2194 @Override
2195 public boolean equals(Object o) {
2196 if (this == o) return true;
2197 if (o == null || getClass() != o.getClass()) return false;
2198
2199 LayoutParams that = (LayoutParams) o;
2200
2201 if (!columnSpec.equals(that.columnSpec)) return false;
2202 if (!rowSpec.equals(that.rowSpec)) return false;
2203
2204 return true;
2205 }
2206
2207 @Override
2208 public int hashCode() {
2209 int result = rowSpec.hashCode();
2210 result = 31 * result + columnSpec.hashCode();
2211 return result;
2212 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002213 }
2214
Philip Milneaa616f32011-05-27 18:38:01 -07002215 /*
2216 In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
2217 Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
2218 */
Philip Milnef6679c82011-09-18 11:36:57 -07002219 final static class Arc {
Philip Milne3f8956d2011-05-13 17:29:00 +01002220 public final Interval span;
Philip Milneaa616f32011-05-27 18:38:01 -07002221 public final MutableInt value;
Philip Milne48b55242011-06-29 11:09:45 -07002222 public boolean valid = true;
Philip Milne3f8956d2011-05-13 17:29:00 +01002223
Philip Milneaa616f32011-05-27 18:38:01 -07002224 public Arc(Interval span, MutableInt value) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002225 this.span = span;
2226 this.value = value;
2227 }
2228
2229 @Override
2230 public String toString() {
Philip Milne48b55242011-06-29 11:09:45 -07002231 return span + " " + (!valid ? "+>" : "->") + " " + value;
Philip Milne3f8956d2011-05-13 17:29:00 +01002232 }
2233 }
2234
2235 // A mutable Integer - used to avoid heap allocation during the layout operation
2236
Philip Milnef6679c82011-09-18 11:36:57 -07002237 final static class MutableInt {
Philip Milne3f8956d2011-05-13 17:29:00 +01002238 public int value;
2239
Philip Milnef6679c82011-09-18 11:36:57 -07002240 public MutableInt() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002241 reset();
2242 }
2243
Philip Milnef6679c82011-09-18 11:36:57 -07002244 public MutableInt(int value) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002245 this.value = value;
2246 }
2247
Philip Milnef6679c82011-09-18 11:36:57 -07002248 public void reset() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002249 value = Integer.MIN_VALUE;
2250 }
Philip Milne48b55242011-06-29 11:09:45 -07002251
2252 @Override
2253 public String toString() {
2254 return Integer.toString(value);
2255 }
2256 }
2257
Philip Milnef6679c82011-09-18 11:36:57 -07002258 final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
Philip Milne48b55242011-06-29 11:09:45 -07002259 private final Class<K> keyType;
2260 private final Class<V> valueType;
2261
2262 private Assoc(Class<K> keyType, Class<V> valueType) {
2263 this.keyType = keyType;
2264 this.valueType = valueType;
2265 }
2266
Philip Milnef6679c82011-09-18 11:36:57 -07002267 public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
Philip Milne48b55242011-06-29 11:09:45 -07002268 return new Assoc<K, V>(keyType, valueType);
2269 }
2270
2271 public void put(K key, V value) {
2272 add(Pair.create(key, value));
2273 }
2274
2275 @SuppressWarnings(value = "unchecked")
2276 public PackedMap<K, V> pack() {
2277 int N = size();
2278 K[] keys = (K[]) Array.newInstance(keyType, N);
2279 V[] values = (V[]) Array.newInstance(valueType, N);
2280 for (int i = 0; i < N; i++) {
2281 keys[i] = get(i).first;
2282 values[i] = get(i).second;
2283 }
2284 return new PackedMap<K, V>(keys, values);
2285 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002286 }
2287
Philip Milneaa616f32011-05-27 18:38:01 -07002288 /*
2289 This data structure is used in place of a Map where we have an index that refers to the order
2290 in which each key/value pairs were added to the map. In this case we store keys and values
2291 in arrays of a length that is equal to the number of unique keys. We also maintain an
2292 array of indexes from insertion order to the compacted arrays of keys and values.
2293
2294 Note that behavior differs from that of a LinkedHashMap in that repeated entries
2295 *do* get added multiples times. So the length of index is equals to the number of
2296 items added.
2297
2298 This is useful in the GridLayout class where we can rely on the order of children not
2299 changing during layout - to use integer-based lookup for our internal structures
2300 rather than using (and storing) an implementation of Map<Key, ?>.
2301 */
Philip Milne3f8956d2011-05-13 17:29:00 +01002302 @SuppressWarnings(value = "unchecked")
Philip Milnef6679c82011-09-18 11:36:57 -07002303 final static class PackedMap<K, V> {
Philip Milne3f8956d2011-05-13 17:29:00 +01002304 public final int[] index;
2305 public final K[] keys;
2306 public final V[] values;
2307
2308 private PackedMap(K[] keys, V[] values) {
2309 this.index = createIndex(keys);
2310
Philip Milneaa616f32011-05-27 18:38:01 -07002311 this.keys = compact(keys, index);
2312 this.values = compact(values, index);
Philip Milne3f8956d2011-05-13 17:29:00 +01002313 }
2314
Philip Milnef6679c82011-09-18 11:36:57 -07002315 public V getValue(int i) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002316 return values[index[i]];
2317 }
2318
2319 private static <K> int[] createIndex(K[] keys) {
2320 int size = keys.length;
2321 int[] result = new int[size];
2322
2323 Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
2324 for (int i = 0; i < size; i++) {
2325 K key = keys[i];
2326 Integer index = keyToIndex.get(key);
2327 if (index == null) {
2328 index = keyToIndex.size();
2329 keyToIndex.put(key, index);
2330 }
2331 result[i] = index;
2332 }
2333 return result;
2334 }
2335
Philip Milneaa616f32011-05-27 18:38:01 -07002336 /*
2337 Create a compact array of keys or values using the supplied index.
2338 */
2339 private static <K> K[] compact(K[] a, int[] index) {
2340 int size = a.length;
2341 Class<?> componentType = a.getClass().getComponentType();
Philip Milne51f17d52011-06-13 10:44:49 -07002342 K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
Philip Milne3f8956d2011-05-13 17:29:00 +01002343
2344 // this overwrite duplicates, retaining the last equivalent entry
2345 for (int i = 0; i < size; i++) {
Philip Milneaa616f32011-05-27 18:38:01 -07002346 result[index[i]] = a[i];
Philip Milne3f8956d2011-05-13 17:29:00 +01002347 }
2348 return result;
2349 }
2350 }
2351
Philip Milneaa616f32011-05-27 18:38:01 -07002352 /*
Philip Milne93cd6a62011-07-12 14:49:45 -07002353 For each group (with a given alignment) we need to store the amount of space required
Philip Milne7fd94872011-06-07 20:14:17 -07002354 before the alignment point and the amount of space required after it. One side of this
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002355 calculation is always 0 for START and END alignments but we don't make use of this.
Philip Milneaa616f32011-05-27 18:38:01 -07002356 For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
2357 simple optimisations are possible.
2358
2359 The general algorithm therefore is to create a Map (actually a PackedMap) from
Philip Milne93cd6a62011-07-12 14:49:45 -07002360 group to Bounds and to loop through all Views in the group taking the maximum
Philip Milneaa616f32011-05-27 18:38:01 -07002361 of the values for each View.
2362 */
Philip Milnef6679c82011-09-18 11:36:57 -07002363 static class Bounds {
Philip Milne7fd94872011-06-07 20:14:17 -07002364 public int before;
2365 public int after;
Philip Milne5125e212011-07-21 11:39:37 -07002366 public int flexibility; // we're flexible iff all included specs are flexible
Philip Milne3f8956d2011-05-13 17:29:00 +01002367
2368 private Bounds() {
2369 reset();
2370 }
2371
Philip Milnea1f7b102011-06-23 11:10:13 -07002372 protected void reset() {
Philip Milne7fd94872011-06-07 20:14:17 -07002373 before = Integer.MIN_VALUE;
2374 after = Integer.MIN_VALUE;
Philip Milne5125e212011-07-21 11:39:37 -07002375 flexibility = CAN_STRETCH; // from the above, we're flexible when empty
Philip Milne3f8956d2011-05-13 17:29:00 +01002376 }
2377
Philip Milnea1f7b102011-06-23 11:10:13 -07002378 protected void include(int before, int after) {
Philip Milne7fd94872011-06-07 20:14:17 -07002379 this.before = max(this.before, before);
2380 this.after = max(this.after, after);
Philip Milne3f8956d2011-05-13 17:29:00 +01002381 }
2382
Philip Milne48b55242011-06-29 11:09:45 -07002383 protected int size(boolean min) {
Philip Milne5d1a9842011-07-07 11:47:08 -07002384 if (!min) {
Philip Milne5125e212011-07-21 11:39:37 -07002385 if (canStretch(flexibility)) {
Philip Milne5d1a9842011-07-07 11:47:08 -07002386 return MAX_SIZE;
2387 }
Philip Milne48b55242011-06-29 11:09:45 -07002388 }
Philip Milne4885f2f2014-05-23 23:12:11 -07002389 return before + after;
Philip Milne3f8956d2011-05-13 17:29:00 +01002390 }
2391
Philip Milne1557fd72012-04-04 23:41:34 -07002392 protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
Philip Milne7a23b492012-04-24 22:12:36 -07002393 return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
Philip Milne1557fd72012-04-04 23:41:34 -07002394 }
2395
Philip Milne95054802014-05-23 19:50:40 -07002396 protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
Philip Milne5125e212011-07-21 11:39:37 -07002397 this.flexibility &= spec.getFlexibility();
Philip Milne1557fd72012-04-04 23:41:34 -07002398 boolean horizontal = axis.horizontal;
Yigit Boyar6dafd872015-03-05 13:59:56 -08002399 Alignment alignment = spec.getAbsoluteAlignment(axis.horizontal);
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002400 // todo test this works correctly when the returned value is UNDEFINED
Philip Milne7a23b492012-04-24 22:12:36 -07002401 int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
Philip Milne48b55242011-06-29 11:09:45 -07002402 include(before, size - before);
Philip Milnea1f7b102011-06-23 11:10:13 -07002403 }
2404
Philip Milne3f8956d2011-05-13 17:29:00 +01002405 @Override
2406 public String toString() {
2407 return "Bounds{" +
Philip Milne7fd94872011-06-07 20:14:17 -07002408 "before=" + before +
2409 ", after=" + after +
Philip Milne3f8956d2011-05-13 17:29:00 +01002410 '}';
2411 }
2412 }
2413
2414 /**
2415 * An Interval represents a contiguous range of values that lie between
2416 * the interval's {@link #min} and {@link #max} values.
2417 * <p>
2418 * Intervals are immutable so may be passed as values and used as keys in hash tables.
2419 * It is not necessary to have multiple instances of Intervals which have the same
2420 * {@link #min} and {@link #max} values.
2421 * <p>
Philip Milne7fd94872011-06-07 20:14:17 -07002422 * Intervals are often written as {@code [min, max]} and represent the set of values
2423 * {@code x} such that {@code min <= x < max}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002424 */
Philip Milnef6679c82011-09-18 11:36:57 -07002425 final static class Interval {
Philip Milne3f8956d2011-05-13 17:29:00 +01002426 /**
2427 * The minimum value.
2428 */
2429 public final int min;
Philip Milneaa616f32011-05-27 18:38:01 -07002430
Philip Milne3f8956d2011-05-13 17:29:00 +01002431 /**
2432 * The maximum value.
2433 */
2434 public final int max;
2435
2436 /**
Philip Milne7fd94872011-06-07 20:14:17 -07002437 * Construct a new Interval, {@code interval}, where:
Philip Milne3f8956d2011-05-13 17:29:00 +01002438 * <ul>
Philip Milne7fd94872011-06-07 20:14:17 -07002439 * <li> {@code interval.min = min} </li>
2440 * <li> {@code interval.max = max} </li>
Philip Milne3f8956d2011-05-13 17:29:00 +01002441 * </ul>
2442 *
2443 * @param min the minimum value.
2444 * @param max the maximum value.
2445 */
2446 public Interval(int min, int max) {
2447 this.min = min;
2448 this.max = max;
2449 }
2450
Philip Milnef6679c82011-09-18 11:36:57 -07002451 int size() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002452 return max - min;
2453 }
2454
Philip Milnef6679c82011-09-18 11:36:57 -07002455 Interval inverse() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002456 return new Interval(max, min);
2457 }
2458
2459 /**
Philip Milne7fd94872011-06-07 20:14:17 -07002460 * Returns {@code true} if the {@link #getClass class},
2461 * {@link #min} and {@link #max} properties of this Interval and the
2462 * supplied parameter are pairwise equal; {@code false} otherwise.
Philip Milne3f8956d2011-05-13 17:29:00 +01002463 *
Philip Milne7fd94872011-06-07 20:14:17 -07002464 * @param that the object to compare this interval with
Philip Milne3f8956d2011-05-13 17:29:00 +01002465 *
2466 * @return {@code true} if the specified object is equal to this
Philip Milne7fd94872011-06-07 20:14:17 -07002467 * {@code Interval}, {@code false} otherwise.
Philip Milne3f8956d2011-05-13 17:29:00 +01002468 */
2469 @Override
2470 public boolean equals(Object that) {
2471 if (this == that) {
2472 return true;
2473 }
2474 if (that == null || getClass() != that.getClass()) {
2475 return false;
2476 }
2477
2478 Interval interval = (Interval) that;
2479
2480 if (max != interval.max) {
2481 return false;
2482 }
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002483 //noinspection RedundantIfStatement
Philip Milne3f8956d2011-05-13 17:29:00 +01002484 if (min != interval.min) {
2485 return false;
2486 }
2487
2488 return true;
2489 }
2490
2491 @Override
2492 public int hashCode() {
2493 int result = min;
2494 result = 31 * result + max;
2495 return result;
2496 }
2497
2498 @Override
2499 public String toString() {
2500 return "[" + min + ", " + max + "]";
2501 }
2502 }
2503
Philip Milne899d5922011-07-21 11:39:37 -07002504 /**
2505 * A Spec defines the horizontal or vertical characteristics of a group of
Philip Milne4a145d72011-09-29 14:14:25 -07002506 * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
2507 * along the appropriate axis.
Philip Milne899d5922011-07-21 11:39:37 -07002508 * <p>
2509 * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2510 * See {@link GridLayout} for a description of the conventions used by GridLayout
2511 * for grid indices.
2512 * <p>
2513 * The <em>alignment</em> property specifies how cells should be aligned in this group.
2514 * For row groups, this specifies the vertical alignment.
2515 * For column groups, this specifies the horizontal alignment.
Philip Milne4a145d72011-09-29 14:14:25 -07002516 * <p>
2517 * Use the following static methods to create specs:
2518 * <ul>
2519 * <li>{@link #spec(int)}</li>
2520 * <li>{@link #spec(int, int)}</li>
2521 * <li>{@link #spec(int, Alignment)}</li>
2522 * <li>{@link #spec(int, int, Alignment)}</li>
Philip Milne87260842014-05-22 13:56:03 -07002523 * <li>{@link #spec(int, float)}</li>
2524 * <li>{@link #spec(int, int, float)}</li>
2525 * <li>{@link #spec(int, Alignment, float)}</li>
2526 * <li>{@link #spec(int, int, Alignment, float)}</li>
Philip Milne4a145d72011-09-29 14:14:25 -07002527 * </ul>
2528 *
Philip Milne899d5922011-07-21 11:39:37 -07002529 */
Philip Milne93cd6a62011-07-12 14:49:45 -07002530 public static class Spec {
Philip Milnef6679c82011-09-18 11:36:57 -07002531 static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
Philip Milne87260842014-05-22 13:56:03 -07002532 static final float DEFAULT_WEIGHT = 0;
Philip Milnef6679c82011-09-18 11:36:57 -07002533
2534 final boolean startDefined;
Philip Milne48b55242011-06-29 11:09:45 -07002535 final Interval span;
Philip Milne93cd6a62011-07-12 14:49:45 -07002536 final Alignment alignment;
Philip Milne87260842014-05-22 13:56:03 -07002537 final float weight;
Philip Milne3f8956d2011-05-13 17:29:00 +01002538
Philip Milne87260842014-05-22 13:56:03 -07002539 private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
Philip Milnef6679c82011-09-18 11:36:57 -07002540 this.startDefined = startDefined;
Philip Milne5d1a9842011-07-07 11:47:08 -07002541 this.span = span;
2542 this.alignment = alignment;
Philip Milne87260842014-05-22 13:56:03 -07002543 this.weight = weight;
Philip Milne5d1a9842011-07-07 11:47:08 -07002544 }
2545
Philip Milne87260842014-05-22 13:56:03 -07002546 private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
2547 this(startDefined, new Interval(start, start + size), alignment, weight);
Philip Milne93cd6a62011-07-12 14:49:45 -07002548 }
2549
Yigit Boyar38d909a2015-03-05 14:39:38 -08002550 private Alignment getAbsoluteAlignment(boolean horizontal) {
Yigit Boyar6dafd872015-03-05 13:59:56 -08002551 if (alignment != UNDEFINED_ALIGNMENT) {
2552 return alignment;
2553 }
2554 if (weight == 0f) {
2555 return horizontal ? START : BASELINE;
2556 }
2557 return FILL;
2558 }
2559
Philip Milnef6679c82011-09-18 11:36:57 -07002560 final Spec copyWriteSpan(Interval span) {
Philip Milne87260842014-05-22 13:56:03 -07002561 return new Spec(startDefined, span, alignment, weight);
Philip Milne93cd6a62011-07-12 14:49:45 -07002562 }
2563
Philip Milnef6679c82011-09-18 11:36:57 -07002564 final Spec copyWriteAlignment(Alignment alignment) {
Philip Milne87260842014-05-22 13:56:03 -07002565 return new Spec(startDefined, span, alignment, weight);
Philip Milne93cd6a62011-07-12 14:49:45 -07002566 }
2567
Philip Milnef6679c82011-09-18 11:36:57 -07002568 final int getFlexibility() {
Philip Milne87260842014-05-22 13:56:03 -07002569 return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
Philip Milne5d1a9842011-07-07 11:47:08 -07002570 }
2571
Philip Milne3f8956d2011-05-13 17:29:00 +01002572 /**
Philip Milne93cd6a62011-07-12 14:49:45 -07002573 * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
2574 * properties of this Spec and the supplied parameter are pairwise equal,
Philip Milne7fd94872011-06-07 20:14:17 -07002575 * {@code false} otherwise.
Philip Milne3f8956d2011-05-13 17:29:00 +01002576 *
Philip Milne93cd6a62011-07-12 14:49:45 -07002577 * @param that the object to compare this spec with
Philip Milne3f8956d2011-05-13 17:29:00 +01002578 *
2579 * @return {@code true} if the specified object is equal to this
Philip Milne93cd6a62011-07-12 14:49:45 -07002580 * {@code Spec}; {@code false} otherwise
Philip Milne3f8956d2011-05-13 17:29:00 +01002581 */
2582 @Override
2583 public boolean equals(Object that) {
2584 if (this == that) {
2585 return true;
2586 }
2587 if (that == null || getClass() != that.getClass()) {
2588 return false;
2589 }
2590
Philip Milne93cd6a62011-07-12 14:49:45 -07002591 Spec spec = (Spec) that;
Philip Milne3f8956d2011-05-13 17:29:00 +01002592
Philip Milne93cd6a62011-07-12 14:49:45 -07002593 if (!alignment.equals(spec.alignment)) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002594 return false;
2595 }
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002596 //noinspection RedundantIfStatement
Philip Milne93cd6a62011-07-12 14:49:45 -07002597 if (!span.equals(spec.span)) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002598 return false;
2599 }
2600
2601 return true;
2602 }
2603
2604 @Override
2605 public int hashCode() {
2606 int result = span.hashCode();
2607 result = 31 * result + alignment.hashCode();
2608 return result;
2609 }
2610 }
2611
Philip Milne3f8956d2011-05-13 17:29:00 +01002612 /**
Philip Milne93cd6a62011-07-12 14:49:45 -07002613 * Return a Spec, {@code spec}, where:
2614 * <ul>
2615 * <li> {@code spec.span = [start, start + size]} </li>
2616 * <li> {@code spec.alignment = alignment} </li>
Philip Milne87260842014-05-22 13:56:03 -07002617 * <li> {@code spec.weight = weight} </li>
Philip Milne93cd6a62011-07-12 14:49:45 -07002618 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002619 * <p>
2620 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne93cd6a62011-07-12 14:49:45 -07002621 *
2622 * @param start the start
2623 * @param size the size
2624 * @param alignment the alignment
Philip Milne87260842014-05-22 13:56:03 -07002625 * @param weight the weight
2626 */
2627 public static Spec spec(int start, int size, Alignment alignment, float weight) {
2628 return new Spec(start != UNDEFINED, start, size, alignment, weight);
2629 }
2630
2631 /**
2632 * Equivalent to: {@code spec(start, 1, alignment, weight)}.
2633 *
2634 * @param start the start
2635 * @param alignment the alignment
2636 * @param weight the weight
2637 */
2638 public static Spec spec(int start, Alignment alignment, float weight) {
2639 return spec(start, 1, alignment, weight);
2640 }
2641
2642 /**
2643 * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
2644 * where {@code default_alignment} is specified in
2645 * {@link android.widget.GridLayout.LayoutParams}.
2646 *
2647 * @param start the start
2648 * @param size the size
2649 * @param weight the weight
2650 */
2651 public static Spec spec(int start, int size, float weight) {
2652 return spec(start, size, UNDEFINED_ALIGNMENT, weight);
2653 }
2654
2655 /**
2656 * Equivalent to: {@code spec(start, 1, weight)}.
2657 *
2658 * @param start the start
2659 * @param weight the weight
2660 */
2661 public static Spec spec(int start, float weight) {
2662 return spec(start, 1, weight);
2663 }
2664
2665 /**
2666 * Equivalent to: {@code spec(start, size, alignment, 0f)}.
2667 *
2668 * @param start the start
2669 * @param size the size
2670 * @param alignment the alignment
Philip Milne93cd6a62011-07-12 14:49:45 -07002671 */
2672 public static Spec spec(int start, int size, Alignment alignment) {
Philip Milne87260842014-05-22 13:56:03 -07002673 return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
Philip Milne93cd6a62011-07-12 14:49:45 -07002674 }
2675
2676 /**
2677 * Return a Spec, {@code spec}, where:
2678 * <ul>
2679 * <li> {@code spec.span = [start, start + 1]} </li>
2680 * <li> {@code spec.alignment = alignment} </li>
2681 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002682 * <p>
2683 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne93cd6a62011-07-12 14:49:45 -07002684 *
2685 * @param start the start index
2686 * @param alignment the alignment
Philip Milne7b757812012-09-19 18:13:44 -07002687 *
2688 * @see #spec(int, int, Alignment)
Philip Milne93cd6a62011-07-12 14:49:45 -07002689 */
2690 public static Spec spec(int start, Alignment alignment) {
2691 return spec(start, 1, alignment);
2692 }
2693
2694 /**
Philip Milne5125e212011-07-21 11:39:37 -07002695 * Return a Spec, {@code spec}, where:
2696 * <ul>
2697 * <li> {@code spec.span = [start, start + size]} </li>
2698 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002699 * <p>
2700 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne5125e212011-07-21 11:39:37 -07002701 *
2702 * @param start the start
2703 * @param size the size
Philip Milne7b757812012-09-19 18:13:44 -07002704 *
2705 * @see #spec(int, Alignment)
Philip Milne5125e212011-07-21 11:39:37 -07002706 */
2707 public static Spec spec(int start, int size) {
2708 return spec(start, size, UNDEFINED_ALIGNMENT);
2709 }
2710
2711 /**
2712 * Return a Spec, {@code spec}, where:
2713 * <ul>
2714 * <li> {@code spec.span = [start, start + 1]} </li>
2715 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002716 * <p>
2717 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne5125e212011-07-21 11:39:37 -07002718 *
2719 * @param start the start index
Philip Milne7b757812012-09-19 18:13:44 -07002720 *
2721 * @see #spec(int, int)
Philip Milne5125e212011-07-21 11:39:37 -07002722 */
2723 public static Spec spec(int start) {
2724 return spec(start, 1);
2725 }
2726
2727 /**
Philip Milne3f8956d2011-05-13 17:29:00 +01002728 * Alignments specify where a view should be placed within a cell group and
2729 * what size it should be.
2730 * <p>
Philip Milne93cd6a62011-07-12 14:49:45 -07002731 * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
2732 * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
2733 * {@code alignment}. Overall placement of the view in the cell
Philip Milne3f8956d2011-05-13 17:29:00 +01002734 * group is specified by the two alignments which act along each axis independently.
2735 * <p>
Philip Milnea1f7b102011-06-23 11:10:13 -07002736 * The GridLayout class defines the most common alignments used in general layout:
Philip Milne6216e872012-02-16 17:15:50 -08002737 * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
2738 * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
Philip Milnea1f7b102011-06-23 11:10:13 -07002739 */
2740 /*
Philip Milnec9885f62011-06-15 17:07:35 -07002741 * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
Philip Milne3f8956d2011-05-13 17:29:00 +01002742 * to return the appropriate value for the type of alignment being defined.
2743 * The enclosing algorithms position the children
Philip Milne1e548252011-06-16 19:02:33 -07002744 * so that the locations defined by the alignment values
Philip Milne3f8956d2011-05-13 17:29:00 +01002745 * are the same for all of the views in a group.
2746 * <p>
Philip Milne3f8956d2011-05-13 17:29:00 +01002747 */
Philip Milnec9885f62011-06-15 17:07:35 -07002748 public static abstract class Alignment {
Philip Milne48b55242011-06-29 11:09:45 -07002749 Alignment() {
Philip Milnea1f7b102011-06-23 11:10:13 -07002750 }
2751
Philip Milne6216e872012-02-16 17:15:50 -08002752 abstract int getGravityOffset(View view, int cellDelta);
2753
Philip Milne3f8956d2011-05-13 17:29:00 +01002754 /**
2755 * Returns an alignment value. In the case of vertical alignments the value
2756 * returned should indicate the distance from the top of the view to the
2757 * alignment location.
2758 * For horizontal alignments measurement is made from the left edge of the component.
2759 *
Philip Milnec9885f62011-06-15 17:07:35 -07002760 * @param view the view to which this alignment should be applied
2761 * @param viewSize the measured size of the view
Philip Milne7a23b492012-04-24 22:12:36 -07002762 * @param mode the basis of alignment: CLIP or OPTICAL
Philip Milneb3a8c542011-06-20 16:02:59 -07002763 * @return the alignment value
Philip Milne3f8956d2011-05-13 17:29:00 +01002764 */
Philip Milne7a23b492012-04-24 22:12:36 -07002765 abstract int getAlignmentValue(View view, int viewSize, int mode);
Philip Milne3f8956d2011-05-13 17:29:00 +01002766
2767 /**
2768 * Returns the size of the view specified by this alignment.
2769 * In the case of vertical alignments this method should return a height; for
2770 * horizontal alignments this method should return the width.
Philip Milnec9885f62011-06-15 17:07:35 -07002771 * <p>
2772 * The default implementation returns {@code viewSize}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002773 *
Philip Milnec9885f62011-06-15 17:07:35 -07002774 * @param view the view to which this alignment should be applied
2775 * @param viewSize the measured size of the view
2776 * @param cellSize the size of the cell into which this view will be placed
Philip Milneb3a8c542011-06-20 16:02:59 -07002777 * @return the aligned size
Philip Milne3f8956d2011-05-13 17:29:00 +01002778 */
Philip Milne6216e872012-02-16 17:15:50 -08002779 int getSizeInCell(View view, int viewSize, int cellSize) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002780 return viewSize;
2781 }
Philip Milnea1f7b102011-06-23 11:10:13 -07002782
Philip Milne48b55242011-06-29 11:09:45 -07002783 Bounds getBounds() {
Philip Milnea1f7b102011-06-23 11:10:13 -07002784 return new Bounds();
2785 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002786 }
2787
Philip Milnef6679c82011-09-18 11:36:57 -07002788 static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002789 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002790 int getGravityOffset(View view, int cellDelta) {
2791 return UNDEFINED;
2792 }
2793
2794 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002795 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne5125e212011-07-21 11:39:37 -07002796 return UNDEFINED;
2797 }
2798 };
2799
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002800 /**
2801 * Indicates that a view should be aligned with the <em>start</em>
2802 * edges of the other views in its cell group.
2803 */
Philip Milnec9885f62011-06-15 17:07:35 -07002804 private static final Alignment LEADING = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002805 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002806 int getGravityOffset(View view, int cellDelta) {
2807 return 0;
2808 }
2809
2810 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002811 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002812 return 0;
2813 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002814 };
2815
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002816 /**
2817 * Indicates that a view should be aligned with the <em>end</em>
2818 * edges of the other views in its cell group.
2819 */
Philip Milnec9885f62011-06-15 17:07:35 -07002820 private static final Alignment TRAILING = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002821 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002822 int getGravityOffset(View view, int cellDelta) {
2823 return cellDelta;
2824 }
2825
2826 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002827 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002828 return viewSize;
2829 }
2830 };
2831
2832 /**
2833 * Indicates that a view should be aligned with the <em>top</em>
2834 * edges of the other views in its cell group.
2835 */
2836 public static final Alignment TOP = LEADING;
2837
2838 /**
2839 * Indicates that a view should be aligned with the <em>bottom</em>
2840 * edges of the other views in its cell group.
2841 */
2842 public static final Alignment BOTTOM = TRAILING;
2843
2844 /**
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002845 * Indicates that a view should be aligned with the <em>start</em>
Philip Milne3f8956d2011-05-13 17:29:00 +01002846 * edges of the other views in its cell group.
2847 */
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002848 public static final Alignment START = LEADING;
2849
2850 /**
2851 * Indicates that a view should be aligned with the <em>end</em>
2852 * edges of the other views in its cell group.
2853 */
2854 public static final Alignment END = TRAILING;
2855
Philip Milne6216e872012-02-16 17:15:50 -08002856 private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002857 return new Alignment() {
2858 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002859 int getGravityOffset(View view, int cellDelta) {
2860 return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
2861 }
2862
2863 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002864 public int getAlignmentValue(View view, int viewSize, int mode) {
2865 return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002866 }
2867 };
2868 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002869
2870 /**
2871 * Indicates that a view should be aligned with the <em>left</em>
2872 * edges of the other views in its cell group.
2873 */
Philip Milne6216e872012-02-16 17:15:50 -08002874 public static final Alignment LEFT = createSwitchingAlignment(START, END);
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002875
2876 /**
2877 * Indicates that a view should be aligned with the <em>right</em>
2878 * edges of the other views in its cell group.
2879 */
Philip Milne6216e872012-02-16 17:15:50 -08002880 public static final Alignment RIGHT = createSwitchingAlignment(END, START);
Philip Milne3f8956d2011-05-13 17:29:00 +01002881
2882 /**
2883 * Indicates that a view should be <em>centered</em> with the other views in its cell group.
Philip Milne93cd6a62011-07-12 14:49:45 -07002884 * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
2885 * LayoutParams#columnSpec columnSpecs}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002886 */
Philip Milnec9885f62011-06-15 17:07:35 -07002887 public static final Alignment CENTER = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002888 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002889 int getGravityOffset(View view, int cellDelta) {
2890 return cellDelta >> 1;
2891 }
2892
2893 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002894 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002895 return viewSize >> 1;
2896 }
2897 };
2898
2899 /**
2900 * Indicates that a view should be aligned with the <em>baselines</em>
2901 * of the other views in its cell group.
Philip Milne93cd6a62011-07-12 14:49:45 -07002902 * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002903 *
2904 * @see View#getBaseline()
2905 */
Philip Milnec9885f62011-06-15 17:07:35 -07002906 public static final Alignment BASELINE = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002907 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002908 int getGravityOffset(View view, int cellDelta) {
2909 return 0; // baseline gravity is top
2910 }
2911
2912 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002913 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milnea8416442013-02-25 10:49:39 -08002914 if (view.getVisibility() == GONE) {
2915 return 0;
2916 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002917 int baseline = view.getBaseline();
Philip Milne7b757812012-09-19 18:13:44 -07002918 return baseline == -1 ? UNDEFINED : baseline;
Philip Milnea1f7b102011-06-23 11:10:13 -07002919 }
2920
2921 @Override
2922 public Bounds getBounds() {
2923 return new Bounds() {
2924 /*
2925 In a baseline aligned row in which some components define a baseline
2926 and some don't, we need a third variable to properly account for all
2927 the sizes. This tracks the maximum size of all the components -
2928 including those that don't define a baseline.
2929 */
2930 private int size;
2931
2932 @Override
2933 protected void reset() {
2934 super.reset();
Philip Milne48b55242011-06-29 11:09:45 -07002935 size = Integer.MIN_VALUE;
Philip Milnea1f7b102011-06-23 11:10:13 -07002936 }
2937
2938 @Override
2939 protected void include(int before, int after) {
2940 super.include(before, after);
2941 size = max(size, before + after);
2942 }
2943
2944 @Override
Philip Milne48b55242011-06-29 11:09:45 -07002945 protected int size(boolean min) {
2946 return max(super.size(min), size);
Philip Milnea1f7b102011-06-23 11:10:13 -07002947 }
2948
2949 @Override
Philip Milne1557fd72012-04-04 23:41:34 -07002950 protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
2951 return max(0, super.getOffset(gl, c, a, size, hrz));
Philip Milnea1f7b102011-06-23 11:10:13 -07002952 }
2953 };
Philip Milne3f8956d2011-05-13 17:29:00 +01002954 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002955 };
2956
2957 /**
2958 * Indicates that a view should expanded to fit the boundaries of its cell group.
Philip Milne93cd6a62011-07-12 14:49:45 -07002959 * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
2960 * {@link LayoutParams#columnSpec columnSpecs}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002961 */
2962 public static final Alignment FILL = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002963 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002964 int getGravityOffset(View view, int cellDelta) {
2965 return 0;
2966 }
2967
2968 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002969 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002970 return UNDEFINED;
2971 }
2972
Philip Milnec9885f62011-06-15 17:07:35 -07002973 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002974 public int getSizeInCell(View view, int viewSize, int cellSize) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002975 return cellSize;
2976 }
2977 };
Philip Milne48b55242011-06-29 11:09:45 -07002978
Philip Milnef6679c82011-09-18 11:36:57 -07002979 static boolean canStretch(int flexibility) {
Philip Milne5d1a9842011-07-07 11:47:08 -07002980 return (flexibility & CAN_STRETCH) != 0;
2981 }
2982
Philip Milne5125e212011-07-21 11:39:37 -07002983 private static final int INFLEXIBLE = 0;
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002984 private static final int CAN_STRETCH = 2;
Jim Miller452eec32011-06-16 18:32:44 -07002985}