blob: 1c15c7ae798739df0ca537633cdaf3b66eb35f53 [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 /**
902 * @hide
903 */
904 @Override
Philip Milne7b757812012-09-19 18:13:44 -0700905 protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
Philip Milne10ca24a2012-04-23 15:38:27 -0700906 // Apply defaults, so as to remove UNDEFINED values
907 LayoutParams lp = new LayoutParams();
908 for (int i = 0; i < getChildCount(); i++) {
909 View c = getChildAt(i);
910 lp.setMargins(
Philip Milne7b757812012-09-19 18:13:44 -0700911 getMargin1(c, true, true),
912 getMargin1(c, false, true),
913 getMargin1(c, true, false),
914 getMargin1(c, false, false));
915 lp.onDebugDraw(c, canvas, paint);
Philip Milne10ca24a2012-04-23 15:38:27 -0700916 }
Philip Milneb5599762011-08-05 11:04:36 -0700917 }
918
Philip Milne10ca24a2012-04-23 15:38:27 -0700919 /**
920 * @hide
921 */
Philip Milne3f8956d2011-05-13 17:29:00 +0100922 @Override
Philip Milne10ca24a2012-04-23 15:38:27 -0700923 protected void onDebugDraw(Canvas canvas) {
Philip Milne10ca24a2012-04-23 15:38:27 -0700924 Paint paint = new Paint();
925 paint.setStyle(Paint.Style.STROKE);
926 paint.setColor(Color.argb(50, 255, 255, 255));
Philip Milne3f8956d2011-05-13 17:29:00 +0100927
Philip Milne7b757812012-09-19 18:13:44 -0700928 Insets insets = getOpticalInsets();
929
930 int top = getPaddingTop() + insets.top;
931 int left = getPaddingLeft() + insets.left;
932 int right = getWidth() - getPaddingRight() - insets.right;
933 int bottom = getHeight() - getPaddingBottom() - insets.bottom;
934
Adam Powell465ea742013-08-29 14:56:51 -0700935 int[] xs = mHorizontalAxis.locations;
Philip Milne10ca24a2012-04-23 15:38:27 -0700936 if (xs != null) {
937 for (int i = 0, length = xs.length; i < length; i++) {
Philip Milne7b757812012-09-19 18:13:44 -0700938 int x = left + xs[i];
939 drawLine(canvas, x, top, x, bottom, paint);
Philip Milne3f8956d2011-05-13 17:29:00 +0100940 }
941 }
Philip Milne10ca24a2012-04-23 15:38:27 -0700942
Adam Powell465ea742013-08-29 14:56:51 -0700943 int[] ys = mVerticalAxis.locations;
Philip Milne10ca24a2012-04-23 15:38:27 -0700944 if (ys != null) {
945 for (int i = 0, length = ys.length; i < length; i++) {
Philip Milne7b757812012-09-19 18:13:44 -0700946 int y = top + ys[i];
947 drawLine(canvas, left, y, right, y, paint);
Philip Milne10ca24a2012-04-23 15:38:27 -0700948 }
949 }
950
951 super.onDebugDraw(canvas);
Philip Milne3f8956d2011-05-13 17:29:00 +0100952 }
953
Philip Milne3f8956d2011-05-13 17:29:00 +0100954 @Override
Adam Powell6690d012015-06-17 16:41:56 -0700955 public void onViewAdded(View child) {
Philip Milnef51d91c2011-07-18 16:12:19 -0700956 super.onViewAdded(child);
Philip Milne3f8956d2011-05-13 17:29:00 +0100957 invalidateStructure();
958 }
959
960 @Override
Adam Powell6690d012015-06-17 16:41:56 -0700961 public void onViewRemoved(View child) {
Philip Milnef51d91c2011-07-18 16:12:19 -0700962 super.onViewRemoved(child);
Philip Milneb0ce49b2011-07-15 15:39:07 -0700963 invalidateStructure();
964 }
965
Philip Milne350f0a62011-07-19 14:00:56 -0700966 /**
967 * We need to call invalidateStructure() when a child's GONE flag changes state.
968 * This implementation is a catch-all, invalidating on any change in the visibility flags.
969 *
970 * @hide
971 */
972 @Override
Chet Haase0d299362012-01-26 10:51:48 -0800973 protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
974 super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
975 if (oldVisibility == GONE || newVisibility == GONE) {
Philip Milnea8416442013-02-25 10:49:39 -0800976 invalidateStructure();
Chet Haase0d299362012-01-26 10:51:48 -0800977 }
Philip Milne350f0a62011-07-19 14:00:56 -0700978 }
979
Philip Milned7dd8902012-01-26 16:55:30 -0800980 private int computeLayoutParamsHashCode() {
981 int result = 1;
982 for (int i = 0, N = getChildCount(); i < N; i++) {
983 View c = getChildAt(i);
984 if (c.getVisibility() == View.GONE) continue;
985 LayoutParams lp = (LayoutParams) c.getLayoutParams();
986 result = 31 * result + lp.hashCode();
987 }
988 return result;
Philip Milneb3a8c542011-06-20 16:02:59 -0700989 }
990
Philip Milneedd69512012-03-14 17:21:33 -0700991 private void consistencyCheck() {
Adam Powell465ea742013-08-29 14:56:51 -0700992 if (mLastLayoutParamsHashCode == UNINITIALIZED_HASH) {
Philip Milneedd69512012-03-14 17:21:33 -0700993 validateLayoutParams();
Adam Powell465ea742013-08-29 14:56:51 -0700994 mLastLayoutParamsHashCode = computeLayoutParamsHashCode();
995 } else if (mLastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
996 mPrinter.println("The fields of some layout parameters were modified in between "
Philip Milne211d0332012-04-24 14:45:14 -0700997 + "layout operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
Philip Milneedd69512012-03-14 17:21:33 -0700998 invalidateStructure();
999 consistencyCheck();
Philip Milned7dd8902012-01-26 16:55:30 -08001000 }
1001 }
1002
1003 // Measurement
1004
Philip Milnee0b85cd2013-04-12 14:38:34 -07001005 // Note: padding has already been removed from the supplied specs
Philip Milne4a145d72011-09-29 14:14:25 -07001006 private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
Philip Milneedd69512012-03-14 17:21:33 -07001007 int childWidth, int childHeight) {
Philip Milne4a145d72011-09-29 14:14:25 -07001008 int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
Philip Milnee0b85cd2013-04-12 14:38:34 -07001009 getTotalMargin(child, true), childWidth);
Philip Milne4a145d72011-09-29 14:14:25 -07001010 int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
Philip Milnee0b85cd2013-04-12 14:38:34 -07001011 getTotalMargin(child, false), childHeight);
Philip Milne4a145d72011-09-29 14:14:25 -07001012 child.measure(childWidthSpec, childHeightSpec);
Philip Milneb3a8c542011-06-20 16:02:59 -07001013 }
1014
Philip Milnee0b85cd2013-04-12 14:38:34 -07001015 // Note: padding has already been removed from the supplied specs
Philip Milne4a145d72011-09-29 14:14:25 -07001016 private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
Philip Milneb3a8c542011-06-20 16:02:59 -07001017 for (int i = 0, N = getChildCount(); i < N; i++) {
1018 View c = getChildAt(i);
Philip Milned7dd8902012-01-26 16:55:30 -08001019 if (c.getVisibility() == View.GONE) continue;
Philip Milne4a145d72011-09-29 14:14:25 -07001020 LayoutParams lp = getLayoutParams(c);
1021 if (firstPass) {
1022 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
1023 } else {
Adam Powell465ea742013-08-29 14:56:51 -07001024 boolean horizontal = (mOrientation == HORIZONTAL);
Philip Milneecab1172011-10-25 15:07:19 -07001025 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Yigit Boyar6dafd872015-03-05 13:59:56 -08001026 if (spec.getAbsoluteAlignment(horizontal) == FILL) {
Philip Milne4a145d72011-09-29 14:14:25 -07001027 Interval span = spec.span;
Adam Powell465ea742013-08-29 14:56:51 -07001028 Axis axis = horizontal ? mHorizontalAxis : mVerticalAxis;
Philip Milne4a145d72011-09-29 14:14:25 -07001029 int[] locations = axis.getLocations();
Philip Milneecab1172011-10-25 15:07:19 -07001030 int cellSize = locations[span.max] - locations[span.min];
1031 int viewSize = cellSize - getTotalMargin(c, horizontal);
1032 if (horizontal) {
1033 measureChildWithMargins2(c, widthSpec, heightSpec, viewSize, lp.height);
Philip Milne4a145d72011-09-29 14:14:25 -07001034 } else {
Philip Milneecab1172011-10-25 15:07:19 -07001035 measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, viewSize);
Philip Milne4a145d72011-09-29 14:14:25 -07001036 }
1037 }
1038 }
Philip Milneb3a8c542011-06-20 16:02:59 -07001039 }
1040 }
1041
Philip Milnee0b85cd2013-04-12 14:38:34 -07001042 static int adjust(int measureSpec, int delta) {
1043 return makeMeasureSpec(
1044 MeasureSpec.getSize(measureSpec + delta), MeasureSpec.getMode(measureSpec));
1045 }
1046
Philip Milne3f8956d2011-05-13 17:29:00 +01001047 @Override
1048 protected void onMeasure(int widthSpec, int heightSpec) {
Philip Milneedd69512012-03-14 17:21:33 -07001049 consistencyCheck();
Philip Milned7dd8902012-01-26 16:55:30 -08001050
Philip Milne4a145d72011-09-29 14:14:25 -07001051 /** If we have been called by {@link View#measure(int, int)}, one of width or height
1052 * is likely to have changed. We must invalidate if so. */
1053 invalidateValues();
Philip Milne3f8956d2011-05-13 17:29:00 +01001054
Philip Milnee0b85cd2013-04-12 14:38:34 -07001055 int hPadding = getPaddingLeft() + getPaddingRight();
1056 int vPadding = getPaddingTop() + getPaddingBottom();
Philip Milne3f8956d2011-05-13 17:29:00 +01001057
Philip Milnee0b85cd2013-04-12 14:38:34 -07001058 int widthSpecSansPadding = adjust( widthSpec, -hPadding);
1059 int heightSpecSansPadding = adjust(heightSpec, -vPadding);
1060
1061 measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, true);
1062
1063 int widthSansPadding;
1064 int heightSansPadding;
Philip Milne4a145d72011-09-29 14:14:25 -07001065
1066 // Use the orientation property to decide which axis should be laid out first.
Adam Powell465ea742013-08-29 14:56:51 -07001067 if (mOrientation == HORIZONTAL) {
1068 widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
Philip Milnee0b85cd2013-04-12 14:38:34 -07001069 measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
Adam Powell465ea742013-08-29 14:56:51 -07001070 heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
Philip Milne4a145d72011-09-29 14:14:25 -07001071 } else {
Adam Powell465ea742013-08-29 14:56:51 -07001072 heightSansPadding = mVerticalAxis.getMeasure(heightSpecSansPadding);
Philip Milnee0b85cd2013-04-12 14:38:34 -07001073 measureChildrenWithMargins(widthSpecSansPadding, heightSpecSansPadding, false);
Adam Powell465ea742013-08-29 14:56:51 -07001074 widthSansPadding = mHorizontalAxis.getMeasure(widthSpecSansPadding);
Philip Milne4a145d72011-09-29 14:14:25 -07001075 }
1076
Philip Milnee0b85cd2013-04-12 14:38:34 -07001077 int measuredWidth = Math.max(widthSansPadding + hPadding, getSuggestedMinimumWidth());
1078 int measuredHeight = Math.max(heightSansPadding + vPadding, getSuggestedMinimumHeight());
Philip Milne09e2d4d2011-06-20 10:28:18 -07001079
Philip Milne3f8956d2011-05-13 17:29:00 +01001080 setMeasuredDimension(
Philip Milnee0b85cd2013-04-12 14:38:34 -07001081 resolveSizeAndState(measuredWidth, widthSpec, 0),
Philip Milne09e2d4d2011-06-20 10:28:18 -07001082 resolveSizeAndState(measuredHeight, heightSpec, 0));
Philip Milne3f8956d2011-05-13 17:29:00 +01001083 }
1084
Philip Milne48b55242011-06-29 11:09:45 -07001085 private int getMeasurement(View c, boolean horizontal) {
Philip Milne7b757812012-09-19 18:13:44 -07001086 return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
Philip Milneaa616f32011-05-27 18:38:01 -07001087 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001088
Philip Milnef6679c82011-09-18 11:36:57 -07001089 final int getMeasurementIncludingMargin(View c, boolean horizontal) {
Philip Milned7dd8902012-01-26 16:55:30 -08001090 if (c.getVisibility() == View.GONE) {
Philip Milne5125e212011-07-21 11:39:37 -07001091 return 0;
1092 }
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001093 return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
Philip Milne3f8956d2011-05-13 17:29:00 +01001094 }
1095
Philip Milne3f8956d2011-05-13 17:29:00 +01001096 @Override
Philip Milneaa616f32011-05-27 18:38:01 -07001097 public void requestLayout() {
1098 super.requestLayout();
Philip Milne3f8956d2011-05-13 17:29:00 +01001099 invalidateValues();
Philip Milneaa616f32011-05-27 18:38:01 -07001100 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001101
Philip Milneaa616f32011-05-27 18:38:01 -07001102 // Layout container
1103
1104 /**
1105 * {@inheritDoc}
1106 */
1107 /*
1108 The layout operation is implemented by delegating the heavy lifting to the
1109 to the mHorizontalAxis and mVerticalAxis instances of the internal Axis class.
1110 Together they compute the locations of the vertical and horizontal lines of
1111 the grid (respectively!).
1112
1113 This method is then left with the simpler task of applying margins, gravity
1114 and sizing to each child view and then placing it in its cell.
1115 */
1116 @Override
Philip Milne09e2d4d2011-06-20 10:28:18 -07001117 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Philip Milneedd69512012-03-14 17:21:33 -07001118 consistencyCheck();
Philip Milned7dd8902012-01-26 16:55:30 -08001119
Philip Milne09e2d4d2011-06-20 10:28:18 -07001120 int targetWidth = right - left;
1121 int targetHeight = bottom - top;
Philip Milne3f8956d2011-05-13 17:29:00 +01001122
1123 int paddingLeft = getPaddingLeft();
1124 int paddingTop = getPaddingTop();
1125 int paddingRight = getPaddingRight();
1126 int paddingBottom = getPaddingBottom();
1127
Adam Powell465ea742013-08-29 14:56:51 -07001128 mHorizontalAxis.layout(targetWidth - paddingLeft - paddingRight);
1129 mVerticalAxis.layout(targetHeight - paddingTop - paddingBottom);
Philip Milne3f8956d2011-05-13 17:29:00 +01001130
Adam Powell465ea742013-08-29 14:56:51 -07001131 int[] hLocations = mHorizontalAxis.getLocations();
1132 int[] vLocations = mVerticalAxis.getLocations();
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001133
Philip Milneb3a8c542011-06-20 16:02:59 -07001134 for (int i = 0, N = getChildCount(); i < N; i++) {
1135 View c = getChildAt(i);
Philip Milned7dd8902012-01-26 16:55:30 -08001136 if (c.getVisibility() == View.GONE) continue;
Philip Milneb3a8c542011-06-20 16:02:59 -07001137 LayoutParams lp = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001138 Spec columnSpec = lp.columnSpec;
1139 Spec rowSpec = lp.rowSpec;
Philip Milne3f8956d2011-05-13 17:29:00 +01001140
Philip Milne93cd6a62011-07-12 14:49:45 -07001141 Interval colSpan = columnSpec.span;
1142 Interval rowSpan = rowSpec.span;
Philip Milne3f8956d2011-05-13 17:29:00 +01001143
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001144 int x1 = hLocations[colSpan.min];
1145 int y1 = vLocations[rowSpan.min];
Philip Milneaa616f32011-05-27 18:38:01 -07001146
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001147 int x2 = hLocations[colSpan.max];
1148 int y2 = vLocations[rowSpan.max];
Philip Milne3f8956d2011-05-13 17:29:00 +01001149
1150 int cellWidth = x2 - x1;
1151 int cellHeight = y2 - y1;
1152
Philip Milne48b55242011-06-29 11:09:45 -07001153 int pWidth = getMeasurement(c, true);
1154 int pHeight = getMeasurement(c, false);
Philip Milne3f8956d2011-05-13 17:29:00 +01001155
Yigit Boyar6dafd872015-03-05 13:59:56 -08001156 Alignment hAlign = columnSpec.getAbsoluteAlignment(true);
1157 Alignment vAlign = rowSpec.getAbsoluteAlignment(false);
Philip Milne3f8956d2011-05-13 17:29:00 +01001158
Adam Powell465ea742013-08-29 14:56:51 -07001159 Bounds boundsX = mHorizontalAxis.getGroupBounds().getValue(i);
1160 Bounds boundsY = mVerticalAxis.getGroupBounds().getValue(i);
Philip Milne7fd94872011-06-07 20:14:17 -07001161
1162 // Gravity offsets: the location of the alignment group relative to its cell group.
Philip Milne6216e872012-02-16 17:15:50 -08001163 int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true));
1164 int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true));
Philip Milne7fd94872011-06-07 20:14:17 -07001165
Philip Milne6216e872012-02-16 17:15:50 -08001166 int leftMargin = getMargin(c, true, true);
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001167 int topMargin = getMargin(c, false, true);
Philip Milne6216e872012-02-16 17:15:50 -08001168 int rightMargin = getMargin(c, true, false);
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001169 int bottomMargin = getMargin(c, false, false);
Philip Milne7fd94872011-06-07 20:14:17 -07001170
Philip Milne1557fd72012-04-04 23:41:34 -07001171 int sumMarginsX = leftMargin + rightMargin;
1172 int sumMarginsY = topMargin + bottomMargin;
Philip Milne7fd94872011-06-07 20:14:17 -07001173
Philip Milne1557fd72012-04-04 23:41:34 -07001174 // Alignment offsets: the location of the view relative to its alignment group.
1175 int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true);
1176 int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false);
1177
1178 int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX);
1179 int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY);
Philip Milne7fd94872011-06-07 20:14:17 -07001180
Philip Milne6216e872012-02-16 17:15:50 -08001181 int dx = x1 + gravityOffsetX + alignmentOffsetX;
Philip Milne3f8956d2011-05-13 17:29:00 +01001182
Philip Milne6216e872012-02-16 17:15:50 -08001183 int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx :
1184 targetWidth - width - paddingRight - rightMargin - dx;
1185 int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
Philip Milne3f8956d2011-05-13 17:29:00 +01001186
Philip Milne899d5922011-07-21 11:39:37 -07001187 if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
1188 c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
1189 }
Philip Milneb3a8c542011-06-20 16:02:59 -07001190 c.layout(cx, cy, cx + width, cy + height);
Philip Milne3f8956d2011-05-13 17:29:00 +01001191 }
1192 }
1193
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001194 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -08001195 public CharSequence getAccessibilityClassName() {
1196 return GridLayout.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001197 }
1198
Philip Milne3f8956d2011-05-13 17:29:00 +01001199 // Inner classes
1200
Philip Milneaa616f32011-05-27 18:38:01 -07001201 /*
1202 This internal class houses the algorithm for computing the locations of grid lines;
1203 along either the horizontal or vertical axis. A GridLayout uses two instances of this class -
1204 distinguished by the "horizontal" flag which is true for the horizontal axis and false
1205 for the vertical one.
1206 */
Philip Milnef6679c82011-09-18 11:36:57 -07001207 final class Axis {
Philip Milne48b55242011-06-29 11:09:45 -07001208 private static final int NEW = 0;
Philip Milne3f8956d2011-05-13 17:29:00 +01001209 private static final int PENDING = 1;
1210 private static final int COMPLETE = 2;
1211
1212 public final boolean horizontal;
1213
Philip Milnef6679c82011-09-18 11:36:57 -07001214 public int definedCount = UNDEFINED;
Philip Milne4a145d72011-09-29 14:14:25 -07001215 private int maxIndex = UNDEFINED;
Philip Milne3f8956d2011-05-13 17:29:00 +01001216
Philip Milne93cd6a62011-07-12 14:49:45 -07001217 PackedMap<Spec, Bounds> groupBounds;
Philip Milne3f8956d2011-05-13 17:29:00 +01001218 public boolean groupBoundsValid = false;
1219
Philip Milne48b55242011-06-29 11:09:45 -07001220 PackedMap<Interval, MutableInt> forwardLinks;
1221 public boolean forwardLinksValid = false;
1222
1223 PackedMap<Interval, MutableInt> backwardLinks;
1224 public boolean backwardLinksValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001225
Philip Milne3f8956d2011-05-13 17:29:00 +01001226 public int[] leadingMargins;
Philip Milneaa616f32011-05-27 18:38:01 -07001227 public boolean leadingMarginsValid = false;
1228
Philip Milne3f8956d2011-05-13 17:29:00 +01001229 public int[] trailingMargins;
Philip Milneaa616f32011-05-27 18:38:01 -07001230 public boolean trailingMarginsValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001231
1232 public Arc[] arcs;
1233 public boolean arcsValid = false;
1234
Philip Milneaa616f32011-05-27 18:38:01 -07001235 public int[] locations;
Philip Milne48b55242011-06-29 11:09:45 -07001236 public boolean locationsValid = false;
Philip Milneaa616f32011-05-27 18:38:01 -07001237
Philip Milne95054802014-05-23 19:50:40 -07001238 public boolean hasWeights;
1239 public boolean hasWeightsValid = false;
Philip Milne95054802014-05-23 19:50:40 -07001240 public int[] deltas;
1241
Philip Milnef6679c82011-09-18 11:36:57 -07001242 boolean orderPreserved = DEFAULT_ORDER_PRESERVED;
Philip Milne3f8956d2011-05-13 17:29:00 +01001243
Philip Milne48b55242011-06-29 11:09:45 -07001244 private MutableInt parentMin = new MutableInt(0);
1245 private MutableInt parentMax = new MutableInt(-MAX_SIZE);
1246
Philip Milne3f8956d2011-05-13 17:29:00 +01001247 private Axis(boolean horizontal) {
1248 this.horizontal = horizontal;
1249 }
1250
Philip Milne4a145d72011-09-29 14:14:25 -07001251 private int calculateMaxIndex() {
1252 // the number Integer.MIN_VALUE + 1 comes up in undefined cells
1253 int result = -1;
Philip Milneb3a8c542011-06-20 16:02:59 -07001254 for (int i = 0, N = getChildCount(); i < N; i++) {
1255 View c = getChildAt(i);
Philip Milneb3a8c542011-06-20 16:02:59 -07001256 LayoutParams params = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001257 Spec spec = horizontal ? params.columnSpec : params.rowSpec;
Philip Milne4a145d72011-09-29 14:14:25 -07001258 Interval span = spec.span;
1259 result = max(result, span.min);
1260 result = max(result, span.max);
Philip Milne0f57cea2012-05-12 09:34:25 -07001261 result = max(result, span.size());
Philip Milne3f8956d2011-05-13 17:29:00 +01001262 }
Philip Milne4a145d72011-09-29 14:14:25 -07001263 return result == -1 ? UNDEFINED : result;
Philip Milne3f8956d2011-05-13 17:29:00 +01001264 }
1265
Philip Milne4a145d72011-09-29 14:14:25 -07001266 private int getMaxIndex() {
1267 if (maxIndex == UNDEFINED) {
1268 maxIndex = max(0, calculateMaxIndex()); // use zero when there are no children
Philip Milne3f8956d2011-05-13 17:29:00 +01001269 }
Philip Milne4a145d72011-09-29 14:14:25 -07001270 return maxIndex;
Philip Milnef6679c82011-09-18 11:36:57 -07001271 }
1272
1273 public int getCount() {
Philip Milne4a145d72011-09-29 14:14:25 -07001274 return max(definedCount, getMaxIndex());
Philip Milne3f8956d2011-05-13 17:29:00 +01001275 }
1276
1277 public void setCount(int count) {
Philip Milne0f57cea2012-05-12 09:34:25 -07001278 if (count != UNDEFINED && count < getMaxIndex()) {
1279 handleInvalidParams((horizontal ? "column" : "row") +
1280 "Count must be greater than or equal to the maximum of all grid indices " +
1281 "(and spans) defined in the LayoutParams of each child");
1282 }
Philip Milnef6679c82011-09-18 11:36:57 -07001283 this.definedCount = count;
Philip Milne3f8956d2011-05-13 17:29:00 +01001284 }
1285
1286 public boolean isOrderPreserved() {
Philip Milnef6679c82011-09-18 11:36:57 -07001287 return orderPreserved;
Philip Milne3f8956d2011-05-13 17:29:00 +01001288 }
1289
1290 public void setOrderPreserved(boolean orderPreserved) {
Philip Milnef6679c82011-09-18 11:36:57 -07001291 this.orderPreserved = orderPreserved;
Philip Milne3f8956d2011-05-13 17:29:00 +01001292 invalidateStructure();
1293 }
1294
Philip Milne93cd6a62011-07-12 14:49:45 -07001295 private PackedMap<Spec, Bounds> createGroupBounds() {
1296 Assoc<Spec, Bounds> assoc = Assoc.of(Spec.class, Bounds.class);
Philip Milne48b55242011-06-29 11:09:45 -07001297 for (int i = 0, N = getChildCount(); i < N; i++) {
Philip Milneb3a8c542011-06-20 16:02:59 -07001298 View c = getChildAt(i);
Philip Milnea8416442013-02-25 10:49:39 -08001299 // we must include views that are GONE here, see introductory javadoc
Philip Milne5125e212011-07-21 11:39:37 -07001300 LayoutParams lp = getLayoutParams(c);
1301 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Yigit Boyar6dafd872015-03-05 13:59:56 -08001302 Bounds bounds = spec.getAbsoluteAlignment(horizontal).getBounds();
Philip Milne5125e212011-07-21 11:39:37 -07001303 assoc.put(spec, bounds);
Philip Milne3f8956d2011-05-13 17:29:00 +01001304 }
Philip Milne48b55242011-06-29 11:09:45 -07001305 return assoc.pack();
Philip Milne3f8956d2011-05-13 17:29:00 +01001306 }
1307
1308 private void computeGroupBounds() {
Philip Milneb3a8c542011-06-20 16:02:59 -07001309 Bounds[] values = groupBounds.values;
1310 for (int i = 0; i < values.length; i++) {
1311 values[i].reset();
Philip Milne3f8956d2011-05-13 17:29:00 +01001312 }
Philip Milneaa616f32011-05-27 18:38:01 -07001313 for (int i = 0, N = getChildCount(); i < N; i++) {
Philip Milne3f8956d2011-05-13 17:29:00 +01001314 View c = getChildAt(i);
Philip Milnea8416442013-02-25 10:49:39 -08001315 // we must include views that are GONE here, see introductory javadoc
Philip Milne3f8956d2011-05-13 17:29:00 +01001316 LayoutParams lp = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001317 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Yigit Boyarfbd99972015-03-05 14:17:19 -08001318 int size = getMeasurementIncludingMargin(c, horizontal) +
1319 ((spec.weight == 0) ? 0 : getDeltas()[i]);
Philip Milne95054802014-05-23 19:50:40 -07001320 groupBounds.getValue(i).include(GridLayout.this, c, spec, this, size);
Philip Milne3f8956d2011-05-13 17:29:00 +01001321 }
1322 }
1323
Philip Milnef6679c82011-09-18 11:36:57 -07001324 public PackedMap<Spec, Bounds> getGroupBounds() {
Philip Milne3f8956d2011-05-13 17:29:00 +01001325 if (groupBounds == null) {
1326 groupBounds = createGroupBounds();
1327 }
1328 if (!groupBoundsValid) {
1329 computeGroupBounds();
1330 groupBoundsValid = true;
1331 }
1332 return groupBounds;
1333 }
1334
1335 // Add values computed by alignment - taking the max of all alignments in each span
Philip Milne48b55242011-06-29 11:09:45 -07001336 private PackedMap<Interval, MutableInt> createLinks(boolean min) {
1337 Assoc<Interval, MutableInt> result = Assoc.of(Interval.class, MutableInt.class);
Philip Milne93cd6a62011-07-12 14:49:45 -07001338 Spec[] keys = getGroupBounds().keys;
Philip Milne48b55242011-06-29 11:09:45 -07001339 for (int i = 0, N = keys.length; i < N; i++) {
1340 Interval span = min ? keys[i].span : keys[i].span.inverse();
1341 result.put(span, new MutableInt());
Philip Milne3f8956d2011-05-13 17:29:00 +01001342 }
Philip Milne48b55242011-06-29 11:09:45 -07001343 return result.pack();
Philip Milne3f8956d2011-05-13 17:29:00 +01001344 }
1345
Philip Milne48b55242011-06-29 11:09:45 -07001346 private void computeLinks(PackedMap<Interval, MutableInt> links, boolean min) {
1347 MutableInt[] spans = links.values;
Philip Milne3f8956d2011-05-13 17:29:00 +01001348 for (int i = 0; i < spans.length; i++) {
1349 spans[i].reset();
1350 }
1351
Philip Milne5d1a9842011-07-07 11:47:08 -07001352 // Use getter to trigger a re-evaluation
Philip Milne48b55242011-06-29 11:09:45 -07001353 Bounds[] bounds = getGroupBounds().values;
Philip Milne3f8956d2011-05-13 17:29:00 +01001354 for (int i = 0; i < bounds.length; i++) {
Philip Milne48b55242011-06-29 11:09:45 -07001355 int size = bounds[i].size(min);
Philip Milne48b55242011-06-29 11:09:45 -07001356 MutableInt valueHolder = links.getValue(i);
Philip Milne5125e212011-07-21 11:39:37 -07001357 // this effectively takes the max() of the minima and the min() of the maxima
1358 valueHolder.value = max(valueHolder.value, min ? size : -size);
Philip Milne3f8956d2011-05-13 17:29:00 +01001359 }
1360 }
1361
Philip Milne48b55242011-06-29 11:09:45 -07001362 private PackedMap<Interval, MutableInt> getForwardLinks() {
1363 if (forwardLinks == null) {
1364 forwardLinks = createLinks(true);
Philip Milne3f8956d2011-05-13 17:29:00 +01001365 }
Philip Milne48b55242011-06-29 11:09:45 -07001366 if (!forwardLinksValid) {
1367 computeLinks(forwardLinks, true);
1368 forwardLinksValid = true;
Philip Milne3f8956d2011-05-13 17:29:00 +01001369 }
Philip Milne48b55242011-06-29 11:09:45 -07001370 return forwardLinks;
Philip Milne3f8956d2011-05-13 17:29:00 +01001371 }
1372
Philip Milne48b55242011-06-29 11:09:45 -07001373 private PackedMap<Interval, MutableInt> getBackwardLinks() {
1374 if (backwardLinks == null) {
1375 backwardLinks = createLinks(false);
1376 }
1377 if (!backwardLinksValid) {
1378 computeLinks(backwardLinks, false);
1379 backwardLinksValid = true;
1380 }
1381 return backwardLinks;
1382 }
1383
1384 private void include(List<Arc> arcs, Interval key, MutableInt size,
Philip Milneedd69512012-03-14 17:21:33 -07001385 boolean ignoreIfAlreadyPresent) {
Philip Milne48b55242011-06-29 11:09:45 -07001386 /*
1387 Remove self referential links.
1388 These appear:
1389 . as parental constraints when GridLayout has no children
1390 . when components have been marked as GONE
1391 */
1392 if (key.size() == 0) {
1393 return;
1394 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001395 // this bit below should really be computed outside here -
Philip Milne48b55242011-06-29 11:09:45 -07001396 // its just to stop default (row/col > 0) constraints obliterating valid entries
1397 if (ignoreIfAlreadyPresent) {
1398 for (Arc arc : arcs) {
1399 Interval span = arc.span;
1400 if (span.equals(key)) {
1401 return;
1402 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001403 }
1404 }
1405 arcs.add(new Arc(key, size));
1406 }
1407
Philip Milne48b55242011-06-29 11:09:45 -07001408 private void include(List<Arc> arcs, Interval key, MutableInt size) {
1409 include(arcs, key, size, true);
Philip Milne3f8956d2011-05-13 17:29:00 +01001410 }
1411
Philip Milneaa616f32011-05-27 18:38:01 -07001412 // Group arcs by their first vertex, returning an array of arrays.
Philip Milne3f8956d2011-05-13 17:29:00 +01001413 // This is linear in the number of arcs.
Philip Milnef6679c82011-09-18 11:36:57 -07001414 Arc[][] groupArcsByFirstVertex(Arc[] arcs) {
Philip Milne48b55242011-06-29 11:09:45 -07001415 int N = getCount() + 1; // the number of vertices
Philip Milne3f8956d2011-05-13 17:29:00 +01001416 Arc[][] result = new Arc[N][];
1417 int[] sizes = new int[N];
1418 for (Arc arc : arcs) {
1419 sizes[arc.span.min]++;
Philip Milne5d1a9842011-07-07 11:47:08 -07001420 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001421 for (int i = 0; i < sizes.length; i++) {
1422 result[i] = new Arc[sizes[i]];
1423 }
1424 // reuse the sizes array to hold the current last elements as we insert each arc
1425 Arrays.fill(sizes, 0);
1426 for (Arc arc : arcs) {
1427 int i = arc.span.min;
1428 result[i][sizes[i]++] = arc;
1429 }
1430
1431 return result;
1432 }
1433
Philip Milne48b55242011-06-29 11:09:45 -07001434 private Arc[] topologicalSort(final Arc[] arcs) {
1435 return new Object() {
1436 Arc[] result = new Arc[arcs.length];
1437 int cursor = result.length - 1;
1438 Arc[][] arcsByVertex = groupArcsByFirstVertex(arcs);
Philip Milne3f8956d2011-05-13 17:29:00 +01001439 int[] visited = new int[getCount() + 1];
1440
Philip Milne48b55242011-06-29 11:09:45 -07001441 void walk(int loc) {
1442 switch (visited[loc]) {
1443 case NEW: {
1444 visited[loc] = PENDING;
1445 for (Arc arc : arcsByVertex[loc]) {
1446 walk(arc.span.max);
1447 result[cursor--] = arc;
Philip Milne3f8956d2011-05-13 17:29:00 +01001448 }
Philip Milne48b55242011-06-29 11:09:45 -07001449 visited[loc] = COMPLETE;
1450 break;
Philip Milne3f8956d2011-05-13 17:29:00 +01001451 }
Philip Milne48b55242011-06-29 11:09:45 -07001452 case PENDING: {
Philip Milneb65408f2012-05-21 10:44:46 -07001453 // le singe est dans l'arbre
Philip Milne48b55242011-06-29 11:09:45 -07001454 assert false;
1455 break;
1456 }
1457 case COMPLETE: {
1458 break;
1459 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001460 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001461 }
Philip Milne48b55242011-06-29 11:09:45 -07001462
1463 Arc[] sort() {
1464 for (int loc = 0, N = arcsByVertex.length; loc < N; loc++) {
1465 walk(loc);
1466 }
1467 assert cursor == -1;
1468 return result;
1469 }
1470 }.sort();
1471 }
1472
1473 private Arc[] topologicalSort(List<Arc> arcs) {
1474 return topologicalSort(arcs.toArray(new Arc[arcs.size()]));
Philip Milne3f8956d2011-05-13 17:29:00 +01001475 }
1476
Philip Milne48b55242011-06-29 11:09:45 -07001477 private void addComponentSizes(List<Arc> result, PackedMap<Interval, MutableInt> links) {
1478 for (int i = 0; i < links.keys.length; i++) {
1479 Interval key = links.keys[i];
1480 include(result, key, links.values[i], false);
Philip Milne3f8956d2011-05-13 17:29:00 +01001481 }
Philip Milne48b55242011-06-29 11:09:45 -07001482 }
1483
1484 private Arc[] createArcs() {
1485 List<Arc> mins = new ArrayList<Arc>();
1486 List<Arc> maxs = new ArrayList<Arc>();
1487
1488 // Add the minimum values from the components.
1489 addComponentSizes(mins, getForwardLinks());
1490 // Add the maximum values from the components.
1491 addComponentSizes(maxs, getBackwardLinks());
Philip Milne3f8956d2011-05-13 17:29:00 +01001492
Philip Milne48b55242011-06-29 11:09:45 -07001493 // Add ordering constraints to prevent row/col sizes from going negative
Philip Milnef6679c82011-09-18 11:36:57 -07001494 if (orderPreserved) {
Philip Milne48b55242011-06-29 11:09:45 -07001495 // Add a constraint for every row/col
Philip Milne3f8956d2011-05-13 17:29:00 +01001496 for (int i = 0; i < getCount(); i++) {
Philip Milne899d5922011-07-21 11:39:37 -07001497 include(mins, new Interval(i, i + 1), new MutableInt(0));
Philip Milne3f8956d2011-05-13 17:29:00 +01001498 }
1499 }
Philip Milne48b55242011-06-29 11:09:45 -07001500
1501 // Add the container constraints. Use the version of include that allows
1502 // duplicate entries in case a child spans the entire grid.
1503 int N = getCount();
1504 include(mins, new Interval(0, N), parentMin, false);
1505 include(maxs, new Interval(N, 0), parentMax, false);
1506
1507 // Sort
1508 Arc[] sMins = topologicalSort(mins);
1509 Arc[] sMaxs = topologicalSort(maxs);
1510
1511 return append(sMins, sMaxs);
1512 }
1513
1514 private void computeArcs() {
1515 // getting the links validates the values that are shared by the arc list
1516 getForwardLinks();
1517 getBackwardLinks();
Philip Milne3f8956d2011-05-13 17:29:00 +01001518 }
1519
Philip Milneaa616f32011-05-27 18:38:01 -07001520 public Arc[] getArcs() {
Philip Milne3f8956d2011-05-13 17:29:00 +01001521 if (arcs == null) {
Philip Milneaa616f32011-05-27 18:38:01 -07001522 arcs = createArcs();
Philip Milne3f8956d2011-05-13 17:29:00 +01001523 }
1524 if (!arcsValid) {
Philip Milne48b55242011-06-29 11:09:45 -07001525 computeArcs();
Philip Milne3f8956d2011-05-13 17:29:00 +01001526 arcsValid = true;
1527 }
1528 return arcs;
1529 }
1530
Philip Milneaa616f32011-05-27 18:38:01 -07001531 private boolean relax(int[] locations, Arc entry) {
Philip Milne48b55242011-06-29 11:09:45 -07001532 if (!entry.valid) {
1533 return false;
1534 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001535 Interval span = entry.span;
1536 int u = span.min;
1537 int v = span.max;
1538 int value = entry.value.value;
1539 int candidate = locations[u] + value;
Philip Milneaa616f32011-05-27 18:38:01 -07001540 if (candidate > locations[v]) {
Philip Milne3f8956d2011-05-13 17:29:00 +01001541 locations[v] = candidate;
1542 return true;
1543 }
1544 return false;
1545 }
1546
Philip Milnef6679c82011-09-18 11:36:57 -07001547 private void init(int[] locations) {
Philip Milne4a145d72011-09-29 14:14:25 -07001548 Arrays.fill(locations, 0);
Philip Milnef6679c82011-09-18 11:36:57 -07001549 }
1550
1551 private String arcsToString(List<Arc> arcs) {
Philip Milne4a145d72011-09-29 14:14:25 -07001552 String var = horizontal ? "x" : "y";
Philip Milnef6679c82011-09-18 11:36:57 -07001553 StringBuilder result = new StringBuilder();
Philip Milne4a145d72011-09-29 14:14:25 -07001554 boolean first = true;
1555 for (Arc arc : arcs) {
1556 if (first) {
1557 first = false;
Philip Milnef6679c82011-09-18 11:36:57 -07001558 } else {
Philip Milne4a145d72011-09-29 14:14:25 -07001559 result = result.append(", ");
Philip Milnef6679c82011-09-18 11:36:57 -07001560 }
1561 int src = arc.span.min;
1562 int dst = arc.span.max;
1563 int value = arc.value.value;
1564 result.append((src < dst) ?
Philip Milneedd69512012-03-14 17:21:33 -07001565 var + dst + "-" + var + src + ">=" + value :
1566 var + src + "-" + var + dst + "<=" + -value);
Philip Milnef6679c82011-09-18 11:36:57 -07001567
1568 }
1569 return result.toString();
1570 }
1571
1572 private void logError(String axisName, Arc[] arcs, boolean[] culprits0) {
1573 List<Arc> culprits = new ArrayList<Arc>();
1574 List<Arc> removed = new ArrayList<Arc>();
1575 for (int c = 0; c < arcs.length; c++) {
1576 Arc arc = arcs[c];
1577 if (culprits0[c]) {
1578 culprits.add(arc);
1579 }
1580 if (!arc.valid) {
1581 removed.add(arc);
1582 }
1583 }
Adam Powell465ea742013-08-29 14:56:51 -07001584 mPrinter.println(axisName + " constraints: " + arcsToString(culprits) +
Philip Milne211d0332012-04-24 14:45:14 -07001585 " are inconsistent; permanently removing: " + arcsToString(removed) + ". ");
Philip Milnef6679c82011-09-18 11:36:57 -07001586 }
1587
Philip Milneaa616f32011-05-27 18:38:01 -07001588 /*
1589 Bellman-Ford variant - modified to reduce typical running time from O(N^2) to O(N)
1590
1591 GridLayout converts its requirements into a system of linear constraints of the
1592 form:
1593
1594 x[i] - x[j] < a[k]
1595
1596 Where the x[i] are variables and the a[k] are constants.
1597
1598 For example, if the variables were instead labeled x, y, z we might have:
1599
1600 x - y < 17
1601 y - z < 23
1602 z - x < 42
1603
1604 This is a special case of the Linear Programming problem that is, in turn,
1605 equivalent to the single-source shortest paths problem on a digraph, for
1606 which the O(n^2) Bellman-Ford algorithm the most commonly used general solution.
Philip Milneaa616f32011-05-27 18:38:01 -07001607 */
Yigit Boyar98d5f042014-11-18 15:47:56 -08001608 private boolean solve(Arc[] arcs, int[] locations) {
1609 return solve(arcs, locations, true);
1610 }
1611
1612 private boolean solve(Arc[] arcs, int[] locations, boolean modifyOnError) {
Philip Milnef6679c82011-09-18 11:36:57 -07001613 String axisName = horizontal ? "horizontal" : "vertical";
Philip Milne3f8956d2011-05-13 17:29:00 +01001614 int N = getCount() + 1; // The number of vertices is the number of columns/rows + 1.
Philip Milnef6679c82011-09-18 11:36:57 -07001615 boolean[] originalCulprits = null;
Philip Milne3f8956d2011-05-13 17:29:00 +01001616
Philip Milnef6679c82011-09-18 11:36:57 -07001617 for (int p = 0; p < arcs.length; p++) {
1618 init(locations);
1619
1620 // We take one extra pass over traditional Bellman-Ford (and omit their final step)
1621 for (int i = 0; i < N; i++) {
1622 boolean changed = false;
1623 for (int j = 0, length = arcs.length; j < length; j++) {
1624 changed |= relax(locations, arcs[j]);
Philip Milne3f8956d2011-05-13 17:29:00 +01001625 }
Philip Milnef6679c82011-09-18 11:36:57 -07001626 if (!changed) {
1627 if (originalCulprits != null) {
1628 logError(axisName, arcs, originalCulprits);
1629 }
Yigit Boyar98d5f042014-11-18 15:47:56 -08001630 return true;
Philip Milne48b55242011-06-29 11:09:45 -07001631 }
Philip Milnef6679c82011-09-18 11:36:57 -07001632 }
1633
Yigit Boyar98d5f042014-11-18 15:47:56 -08001634 if (!modifyOnError) {
1635 return false; // cannot solve with these constraints
1636 }
1637
Philip Milnef6679c82011-09-18 11:36:57 -07001638 boolean[] culprits = new boolean[arcs.length];
1639 for (int i = 0; i < N; i++) {
1640 for (int j = 0, length = arcs.length; j < length; j++) {
1641 culprits[j] |= relax(locations, arcs[j]);
1642 }
1643 }
1644
1645 if (p == 0) {
1646 originalCulprits = culprits;
1647 }
1648
1649 for (int i = 0; i < arcs.length; i++) {
1650 if (culprits[i]) {
1651 Arc arc = arcs[i];
1652 // Only remove max values, min values alone cannot be inconsistent
1653 if (arc.span.min < arc.span.max) {
1654 continue;
1655 }
1656 arc.valid = false;
1657 break;
1658 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001659 }
1660 }
Yigit Boyar98d5f042014-11-18 15:47:56 -08001661 return true;
Philip Milne3f8956d2011-05-13 17:29:00 +01001662 }
1663
Philip Milneaa616f32011-05-27 18:38:01 -07001664 private void computeMargins(boolean leading) {
1665 int[] margins = leading ? leadingMargins : trailingMargins;
Philip Milneb3a8c542011-06-20 16:02:59 -07001666 for (int i = 0, N = getChildCount(); i < N; i++) {
Philip Milne3f8956d2011-05-13 17:29:00 +01001667 View c = getChildAt(i);
Philip Milned7dd8902012-01-26 16:55:30 -08001668 if (c.getVisibility() == View.GONE) continue;
Philip Milne3f8956d2011-05-13 17:29:00 +01001669 LayoutParams lp = getLayoutParams(c);
Philip Milne93cd6a62011-07-12 14:49:45 -07001670 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1671 Interval span = spec.span;
Philip Milne3f8956d2011-05-13 17:29:00 +01001672 int index = leading ? span.min : span.max;
Philip Milne4c8cf4c2011-08-03 11:50:50 -07001673 margins[index] = max(margins[index], getMargin1(c, horizontal, leading));
Philip Milne3f8956d2011-05-13 17:29:00 +01001674 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001675 }
1676
Philip Milnef6679c82011-09-18 11:36:57 -07001677 // External entry points
1678
1679 public int[] getLeadingMargins() {
Philip Milneaa616f32011-05-27 18:38:01 -07001680 if (leadingMargins == null) {
1681 leadingMargins = new int[getCount() + 1];
1682 }
1683 if (!leadingMarginsValid) {
1684 computeMargins(true);
1685 leadingMarginsValid = true;
1686 }
1687 return leadingMargins;
1688 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001689
Philip Milnef6679c82011-09-18 11:36:57 -07001690 public int[] getTrailingMargins() {
Philip Milneaa616f32011-05-27 18:38:01 -07001691 if (trailingMargins == null) {
1692 trailingMargins = new int[getCount() + 1];
1693 }
1694 if (!trailingMarginsValid) {
1695 computeMargins(false);
1696 trailingMarginsValid = true;
1697 }
1698 return trailingMargins;
1699 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001700
Yigit Boyar98d5f042014-11-18 15:47:56 -08001701 private boolean solve(int[] a) {
1702 return solve(getArcs(), a);
Philip Milne87260842014-05-22 13:56:03 -07001703 }
1704
Philip Milne95054802014-05-23 19:50:40 -07001705 private boolean computeHasWeights() {
1706 for (int i = 0, N = getChildCount(); i < N; i++) {
Yigit Boyar6dafd872015-03-05 13:59:56 -08001707 final View child = getChildAt(i);
1708 if (child.getVisibility() == View.GONE) {
1709 continue;
1710 }
1711 LayoutParams lp = getLayoutParams(child);
Philip Milne95054802014-05-23 19:50:40 -07001712 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1713 if (spec.weight != 0) {
Philip Milne87260842014-05-22 13:56:03 -07001714 return true;
1715 }
1716 }
1717 return false;
1718 }
1719
Philip Milne95054802014-05-23 19:50:40 -07001720 private boolean hasWeights() {
1721 if (!hasWeightsValid) {
1722 hasWeights = computeHasWeights();
1723 hasWeightsValid = true;
1724 }
1725 return hasWeights;
1726 }
1727
Philip Milne95054802014-05-23 19:50:40 -07001728 public int[] getDeltas() {
1729 if (deltas == null) {
1730 deltas = new int[getChildCount()];
1731 }
1732 return deltas;
1733 }
1734
Yigit Boyar98d5f042014-11-18 15:47:56 -08001735 private void shareOutDelta(int totalDelta, float totalWeight) {
1736 Arrays.fill(deltas, 0);
Philip Milne95054802014-05-23 19:50:40 -07001737 for (int i = 0, N = getChildCount(); i < N; i++) {
Yigit Boyar6dafd872015-03-05 13:59:56 -08001738 final View c = getChildAt(i);
1739 if (c.getVisibility() == View.GONE) {
1740 continue;
1741 }
Philip Milne95054802014-05-23 19:50:40 -07001742 LayoutParams lp = getLayoutParams(c);
1743 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1744 float weight = spec.weight;
1745 if (weight != 0) {
Philip Milne87260842014-05-22 13:56:03 -07001746 int delta = Math.round((weight * totalDelta / totalWeight));
Philip Milne95054802014-05-23 19:50:40 -07001747 deltas[i] = delta;
Yigit Boyar98d5f042014-11-18 15:47:56 -08001748 // the two adjustments below are to counter the above rounding and avoid
1749 // off-by-ones at the end
Philip Milne87260842014-05-22 13:56:03 -07001750 totalDelta -= delta;
1751 totalWeight -= weight;
1752 }
1753 }
1754 }
1755
1756 private void solveAndDistributeSpace(int[] a) {
Philip Milne95054802014-05-23 19:50:40 -07001757 Arrays.fill(getDeltas(), 0);
Philip Milne87260842014-05-22 13:56:03 -07001758 solve(a);
Yigit Boyar98d5f042014-11-18 15:47:56 -08001759 int deltaMax = parentMin.value * getChildCount() + 1; //exclusive
1760 if (deltaMax < 2) {
1761 return; //don't have any delta to distribute
1762 }
1763 int deltaMin = 0; //inclusive
1764
1765 float totalWeight = calculateTotalWeight();
1766
1767 int validDelta = -1; //delta for which a solution exists
1768 boolean validSolution = true;
1769 // do a binary search to find the max delta that won't conflict with constraints
1770 while(deltaMin < deltaMax) {
Deepanshu Gupta7a8fb262016-01-07 13:29:04 -08001771 // cast to long to prevent overflow.
1772 final int delta = (int) (((long) deltaMin + deltaMax) / 2);
Yigit Boyar98d5f042014-11-18 15:47:56 -08001773 invalidateValues();
1774 shareOutDelta(delta, totalWeight);
1775 validSolution = solve(getArcs(), a, false);
1776 if (validSolution) {
1777 validDelta = delta;
1778 deltaMin = delta + 1;
1779 } else {
1780 deltaMax = delta;
1781 }
1782 }
1783 if (validDelta > 0 && !validSolution) {
1784 // last solution was not successful but we have a successful one. Use it.
1785 invalidateValues();
1786 shareOutDelta(validDelta, totalWeight);
1787 solve(a);
1788 }
1789 }
1790
1791 private float calculateTotalWeight() {
1792 float totalWeight = 0f;
1793 for (int i = 0, N = getChildCount(); i < N; i++) {
1794 View c = getChildAt(i);
Yigit Boyar6dafd872015-03-05 13:59:56 -08001795 if (c.getVisibility() == View.GONE) {
1796 continue;
1797 }
Yigit Boyar98d5f042014-11-18 15:47:56 -08001798 LayoutParams lp = getLayoutParams(c);
1799 Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
1800 totalWeight += spec.weight;
1801 }
1802 return totalWeight;
Philip Milne87260842014-05-22 13:56:03 -07001803 }
1804
1805 private void computeLocations(int[] a) {
Philip Milne95054802014-05-23 19:50:40 -07001806 if (!hasWeights()) {
1807 solve(a);
1808 } else {
1809 solveAndDistributeSpace(a);
1810 }
Philip Milne4a145d72011-09-29 14:14:25 -07001811 if (!orderPreserved) {
1812 // Solve returns the smallest solution to the constraint system for which all
1813 // values are positive. One value is therefore zero - though if the row/col
1814 // order is not preserved this may not be the first vertex. For consistency,
1815 // translate all the values so that they measure the distance from a[0]; the
1816 // leading edge of the parent. After this transformation some values may be
1817 // negative.
1818 int a0 = a[0];
1819 for (int i = 0, N = a.length; i < N; i++) {
1820 a[i] = a[i] - a0;
1821 }
1822 }
Philip Milneaa616f32011-05-27 18:38:01 -07001823 }
1824
Philip Milnef6679c82011-09-18 11:36:57 -07001825 public int[] getLocations() {
Philip Milneaa616f32011-05-27 18:38:01 -07001826 if (locations == null) {
1827 int N = getCount() + 1;
1828 locations = new int[N];
1829 }
Philip Milne48b55242011-06-29 11:09:45 -07001830 if (!locationsValid) {
1831 computeLocations(locations);
1832 locationsValid = true;
1833 }
Philip Milneaa616f32011-05-27 18:38:01 -07001834 return locations;
1835 }
1836
Philip Milne3f8956d2011-05-13 17:29:00 +01001837 private int size(int[] locations) {
Philip Milne4a145d72011-09-29 14:14:25 -07001838 // The parental edges are attached to vertices 0 and N - even when order is not
1839 // being preserved and other vertices fall outside this range. Measure the distance
1840 // between vertices 0 and N, assuming that locations[0] = 0.
1841 return locations[getCount()];
Philip Milne3f8956d2011-05-13 17:29:00 +01001842 }
1843
Philip Milne48b55242011-06-29 11:09:45 -07001844 private void setParentConstraints(int min, int max) {
1845 parentMin.value = min;
1846 parentMax.value = -max;
1847 locationsValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001848 }
1849
Philip Milne48b55242011-06-29 11:09:45 -07001850 private int getMeasure(int min, int max) {
1851 setParentConstraints(min, max);
1852 return size(getLocations());
1853 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001854
Philip Milnef6679c82011-09-18 11:36:57 -07001855 public int getMeasure(int measureSpec) {
Philip Milne48b55242011-06-29 11:09:45 -07001856 int mode = MeasureSpec.getMode(measureSpec);
1857 int size = MeasureSpec.getSize(measureSpec);
1858 switch (mode) {
1859 case MeasureSpec.UNSPECIFIED: {
Philip Milne93cd6a62011-07-12 14:49:45 -07001860 return getMeasure(0, MAX_SIZE);
Philip Milne48b55242011-06-29 11:09:45 -07001861 }
1862 case MeasureSpec.EXACTLY: {
1863 return getMeasure(size, size);
1864 }
1865 case MeasureSpec.AT_MOST: {
1866 return getMeasure(0, size);
1867 }
1868 default: {
1869 assert false;
1870 return 0;
1871 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001872 }
Philip Milne48b55242011-06-29 11:09:45 -07001873 }
Philip Milne3f8956d2011-05-13 17:29:00 +01001874
Philip Milnef6679c82011-09-18 11:36:57 -07001875 public void layout(int size) {
Philip Milne48b55242011-06-29 11:09:45 -07001876 setParentConstraints(size, size);
1877 getLocations();
Philip Milne3f8956d2011-05-13 17:29:00 +01001878 }
1879
Philip Milnef6679c82011-09-18 11:36:57 -07001880 public void invalidateStructure() {
Philip Milne4a145d72011-09-29 14:14:25 -07001881 maxIndex = UNDEFINED;
Philip Milneaa616f32011-05-27 18:38:01 -07001882
Philip Milne3f8956d2011-05-13 17:29:00 +01001883 groupBounds = null;
Philip Milne48b55242011-06-29 11:09:45 -07001884 forwardLinks = null;
1885 backwardLinks = null;
1886
Philip Milneaa616f32011-05-27 18:38:01 -07001887 leadingMargins = null;
1888 trailingMargins = null;
Philip Milnec9885f62011-06-15 17:07:35 -07001889 arcs = null;
Philip Milne48b55242011-06-29 11:09:45 -07001890
Philip Milneaa616f32011-05-27 18:38:01 -07001891 locations = null;
Philip Milne3f8956d2011-05-13 17:29:00 +01001892
Philip Milne95054802014-05-23 19:50:40 -07001893 deltas = null;
1894 hasWeightsValid = false;
1895
Philip Milne3f8956d2011-05-13 17:29:00 +01001896 invalidateValues();
1897 }
1898
Philip Milnef6679c82011-09-18 11:36:57 -07001899 public void invalidateValues() {
Philip Milne3f8956d2011-05-13 17:29:00 +01001900 groupBoundsValid = false;
Philip Milne48b55242011-06-29 11:09:45 -07001901 forwardLinksValid = false;
1902 backwardLinksValid = false;
1903
Philip Milneaa616f32011-05-27 18:38:01 -07001904 leadingMarginsValid = false;
1905 trailingMarginsValid = false;
Philip Milne48b55242011-06-29 11:09:45 -07001906 arcsValid = false;
1907
1908 locationsValid = false;
Philip Milne3f8956d2011-05-13 17:29:00 +01001909 }
1910 }
1911
1912 /**
1913 * Layout information associated with each of the children of a GridLayout.
1914 * <p>
1915 * GridLayout supports both row and column spanning and arbitrary forms of alignment within
1916 * each cell group. The fundamental parameters associated with each cell group are
1917 * gathered into their vertical and horizontal components and stored
Philip Milne93cd6a62011-07-12 14:49:45 -07001918 * in the {@link #rowSpec} and {@link #columnSpec} layout parameters.
Philip Milne6216e872012-02-16 17:15:50 -08001919 * {@link GridLayout.Spec Specs} are immutable structures
Philip Milneb0ce49b2011-07-15 15:39:07 -07001920 * and may be shared between the layout parameters of different children.
Philip Milne3f8956d2011-05-13 17:29:00 +01001921 * <p>
Philip Milne93cd6a62011-07-12 14:49:45 -07001922 * The row and column specs contain the leading and trailing indices along each axis
Philip Milneaa616f32011-05-27 18:38:01 -07001923 * and together specify the four grid indices that delimit the cells of this cell group.
Philip Milne3f8956d2011-05-13 17:29:00 +01001924 * <p>
Philip Milne93cd6a62011-07-12 14:49:45 -07001925 * The alignment properties of the row and column specs together specify
Philip Milne3f8956d2011-05-13 17:29:00 +01001926 * both aspects of alignment within the cell group. It is also possible to specify a child's
1927 * alignment within its cell group by using the {@link GridLayout.LayoutParams#setGravity(int)}
1928 * method.
Philip Milne87260842014-05-22 13:56:03 -07001929 * <p>
1930 * The weight property is also included in Spec and specifies the proportion of any
Philip Milne95054802014-05-23 19:50:40 -07001931 * excess space that is due to the associated view.
Philip Milnef6679c82011-09-18 11:36:57 -07001932 *
1933 * <h4>WRAP_CONTENT and MATCH_PARENT</h4>
1934 *
1935 * Because the default values of the {@link #width} and {@link #height}
1936 * properties are both {@link #WRAP_CONTENT}, this value never needs to be explicitly
1937 * declared in the layout parameters of GridLayout's children. In addition,
1938 * GridLayout does not distinguish the special size value {@link #MATCH_PARENT} from
1939 * {@link #WRAP_CONTENT}. A component's ability to expand to the size of the parent is
1940 * instead controlled by the principle of <em>flexibility</em>,
1941 * as discussed in {@link GridLayout}.
1942 *
1943 * <h4>Summary</h4>
1944 *
1945 * You should not need to use either of the special size values:
1946 * {@code WRAP_CONTENT} or {@code MATCH_PARENT} when configuring the children of
1947 * a GridLayout.
Philip Milne3f8956d2011-05-13 17:29:00 +01001948 *
1949 * <h4>Default values</h4>
1950 *
1951 * <ul>
1952 * <li>{@link #width} = {@link #WRAP_CONTENT}</li>
1953 * <li>{@link #height} = {@link #WRAP_CONTENT}</li>
1954 * <li>{@link #topMargin} = 0 when
1955 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001956 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001957 * indicate that a default value should be computed on demand. </li>
1958 * <li>{@link #leftMargin} = 0 when
1959 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001960 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001961 * indicate that a default value should be computed on demand. </li>
1962 * <li>{@link #bottomMargin} = 0 when
1963 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001964 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001965 * indicate that a default value should be computed on demand. </li>
1966 * <li>{@link #rightMargin} = 0 when
1967 * {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
Philip Milne7fd94872011-06-07 20:14:17 -07001968 * {@code false}; otherwise {@link #UNDEFINED}, to
Philip Milne3f8956d2011-05-13 17:29:00 +01001969 * indicate that a default value should be computed on demand. </li>
Philip Milnef6679c82011-09-18 11:36:57 -07001970 * <li>{@link #rowSpec}<code>.row</code> = {@link #UNDEFINED} </li>
1971 * <li>{@link #rowSpec}<code>.rowSpan</code> = 1 </li>
1972 * <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li>
Philip Milne87260842014-05-22 13:56:03 -07001973 * <li>{@link #rowSpec}<code>.weight</code> = 0 </li>
Philip Milnef6679c82011-09-18 11:36:57 -07001974 * <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li>
1975 * <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li>
Philip Milne6216e872012-02-16 17:15:50 -08001976 * <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li>
Philip Milne87260842014-05-22 13:56:03 -07001977 * <li>{@link #columnSpec}<code>.weight</code> = 0 </li>
Philip Milne3f8956d2011-05-13 17:29:00 +01001978 * </ul>
1979 *
Philip Milnef6679c82011-09-18 11:36:57 -07001980 * See {@link GridLayout} for a more complete description of the conventions
1981 * used by GridLayout in the interpretation of the properties of this class.
1982 *
Philip Milne3f8956d2011-05-13 17:29:00 +01001983 * @attr ref android.R.styleable#GridLayout_Layout_layout_row
1984 * @attr ref android.R.styleable#GridLayout_Layout_layout_rowSpan
Philip Milne87260842014-05-22 13:56:03 -07001985 * @attr ref android.R.styleable#GridLayout_Layout_layout_rowWeight
Philip Milne3f8956d2011-05-13 17:29:00 +01001986 * @attr ref android.R.styleable#GridLayout_Layout_layout_column
1987 * @attr ref android.R.styleable#GridLayout_Layout_layout_columnSpan
Philip Milne87260842014-05-22 13:56:03 -07001988 * @attr ref android.R.styleable#GridLayout_Layout_layout_columnWeight
Philip Milne3f8956d2011-05-13 17:29:00 +01001989 * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
1990 */
1991 public static class LayoutParams extends MarginLayoutParams {
1992
1993 // Default values
1994
1995 private static final int DEFAULT_WIDTH = WRAP_CONTENT;
1996 private static final int DEFAULT_HEIGHT = WRAP_CONTENT;
1997 private static final int DEFAULT_MARGIN = UNDEFINED;
1998 private static final int DEFAULT_ROW = UNDEFINED;
1999 private static final int DEFAULT_COLUMN = UNDEFINED;
Philip Milnef4748702011-06-09 18:30:32 -07002000 private static final Interval DEFAULT_SPAN = new Interval(UNDEFINED, UNDEFINED + 1);
Philip Milne3f8956d2011-05-13 17:29:00 +01002001 private static final int DEFAULT_SPAN_SIZE = DEFAULT_SPAN.size();
Philip Milne3f8956d2011-05-13 17:29:00 +01002002
2003 // TypedArray indices
2004
Philip Milneb0ce49b2011-07-15 15:39:07 -07002005 private static final int MARGIN = R.styleable.ViewGroup_MarginLayout_layout_margin;
2006 private static final int LEFT_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginLeft;
2007 private static final int TOP_MARGIN = R.styleable.ViewGroup_MarginLayout_layout_marginTop;
2008 private static final int RIGHT_MARGIN =
2009 R.styleable.ViewGroup_MarginLayout_layout_marginRight;
Philip Milne3f8956d2011-05-13 17:29:00 +01002010 private static final int BOTTOM_MARGIN =
Philip Milneb0ce49b2011-07-15 15:39:07 -07002011 R.styleable.ViewGroup_MarginLayout_layout_marginBottom;
Philip Milneb0ce49b2011-07-15 15:39:07 -07002012 private static final int COLUMN = R.styleable.GridLayout_Layout_layout_column;
2013 private static final int COLUMN_SPAN = R.styleable.GridLayout_Layout_layout_columnSpan;
Philip Milne87260842014-05-22 13:56:03 -07002014 private static final int COLUMN_WEIGHT = R.styleable.GridLayout_Layout_layout_columnWeight;
Philip Milne5d1a9842011-07-07 11:47:08 -07002015
Philip Milneb0ce49b2011-07-15 15:39:07 -07002016 private static final int ROW = R.styleable.GridLayout_Layout_layout_row;
2017 private static final int ROW_SPAN = R.styleable.GridLayout_Layout_layout_rowSpan;
Philip Milne87260842014-05-22 13:56:03 -07002018 private static final int ROW_WEIGHT = R.styleable.GridLayout_Layout_layout_rowWeight;
Philip Milne5d1a9842011-07-07 11:47:08 -07002019
Philip Milneb0ce49b2011-07-15 15:39:07 -07002020 private static final int GRAVITY = R.styleable.GridLayout_Layout_layout_gravity;
Philip Milne3f8956d2011-05-13 17:29:00 +01002021
2022 // Instance variables
2023
2024 /**
Philip Milnef6679c82011-09-18 11:36:57 -07002025 * The spec that defines the vertical characteristics of the cell group
Philip Milne3f8956d2011-05-13 17:29:00 +01002026 * described by these layout parameters.
Philip Milned7dd8902012-01-26 16:55:30 -08002027 * If an assignment is made to this field after a measurement or layout operation
2028 * has already taken place, a call to
2029 * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2030 * must be made to notify GridLayout of the change. GridLayout is normally able
2031 * to detect when code fails to observe this rule, issue a warning and take steps to
2032 * compensate for the omission. This facility is implemented on a best effort basis
2033 * and should not be relied upon in production code - so it is best to include the above
2034 * calls to remove the warnings as soon as it is practical.
Philip Milne3f8956d2011-05-13 17:29:00 +01002035 */
Philip Milnef6679c82011-09-18 11:36:57 -07002036 public Spec rowSpec = Spec.UNDEFINED;
2037
Philip Milne3f8956d2011-05-13 17:29:00 +01002038 /**
Philip Milnef6679c82011-09-18 11:36:57 -07002039 * The spec that defines the horizontal characteristics of the cell group
Philip Milne3f8956d2011-05-13 17:29:00 +01002040 * described by these layout parameters.
Philip Milned7dd8902012-01-26 16:55:30 -08002041 * If an assignment is made to this field after a measurement or layout operation
2042 * has already taken place, a call to
2043 * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
2044 * must be made to notify GridLayout of the change. GridLayout is normally able
2045 * to detect when code fails to observe this rule, issue a warning and take steps to
2046 * compensate for the omission. This facility is implemented on a best effort basis
2047 * and should not be relied upon in production code - so it is best to include the above
2048 * calls to remove the warnings as soon as it is practical.
Philip Milne3f8956d2011-05-13 17:29:00 +01002049 */
Philip Milnef6679c82011-09-18 11:36:57 -07002050 public Spec columnSpec = Spec.UNDEFINED;
Philip Milne3f8956d2011-05-13 17:29:00 +01002051
2052 // Constructors
2053
2054 private LayoutParams(
2055 int width, int height,
2056 int left, int top, int right, int bottom,
Philip Milne93cd6a62011-07-12 14:49:45 -07002057 Spec rowSpec, Spec columnSpec) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002058 super(width, height);
2059 setMargins(left, top, right, bottom);
Philip Milne93cd6a62011-07-12 14:49:45 -07002060 this.rowSpec = rowSpec;
2061 this.columnSpec = columnSpec;
Philip Milne3f8956d2011-05-13 17:29:00 +01002062 }
2063
2064 /**
Philip Milne93cd6a62011-07-12 14:49:45 -07002065 * Constructs a new LayoutParams instance for this <code>rowSpec</code>
2066 * and <code>columnSpec</code>. All other fields are initialized with
Philip Milne3f8956d2011-05-13 17:29:00 +01002067 * default values as defined in {@link LayoutParams}.
2068 *
Philip Milne93cd6a62011-07-12 14:49:45 -07002069 * @param rowSpec the rowSpec
2070 * @param columnSpec the columnSpec
Philip Milne3f8956d2011-05-13 17:29:00 +01002071 */
Philip Milne93cd6a62011-07-12 14:49:45 -07002072 public LayoutParams(Spec rowSpec, Spec columnSpec) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002073 this(DEFAULT_WIDTH, DEFAULT_HEIGHT,
2074 DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN,
Philip Milne93cd6a62011-07-12 14:49:45 -07002075 rowSpec, columnSpec);
Philip Milne3f8956d2011-05-13 17:29:00 +01002076 }
2077
2078 /**
2079 * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
2080 */
2081 public LayoutParams() {
Philip Milnef6679c82011-09-18 11:36:57 -07002082 this(Spec.UNDEFINED, Spec.UNDEFINED);
Philip Milne3f8956d2011-05-13 17:29:00 +01002083 }
2084
2085 // Copying constructors
2086
2087 /**
2088 * {@inheritDoc}
2089 */
2090 public LayoutParams(ViewGroup.LayoutParams params) {
2091 super(params);
2092 }
2093
2094 /**
2095 * {@inheritDoc}
2096 */
2097 public LayoutParams(MarginLayoutParams params) {
2098 super(params);
2099 }
2100
2101 /**
Alan Viverette0a0e1552013-08-07 13:24:09 -07002102 * Copy constructor. Clones the width, height, margin values, row spec,
2103 * and column spec of the source.
2104 *
2105 * @param source The layout params to copy from.
Philip Milne3f8956d2011-05-13 17:29:00 +01002106 */
Alan Viverette0a0e1552013-08-07 13:24:09 -07002107 public LayoutParams(LayoutParams source) {
2108 super(source);
2109
2110 this.rowSpec = source.rowSpec;
2111 this.columnSpec = source.columnSpec;
Philip Milne3f8956d2011-05-13 17:29:00 +01002112 }
2113
2114 // AttributeSet constructors
2115
Philip Milne3f8956d2011-05-13 17:29:00 +01002116 /**
2117 * {@inheritDoc}
2118 *
2119 * Values not defined in the attribute set take the default values
2120 * defined in {@link LayoutParams}.
2121 */
2122 public LayoutParams(Context context, AttributeSet attrs) {
Philip Milne5125e212011-07-21 11:39:37 -07002123 super(context, attrs);
2124 reInitSuper(context, attrs);
2125 init(context, attrs);
Philip Milne3f8956d2011-05-13 17:29:00 +01002126 }
2127
2128 // Implementation
2129
Philip Milne3f8956d2011-05-13 17:29:00 +01002130 // Reinitialise the margins using a different default policy than MarginLayoutParams.
2131 // Here we use the value UNDEFINED (as distinct from zero) to represent the undefined state
2132 // so that a layout manager default can be accessed post set up. We need this as, at the
2133 // point of installation, we do not know how many rows/cols there are and therefore
2134 // which elements are positioned next to the container's trailing edges. We need to
2135 // know this as margins around the container's boundary should have different
2136 // defaults to those between peers.
2137
2138 // This method could be parametrized and moved into MarginLayout.
2139 private void reInitSuper(Context context, AttributeSet attrs) {
Philip Milneb0ce49b2011-07-15 15:39:07 -07002140 TypedArray a =
2141 context.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
Philip Milne3f8956d2011-05-13 17:29:00 +01002142 try {
2143 int margin = a.getDimensionPixelSize(MARGIN, DEFAULT_MARGIN);
2144
2145 this.leftMargin = a.getDimensionPixelSize(LEFT_MARGIN, margin);
2146 this.topMargin = a.getDimensionPixelSize(TOP_MARGIN, margin);
2147 this.rightMargin = a.getDimensionPixelSize(RIGHT_MARGIN, margin);
2148 this.bottomMargin = a.getDimensionPixelSize(BOTTOM_MARGIN, margin);
2149 } finally {
2150 a.recycle();
2151 }
2152 }
2153
Philip Milne5125e212011-07-21 11:39:37 -07002154 private void init(Context context, AttributeSet attrs) {
Philip Milneb0ce49b2011-07-15 15:39:07 -07002155 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridLayout_Layout);
Philip Milne3f8956d2011-05-13 17:29:00 +01002156 try {
Philip Milne5125e212011-07-21 11:39:37 -07002157 int gravity = a.getInt(GRAVITY, Gravity.NO_GRAVITY);
Philip Milne3f8956d2011-05-13 17:29:00 +01002158
Philip Milne1e548252011-06-16 19:02:33 -07002159 int column = a.getInt(COLUMN, DEFAULT_COLUMN);
Philip Milne5125e212011-07-21 11:39:37 -07002160 int colSpan = a.getInt(COLUMN_SPAN, DEFAULT_SPAN_SIZE);
Philip Milne87260842014-05-22 13:56:03 -07002161 float colWeight = a.getFloat(COLUMN_WEIGHT, Spec.DEFAULT_WEIGHT);
2162 this.columnSpec = spec(column, colSpan, getAlignment(gravity, true), colWeight);
Philip Milne3f8956d2011-05-13 17:29:00 +01002163
Philip Milne1e548252011-06-16 19:02:33 -07002164 int row = a.getInt(ROW, DEFAULT_ROW);
2165 int rowSpan = a.getInt(ROW_SPAN, DEFAULT_SPAN_SIZE);
Philip Milne87260842014-05-22 13:56:03 -07002166 float rowWeight = a.getFloat(ROW_WEIGHT, Spec.DEFAULT_WEIGHT);
2167 this.rowSpec = spec(row, rowSpan, getAlignment(gravity, false), rowWeight);
Philip Milne3f8956d2011-05-13 17:29:00 +01002168 } finally {
2169 a.recycle();
2170 }
2171 }
2172
2173 /**
Philip Milne7fd94872011-06-07 20:14:17 -07002174 * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
Philip Milne6216e872012-02-16 17:15:50 -08002175 * See {@link Gravity}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002176 *
Philip Milne7fd94872011-06-07 20:14:17 -07002177 * @param gravity the new gravity value
Philip Milne3f8956d2011-05-13 17:29:00 +01002178 *
2179 * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
2180 */
2181 public void setGravity(int gravity) {
Philip Milne5125e212011-07-21 11:39:37 -07002182 rowSpec = rowSpec.copyWriteAlignment(getAlignment(gravity, false));
2183 columnSpec = columnSpec.copyWriteAlignment(getAlignment(gravity, true));
Philip Milne3f8956d2011-05-13 17:29:00 +01002184 }
2185
2186 @Override
2187 protected void setBaseAttributes(TypedArray attributes, int widthAttr, int heightAttr) {
2188 this.width = attributes.getLayoutDimension(widthAttr, DEFAULT_WIDTH);
2189 this.height = attributes.getLayoutDimension(heightAttr, DEFAULT_HEIGHT);
2190 }
2191
Philip Milnef6679c82011-09-18 11:36:57 -07002192 final void setRowSpecSpan(Interval span) {
Philip Milne93cd6a62011-07-12 14:49:45 -07002193 rowSpec = rowSpec.copyWriteSpan(span);
Philip Milne3f8956d2011-05-13 17:29:00 +01002194 }
2195
Philip Milnef6679c82011-09-18 11:36:57 -07002196 final void setColumnSpecSpan(Interval span) {
Philip Milne93cd6a62011-07-12 14:49:45 -07002197 columnSpec = columnSpec.copyWriteSpan(span);
Philip Milne3f8956d2011-05-13 17:29:00 +01002198 }
Philip Milned7dd8902012-01-26 16:55:30 -08002199
2200 @Override
2201 public boolean equals(Object o) {
2202 if (this == o) return true;
2203 if (o == null || getClass() != o.getClass()) return false;
2204
2205 LayoutParams that = (LayoutParams) o;
2206
2207 if (!columnSpec.equals(that.columnSpec)) return false;
2208 if (!rowSpec.equals(that.rowSpec)) return false;
2209
2210 return true;
2211 }
2212
2213 @Override
2214 public int hashCode() {
2215 int result = rowSpec.hashCode();
2216 result = 31 * result + columnSpec.hashCode();
2217 return result;
2218 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002219 }
2220
Philip Milneaa616f32011-05-27 18:38:01 -07002221 /*
2222 In place of a HashMap from span to Int, use an array of key/value pairs - stored in Arcs.
2223 Add the mutables completesCycle flag to avoid creating another hash table for detecting cycles.
2224 */
Philip Milnef6679c82011-09-18 11:36:57 -07002225 final static class Arc {
Philip Milne3f8956d2011-05-13 17:29:00 +01002226 public final Interval span;
Philip Milneaa616f32011-05-27 18:38:01 -07002227 public final MutableInt value;
Philip Milne48b55242011-06-29 11:09:45 -07002228 public boolean valid = true;
Philip Milne3f8956d2011-05-13 17:29:00 +01002229
Philip Milneaa616f32011-05-27 18:38:01 -07002230 public Arc(Interval span, MutableInt value) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002231 this.span = span;
2232 this.value = value;
2233 }
2234
2235 @Override
2236 public String toString() {
Philip Milne48b55242011-06-29 11:09:45 -07002237 return span + " " + (!valid ? "+>" : "->") + " " + value;
Philip Milne3f8956d2011-05-13 17:29:00 +01002238 }
2239 }
2240
2241 // A mutable Integer - used to avoid heap allocation during the layout operation
2242
Philip Milnef6679c82011-09-18 11:36:57 -07002243 final static class MutableInt {
Philip Milne3f8956d2011-05-13 17:29:00 +01002244 public int value;
2245
Philip Milnef6679c82011-09-18 11:36:57 -07002246 public MutableInt() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002247 reset();
2248 }
2249
Philip Milnef6679c82011-09-18 11:36:57 -07002250 public MutableInt(int value) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002251 this.value = value;
2252 }
2253
Philip Milnef6679c82011-09-18 11:36:57 -07002254 public void reset() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002255 value = Integer.MIN_VALUE;
2256 }
Philip Milne48b55242011-06-29 11:09:45 -07002257
2258 @Override
2259 public String toString() {
2260 return Integer.toString(value);
2261 }
2262 }
2263
Philip Milnef6679c82011-09-18 11:36:57 -07002264 final static class Assoc<K, V> extends ArrayList<Pair<K, V>> {
Philip Milne48b55242011-06-29 11:09:45 -07002265 private final Class<K> keyType;
2266 private final Class<V> valueType;
2267
2268 private Assoc(Class<K> keyType, Class<V> valueType) {
2269 this.keyType = keyType;
2270 this.valueType = valueType;
2271 }
2272
Philip Milnef6679c82011-09-18 11:36:57 -07002273 public static <K, V> Assoc<K, V> of(Class<K> keyType, Class<V> valueType) {
Philip Milne48b55242011-06-29 11:09:45 -07002274 return new Assoc<K, V>(keyType, valueType);
2275 }
2276
2277 public void put(K key, V value) {
2278 add(Pair.create(key, value));
2279 }
2280
2281 @SuppressWarnings(value = "unchecked")
2282 public PackedMap<K, V> pack() {
2283 int N = size();
2284 K[] keys = (K[]) Array.newInstance(keyType, N);
2285 V[] values = (V[]) Array.newInstance(valueType, N);
2286 for (int i = 0; i < N; i++) {
2287 keys[i] = get(i).first;
2288 values[i] = get(i).second;
2289 }
2290 return new PackedMap<K, V>(keys, values);
2291 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002292 }
2293
Philip Milneaa616f32011-05-27 18:38:01 -07002294 /*
2295 This data structure is used in place of a Map where we have an index that refers to the order
2296 in which each key/value pairs were added to the map. In this case we store keys and values
2297 in arrays of a length that is equal to the number of unique keys. We also maintain an
2298 array of indexes from insertion order to the compacted arrays of keys and values.
2299
2300 Note that behavior differs from that of a LinkedHashMap in that repeated entries
2301 *do* get added multiples times. So the length of index is equals to the number of
2302 items added.
2303
2304 This is useful in the GridLayout class where we can rely on the order of children not
2305 changing during layout - to use integer-based lookup for our internal structures
2306 rather than using (and storing) an implementation of Map<Key, ?>.
2307 */
Philip Milne3f8956d2011-05-13 17:29:00 +01002308 @SuppressWarnings(value = "unchecked")
Philip Milnef6679c82011-09-18 11:36:57 -07002309 final static class PackedMap<K, V> {
Philip Milne3f8956d2011-05-13 17:29:00 +01002310 public final int[] index;
2311 public final K[] keys;
2312 public final V[] values;
2313
2314 private PackedMap(K[] keys, V[] values) {
2315 this.index = createIndex(keys);
2316
Philip Milneaa616f32011-05-27 18:38:01 -07002317 this.keys = compact(keys, index);
2318 this.values = compact(values, index);
Philip Milne3f8956d2011-05-13 17:29:00 +01002319 }
2320
Philip Milnef6679c82011-09-18 11:36:57 -07002321 public V getValue(int i) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002322 return values[index[i]];
2323 }
2324
2325 private static <K> int[] createIndex(K[] keys) {
2326 int size = keys.length;
2327 int[] result = new int[size];
2328
2329 Map<K, Integer> keyToIndex = new HashMap<K, Integer>();
2330 for (int i = 0; i < size; i++) {
2331 K key = keys[i];
2332 Integer index = keyToIndex.get(key);
2333 if (index == null) {
2334 index = keyToIndex.size();
2335 keyToIndex.put(key, index);
2336 }
2337 result[i] = index;
2338 }
2339 return result;
2340 }
2341
Philip Milneaa616f32011-05-27 18:38:01 -07002342 /*
2343 Create a compact array of keys or values using the supplied index.
2344 */
2345 private static <K> K[] compact(K[] a, int[] index) {
2346 int size = a.length;
2347 Class<?> componentType = a.getClass().getComponentType();
Philip Milne51f17d52011-06-13 10:44:49 -07002348 K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
Philip Milne3f8956d2011-05-13 17:29:00 +01002349
2350 // this overwrite duplicates, retaining the last equivalent entry
2351 for (int i = 0; i < size; i++) {
Philip Milneaa616f32011-05-27 18:38:01 -07002352 result[index[i]] = a[i];
Philip Milne3f8956d2011-05-13 17:29:00 +01002353 }
2354 return result;
2355 }
2356 }
2357
Philip Milneaa616f32011-05-27 18:38:01 -07002358 /*
Philip Milne93cd6a62011-07-12 14:49:45 -07002359 For each group (with a given alignment) we need to store the amount of space required
Philip Milne7fd94872011-06-07 20:14:17 -07002360 before the alignment point and the amount of space required after it. One side of this
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002361 calculation is always 0 for START and END alignments but we don't make use of this.
Philip Milneaa616f32011-05-27 18:38:01 -07002362 For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
2363 simple optimisations are possible.
2364
2365 The general algorithm therefore is to create a Map (actually a PackedMap) from
Philip Milne93cd6a62011-07-12 14:49:45 -07002366 group to Bounds and to loop through all Views in the group taking the maximum
Philip Milneaa616f32011-05-27 18:38:01 -07002367 of the values for each View.
2368 */
Philip Milnef6679c82011-09-18 11:36:57 -07002369 static class Bounds {
Philip Milne7fd94872011-06-07 20:14:17 -07002370 public int before;
2371 public int after;
Philip Milne5125e212011-07-21 11:39:37 -07002372 public int flexibility; // we're flexible iff all included specs are flexible
Philip Milne3f8956d2011-05-13 17:29:00 +01002373
2374 private Bounds() {
2375 reset();
2376 }
2377
Philip Milnea1f7b102011-06-23 11:10:13 -07002378 protected void reset() {
Philip Milne7fd94872011-06-07 20:14:17 -07002379 before = Integer.MIN_VALUE;
2380 after = Integer.MIN_VALUE;
Philip Milne5125e212011-07-21 11:39:37 -07002381 flexibility = CAN_STRETCH; // from the above, we're flexible when empty
Philip Milne3f8956d2011-05-13 17:29:00 +01002382 }
2383
Philip Milnea1f7b102011-06-23 11:10:13 -07002384 protected void include(int before, int after) {
Philip Milne7fd94872011-06-07 20:14:17 -07002385 this.before = max(this.before, before);
2386 this.after = max(this.after, after);
Philip Milne3f8956d2011-05-13 17:29:00 +01002387 }
2388
Philip Milne48b55242011-06-29 11:09:45 -07002389 protected int size(boolean min) {
Philip Milne5d1a9842011-07-07 11:47:08 -07002390 if (!min) {
Philip Milne5125e212011-07-21 11:39:37 -07002391 if (canStretch(flexibility)) {
Philip Milne5d1a9842011-07-07 11:47:08 -07002392 return MAX_SIZE;
2393 }
Philip Milne48b55242011-06-29 11:09:45 -07002394 }
Philip Milne4885f2f2014-05-23 23:12:11 -07002395 return before + after;
Philip Milne3f8956d2011-05-13 17:29:00 +01002396 }
2397
Philip Milne1557fd72012-04-04 23:41:34 -07002398 protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) {
Philip Milne7a23b492012-04-24 22:12:36 -07002399 return before - a.getAlignmentValue(c, size, gl.getLayoutMode());
Philip Milne1557fd72012-04-04 23:41:34 -07002400 }
2401
Philip Milne95054802014-05-23 19:50:40 -07002402 protected final void include(GridLayout gl, View c, Spec spec, Axis axis, int size) {
Philip Milne5125e212011-07-21 11:39:37 -07002403 this.flexibility &= spec.getFlexibility();
Philip Milne1557fd72012-04-04 23:41:34 -07002404 boolean horizontal = axis.horizontal;
Yigit Boyar6dafd872015-03-05 13:59:56 -08002405 Alignment alignment = spec.getAbsoluteAlignment(axis.horizontal);
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002406 // todo test this works correctly when the returned value is UNDEFINED
Philip Milne7a23b492012-04-24 22:12:36 -07002407 int before = alignment.getAlignmentValue(c, size, gl.getLayoutMode());
Philip Milne48b55242011-06-29 11:09:45 -07002408 include(before, size - before);
Philip Milnea1f7b102011-06-23 11:10:13 -07002409 }
2410
Philip Milne3f8956d2011-05-13 17:29:00 +01002411 @Override
2412 public String toString() {
2413 return "Bounds{" +
Philip Milne7fd94872011-06-07 20:14:17 -07002414 "before=" + before +
2415 ", after=" + after +
Philip Milne3f8956d2011-05-13 17:29:00 +01002416 '}';
2417 }
2418 }
2419
2420 /**
2421 * An Interval represents a contiguous range of values that lie between
2422 * the interval's {@link #min} and {@link #max} values.
2423 * <p>
2424 * Intervals are immutable so may be passed as values and used as keys in hash tables.
2425 * It is not necessary to have multiple instances of Intervals which have the same
2426 * {@link #min} and {@link #max} values.
2427 * <p>
Philip Milne7fd94872011-06-07 20:14:17 -07002428 * Intervals are often written as {@code [min, max]} and represent the set of values
2429 * {@code x} such that {@code min <= x < max}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002430 */
Philip Milnef6679c82011-09-18 11:36:57 -07002431 final static class Interval {
Philip Milne3f8956d2011-05-13 17:29:00 +01002432 /**
2433 * The minimum value.
2434 */
2435 public final int min;
Philip Milneaa616f32011-05-27 18:38:01 -07002436
Philip Milne3f8956d2011-05-13 17:29:00 +01002437 /**
2438 * The maximum value.
2439 */
2440 public final int max;
2441
2442 /**
Philip Milne7fd94872011-06-07 20:14:17 -07002443 * Construct a new Interval, {@code interval}, where:
Philip Milne3f8956d2011-05-13 17:29:00 +01002444 * <ul>
Philip Milne7fd94872011-06-07 20:14:17 -07002445 * <li> {@code interval.min = min} </li>
2446 * <li> {@code interval.max = max} </li>
Philip Milne3f8956d2011-05-13 17:29:00 +01002447 * </ul>
2448 *
2449 * @param min the minimum value.
2450 * @param max the maximum value.
2451 */
2452 public Interval(int min, int max) {
2453 this.min = min;
2454 this.max = max;
2455 }
2456
Philip Milnef6679c82011-09-18 11:36:57 -07002457 int size() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002458 return max - min;
2459 }
2460
Philip Milnef6679c82011-09-18 11:36:57 -07002461 Interval inverse() {
Philip Milne3f8956d2011-05-13 17:29:00 +01002462 return new Interval(max, min);
2463 }
2464
2465 /**
Philip Milne7fd94872011-06-07 20:14:17 -07002466 * Returns {@code true} if the {@link #getClass class},
2467 * {@link #min} and {@link #max} properties of this Interval and the
2468 * supplied parameter are pairwise equal; {@code false} otherwise.
Philip Milne3f8956d2011-05-13 17:29:00 +01002469 *
Philip Milne7fd94872011-06-07 20:14:17 -07002470 * @param that the object to compare this interval with
Philip Milne3f8956d2011-05-13 17:29:00 +01002471 *
2472 * @return {@code true} if the specified object is equal to this
Philip Milne7fd94872011-06-07 20:14:17 -07002473 * {@code Interval}, {@code false} otherwise.
Philip Milne3f8956d2011-05-13 17:29:00 +01002474 */
2475 @Override
2476 public boolean equals(Object that) {
2477 if (this == that) {
2478 return true;
2479 }
2480 if (that == null || getClass() != that.getClass()) {
2481 return false;
2482 }
2483
2484 Interval interval = (Interval) that;
2485
2486 if (max != interval.max) {
2487 return false;
2488 }
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002489 //noinspection RedundantIfStatement
Philip Milne3f8956d2011-05-13 17:29:00 +01002490 if (min != interval.min) {
2491 return false;
2492 }
2493
2494 return true;
2495 }
2496
2497 @Override
2498 public int hashCode() {
2499 int result = min;
2500 result = 31 * result + max;
2501 return result;
2502 }
2503
2504 @Override
2505 public String toString() {
2506 return "[" + min + ", " + max + "]";
2507 }
2508 }
2509
Philip Milne899d5922011-07-21 11:39:37 -07002510 /**
2511 * A Spec defines the horizontal or vertical characteristics of a group of
Philip Milne4a145d72011-09-29 14:14:25 -07002512 * cells. Each spec. defines the <em>grid indices</em> and <em>alignment</em>
2513 * along the appropriate axis.
Philip Milne899d5922011-07-21 11:39:37 -07002514 * <p>
2515 * The <em>grid indices</em> are the leading and trailing edges of this cell group.
2516 * See {@link GridLayout} for a description of the conventions used by GridLayout
2517 * for grid indices.
2518 * <p>
2519 * The <em>alignment</em> property specifies how cells should be aligned in this group.
2520 * For row groups, this specifies the vertical alignment.
2521 * For column groups, this specifies the horizontal alignment.
Philip Milne4a145d72011-09-29 14:14:25 -07002522 * <p>
2523 * Use the following static methods to create specs:
2524 * <ul>
2525 * <li>{@link #spec(int)}</li>
2526 * <li>{@link #spec(int, int)}</li>
2527 * <li>{@link #spec(int, Alignment)}</li>
2528 * <li>{@link #spec(int, int, Alignment)}</li>
Philip Milne87260842014-05-22 13:56:03 -07002529 * <li>{@link #spec(int, float)}</li>
2530 * <li>{@link #spec(int, int, float)}</li>
2531 * <li>{@link #spec(int, Alignment, float)}</li>
2532 * <li>{@link #spec(int, int, Alignment, float)}</li>
Philip Milne4a145d72011-09-29 14:14:25 -07002533 * </ul>
2534 *
Philip Milne899d5922011-07-21 11:39:37 -07002535 */
Philip Milne93cd6a62011-07-12 14:49:45 -07002536 public static class Spec {
Philip Milnef6679c82011-09-18 11:36:57 -07002537 static final Spec UNDEFINED = spec(GridLayout.UNDEFINED);
Philip Milne87260842014-05-22 13:56:03 -07002538 static final float DEFAULT_WEIGHT = 0;
Philip Milnef6679c82011-09-18 11:36:57 -07002539
2540 final boolean startDefined;
Philip Milne48b55242011-06-29 11:09:45 -07002541 final Interval span;
Philip Milne93cd6a62011-07-12 14:49:45 -07002542 final Alignment alignment;
Philip Milne87260842014-05-22 13:56:03 -07002543 final float weight;
Philip Milne3f8956d2011-05-13 17:29:00 +01002544
Philip Milne87260842014-05-22 13:56:03 -07002545 private Spec(boolean startDefined, Interval span, Alignment alignment, float weight) {
Philip Milnef6679c82011-09-18 11:36:57 -07002546 this.startDefined = startDefined;
Philip Milne5d1a9842011-07-07 11:47:08 -07002547 this.span = span;
2548 this.alignment = alignment;
Philip Milne87260842014-05-22 13:56:03 -07002549 this.weight = weight;
Philip Milne5d1a9842011-07-07 11:47:08 -07002550 }
2551
Philip Milne87260842014-05-22 13:56:03 -07002552 private Spec(boolean startDefined, int start, int size, Alignment alignment, float weight) {
2553 this(startDefined, new Interval(start, start + size), alignment, weight);
Philip Milne93cd6a62011-07-12 14:49:45 -07002554 }
2555
Yigit Boyar38d909a2015-03-05 14:39:38 -08002556 private Alignment getAbsoluteAlignment(boolean horizontal) {
Yigit Boyar6dafd872015-03-05 13:59:56 -08002557 if (alignment != UNDEFINED_ALIGNMENT) {
2558 return alignment;
2559 }
2560 if (weight == 0f) {
2561 return horizontal ? START : BASELINE;
2562 }
2563 return FILL;
2564 }
2565
Philip Milnef6679c82011-09-18 11:36:57 -07002566 final Spec copyWriteSpan(Interval span) {
Philip Milne87260842014-05-22 13:56:03 -07002567 return new Spec(startDefined, span, alignment, weight);
Philip Milne93cd6a62011-07-12 14:49:45 -07002568 }
2569
Philip Milnef6679c82011-09-18 11:36:57 -07002570 final Spec copyWriteAlignment(Alignment alignment) {
Philip Milne87260842014-05-22 13:56:03 -07002571 return new Spec(startDefined, span, alignment, weight);
Philip Milne93cd6a62011-07-12 14:49:45 -07002572 }
2573
Philip Milnef6679c82011-09-18 11:36:57 -07002574 final int getFlexibility() {
Philip Milne87260842014-05-22 13:56:03 -07002575 return (alignment == UNDEFINED_ALIGNMENT && weight == 0) ? INFLEXIBLE : CAN_STRETCH;
Philip Milne5d1a9842011-07-07 11:47:08 -07002576 }
2577
Philip Milne3f8956d2011-05-13 17:29:00 +01002578 /**
Philip Milne93cd6a62011-07-12 14:49:45 -07002579 * Returns {@code true} if the {@code class}, {@code alignment} and {@code span}
2580 * properties of this Spec and the supplied parameter are pairwise equal,
Philip Milne7fd94872011-06-07 20:14:17 -07002581 * {@code false} otherwise.
Philip Milne3f8956d2011-05-13 17:29:00 +01002582 *
Philip Milne93cd6a62011-07-12 14:49:45 -07002583 * @param that the object to compare this spec with
Philip Milne3f8956d2011-05-13 17:29:00 +01002584 *
2585 * @return {@code true} if the specified object is equal to this
Philip Milne93cd6a62011-07-12 14:49:45 -07002586 * {@code Spec}; {@code false} otherwise
Philip Milne3f8956d2011-05-13 17:29:00 +01002587 */
2588 @Override
2589 public boolean equals(Object that) {
2590 if (this == that) {
2591 return true;
2592 }
2593 if (that == null || getClass() != that.getClass()) {
2594 return false;
2595 }
2596
Philip Milne93cd6a62011-07-12 14:49:45 -07002597 Spec spec = (Spec) that;
Philip Milne3f8956d2011-05-13 17:29:00 +01002598
Philip Milne93cd6a62011-07-12 14:49:45 -07002599 if (!alignment.equals(spec.alignment)) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002600 return false;
2601 }
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002602 //noinspection RedundantIfStatement
Philip Milne93cd6a62011-07-12 14:49:45 -07002603 if (!span.equals(spec.span)) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002604 return false;
2605 }
2606
2607 return true;
2608 }
2609
2610 @Override
2611 public int hashCode() {
2612 int result = span.hashCode();
2613 result = 31 * result + alignment.hashCode();
2614 return result;
2615 }
2616 }
2617
Philip Milne3f8956d2011-05-13 17:29:00 +01002618 /**
Philip Milne93cd6a62011-07-12 14:49:45 -07002619 * Return a Spec, {@code spec}, where:
2620 * <ul>
2621 * <li> {@code spec.span = [start, start + size]} </li>
2622 * <li> {@code spec.alignment = alignment} </li>
Philip Milne87260842014-05-22 13:56:03 -07002623 * <li> {@code spec.weight = weight} </li>
Philip Milne93cd6a62011-07-12 14:49:45 -07002624 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002625 * <p>
2626 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne93cd6a62011-07-12 14:49:45 -07002627 *
2628 * @param start the start
2629 * @param size the size
2630 * @param alignment the alignment
Philip Milne87260842014-05-22 13:56:03 -07002631 * @param weight the weight
2632 */
2633 public static Spec spec(int start, int size, Alignment alignment, float weight) {
2634 return new Spec(start != UNDEFINED, start, size, alignment, weight);
2635 }
2636
2637 /**
2638 * Equivalent to: {@code spec(start, 1, alignment, weight)}.
2639 *
2640 * @param start the start
2641 * @param alignment the alignment
2642 * @param weight the weight
2643 */
2644 public static Spec spec(int start, Alignment alignment, float weight) {
2645 return spec(start, 1, alignment, weight);
2646 }
2647
2648 /**
2649 * Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
2650 * where {@code default_alignment} is specified in
2651 * {@link android.widget.GridLayout.LayoutParams}.
2652 *
2653 * @param start the start
2654 * @param size the size
2655 * @param weight the weight
2656 */
2657 public static Spec spec(int start, int size, float weight) {
2658 return spec(start, size, UNDEFINED_ALIGNMENT, weight);
2659 }
2660
2661 /**
2662 * Equivalent to: {@code spec(start, 1, weight)}.
2663 *
2664 * @param start the start
2665 * @param weight the weight
2666 */
2667 public static Spec spec(int start, float weight) {
2668 return spec(start, 1, weight);
2669 }
2670
2671 /**
2672 * Equivalent to: {@code spec(start, size, alignment, 0f)}.
2673 *
2674 * @param start the start
2675 * @param size the size
2676 * @param alignment the alignment
Philip Milne93cd6a62011-07-12 14:49:45 -07002677 */
2678 public static Spec spec(int start, int size, Alignment alignment) {
Philip Milne87260842014-05-22 13:56:03 -07002679 return spec(start, size, alignment, Spec.DEFAULT_WEIGHT);
Philip Milne93cd6a62011-07-12 14:49:45 -07002680 }
2681
2682 /**
2683 * Return a Spec, {@code spec}, where:
2684 * <ul>
2685 * <li> {@code spec.span = [start, start + 1]} </li>
2686 * <li> {@code spec.alignment = alignment} </li>
2687 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002688 * <p>
2689 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne93cd6a62011-07-12 14:49:45 -07002690 *
2691 * @param start the start index
2692 * @param alignment the alignment
Philip Milne7b757812012-09-19 18:13:44 -07002693 *
2694 * @see #spec(int, int, Alignment)
Philip Milne93cd6a62011-07-12 14:49:45 -07002695 */
2696 public static Spec spec(int start, Alignment alignment) {
2697 return spec(start, 1, alignment);
2698 }
2699
2700 /**
Philip Milne5125e212011-07-21 11:39:37 -07002701 * Return a Spec, {@code spec}, where:
2702 * <ul>
2703 * <li> {@code spec.span = [start, start + size]} </li>
2704 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002705 * <p>
2706 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne5125e212011-07-21 11:39:37 -07002707 *
2708 * @param start the start
2709 * @param size the size
Philip Milne7b757812012-09-19 18:13:44 -07002710 *
2711 * @see #spec(int, Alignment)
Philip Milne5125e212011-07-21 11:39:37 -07002712 */
2713 public static Spec spec(int start, int size) {
2714 return spec(start, size, UNDEFINED_ALIGNMENT);
2715 }
2716
2717 /**
2718 * Return a Spec, {@code spec}, where:
2719 * <ul>
2720 * <li> {@code spec.span = [start, start + 1]} </li>
2721 * </ul>
Philip Milne7b757812012-09-19 18:13:44 -07002722 * <p>
2723 * To leave the start index undefined, use the value {@link #UNDEFINED}.
Philip Milne5125e212011-07-21 11:39:37 -07002724 *
2725 * @param start the start index
Philip Milne7b757812012-09-19 18:13:44 -07002726 *
2727 * @see #spec(int, int)
Philip Milne5125e212011-07-21 11:39:37 -07002728 */
2729 public static Spec spec(int start) {
2730 return spec(start, 1);
2731 }
2732
2733 /**
Philip Milne3f8956d2011-05-13 17:29:00 +01002734 * Alignments specify where a view should be placed within a cell group and
2735 * what size it should be.
2736 * <p>
Philip Milne93cd6a62011-07-12 14:49:45 -07002737 * The {@link LayoutParams} class contains a {@link LayoutParams#rowSpec rowSpec}
2738 * and a {@link LayoutParams#columnSpec columnSpec} each of which contains an
2739 * {@code alignment}. Overall placement of the view in the cell
Philip Milne3f8956d2011-05-13 17:29:00 +01002740 * group is specified by the two alignments which act along each axis independently.
2741 * <p>
Philip Milnea1f7b102011-06-23 11:10:13 -07002742 * The GridLayout class defines the most common alignments used in general layout:
Philip Milne6216e872012-02-16 17:15:50 -08002743 * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START},
2744 * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}.
Philip Milnea1f7b102011-06-23 11:10:13 -07002745 */
2746 /*
Philip Milnec9885f62011-06-15 17:07:35 -07002747 * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)},
Philip Milne3f8956d2011-05-13 17:29:00 +01002748 * to return the appropriate value for the type of alignment being defined.
2749 * The enclosing algorithms position the children
Philip Milne1e548252011-06-16 19:02:33 -07002750 * so that the locations defined by the alignment values
Philip Milne3f8956d2011-05-13 17:29:00 +01002751 * are the same for all of the views in a group.
2752 * <p>
Philip Milne3f8956d2011-05-13 17:29:00 +01002753 */
Philip Milnec9885f62011-06-15 17:07:35 -07002754 public static abstract class Alignment {
Philip Milne48b55242011-06-29 11:09:45 -07002755 Alignment() {
Philip Milnea1f7b102011-06-23 11:10:13 -07002756 }
2757
Philip Milne6216e872012-02-16 17:15:50 -08002758 abstract int getGravityOffset(View view, int cellDelta);
2759
Philip Milne3f8956d2011-05-13 17:29:00 +01002760 /**
2761 * Returns an alignment value. In the case of vertical alignments the value
2762 * returned should indicate the distance from the top of the view to the
2763 * alignment location.
2764 * For horizontal alignments measurement is made from the left edge of the component.
2765 *
Philip Milnec9885f62011-06-15 17:07:35 -07002766 * @param view the view to which this alignment should be applied
2767 * @param viewSize the measured size of the view
Philip Milne7a23b492012-04-24 22:12:36 -07002768 * @param mode the basis of alignment: CLIP or OPTICAL
Philip Milneb3a8c542011-06-20 16:02:59 -07002769 * @return the alignment value
Philip Milne3f8956d2011-05-13 17:29:00 +01002770 */
Philip Milne7a23b492012-04-24 22:12:36 -07002771 abstract int getAlignmentValue(View view, int viewSize, int mode);
Philip Milne3f8956d2011-05-13 17:29:00 +01002772
2773 /**
2774 * Returns the size of the view specified by this alignment.
2775 * In the case of vertical alignments this method should return a height; for
2776 * horizontal alignments this method should return the width.
Philip Milnec9885f62011-06-15 17:07:35 -07002777 * <p>
2778 * The default implementation returns {@code viewSize}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002779 *
Philip Milnec9885f62011-06-15 17:07:35 -07002780 * @param view the view to which this alignment should be applied
2781 * @param viewSize the measured size of the view
2782 * @param cellSize the size of the cell into which this view will be placed
Philip Milneb3a8c542011-06-20 16:02:59 -07002783 * @return the aligned size
Philip Milne3f8956d2011-05-13 17:29:00 +01002784 */
Philip Milne6216e872012-02-16 17:15:50 -08002785 int getSizeInCell(View view, int viewSize, int cellSize) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002786 return viewSize;
2787 }
Philip Milnea1f7b102011-06-23 11:10:13 -07002788
Philip Milne48b55242011-06-29 11:09:45 -07002789 Bounds getBounds() {
Philip Milnea1f7b102011-06-23 11:10:13 -07002790 return new Bounds();
2791 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002792 }
2793
Philip Milnef6679c82011-09-18 11:36:57 -07002794 static final Alignment UNDEFINED_ALIGNMENT = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002795 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002796 int getGravityOffset(View view, int cellDelta) {
2797 return UNDEFINED;
2798 }
2799
2800 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002801 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne5125e212011-07-21 11:39:37 -07002802 return UNDEFINED;
2803 }
2804 };
2805
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002806 /**
2807 * Indicates that a view should be aligned with the <em>start</em>
2808 * edges of the other views in its cell group.
2809 */
Philip Milnec9885f62011-06-15 17:07:35 -07002810 private static final Alignment LEADING = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002811 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002812 int getGravityOffset(View view, int cellDelta) {
2813 return 0;
2814 }
2815
2816 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002817 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002818 return 0;
2819 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002820 };
2821
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002822 /**
2823 * Indicates that a view should be aligned with the <em>end</em>
2824 * edges of the other views in its cell group.
2825 */
Philip Milnec9885f62011-06-15 17:07:35 -07002826 private static final Alignment TRAILING = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002827 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002828 int getGravityOffset(View view, int cellDelta) {
2829 return cellDelta;
2830 }
2831
2832 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002833 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002834 return viewSize;
2835 }
2836 };
2837
2838 /**
2839 * Indicates that a view should be aligned with the <em>top</em>
2840 * edges of the other views in its cell group.
2841 */
2842 public static final Alignment TOP = LEADING;
2843
2844 /**
2845 * Indicates that a view should be aligned with the <em>bottom</em>
2846 * edges of the other views in its cell group.
2847 */
2848 public static final Alignment BOTTOM = TRAILING;
2849
2850 /**
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002851 * Indicates that a view should be aligned with the <em>start</em>
Philip Milne3f8956d2011-05-13 17:29:00 +01002852 * edges of the other views in its cell group.
2853 */
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002854 public static final Alignment START = LEADING;
2855
2856 /**
2857 * Indicates that a view should be aligned with the <em>end</em>
2858 * edges of the other views in its cell group.
2859 */
2860 public static final Alignment END = TRAILING;
2861
Philip Milne6216e872012-02-16 17:15:50 -08002862 private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002863 return new Alignment() {
2864 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002865 int getGravityOffset(View view, int cellDelta) {
2866 return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta);
2867 }
2868
2869 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002870 public int getAlignmentValue(View view, int viewSize, int mode) {
2871 return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize, mode);
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002872 }
2873 };
2874 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002875
2876 /**
2877 * Indicates that a view should be aligned with the <em>left</em>
2878 * edges of the other views in its cell group.
2879 */
Philip Milne6216e872012-02-16 17:15:50 -08002880 public static final Alignment LEFT = createSwitchingAlignment(START, END);
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002881
2882 /**
2883 * Indicates that a view should be aligned with the <em>right</em>
2884 * edges of the other views in its cell group.
2885 */
Philip Milne6216e872012-02-16 17:15:50 -08002886 public static final Alignment RIGHT = createSwitchingAlignment(END, START);
Philip Milne3f8956d2011-05-13 17:29:00 +01002887
2888 /**
2889 * Indicates that a view should be <em>centered</em> with the other views in its cell group.
Philip Milne93cd6a62011-07-12 14:49:45 -07002890 * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and {@link
2891 * LayoutParams#columnSpec columnSpecs}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002892 */
Philip Milnec9885f62011-06-15 17:07:35 -07002893 public static final Alignment CENTER = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002894 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002895 int getGravityOffset(View view, int cellDelta) {
2896 return cellDelta >> 1;
2897 }
2898
2899 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002900 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002901 return viewSize >> 1;
2902 }
2903 };
2904
2905 /**
2906 * Indicates that a view should be aligned with the <em>baselines</em>
2907 * of the other views in its cell group.
Philip Milne93cd6a62011-07-12 14:49:45 -07002908 * This constant may only be used as an alignment in {@link LayoutParams#rowSpec rowSpecs}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002909 *
2910 * @see View#getBaseline()
2911 */
Philip Milnec9885f62011-06-15 17:07:35 -07002912 public static final Alignment BASELINE = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002913 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002914 int getGravityOffset(View view, int cellDelta) {
2915 return 0; // baseline gravity is top
2916 }
2917
2918 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002919 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milnea8416442013-02-25 10:49:39 -08002920 if (view.getVisibility() == GONE) {
2921 return 0;
2922 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002923 int baseline = view.getBaseline();
Philip Milne7b757812012-09-19 18:13:44 -07002924 return baseline == -1 ? UNDEFINED : baseline;
Philip Milnea1f7b102011-06-23 11:10:13 -07002925 }
2926
2927 @Override
2928 public Bounds getBounds() {
2929 return new Bounds() {
2930 /*
2931 In a baseline aligned row in which some components define a baseline
2932 and some don't, we need a third variable to properly account for all
2933 the sizes. This tracks the maximum size of all the components -
2934 including those that don't define a baseline.
2935 */
2936 private int size;
2937
2938 @Override
2939 protected void reset() {
2940 super.reset();
Philip Milne48b55242011-06-29 11:09:45 -07002941 size = Integer.MIN_VALUE;
Philip Milnea1f7b102011-06-23 11:10:13 -07002942 }
2943
2944 @Override
2945 protected void include(int before, int after) {
2946 super.include(before, after);
2947 size = max(size, before + after);
2948 }
2949
2950 @Override
Philip Milne48b55242011-06-29 11:09:45 -07002951 protected int size(boolean min) {
2952 return max(super.size(min), size);
Philip Milnea1f7b102011-06-23 11:10:13 -07002953 }
2954
2955 @Override
Philip Milne1557fd72012-04-04 23:41:34 -07002956 protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) {
2957 return max(0, super.getOffset(gl, c, a, size, hrz));
Philip Milnea1f7b102011-06-23 11:10:13 -07002958 }
2959 };
Philip Milne3f8956d2011-05-13 17:29:00 +01002960 }
Philip Milne3f8956d2011-05-13 17:29:00 +01002961 };
2962
2963 /**
2964 * Indicates that a view should expanded to fit the boundaries of its cell group.
Philip Milne93cd6a62011-07-12 14:49:45 -07002965 * This constant may be used in both {@link LayoutParams#rowSpec rowSpecs} and
2966 * {@link LayoutParams#columnSpec columnSpecs}.
Philip Milne3f8956d2011-05-13 17:29:00 +01002967 */
2968 public static final Alignment FILL = new Alignment() {
Fabrice Di Meglio47d248e2012-02-08 17:57:48 -08002969 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002970 int getGravityOffset(View view, int cellDelta) {
2971 return 0;
2972 }
2973
2974 @Override
Philip Milne7a23b492012-04-24 22:12:36 -07002975 public int getAlignmentValue(View view, int viewSize, int mode) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002976 return UNDEFINED;
2977 }
2978
Philip Milnec9885f62011-06-15 17:07:35 -07002979 @Override
Philip Milne6216e872012-02-16 17:15:50 -08002980 public int getSizeInCell(View view, int viewSize, int cellSize) {
Philip Milne3f8956d2011-05-13 17:29:00 +01002981 return cellSize;
2982 }
2983 };
Philip Milne48b55242011-06-29 11:09:45 -07002984
Philip Milnef6679c82011-09-18 11:36:57 -07002985 static boolean canStretch(int flexibility) {
Philip Milne5d1a9842011-07-07 11:47:08 -07002986 return (flexibility & CAN_STRETCH) != 0;
2987 }
2988
Philip Milne5125e212011-07-21 11:39:37 -07002989 private static final int INFLEXIBLE = 0;
Philip Milne4c8cf4c2011-08-03 11:50:50 -07002990 private static final int CAN_STRETCH = 2;
Jim Miller452eec32011-06-16 18:32:44 -07002991}