blob: 8ee31e212cbfce928d762d00c8c2741e7d9415e0 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
20
Siva Velusamy94a6d152015-05-05 15:07:00 -070021import android.annotation.NonNull;
Mathew Inwood978c6e22018-08-21 15:58:55 +010022import android.annotation.UnsupportedAppUsage;
svetoslavganov75986cf2009-05-14 22:28:01 -070023import android.content.Context;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070024import android.content.res.TypedArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070025import android.graphics.Rect;
Adam Powell7da4b732012-12-07 15:28:33 -080026import android.os.Build;
Aurimas Liutikas99441c52016-10-11 16:48:32 -070027import android.util.ArrayMap;
svetoslavganov75986cf2009-05-14 22:28:01 -070028import android.util.AttributeSet;
Romain Guy6876b4f2013-06-03 14:25:56 -070029import android.util.Pools.SynchronizedPool;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070030import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070031import android.view.Gravity;
32import android.view.View;
33import android.view.ViewDebug;
34import android.view.ViewGroup;
Siva Velusamy94a6d152015-05-05 15:07:00 -070035import android.view.ViewHierarchyEncoder;
svetoslavganov75986cf2009-05-14 22:28:01 -070036import android.view.accessibility.AccessibilityEvent;
Ashley Rose55f9f922019-01-28 19:29:36 -050037import android.view.inspector.InspectableProperty;
svetoslavganov75986cf2009-05-14 22:28:01 -070038import android.widget.RemoteViews.RemoteView;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070039
Aurimas Liutikas99441c52016-10-11 16:48:32 -070040import com.android.internal.R;
41
42import java.util.ArrayDeque;
43import java.util.ArrayList;
44import java.util.Comparator;
45import java.util.SortedSet;
46import java.util.TreeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
49 * A Layout where the positions of the children can be described in relation to each other or to the
Romain Guy520c4202010-01-04 10:56:38 -080050 * parent.
Romain Guya1f3e4a2009-06-04 15:10:46 -070051 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052 * <p>
53 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
54 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
55 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
56 * {@link #ALIGN_PARENT_BOTTOM}.
57 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070058 *
Adam Powell2c8cc972012-12-07 18:04:51 -080059 * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
60 * a measurement bug that could cause child views to be measured with incorrect
61 * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
62 * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
63 * for more details.) This was triggered when a RelativeLayout container was placed in
64 * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
65 * not equipped to properly measure with the MeasureSpec mode
66 * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
67 * this would silently work anyway as RelativeLayout would pass a very large
68 * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
69 *
70 * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
71 * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
72 * version 18 or newer will receive the correct behavior</p>
73 *
Scott Main4c359b72012-07-24 15:51:27 -070074 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
75 * Layout</a> guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070076 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 * <p>
78 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
79 * layout attributes
80 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070081 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 * @attr ref android.R.styleable#RelativeLayout_gravity
83 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
84 */
85@RemoteView
86public class RelativeLayout extends ViewGroup {
87 public static final int TRUE = -1;
88
89 /**
90 * Rule that aligns a child's right edge with another child's left edge.
91 */
92 public static final int LEFT_OF = 0;
93 /**
94 * Rule that aligns a child's left edge with another child's right edge.
95 */
96 public static final int RIGHT_OF = 1;
97 /**
98 * Rule that aligns a child's bottom edge with another child's top edge.
99 */
100 public static final int ABOVE = 2;
101 /**
102 * Rule that aligns a child's top edge with another child's bottom edge.
103 */
104 public static final int BELOW = 3;
105
106 /**
107 * Rule that aligns a child's baseline with another child's baseline.
108 */
109 public static final int ALIGN_BASELINE = 4;
110 /**
111 * Rule that aligns a child's left edge with another child's left edge.
112 */
113 public static final int ALIGN_LEFT = 5;
114 /**
115 * Rule that aligns a child's top edge with another child's top edge.
116 */
117 public static final int ALIGN_TOP = 6;
118 /**
119 * Rule that aligns a child's right edge with another child's right edge.
120 */
121 public static final int ALIGN_RIGHT = 7;
122 /**
123 * Rule that aligns a child's bottom edge with another child's bottom edge.
124 */
125 public static final int ALIGN_BOTTOM = 8;
126
127 /**
128 * Rule that aligns the child's left edge with its RelativeLayout
129 * parent's left edge.
130 */
131 public static final int ALIGN_PARENT_LEFT = 9;
132 /**
133 * Rule that aligns the child's top edge with its RelativeLayout
134 * parent's top edge.
135 */
136 public static final int ALIGN_PARENT_TOP = 10;
137 /**
138 * Rule that aligns the child's right edge with its RelativeLayout
139 * parent's right edge.
140 */
141 public static final int ALIGN_PARENT_RIGHT = 11;
142 /**
143 * Rule that aligns the child's bottom edge with its RelativeLayout
144 * parent's bottom edge.
145 */
146 public static final int ALIGN_PARENT_BOTTOM = 12;
147
148 /**
149 * Rule that centers the child with respect to the bounds of its
150 * RelativeLayout parent.
151 */
152 public static final int CENTER_IN_PARENT = 13;
153 /**
154 * Rule that centers the child horizontally with respect to the
155 * bounds of its RelativeLayout parent.
156 */
157 public static final int CENTER_HORIZONTAL = 14;
158 /**
159 * Rule that centers the child vertically with respect to the
160 * bounds of its RelativeLayout parent.
161 */
162 public static final int CENTER_VERTICAL = 15;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700163 /**
164 * Rule that aligns a child's end edge with another child's start edge.
165 */
166 public static final int START_OF = 16;
167 /**
168 * Rule that aligns a child's start edge with another child's end edge.
169 */
170 public static final int END_OF = 17;
171 /**
172 * Rule that aligns a child's start edge with another child's start edge.
173 */
174 public static final int ALIGN_START = 18;
175 /**
176 * Rule that aligns a child's end edge with another child's end edge.
177 */
178 public static final int ALIGN_END = 19;
179 /**
180 * Rule that aligns the child's start edge with its RelativeLayout
181 * parent's start edge.
182 */
183 public static final int ALIGN_PARENT_START = 20;
184 /**
185 * Rule that aligns the child's end edge with its RelativeLayout
186 * parent's end edge.
187 */
188 public static final int ALIGN_PARENT_END = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700190 private static final int VERB_COUNT = 22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191
Romain Guybc5d8762012-01-06 16:40:49 -0800192
193 private static final int[] RULES_VERTICAL = {
194 ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
195 };
196
197 private static final int[] RULES_HORIZONTAL = {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700198 LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
Romain Guybc5d8762012-01-06 16:40:49 -0800199 };
200
Chet Haase28308e82014-07-22 16:38:16 -0700201 /**
202 * Used to indicate left/right/top/bottom should be inferred from constraints
203 */
204 private static final int VALUE_NOT_SET = Integer.MIN_VALUE;
205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 private View mBaselineView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207
Clara Bayarri60368582018-11-22 15:13:52 +0000208 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700209 private int mGravity = Gravity.START | Gravity.TOP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 private final Rect mContentBounds = new Rect();
211 private final Rect mSelfBounds = new Rect();
212 private int mIgnoreGravity;
213
Romain Guy725015a2009-06-23 14:27:34 -0700214 private SortedSet<View> mTopToBottomLeftToRightSet = null;
Aurimas Liutikas99441c52016-10-11 16:48:32 -0700215
Romain Guy725015a2009-06-23 14:27:34 -0700216 private boolean mDirtyHierarchy;
Romain Guy6876b4f2013-06-03 14:25:56 -0700217 private View[] mSortedHorizontalChildren;
218 private View[] mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700219 private final DependencyGraph mGraph = new DependencyGraph();
svetoslavganov75986cf2009-05-14 22:28:01 -0700220
Adam Powell7da4b732012-12-07 15:28:33 -0800221 // Compatibility hack. Old versions of the platform had problems
222 // with MeasureSpec value overflow and RelativeLayout was one source of them.
223 // Some apps came to rely on them. :(
224 private boolean mAllowBrokenMeasureSpecs = false;
Romain Guy76d59a32013-04-08 10:51:35 -0700225 // Compatibility hack. Old versions of the platform would not take
226 // margins and padding into account when generating the height measure spec
227 // for children during the horizontal measure pass.
228 private boolean mMeasureVerticalWithPaddingMargin = false;
Adam Powell7da4b732012-12-07 15:28:33 -0800229
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800230 // A default width used for RTL measure pass
Philip Milneca2e9e12013-04-22 12:44:29 -0700231 /**
232 * Value reduced so as not to interfere with View's measurement spec. flags. See:
233 * {@link View#MEASURED_SIZE_MASK}.
234 * {@link View#MEASURED_STATE_TOO_SMALL}.
235 **/
Philip Milne3c647d22013-04-23 14:31:23 -0700236 private static final int DEFAULT_WIDTH = 0x00010000;
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 public RelativeLayout(Context context) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700239 this(context, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 }
241
242 public RelativeLayout(Context context, AttributeSet attrs) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700243 this(context, attrs, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
245
Alan Viverette617feb92013-09-09 18:09:13 -0700246 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700247 this(context, attrs, defStyleAttr, 0);
Alan Viverette617feb92013-09-09 18:09:13 -0700248 }
249
250 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
251 super(context, attrs, defStyleAttr, defStyleRes);
Alan Viveretted6479ec2013-09-10 17:03:02 -0700252 initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
Romain Guy76d59a32013-04-08 10:51:35 -0700253 queryCompatibilityModes(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 }
255
Alan Viveretted6479ec2013-09-10 17:03:02 -0700256 private void initFromAttributes(
257 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
258 final TypedArray a = context.obtainStyledAttributes(
259 attrs, R.styleable.RelativeLayout, defStyleAttr, defStyleRes);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -0800260 saveAttributeDataForStyleable(context, R.styleable.RelativeLayout,
261 attrs, a, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
263 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
264 a.recycle();
265 }
266
Romain Guy76d59a32013-04-08 10:51:35 -0700267 private void queryCompatibilityModes(Context context) {
268 int version = context.getApplicationInfo().targetSdkVersion;
269 mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
270 mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
271 }
272
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700273 @Override
274 public boolean shouldDelayChildPressedState() {
275 return false;
276 }
277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 /**
279 * Defines which View is ignored when the gravity is applied. This setting has no
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700280 * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 *
282 * @param viewId The id of the View to be ignored by gravity, or 0 if no View
283 * should be ignored.
284 *
285 * @see #setGravity(int)
286 *
287 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
288 */
289 @android.view.RemotableViewMethod
290 public void setIgnoreGravity(int viewId) {
291 mIgnoreGravity = viewId;
292 }
293
294 /**
Ashley Rose55f9f922019-01-28 19:29:36 -0500295 * Get the id of the View to be ignored by gravity
296 *
297 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
298 */
299 @InspectableProperty
300 public int getIgnoreGravity() {
301 return mIgnoreGravity;
302 }
303
304 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700305 * Describes how the child views are positioned.
306 *
307 * @return the gravity.
308 *
309 * @see #setGravity(int)
310 * @see android.view.Gravity
311 *
312 * @attr ref android.R.styleable#RelativeLayout_gravity
313 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500314 @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
Philip Milne1018fb42012-03-13 12:00:04 -0700315 public int getGravity() {
316 return mGravity;
317 }
318
319 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 * Describes how the child views are positioned. Defaults to
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700321 * <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 *
Adam Powell1fec24e2011-09-15 14:19:30 -0700323 * <p>Note that since RelativeLayout considers the positioning of each child
324 * relative to one another to be significant, setting gravity will affect
325 * the positioning of all children as a single unit within the parent.
326 * This happens after children have been relatively positioned.</p>
327 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 * @param gravity See {@link android.view.Gravity}
329 *
330 * @see #setHorizontalGravity(int)
331 * @see #setVerticalGravity(int)
332 *
333 * @attr ref android.R.styleable#RelativeLayout_gravity
334 */
335 @android.view.RemotableViewMethod
336 public void setGravity(int gravity) {
337 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700338 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700339 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 }
341
342 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
343 gravity |= Gravity.TOP;
344 }
345
346 mGravity = gravity;
347 requestLayout();
348 }
349 }
350
351 @android.view.RemotableViewMethod
352 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700353 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
354 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
355 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 requestLayout();
357 }
358 }
359
360 @android.view.RemotableViewMethod
361 public void setVerticalGravity(int verticalGravity) {
362 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
363 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
364 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
365 requestLayout();
366 }
367 }
368
369 @Override
370 public int getBaseline() {
371 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
372 }
373
374 @Override
Romain Guy725015a2009-06-23 14:27:34 -0700375 public void requestLayout() {
376 super.requestLayout();
377 mDirtyHierarchy = true;
378 }
379
380 private void sortChildren() {
Romain Guy6876b4f2013-06-03 14:25:56 -0700381 final int count = getChildCount();
382 if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
383 mSortedVerticalChildren = new View[count];
384 }
385
386 if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
387 mSortedHorizontalChildren = new View[count];
388 }
Romain Guy725015a2009-06-23 14:27:34 -0700389
390 final DependencyGraph graph = mGraph;
391 graph.clear();
392
393 for (int i = 0; i < count; i++) {
Romain Guy6876b4f2013-06-03 14:25:56 -0700394 graph.add(getChildAt(i));
Romain Guy725015a2009-06-23 14:27:34 -0700395 }
396
Romain Guybc5d8762012-01-06 16:40:49 -0800397 graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
398 graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
Romain Guy725015a2009-06-23 14:27:34 -0700399 }
400
401 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Romain Guy725015a2009-06-23 14:27:34 -0700403 if (mDirtyHierarchy) {
404 mDirtyHierarchy = false;
405 sortChildren();
406 }
407
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 int myWidth = -1;
409 int myHeight = -1;
410
411 int width = 0;
412 int height = 0;
413
Adam Powell132a7422012-09-30 12:39:56 -0700414 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
415 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
416 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
417 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418
419 // Record our dimensions if they are known;
420 if (widthMode != MeasureSpec.UNSPECIFIED) {
421 myWidth = widthSize;
422 }
423
424 if (heightMode != MeasureSpec.UNSPECIFIED) {
425 myHeight = heightSize;
426 }
427
428 if (widthMode == MeasureSpec.EXACTLY) {
429 width = myWidth;
430 }
431
432 if (heightMode == MeasureSpec.EXACTLY) {
433 height = myHeight;
434 }
435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 View ignore = null;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700437 int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700438 final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
440 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
441
442 int left = Integer.MAX_VALUE;
443 int top = Integer.MAX_VALUE;
444 int right = Integer.MIN_VALUE;
445 int bottom = Integer.MIN_VALUE;
446
Romain Guyf7dabb02009-06-25 14:47:14 -0700447 boolean offsetHorizontalAxis = false;
448 boolean offsetVerticalAxis = false;
449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
451 ignore = findViewById(mIgnoreGravity);
452 }
453
Romain Guyf7dabb02009-06-25 14:47:14 -0700454 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
455 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
456
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800457 // We need to know our size for doing the correct computation of children positioning in RTL
458 // mode but there is no practical way to get it instead of running the code below.
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800459 // So, instead of running the code twice, we just set the width to a "default display width"
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800460 // before the computation and then, as a last pass, we will update their real position with
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800461 // an offset equals to "DEFAULT_WIDTH - width".
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800462 final int layoutDirection = getLayoutDirection();
463 if (isLayoutRtl() && myWidth == -1) {
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800464 myWidth = DEFAULT_WIDTH;
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800465 }
466
Romain Guye24ef602009-06-25 13:01:55 -0700467 View[] views = mSortedHorizontalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700468 int count = views.length;
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800469
Romain Guy725015a2009-06-23 14:27:34 -0700470 for (int i = 0; i < count; i++) {
471 View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 if (child.getVisibility() != GONE) {
473 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800474 int[] rules = params.getRules(layoutDirection);
Romain Guy95607032009-06-24 14:37:03 -0700475
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800476 applyHorizontalSizeRules(params, myWidth, rules);
Romain Guyf782e602009-06-25 15:26:49 -0700477 measureChildHorizontal(child, params, myWidth, myHeight);
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800478
Romain Guyf7dabb02009-06-25 14:47:14 -0700479 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
480 offsetHorizontalAxis = true;
481 }
Romain Guy725015a2009-06-23 14:27:34 -0700482 }
483 }
484
Romain Guye24ef602009-06-25 13:01:55 -0700485 views = mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700486 count = views.length;
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700487 final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
Romain Guyf7dabb02009-06-25 14:47:14 -0700488
Romain Guy725015a2009-06-23 14:27:34 -0700489 for (int i = 0; i < count; i++) {
Alan Viverettee1a65712015-01-14 12:30:04 -0800490 final View child = views[i];
Romain Guy725015a2009-06-23 14:27:34 -0700491 if (child.getVisibility() != GONE) {
Alan Viverettee1a65712015-01-14 12:30:04 -0800492 final LayoutParams params = (LayoutParams) child.getLayoutParams();
493
494 applyVerticalSizeRules(params, myHeight, child.getBaseline());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 measureChild(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700496 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
497 offsetVerticalAxis = true;
498 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499
Romain Guyf7dabb02009-06-25 14:47:14 -0700500 if (isWrapContentWidth) {
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800501 if (isLayoutRtl()) {
Chet Haasee8222dd2013-09-05 07:44:18 -0700502 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700503 width = Math.max(width, myWidth - params.mLeft);
504 } else {
Roozbeh Pournader4d4596a2017-07-13 18:35:09 -0700505 width = Math.max(width, myWidth - params.mLeft + params.leftMargin);
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700506 }
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800507 } else {
Chet Haasee8222dd2013-09-05 07:44:18 -0700508 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700509 width = Math.max(width, params.mRight);
510 } else {
511 width = Math.max(width, params.mRight + params.rightMargin);
512 }
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800513 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700515
516 if (isWrapContentHeight) {
Chet Haasee8222dd2013-09-05 07:44:18 -0700517 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700518 height = Math.max(height, params.mBottom);
519 } else {
520 height = Math.max(height, params.mBottom + params.bottomMargin);
521 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 }
523
524 if (child != ignore || verticalGravity) {
525 left = Math.min(left, params.mLeft - params.leftMargin);
526 top = Math.min(top, params.mTop - params.topMargin);
527 }
528
529 if (child != ignore || horizontalGravity) {
530 right = Math.max(right, params.mRight + params.rightMargin);
531 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
532 }
533 }
534 }
535
Alan Viverette0e14a852015-03-09 18:01:19 -0700536 // Use the top-start-most laid out view as the baseline. RTL offsets are
537 // applied later, so we can use the left-most edge as the starting edge.
538 View baselineView = null;
539 LayoutParams baselineParams = null;
540 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000541 final View child = views[i];
Alan Viverette0e14a852015-03-09 18:01:19 -0700542 if (child.getVisibility() != GONE) {
543 final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
544 if (baselineView == null || baselineParams == null
545 || compareLayoutPosition(childParams, baselineParams) < 0) {
546 baselineView = child;
547 baselineParams = childParams;
548 }
549 }
550 }
551 mBaselineView = baselineView;
552
Romain Guyf7dabb02009-06-25 14:47:14 -0700553 if (isWrapContentWidth) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700554 // Width already has left padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 // the right of each child view
556 width += mPaddingRight;
557
Adam Powell758d5a42013-09-13 09:22:30 -0700558 if (mLayoutParams != null && mLayoutParams.width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 width = Math.max(width, mLayoutParams.width);
560 }
561
562 width = Math.max(width, getSuggestedMinimumWidth());
563 width = resolveSize(width, widthMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700564
565 if (offsetHorizontalAxis) {
Romain Guyd10a5762009-07-28 11:18:14 -0700566 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000567 final View child = views[i];
Romain Guyf7dabb02009-06-25 14:47:14 -0700568 if (child.getVisibility() != GONE) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000569 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700570 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700571 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
572 centerHorizontal(child, params, width);
Romain Guy42460ac2010-01-11 16:46:33 -0800573 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
574 final int childWidth = child.getMeasuredWidth();
575 params.mLeft = width - mPaddingRight - childWidth;
576 params.mRight = params.mLeft + childWidth;
Romain Guyf7dabb02009-06-25 14:47:14 -0700577 }
578 }
579 }
580 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700582
583 if (isWrapContentHeight) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700584 // Height already has top padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 // the bottom of each child view
586 height += mPaddingBottom;
587
Adam Powell758d5a42013-09-13 09:22:30 -0700588 if (mLayoutParams != null && mLayoutParams.height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 height = Math.max(height, mLayoutParams.height);
590 }
591
592 height = Math.max(height, getSuggestedMinimumHeight());
593 height = resolveSize(height, heightMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700594
595 if (offsetVerticalAxis) {
596 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000597 final View child = views[i];
Romain Guyf7dabb02009-06-25 14:47:14 -0700598 if (child.getVisibility() != GONE) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000599 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700600 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700601 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
602 centerVertical(child, params, height);
Romain Guy42460ac2010-01-11 16:46:33 -0800603 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
604 final int childHeight = child.getMeasuredHeight();
605 params.mTop = height - mPaddingBottom - childHeight;
606 params.mBottom = params.mTop + childHeight;
Romain Guyf7dabb02009-06-25 14:47:14 -0700607 }
608 }
609 }
610 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 }
612
613 if (horizontalGravity || verticalGravity) {
614 final Rect selfBounds = mSelfBounds;
615 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
616 height - mPaddingBottom);
617
618 final Rect contentBounds = mContentBounds;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700619 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
Fabrice Di Meglioc0053222011-06-13 12:16:51 -0700620 layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621
622 final int horizontalOffset = contentBounds.left - left;
623 final int verticalOffset = contentBounds.top - top;
624 if (horizontalOffset != 0 || verticalOffset != 0) {
Romain Guy725015a2009-06-23 14:27:34 -0700625 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000626 final View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 if (child.getVisibility() != GONE && child != ignore) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000628 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guyd10a5762009-07-28 11:18:14 -0700629 if (horizontalGravity) {
630 params.mLeft += horizontalOffset;
631 params.mRight += horizontalOffset;
632 }
633 if (verticalGravity) {
634 params.mTop += verticalOffset;
635 params.mBottom += verticalOffset;
636 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638 }
639 }
640 }
641
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800642 if (isLayoutRtl()) {
643 final int offsetWidth = myWidth - width;
644 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000645 final View child = views[i];
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800646 if (child.getVisibility() != GONE) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000647 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800648 params.mLeft -= offsetWidth;
649 params.mRight -= offsetWidth;
650 }
651 }
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800652 }
653
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 setMeasuredDimension(width, height);
655 }
656
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 /**
Alan Viverette0e14a852015-03-09 18:01:19 -0700658 * @return a negative number if the top of {@code p1} is above the top of
659 * {@code p2} or if they have identical top values and the left of
660 * {@code p1} is to the left of {@code p2}, or a positive number
661 * otherwise
662 */
663 private int compareLayoutPosition(LayoutParams p1, LayoutParams p2) {
664 final int topDiff = p1.mTop - p2.mTop;
665 if (topDiff != 0) {
666 return topDiff;
667 }
668 return p1.mLeft - p2.mLeft;
669 }
670
671 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 * Measure a child. The child should have left, top, right and bottom information
Chet Haase28308e82014-07-22 16:38:16 -0700673 * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
674 * that the view can extend up to the corresponding edge.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 *
676 * @param child Child to measure
677 * @param params LayoutParams associated with child
678 * @param myWidth Width of the the RelativeLayout
679 * @param myHeight Height of the RelativeLayout
680 */
Romain Guy725015a2009-06-23 14:27:34 -0700681 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
Romain Guye8fb03c2013-04-25 11:40:45 -0700682 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
683 params.mRight, params.width,
684 params.leftMargin, params.rightMargin,
685 mPaddingLeft, mPaddingRight,
686 myWidth);
Romain Guy1b7c7912013-04-25 14:55:28 -0700687 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
688 params.mBottom, params.height,
689 params.topMargin, params.bottomMargin,
690 mPaddingTop, mPaddingBottom,
691 myHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
693 }
694
Alan Viverette7a40d212015-07-29 15:55:20 -0400695 private void measureChildHorizontal(
696 View child, LayoutParams params, int myWidth, int myHeight) {
697 final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
698 params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
Romain Guy95607032009-06-24 14:37:03 -0700699 myWidth);
Alan Viverette7a40d212015-07-29 15:55:20 -0400700
701 final int childHeightMeasureSpec;
Adam Powell7da4b732012-12-07 15:28:33 -0800702 if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
Romain Guyf16c7a92013-02-11 17:43:59 -0800703 if (params.height >= 0) {
704 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
705 params.height, MeasureSpec.EXACTLY);
706 } else {
Alan Viverette7a40d212015-07-29 15:55:20 -0400707 // Negative values in a mySize/myWidth/myWidth value in
708 // RelativeLayout measurement is code for, "we got an
709 // unspecified mode in the RelativeLayout's measure spec."
Romain Guyf16c7a92013-02-11 17:43:59 -0800710 // Carry it forward.
711 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
712 }
Romain Guyf782e602009-06-25 15:26:49 -0700713 } else {
Alan Viverette7a40d212015-07-29 15:55:20 -0400714 final int maxHeight;
715 if (mMeasureVerticalWithPaddingMargin) {
716 maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
717 - params.topMargin - params.bottomMargin);
718 } else {
719 maxHeight = Math.max(0, myHeight);
720 }
721
722 final int heightMode;
Alan Viverette130ce742015-08-12 13:04:16 -0400723 if (params.height == LayoutParams.MATCH_PARENT) {
Alan Viverette7a40d212015-07-29 15:55:20 -0400724 heightMode = MeasureSpec.EXACTLY;
725 } else {
726 heightMode = MeasureSpec.AT_MOST;
727 }
728 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
Romain Guyf782e602009-06-25 15:26:49 -0700729 }
Alan Viverette7a40d212015-07-29 15:55:20 -0400730
Romain Guy725015a2009-06-23 14:27:34 -0700731 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
732 }
733
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 /**
735 * Get a measure spec that accounts for all of the constraints on this view.
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700736 * This includes size constraints imposed by the RelativeLayout as well as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 * the View's desired dimension.
738 *
739 * @param childStart The left or top field of the child's layout params
740 * @param childEnd The right or bottom field of the child's layout params
741 * @param childSize The child's desired size (the width or height field of
742 * the child's layout params)
743 * @param startMargin The left or top margin
744 * @param endMargin The right or bottom margin
745 * @param startPadding mPaddingLeft or mPaddingTop
746 * @param endPadding mPaddingRight or mPaddingBottom
747 * @param mySize The width or height of this view (the RelativeLayout)
748 * @return MeasureSpec for the child
749 */
750 private int getChildMeasureSpec(int childStart, int childEnd,
751 int childSize, int startMargin, int endMargin, int startPadding,
752 int endPadding, int mySize) {
753 int childSpecMode = 0;
754 int childSpecSize = 0;
755
Chet Haase28308e82014-07-22 16:38:16 -0700756 // Negative values in a mySize value in RelativeLayout
Alan Viverette517a0042013-12-04 17:08:44 -0800757 // measurement is code for, "we got an unspecified mode in the
758 // RelativeLayout's measure spec."
Alan Viverette5b39ec82015-07-22 15:37:14 -0400759 final boolean isUnspecified = mySize < 0;
760 if (isUnspecified && !mAllowBrokenMeasureSpecs) {
Chet Haase28308e82014-07-22 16:38:16 -0700761 if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
Alan Viverette517a0042013-12-04 17:08:44 -0800762 // Constraints fixed both edges, so child has an exact size.
763 childSpecSize = Math.max(0, childEnd - childStart);
764 childSpecMode = MeasureSpec.EXACTLY;
Alan Viveretted5694f02014-02-12 15:49:18 -0800765 } else if (childSize >= 0) {
766 // The child specified an exact size.
767 childSpecSize = childSize;
768 childSpecMode = MeasureSpec.EXACTLY;
Alan Viverette517a0042013-12-04 17:08:44 -0800769 } else {
770 // Allow the child to be whatever size it wants.
771 childSpecSize = 0;
772 childSpecMode = MeasureSpec.UNSPECIFIED;
773 }
774
775 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
776 }
777
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 // Figure out start and end bounds.
779 int tempStart = childStart;
780 int tempEnd = childEnd;
781
782 // If the view did not express a layout constraint for an edge, use
783 // view's margins and our padding
Chet Haase28308e82014-07-22 16:38:16 -0700784 if (tempStart == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 tempStart = startPadding + startMargin;
786 }
Chet Haase28308e82014-07-22 16:38:16 -0700787 if (tempEnd == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 tempEnd = mySize - endPadding - endMargin;
789 }
790
791 // Figure out maximum size available to this view
Alan Viverette39310d32015-06-24 17:03:48 -0700792 final int maxAvailable = tempEnd - tempStart;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793
Chet Haase28308e82014-07-22 16:38:16 -0700794 if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
Alan Viverette39310d32015-06-24 17:03:48 -0700795 // Constraints fixed both edges, so child must be an exact size.
Alan Viverette5b39ec82015-07-22 15:37:14 -0400796 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
Alan Viverette39310d32015-06-24 17:03:48 -0700797 childSpecSize = Math.max(0, maxAvailable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 } else {
799 if (childSize >= 0) {
Alan Viverette39310d32015-06-24 17:03:48 -0700800 // Child wanted an exact size. Give as much as possible.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 childSpecMode = MeasureSpec.EXACTLY;
802
803 if (maxAvailable >= 0) {
Alan Viverette39310d32015-06-24 17:03:48 -0700804 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 childSpecSize = Math.min(maxAvailable, childSize);
806 } else {
807 // We can grow in this dimension.
808 childSpecSize = childSize;
809 }
Romain Guy980a9382010-01-08 15:06:28 -0800810 } else if (childSize == LayoutParams.MATCH_PARENT) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700811 // Child wanted to be as big as possible. Give all available
Alan Viverette39310d32015-06-24 17:03:48 -0700812 // space.
Alan Viverette5b39ec82015-07-22 15:37:14 -0400813 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
Alan Viverette39310d32015-06-24 17:03:48 -0700814 childSpecSize = Math.max(0, maxAvailable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 } else if (childSize == LayoutParams.WRAP_CONTENT) {
Alan Viverette39310d32015-06-24 17:03:48 -0700816 // Child wants to wrap content. Use AT_MOST to communicate
817 // available space if we know our max size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 if (maxAvailable >= 0) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700819 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 childSpecMode = MeasureSpec.AT_MOST;
821 childSpecSize = maxAvailable;
822 } else {
823 // We can grow in this dimension. Child can be as big as it
Alan Viverette39310d32015-06-24 17:03:48 -0700824 // wants.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 childSpecMode = MeasureSpec.UNSPECIFIED;
826 childSpecSize = 0;
827 }
828 }
829 }
830
831 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
832 }
833
Romain Guyf7dabb02009-06-25 14:47:14 -0700834 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
835 boolean wrapContent) {
836
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700837 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700838 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839
Chet Haase28308e82014-07-22 16:38:16 -0700840 if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 // Right is fixed, but left varies
842 params.mLeft = params.mRight - child.getMeasuredWidth();
Chet Haase28308e82014-07-22 16:38:16 -0700843 } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 // Left is fixed, but right varies
845 params.mRight = params.mLeft + child.getMeasuredWidth();
Chet Haase28308e82014-07-22 16:38:16 -0700846 } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 // Both left and right vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700848 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
849 if (!wrapContent) {
850 centerHorizontal(child, params, myWidth);
851 } else {
Chet Haasebcd35202017-06-28 17:06:50 -0700852 positionAtEdge(child, params, myWidth);
Romain Guyf7dabb02009-06-25 14:47:14 -0700853 }
854 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 } else {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700856 // This is the default case. For RTL we start from the right and for LTR we start
857 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
Chet Haasebcd35202017-06-28 17:06:50 -0700858 positionAtEdge(child, params, myWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 }
860 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700861 return rules[ALIGN_PARENT_END] != 0;
Romain Guy725015a2009-06-23 14:27:34 -0700862 }
863
Chet Haasebcd35202017-06-28 17:06:50 -0700864 private void positionAtEdge(View child, LayoutParams params, int myWidth) {
865 if (isLayoutRtl()) {
866 params.mRight = myWidth - mPaddingRight - params.rightMargin;
867 params.mLeft = params.mRight - child.getMeasuredWidth();
868 } else {
869 params.mLeft = mPaddingLeft + params.leftMargin;
870 params.mRight = params.mLeft + child.getMeasuredWidth();
871 }
872 }
873
Romain Guyf7dabb02009-06-25 14:47:14 -0700874 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
875 boolean wrapContent) {
876
Romain Guy725015a2009-06-23 14:27:34 -0700877 int[] rules = params.getRules();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878
Chet Haase28308e82014-07-22 16:38:16 -0700879 if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 // Bottom is fixed, but top varies
881 params.mTop = params.mBottom - child.getMeasuredHeight();
Chet Haase28308e82014-07-22 16:38:16 -0700882 } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 // Top is fixed, but bottom varies
884 params.mBottom = params.mTop + child.getMeasuredHeight();
Chet Haase28308e82014-07-22 16:38:16 -0700885 } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 // Both top and bottom vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700887 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
888 if (!wrapContent) {
889 centerVertical(child, params, myHeight);
890 } else {
891 params.mTop = mPaddingTop + params.topMargin;
892 params.mBottom = params.mTop + child.getMeasuredHeight();
893 }
894 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 } else {
896 params.mTop = mPaddingTop + params.topMargin;
897 params.mBottom = params.mTop + child.getMeasuredHeight();
898 }
899 }
Romain Guy42460ac2010-01-11 16:46:33 -0800900 return rules[ALIGN_PARENT_BOTTOM] != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 }
902
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800903 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 RelativeLayout.LayoutParams anchorParams;
905
Chet Haase28308e82014-07-22 16:38:16 -0700906 // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
907 // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
908 // wants to the right
909 // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
910 // wants to the left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800911 // left=10, right=20 means the left and right ends are both fixed
Chet Haase28308e82014-07-22 16:38:16 -0700912 childParams.mLeft = VALUE_NOT_SET;
913 childParams.mRight = VALUE_NOT_SET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914
915 anchorParams = getRelatedViewParams(rules, LEFT_OF);
916 if (anchorParams != null) {
917 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
918 childParams.rightMargin);
919 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
920 if (myWidth >= 0) {
921 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
923 }
924
925 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
926 if (anchorParams != null) {
927 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
928 childParams.leftMargin);
929 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
930 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
931 }
932
933 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
934 if (anchorParams != null) {
935 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
936 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
937 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
938 }
939
940 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
941 if (anchorParams != null) {
942 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
943 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
944 if (myWidth >= 0) {
945 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 }
947 }
948
Romain Guy1b7c7912013-04-25 14:55:28 -0700949 if (0 != rules[ALIGN_PARENT_LEFT]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
951 }
952
Romain Guy1b7c7912013-04-25 14:55:28 -0700953 if (0 != rules[ALIGN_PARENT_RIGHT]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 if (myWidth >= 0) {
955 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 }
957 }
Romain Guy725015a2009-06-23 14:27:34 -0700958 }
959
Alan Viverettee1a65712015-01-14 12:30:04 -0800960 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {
961 final int[] rules = childParams.getRules();
962
963 // Baseline alignment overrides any explicitly specified top or bottom.
964 int baselineOffset = getRelatedViewBaselineOffset(rules);
965 if (baselineOffset != -1) {
966 if (myBaseline != -1) {
967 baselineOffset -= myBaseline;
968 }
969 childParams.mTop = baselineOffset;
970 childParams.mBottom = VALUE_NOT_SET;
971 return;
972 }
973
Romain Guy725015a2009-06-23 14:27:34 -0700974 RelativeLayout.LayoutParams anchorParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975
Chet Haase28308e82014-07-22 16:38:16 -0700976 childParams.mTop = VALUE_NOT_SET;
977 childParams.mBottom = VALUE_NOT_SET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978
979 anchorParams = getRelatedViewParams(rules, ABOVE);
980 if (anchorParams != null) {
981 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
982 childParams.bottomMargin);
983 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
984 if (myHeight >= 0) {
985 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 }
987 }
988
989 anchorParams = getRelatedViewParams(rules, BELOW);
990 if (anchorParams != null) {
991 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
992 childParams.topMargin);
993 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
994 childParams.mTop = mPaddingTop + childParams.topMargin;
995 }
996
997 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
998 if (anchorParams != null) {
999 childParams.mTop = anchorParams.mTop + childParams.topMargin;
1000 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
1001 childParams.mTop = mPaddingTop + childParams.topMargin;
1002 }
1003
1004 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
1005 if (anchorParams != null) {
1006 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
1007 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
1008 if (myHeight >= 0) {
1009 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 }
1011 }
1012
Romain Guy1b7c7912013-04-25 14:55:28 -07001013 if (0 != rules[ALIGN_PARENT_TOP]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 childParams.mTop = mPaddingTop + childParams.topMargin;
1015 }
1016
Romain Guy1b7c7912013-04-25 14:55:28 -07001017 if (0 != rules[ALIGN_PARENT_BOTTOM]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 if (myHeight >= 0) {
1019 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 }
1021 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 }
1023
1024 private View getRelatedView(int[] rules, int relation) {
1025 int id = rules[relation];
1026 if (id != 0) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001027 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
Romain Guya0fd1d72009-06-24 14:25:43 -07001028 if (node == null) return null;
1029 View v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030
1031 // Find the first non-GONE view up the chain
1032 while (v.getVisibility() == View.GONE) {
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001033 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
Romain Guy1ab621e2009-06-25 13:31:57 -07001034 node = mGraph.mKeyNodes.get((rules[relation]));
Yuraa354c4f2017-01-24 19:41:41 +00001035 // ignore self dependency. for more info look in git commit: da3003
1036 if (node == null || v == node.view) return null;
Romain Guya0fd1d72009-06-24 14:25:43 -07001037 v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 }
1039
1040 return v;
1041 }
1042
1043 return null;
1044 }
1045
1046 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1047 View v = getRelatedView(rules, relation);
1048 if (v != null) {
1049 ViewGroup.LayoutParams params = v.getLayoutParams();
1050 if (params instanceof LayoutParams) {
1051 return (LayoutParams) v.getLayoutParams();
1052 }
1053 }
1054 return null;
1055 }
1056
Alan Viverettee1a65712015-01-14 12:30:04 -08001057 private int getRelatedViewBaselineOffset(int[] rules) {
1058 final View v = getRelatedView(rules, ALIGN_BASELINE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 if (v != null) {
Alan Viverettee1a65712015-01-14 12:30:04 -08001060 final int baseline = v.getBaseline();
1061 if (baseline != -1) {
1062 final ViewGroup.LayoutParams params = v.getLayoutParams();
1063 if (params instanceof LayoutParams) {
1064 final LayoutParams anchorParams = (LayoutParams) v.getLayoutParams();
1065 return anchorParams.mTop + baseline;
1066 }
1067 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 }
1069 return -1;
1070 }
1071
Romain Guyf16c7a92013-02-11 17:43:59 -08001072 private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 int childWidth = child.getMeasuredWidth();
1074 int left = (myWidth - childWidth) / 2;
1075
1076 params.mLeft = left;
1077 params.mRight = left + childWidth;
1078 }
1079
Romain Guyf16c7a92013-02-11 17:43:59 -08001080 private static void centerVertical(View child, LayoutParams params, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 int childHeight = child.getMeasuredHeight();
1082 int top = (myHeight - childHeight) / 2;
1083
1084 params.mTop = top;
1085 params.mBottom = top + childHeight;
1086 }
1087
1088 @Override
1089 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1090 // The layout has actually already been performed and the positions
1091 // cached. Apply the cached values to the children.
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001092 final int count = getChildCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093
1094 for (int i = 0; i < count; i++) {
1095 View child = getChildAt(i);
1096 if (child.getVisibility() != GONE) {
1097 RelativeLayout.LayoutParams st =
1098 (RelativeLayout.LayoutParams) child.getLayoutParams();
1099 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
1101 }
1102 }
1103
1104 @Override
1105 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1106 return new RelativeLayout.LayoutParams(getContext(), attrs);
1107 }
1108
1109 /**
1110 * Returns a set of layout parameters with a width of
1111 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1112 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1113 */
1114 @Override
1115 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1116 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1117 }
1118
1119 // Override to allow type-checking of LayoutParams.
1120 @Override
1121 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1122 return p instanceof RelativeLayout.LayoutParams;
1123 }
1124
1125 @Override
Yigit Boyar885c50b2016-03-22 16:53:42 -07001126 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
Yigit Boyar2dd20a62016-08-01 17:42:28 -07001127 if (sPreserveMarginParamsInLayoutParamConversion) {
1128 if (lp instanceof LayoutParams) {
1129 return new LayoutParams((LayoutParams) lp);
1130 } else if (lp instanceof MarginLayoutParams) {
1131 return new LayoutParams((MarginLayoutParams) lp);
1132 }
Yigit Boyar885c50b2016-03-22 16:53:42 -07001133 }
Yigit Boyar2dd20a62016-08-01 17:42:28 -07001134 return new LayoutParams(lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 }
1136
Alan Viverettea54956a2015-01-07 16:05:02 -08001137 /** @hide */
svetoslavganov75986cf2009-05-14 22:28:01 -07001138 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -08001139 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001140 if (mTopToBottomLeftToRightSet == null) {
1141 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1142 }
1143
1144 // sort children top-to-bottom and left-to-right
1145 for (int i = 0, count = getChildCount(); i < count; i++) {
1146 mTopToBottomLeftToRightSet.add(getChildAt(i));
1147 }
1148
1149 for (View view : mTopToBottomLeftToRightSet) {
Svetoslav Ganov0b0a41d2011-09-07 18:06:03 -07001150 if (view.getVisibility() == View.VISIBLE
1151 && view.dispatchPopulateAccessibilityEvent(event)) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001152 mTopToBottomLeftToRightSet.clear();
1153 return true;
1154 }
1155 }
1156
1157 mTopToBottomLeftToRightSet.clear();
1158 return false;
1159 }
1160
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001161 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -08001162 public CharSequence getAccessibilityClassName() {
1163 return RelativeLayout.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001164 }
1165
svetoslavganov75986cf2009-05-14 22:28:01 -07001166 /**
1167 * Compares two views in left-to-right and top-to-bottom fashion.
1168 */
1169 private class TopToBottomLeftToRightComparator implements Comparator<View> {
1170 public int compare(View first, View second) {
1171 // top - bottom
1172 int topDifference = first.getTop() - second.getTop();
1173 if (topDifference != 0) {
1174 return topDifference;
1175 }
1176 // left - right
1177 int leftDifference = first.getLeft() - second.getLeft();
1178 if (leftDifference != 0) {
1179 return leftDifference;
1180 }
1181 // break tie by height
1182 int heightDiference = first.getHeight() - second.getHeight();
1183 if (heightDiference != 0) {
1184 return heightDiference;
1185 }
1186 // break tie by width
1187 int widthDiference = first.getWidth() - second.getWidth();
1188 if (widthDiference != 0) {
1189 return widthDiference;
1190 }
1191 return 0;
1192 }
1193 }
1194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 /**
Joe Fernandeza1bd1712017-04-25 22:28:27 -07001196 * Specifies how a view is positioned within a {@link RelativeLayout}.
1197 * The relative layout containing the view uses the value of these layout parameters to
1198 * determine where to position the view on the screen. If the view is not contained
1199 * within a relative layout, these attributes are ignored.
1200 *
Andrew Solovay666f0fe2018-03-14 11:12:00 -07001201 * See the <a href="/guide/topics/ui/layout/relative.html">
Joe Fernandeza1bd1712017-04-25 22:28:27 -07001202 * Relative Layout</a> guide for example code demonstrating how to use relative layout’s
1203 * layout parameters in a layout XML.
1204 *
1205 * To learn more about layout parameters and how they differ from typical view attributes,
Andrew Solovay666f0fe2018-03-14 11:12:00 -07001206 * see the <a href="/guide/topics/ui/declaring-layout.html#attributes">
Joe Fernandeza1bd1712017-04-25 22:28:27 -07001207 * Layouts guide</a>.
1208 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 *
1210 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1211 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1212 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1213 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1214 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1215 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1216 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1217 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1218 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1219 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1220 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1221 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1222 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1223 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1224 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1225 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1226 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001227 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1228 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1229 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1230 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1231 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1232 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 */
1234 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001235 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001236 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1237 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1238 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1239 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1240 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1241 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1242 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1243 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1244 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1245 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1246 @ViewDebug.IntToString(from = BELOW, to = "below"),
1247 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1248 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1249 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1250 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001251 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"),
1252 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"),
1253 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"),
1254 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"),
1255 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"),
1256 @ViewDebug.IntToString(from = START_OF, to = "startOf"),
1257 @ViewDebug.IntToString(from = END_OF, to = "endOf")
The Android Open Source Project10592532009-03-18 17:39:46 -07001258 }, mapping = {
1259 @ViewDebug.IntToString(from = TRUE, to = "true"),
Romain Guya1f3e4a2009-06-04 15:10:46 -07001260 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
The Android Open Source Project10592532009-03-18 17:39:46 -07001261 })
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 private int[] mRules = new int[VERB_COUNT];
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001264 private int[] mInitialRules = new int[VERB_COUNT];
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001265
Mathew Inwood978c6e22018-08-21 15:58:55 +01001266 @UnsupportedAppUsage
Mathew Inwood74e7aed2018-08-16 17:29:31 +01001267 private int mLeft;
Mathew Inwood978c6e22018-08-21 15:58:55 +01001268 @UnsupportedAppUsage
Mathew Inwood74e7aed2018-08-16 17:29:31 +01001269 private int mTop;
Mathew Inwood978c6e22018-08-21 15:58:55 +01001270 @UnsupportedAppUsage
Mathew Inwood74e7aed2018-08-16 17:29:31 +01001271 private int mRight;
Mathew Inwood978c6e22018-08-21 15:58:55 +01001272 @UnsupportedAppUsage
Mathew Inwood74e7aed2018-08-16 17:29:31 +01001273 private int mBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274
Alan Viverette895615c2015-08-18 14:51:48 -04001275 /**
1276 * Whether this view had any relative rules modified following the most
1277 * recent resolution of layout direction.
1278 */
1279 private boolean mNeedsLayoutResolution;
1280
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001281 private boolean mRulesChanged = false;
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001282 private boolean mIsRtlCompatibilityMode = false;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001283
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001284 /**
1285 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1286 * the anchor's visibility is GONE.
1287 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001288 @ViewDebug.ExportedProperty(category = "layout")
Ashley Rosede3b4a72019-03-04 16:44:07 -05001289 @InspectableProperty(name = "layout_alignWithParentIfMissing")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 public boolean alignWithParent;
1291
1292 public LayoutParams(Context c, AttributeSet attrs) {
1293 super(c, attrs);
1294
1295 TypedArray a = c.obtainStyledAttributes(attrs,
1296 com.android.internal.R.styleable.RelativeLayout_Layout);
1297
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001298 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1299 mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1300 !c.getApplicationInfo().hasRtlSupport());
1301
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 final int[] rules = mRules;
Romain Guyf16c7a92013-02-11 17:43:59 -08001303 //noinspection MismatchedReadAndWriteOfArray
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001304 final int[] initialRules = mInitialRules;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305
1306 final int N = a.getIndexCount();
1307 for (int i = 0; i < N; i++) {
1308 int attr = a.getIndex(i);
1309 switch (attr) {
1310 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1311 alignWithParent = a.getBoolean(attr, false);
1312 break;
1313 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1314 rules[LEFT_OF] = a.getResourceId(attr, 0);
1315 break;
1316 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1317 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1318 break;
1319 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1320 rules[ABOVE] = a.getResourceId(attr, 0);
1321 break;
1322 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1323 rules[BELOW] = a.getResourceId(attr, 0);
1324 break;
1325 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1326 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1327 break;
1328 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1329 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1330 break;
1331 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1332 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1333 break;
1334 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1335 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1336 break;
1337 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1338 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1339 break;
1340 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1341 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1342 break;
1343 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1344 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1345 break;
1346 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1347 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1348 break;
1349 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1350 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1351 break;
1352 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1353 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1354 break;
1355 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1356 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1357 break;
1358 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1359 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1360 break;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001361 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1362 rules[START_OF] = a.getResourceId(attr, 0);
1363 break;
1364 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1365 rules[END_OF] = a.getResourceId(attr, 0);
1366 break;
1367 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1368 rules[ALIGN_START] = a.getResourceId(attr, 0);
1369 break;
1370 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1371 rules[ALIGN_END] = a.getResourceId(attr, 0);
1372 break;
1373 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1374 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1375 break;
1376 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1377 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1378 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 }
1380 }
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001381 mRulesChanged = true;
Romain Guyf16c7a92013-02-11 17:43:59 -08001382 System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 a.recycle();
1385 }
1386
1387 public LayoutParams(int w, int h) {
1388 super(w, h);
1389 }
1390
1391 /**
1392 * {@inheritDoc}
1393 */
1394 public LayoutParams(ViewGroup.LayoutParams source) {
1395 super(source);
1396 }
1397
1398 /**
1399 * {@inheritDoc}
1400 */
1401 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1402 super(source);
1403 }
1404
Alan Viverette0a0e1552013-08-07 13:24:09 -07001405 /**
1406 * Copy constructor. Clones the width, height, margin values, and rules
1407 * of the source.
1408 *
1409 * @param source The layout params to copy from.
1410 */
1411 public LayoutParams(LayoutParams source) {
1412 super(source);
1413
1414 this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
1415 this.mRulesChanged = source.mRulesChanged;
1416 this.alignWithParent = source.alignWithParent;
1417
1418 System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT);
1419 System.arraycopy(
1420 source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT);
1421 }
1422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 @Override
1424 public String debug(String output) {
1425 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1426 ", height=" + sizeToString(height) + " }";
1427 }
1428
1429 /**
Alan Viverette895615c2015-08-18 14:51:48 -04001430 * Adds a layout rule to be interpreted by the RelativeLayout.
1431 * <p>
1432 * This method should only be used for verbs that don't refer to a
1433 * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean
1434 * value ({@link #TRUE} for true or 0 for false). To
1435 * specify a verb that takes a subject, use {@link #addRule(int, int)}.
1436 * <p>
1437 * If the rule is relative to the layout direction (ex.
1438 * {@link #ALIGN_PARENT_START}), then the layout direction must be
1439 * resolved using {@link #resolveLayoutDirection(int)} before calling
1440 * {@link #getRule(int)} an absolute rule (ex.
1441 * {@link #ALIGN_PARENT_LEFT}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 *
Alan Viverette895615c2015-08-18 14:51:48 -04001443 * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 * @see #addRule(int, int)
Alan Viverette895615c2015-08-18 14:51:48 -04001445 * @see #removeRule(int)
Alan Viverette62c79e92015-02-26 09:47:10 -08001446 * @see #getRule(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 */
1448 public void addRule(int verb) {
Alan Viverette895615c2015-08-18 14:51:48 -04001449 addRule(verb, TRUE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450 }
1451
1452 /**
Alan Viverette895615c2015-08-18 14:51:48 -04001453 * Adds a layout rule to be interpreted by the RelativeLayout.
1454 * <p>
1455 * Use this for verbs that refer to a sibling (ex.
1456 * {@link #ALIGN_RIGHT}) or take a boolean value (ex.
1457 * {@link #CENTER_IN_PARENT}).
1458 * <p>
1459 * If the rule is relative to the layout direction (ex.
1460 * {@link #START_OF}), then the layout direction must be resolved using
1461 * {@link #resolveLayoutDirection(int)} before calling
1462 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 *
Alan Viverette895615c2015-08-18 14:51:48 -04001464 * @param verb a layout verb, such as {@link #ALIGN_RIGHT}
1465 * @param subject the ID of another view to use as an anchor, or a
1466 * boolean value (represented as {@link #TRUE} for true
1467 * or 0 for false)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 * @see #addRule(int)
Alan Viverette895615c2015-08-18 14:51:48 -04001469 * @see #removeRule(int)
Alan Viverette62c79e92015-02-26 09:47:10 -08001470 * @see #getRule(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 */
Alan Viverette895615c2015-08-18 14:51:48 -04001472 public void addRule(int verb, int subject) {
1473 // If we're removing a relative rule, we'll need to force layout
1474 // resolution the next time it's requested.
1475 if (!mNeedsLayoutResolution && isRelativeRule(verb)
1476 && mInitialRules[verb] != 0 && subject == 0) {
1477 mNeedsLayoutResolution = true;
1478 }
1479
1480 mRules[verb] = subject;
1481 mInitialRules[verb] = subject;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001482 mRulesChanged = true;
1483 }
1484
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001485 /**
1486 * Removes a layout rule to be interpreted by the RelativeLayout.
Alan Viverette895615c2015-08-18 14:51:48 -04001487 * <p>
1488 * If the rule is relative to the layout direction (ex.
1489 * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the
1490 * layout direction must be resolved using
1491 * {@link #resolveLayoutDirection(int)} before before calling
1492 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001493 *
1494 * @param verb One of the verbs defined by
1495 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1496 * ALIGN_WITH_PARENT_LEFT.
1497 * @see #addRule(int)
1498 * @see #addRule(int, int)
Alan Viverette62c79e92015-02-26 09:47:10 -08001499 * @see #getRule(int)
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001500 */
1501 public void removeRule(int verb) {
Alan Viverette895615c2015-08-18 14:51:48 -04001502 addRule(verb, 0);
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001503 }
1504
Alan Viverette62c79e92015-02-26 09:47:10 -08001505 /**
1506 * Returns the layout rule associated with a specific verb.
1507 *
1508 * @param verb one of the verbs defined by {@link RelativeLayout}, such
1509 * as ALIGN_WITH_PARENT_LEFT
1510 * @return the id of another view to use as an anchor, a boolean value
1511 * (represented as {@link RelativeLayout#TRUE} for true
1512 * or 0 for false), or -1 for verbs that don't refer to another
1513 * sibling (for example, ALIGN_WITH_PARENT_BOTTOM)
1514 * @see #addRule(int)
1515 * @see #addRule(int, int)
1516 */
1517 public int getRule(int verb) {
1518 return mRules[verb];
1519 }
1520
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001521 private boolean hasRelativeRules() {
1522 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1523 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1524 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1525 }
1526
Alan Viverette895615c2015-08-18 14:51:48 -04001527 private boolean isRelativeRule(int rule) {
1528 return rule == START_OF || rule == END_OF
1529 || rule == ALIGN_START || rule == ALIGN_END
1530 || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END;
1531 }
1532
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001533 // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1534 // or not.
1535 //
1536 // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1537 // predominance over any "start/end" rules that could have been defined. A special case:
1538 // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1539 // resolve those "start"/"end" rules to "left"/"right" respectively.
1540 //
1541 // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1542 // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1543 //
1544 // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1545 // only the "left"/"right" rules at the end.
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001546 private void resolveRules(int layoutDirection) {
1547 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001548
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001549 // Reset to initial state
Romain Guyf16c7a92013-02-11 17:43:59 -08001550 System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001551
1552 // Apply rules depending on direction and if we are in RTL compatibility mode
1553 if (mIsRtlCompatibilityMode) {
1554 if (mRules[ALIGN_START] != 0) {
1555 if (mRules[ALIGN_LEFT] == 0) {
1556 // "left" rule is not defined but "start" rule is: use the "start" rule as
1557 // the "left" rule
1558 mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1559 }
1560 mRules[ALIGN_START] = 0;
1561 }
1562
1563 if (mRules[ALIGN_END] != 0) {
1564 if (mRules[ALIGN_RIGHT] == 0) {
1565 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1566 // "right" rule
1567 mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1568 }
1569 mRules[ALIGN_END] = 0;
1570 }
1571
1572 if (mRules[START_OF] != 0) {
1573 if (mRules[LEFT_OF] == 0) {
1574 // "left" rule is not defined but "start" rule is: use the "start" rule as
1575 // the "left" rule
1576 mRules[LEFT_OF] = mRules[START_OF];
1577 }
1578 mRules[START_OF] = 0;
1579 }
1580
1581 if (mRules[END_OF] != 0) {
1582 if (mRules[RIGHT_OF] == 0) {
1583 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1584 // "right" rule
1585 mRules[RIGHT_OF] = mRules[END_OF];
1586 }
1587 mRules[END_OF] = 0;
1588 }
1589
1590 if (mRules[ALIGN_PARENT_START] != 0) {
1591 if (mRules[ALIGN_PARENT_LEFT] == 0) {
1592 // "left" rule is not defined but "start" rule is: use the "start" rule as
1593 // the "left" rule
1594 mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1595 }
1596 mRules[ALIGN_PARENT_START] = 0;
1597 }
1598
Alan Viverette567e4472014-08-19 15:41:51 -07001599 if (mRules[ALIGN_PARENT_END] != 0) {
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001600 if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1601 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1602 // "right" rule
1603 mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1604 }
1605 mRules[ALIGN_PARENT_END] = 0;
1606 }
1607 } else {
1608 // JB MR1+ case
1609 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1610 (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1611 // "start"/"end" rules take precedence over "left"/"right" rules
1612 mRules[ALIGN_LEFT] = 0;
1613 mRules[ALIGN_RIGHT] = 0;
1614 }
1615 if (mRules[ALIGN_START] != 0) {
1616 // "start" rule resolved to "left" or "right" depending on the direction
1617 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1618 mRules[ALIGN_START] = 0;
1619 }
1620 if (mRules[ALIGN_END] != 0) {
1621 // "end" rule resolved to "left" or "right" depending on the direction
1622 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1623 mRules[ALIGN_END] = 0;
1624 }
1625
1626 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1627 (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1628 // "start"/"end" rules take precedence over "left"/"right" rules
1629 mRules[LEFT_OF] = 0;
1630 mRules[RIGHT_OF] = 0;
1631 }
1632 if (mRules[START_OF] != 0) {
1633 // "start" rule resolved to "left" or "right" depending on the direction
1634 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1635 mRules[START_OF] = 0;
1636 }
1637 if (mRules[END_OF] != 0) {
1638 // "end" rule resolved to "left" or "right" depending on the direction
1639 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1640 mRules[END_OF] = 0;
1641 }
1642
1643 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1644 (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1645 // "start"/"end" rules take precedence over "left"/"right" rules
1646 mRules[ALIGN_PARENT_LEFT] = 0;
1647 mRules[ALIGN_PARENT_RIGHT] = 0;
1648 }
1649 if (mRules[ALIGN_PARENT_START] != 0) {
1650 // "start" rule resolved to "left" or "right" depending on the direction
1651 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1652 mRules[ALIGN_PARENT_START] = 0;
1653 }
1654 if (mRules[ALIGN_PARENT_END] != 0) {
1655 // "end" rule resolved to "left" or "right" depending on the direction
1656 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1657 mRules[ALIGN_PARENT_END] = 0;
1658 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001659 }
Alan Viverette895615c2015-08-18 14:51:48 -04001660
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001661 mRulesChanged = false;
Alan Viverette895615c2015-08-18 14:51:48 -04001662 mNeedsLayoutResolution = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 }
1664
1665 /**
1666 * Retrieves a complete list of all supported rules, where the index is the rule
1667 * verb, and the element value is the value specified, or "false" if it was never
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001668 * set. If there are relative rules defined (*_START / *_END), they will be resolved
1669 * depending on the layout direction.
1670 *
1671 * @param layoutDirection the direction of the layout.
1672 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
1673 * or {@link View#LAYOUT_DIRECTION_RTL}
1674 * @return the supported rules
1675 * @see #addRule(int, int)
1676 *
1677 * @hide
1678 */
1679 public int[] getRules(int layoutDirection) {
Alan Viverette895615c2015-08-18 14:51:48 -04001680 resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001681 return mRules;
1682 }
1683
1684 /**
1685 * Retrieves a complete list of all supported rules, where the index is the rule
1686 * verb, and the element value is the value specified, or "false" if it was never
1687 * set. There will be no resolution of relative rules done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 *
1689 * @return the supported rules
1690 * @see #addRule(int, int)
1691 */
1692 public int[] getRules() {
1693 return mRules;
1694 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001695
Alan Viverette895615c2015-08-18 14:51:48 -04001696 /**
1697 * This will be called by {@link android.view.View#requestLayout()} to
1698 * resolve layout parameters that are relative to the layout direction.
1699 * <p>
1700 * After this method is called, any rules using layout-relative verbs
1701 * (ex. {@link #START_OF}) previously added via {@link #addRule(int)}
1702 * may only be accessed via their resolved absolute verbs (ex.
1703 * {@link #LEFT_OF}).
1704 */
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001705 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001706 public void resolveLayoutDirection(int layoutDirection) {
Alan Viverette895615c2015-08-18 14:51:48 -04001707 if (shouldResolveLayoutDirection(layoutDirection)) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001708 resolveRules(layoutDirection);
1709 }
Alan Viverette895615c2015-08-18 14:51:48 -04001710
1711 // This will set the layout direction.
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001712 super.resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001713 }
Siva Velusamy94a6d152015-05-05 15:07:00 -07001714
Alan Viverette895615c2015-08-18 14:51:48 -04001715 private boolean shouldResolveLayoutDirection(int layoutDirection) {
1716 return (mNeedsLayoutResolution || hasRelativeRules())
1717 && (mRulesChanged || layoutDirection != getLayoutDirection());
1718 }
1719
Siva Velusamy94a6d152015-05-05 15:07:00 -07001720 /** @hide */
1721 @Override
1722 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1723 super.encodeProperties(encoder);
1724 encoder.addProperty("layout:alignWithParent", alignWithParent);
1725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 }
Romain Guy725015a2009-06-23 14:27:34 -07001727
1728 private static class DependencyGraph {
1729 /**
Romain Guy1ab621e2009-06-25 13:31:57 -07001730 * List of all views in the graph.
1731 */
1732 private ArrayList<Node> mNodes = new ArrayList<Node>();
1733
1734 /**
Romain Guy725015a2009-06-23 14:27:34 -07001735 * List of nodes in the graph. Each node is identified by its
1736 * view id (see View#getId()).
1737 */
Romain Guy1ab621e2009-06-25 13:31:57 -07001738 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001739
1740 /**
1741 * Temporary data structure used to build the list of roots
1742 * for this graph.
1743 */
Romain Guybc5d8762012-01-06 16:40:49 -08001744 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001745
1746 /**
1747 * Clears the graph.
1748 */
1749 void clear() {
Romain Guy1ab621e2009-06-25 13:31:57 -07001750 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001751 final int count = nodes.size();
1752
1753 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001754 nodes.get(i).release();
Romain Guy725015a2009-06-23 14:27:34 -07001755 }
1756 nodes.clear();
1757
Romain Guy1ab621e2009-06-25 13:31:57 -07001758 mKeyNodes.clear();
Romain Guy725015a2009-06-23 14:27:34 -07001759 mRoots.clear();
1760 }
1761
1762 /**
1763 * Adds a view to the graph.
1764 *
1765 * @param view The view to be added as a node to the graph.
1766 */
1767 void add(View view) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001768 final int id = view.getId();
1769 final Node node = Node.acquire(view);
1770
1771 if (id != View.NO_ID) {
1772 mKeyNodes.put(id, node);
1773 }
1774
1775 mNodes.add(node);
Romain Guy725015a2009-06-23 14:27:34 -07001776 }
1777
1778 /**
1779 * Builds a sorted list of views. The sorting order depends on the dependencies
1780 * between the view. For instance, if view C needs view A to be processed first
1781 * and view A needs view B to be processed first, the dependency graph
1782 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1783 *
1784 * @param sorted The sorted list of views. The length of this array must
1785 * be equal to getChildCount().
1786 * @param rules The list of rules to take into account.
1787 */
1788 void getSortedViews(View[] sorted, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001789 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001790 int index = 0;
1791
Romain Guybc5d8762012-01-06 16:40:49 -08001792 Node node;
1793 while ((node = roots.pollLast()) != null) {
Romain Guy725015a2009-06-23 14:27:34 -07001794 final View view = node.view;
1795 final int key = view.getId();
1796
1797 sorted[index++] = view;
1798
Romain Guy6876b4f2013-06-03 14:25:56 -07001799 final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
1800 final int count = dependents.size();
1801 for (int i = 0; i < count; i++) {
1802 final Node dependent = dependents.keyAt(i);
Romain Guy725015a2009-06-23 14:27:34 -07001803 final SparseArray<Node> dependencies = dependent.dependencies;
1804
1805 dependencies.remove(key);
1806 if (dependencies.size() == 0) {
1807 roots.add(dependent);
1808 }
1809 }
1810 }
1811
1812 if (index < sorted.length) {
1813 throw new IllegalStateException("Circular dependencies cannot exist"
1814 + " in RelativeLayout");
1815 }
1816 }
1817
1818 /**
1819 * Finds the roots of the graph. A root is a node with no dependency and
1820 * with [0..n] dependents.
1821 *
1822 * @param rulesFilter The list of rules to consider when building the
1823 * dependencies
1824 *
1825 * @return A list of node, each being a root of the graph
1826 */
Romain Guybc5d8762012-01-06 16:40:49 -08001827 private ArrayDeque<Node> findRoots(int[] rulesFilter) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001828 final SparseArray<Node> keyNodes = mKeyNodes;
1829 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001830 final int count = nodes.size();
1831
1832 // Find roots can be invoked several times, so make sure to clear
1833 // all dependents and dependencies before running the algorithm
1834 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001835 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001836 node.dependents.clear();
1837 node.dependencies.clear();
1838 }
1839
1840 // Builds up the dependents and dependencies for each node of the graph
1841 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001842 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001843
1844 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1845 final int[] rules = layoutParams.mRules;
1846 final int rulesCount = rulesFilter.length;
1847
1848 // Look only the the rules passed in parameter, this way we build only the
1849 // dependencies for a specific set of rules
1850 for (int j = 0; j < rulesCount; j++) {
1851 final int rule = rules[rulesFilter[j]];
1852 if (rule > 0) {
1853 // The node this node depends on
Romain Guy1ab621e2009-06-25 13:31:57 -07001854 final Node dependency = keyNodes.get(rule);
Romain Guyda3003e2009-07-19 19:47:42 -07001855 // Skip unknowns and self dependencies
1856 if (dependency == null || dependency == node) {
Romain Guyb8f8de82009-06-25 12:03:56 -07001857 continue;
1858 }
Romain Guy725015a2009-06-23 14:27:34 -07001859 // Add the current node as a dependent
Romain Guybc5d8762012-01-06 16:40:49 -08001860 dependency.dependents.put(node, this);
Romain Guy725015a2009-06-23 14:27:34 -07001861 // Add a dependency to the current node
1862 node.dependencies.put(rule, dependency);
1863 }
1864 }
1865 }
1866
Romain Guybc5d8762012-01-06 16:40:49 -08001867 final ArrayDeque<Node> roots = mRoots;
Romain Guy725015a2009-06-23 14:27:34 -07001868 roots.clear();
1869
1870 // Finds all the roots in the graph: all nodes with no dependencies
1871 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001872 final Node node = nodes.get(i);
Romain Guybc5d8762012-01-06 16:40:49 -08001873 if (node.dependencies.size() == 0) roots.addLast(node);
Romain Guy725015a2009-06-23 14:27:34 -07001874 }
1875
1876 return roots;
1877 }
1878
1879 /**
Romain Guy725015a2009-06-23 14:27:34 -07001880 * A node in the dependency graph. A node is a view, its list of dependencies
1881 * and its list of dependents.
1882 *
1883 * A node with no dependent is considered a root of the graph.
1884 */
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001885 static class Node {
Romain Guy725015a2009-06-23 14:27:34 -07001886 /**
1887 * The view representing this node in the layout.
1888 */
1889 View view;
1890
1891 /**
1892 * The list of dependents for this node; a dependent is a node
1893 * that needs this node to be processed first.
1894 */
Romain Guy6876b4f2013-06-03 14:25:56 -07001895 final ArrayMap<Node, DependencyGraph> dependents =
1896 new ArrayMap<Node, DependencyGraph>();
Romain Guy725015a2009-06-23 14:27:34 -07001897
1898 /**
1899 * The list of dependencies for this node.
1900 */
1901 final SparseArray<Node> dependencies = new SparseArray<Node>();
1902
1903 /*
1904 * START POOL IMPLEMENTATION
1905 */
Romain Guybaac4632009-06-29 14:28:29 -07001906 // The pool is static, so all nodes instances are shared across
1907 // activities, that's why we give it a rather high limit
1908 private static final int POOL_LIMIT = 100;
Romain Guy6876b4f2013-06-03 14:25:56 -07001909 private static final SynchronizedPool<Node> sPool =
1910 new SynchronizedPool<Node>(POOL_LIMIT);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001911
Romain Guy725015a2009-06-23 14:27:34 -07001912 static Node acquire(View view) {
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001913 Node node = sPool.acquire();
1914 if (node == null) {
1915 node = new Node();
1916 }
Romain Guy725015a2009-06-23 14:27:34 -07001917 node.view = view;
Romain Guy725015a2009-06-23 14:27:34 -07001918 return node;
1919 }
1920
1921 void release() {
1922 view = null;
1923 dependents.clear();
1924 dependencies.clear();
1925
1926 sPool.release(this);
1927 }
1928 /*
1929 * END POOL IMPLEMENTATION
1930 */
1931 }
1932 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001933}