blob: a189d3c0cc9126bc2c2c5304c22b1a3dbda0d1e3 [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
Siva Velusamy94a6d152015-05-05 15:07:00 -070019import android.annotation.NonNull;
Romain Guy6876b4f2013-06-03 14:25:56 -070020import android.util.ArrayMap;
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070021import com.android.internal.R;
22
Romain Guybc5d8762012-01-06 16:40:49 -080023import java.util.ArrayDeque;
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070024import java.util.ArrayList;
25import java.util.Comparator;
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -070026import java.util.SortedSet;
27import java.util.TreeSet;
28
svetoslavganov75986cf2009-05-14 22:28:01 -070029import android.content.Context;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070030import android.content.res.TypedArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070031import android.graphics.Rect;
Adam Powell7da4b732012-12-07 15:28:33 -080032import android.os.Build;
svetoslavganov75986cf2009-05-14 22:28:01 -070033import android.util.AttributeSet;
Romain Guy6876b4f2013-06-03 14:25:56 -070034import android.util.Pools.SynchronizedPool;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070035import android.util.SparseArray;
svetoslavganov75986cf2009-05-14 22:28:01 -070036import android.view.Gravity;
37import android.view.View;
38import android.view.ViewDebug;
39import android.view.ViewGroup;
Siva Velusamy94a6d152015-05-05 15:07:00 -070040import android.view.ViewHierarchyEncoder;
svetoslavganov75986cf2009-05-14 22:28:01 -070041import android.view.accessibility.AccessibilityEvent;
42import android.widget.RemoteViews.RemoteView;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -070043
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -080044import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
46/**
47 * 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 -080048 * parent.
Romain Guya1f3e4a2009-06-04 15:10:46 -070049 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 * <p>
51 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
52 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
53 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
54 * {@link #ALIGN_PARENT_BOTTOM}.
55 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070056 *
Adam Powell2c8cc972012-12-07 18:04:51 -080057 * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
58 * a measurement bug that could cause child views to be measured with incorrect
59 * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
60 * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
61 * for more details.) This was triggered when a RelativeLayout container was placed in
62 * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
63 * not equipped to properly measure with the MeasureSpec mode
64 * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
65 * this would silently work anyway as RelativeLayout would pass a very large
66 * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
67 *
68 * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
69 * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
70 * version 18 or newer will receive the correct behavior</p>
71 *
Scott Main4c359b72012-07-24 15:51:27 -070072 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
73 * Layout</a> guide.</p>
Scott Main41ec6532010-08-19 16:57:07 -070074 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 * <p>
76 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
77 * layout attributes
78 * </p>
Romain Guya1f3e4a2009-06-04 15:10:46 -070079 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 * @attr ref android.R.styleable#RelativeLayout_gravity
81 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
82 */
83@RemoteView
84public class RelativeLayout extends ViewGroup {
85 public static final int TRUE = -1;
86
87 /**
88 * Rule that aligns a child's right edge with another child's left edge.
89 */
90 public static final int LEFT_OF = 0;
91 /**
92 * Rule that aligns a child's left edge with another child's right edge.
93 */
94 public static final int RIGHT_OF = 1;
95 /**
96 * Rule that aligns a child's bottom edge with another child's top edge.
97 */
98 public static final int ABOVE = 2;
99 /**
100 * Rule that aligns a child's top edge with another child's bottom edge.
101 */
102 public static final int BELOW = 3;
103
104 /**
105 * Rule that aligns a child's baseline with another child's baseline.
106 */
107 public static final int ALIGN_BASELINE = 4;
108 /**
109 * Rule that aligns a child's left edge with another child's left edge.
110 */
111 public static final int ALIGN_LEFT = 5;
112 /**
113 * Rule that aligns a child's top edge with another child's top edge.
114 */
115 public static final int ALIGN_TOP = 6;
116 /**
117 * Rule that aligns a child's right edge with another child's right edge.
118 */
119 public static final int ALIGN_RIGHT = 7;
120 /**
121 * Rule that aligns a child's bottom edge with another child's bottom edge.
122 */
123 public static final int ALIGN_BOTTOM = 8;
124
125 /**
126 * Rule that aligns the child's left edge with its RelativeLayout
127 * parent's left edge.
128 */
129 public static final int ALIGN_PARENT_LEFT = 9;
130 /**
131 * Rule that aligns the child's top edge with its RelativeLayout
132 * parent's top edge.
133 */
134 public static final int ALIGN_PARENT_TOP = 10;
135 /**
136 * Rule that aligns the child's right edge with its RelativeLayout
137 * parent's right edge.
138 */
139 public static final int ALIGN_PARENT_RIGHT = 11;
140 /**
141 * Rule that aligns the child's bottom edge with its RelativeLayout
142 * parent's bottom edge.
143 */
144 public static final int ALIGN_PARENT_BOTTOM = 12;
145
146 /**
147 * Rule that centers the child with respect to the bounds of its
148 * RelativeLayout parent.
149 */
150 public static final int CENTER_IN_PARENT = 13;
151 /**
152 * Rule that centers the child horizontally with respect to the
153 * bounds of its RelativeLayout parent.
154 */
155 public static final int CENTER_HORIZONTAL = 14;
156 /**
157 * Rule that centers the child vertically with respect to the
158 * bounds of its RelativeLayout parent.
159 */
160 public static final int CENTER_VERTICAL = 15;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700161 /**
162 * Rule that aligns a child's end edge with another child's start edge.
163 */
164 public static final int START_OF = 16;
165 /**
166 * Rule that aligns a child's start edge with another child's end edge.
167 */
168 public static final int END_OF = 17;
169 /**
170 * Rule that aligns a child's start edge with another child's start edge.
171 */
172 public static final int ALIGN_START = 18;
173 /**
174 * Rule that aligns a child's end edge with another child's end edge.
175 */
176 public static final int ALIGN_END = 19;
177 /**
178 * Rule that aligns the child's start edge with its RelativeLayout
179 * parent's start edge.
180 */
181 public static final int ALIGN_PARENT_START = 20;
182 /**
183 * Rule that aligns the child's end edge with its RelativeLayout
184 * parent's end edge.
185 */
186 public static final int ALIGN_PARENT_END = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700188 private static final int VERB_COUNT = 22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
Romain Guybc5d8762012-01-06 16:40:49 -0800190
191 private static final int[] RULES_VERTICAL = {
192 ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
193 };
194
195 private static final int[] RULES_HORIZONTAL = {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700196 LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
Romain Guybc5d8762012-01-06 16:40:49 -0800197 };
198
Chet Haase28308e82014-07-22 16:38:16 -0700199 /**
200 * Used to indicate left/right/top/bottom should be inferred from constraints
201 */
202 private static final int VALUE_NOT_SET = Integer.MIN_VALUE;
203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 private View mBaselineView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700206 private int mGravity = Gravity.START | Gravity.TOP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 private final Rect mContentBounds = new Rect();
208 private final Rect mSelfBounds = new Rect();
209 private int mIgnoreGravity;
210
Romain Guy725015a2009-06-23 14:27:34 -0700211 private SortedSet<View> mTopToBottomLeftToRightSet = null;
212
213 private boolean mDirtyHierarchy;
Romain Guy6876b4f2013-06-03 14:25:56 -0700214 private View[] mSortedHorizontalChildren;
215 private View[] mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700216 private final DependencyGraph mGraph = new DependencyGraph();
svetoslavganov75986cf2009-05-14 22:28:01 -0700217
Adam Powell7da4b732012-12-07 15:28:33 -0800218 // Compatibility hack. Old versions of the platform had problems
219 // with MeasureSpec value overflow and RelativeLayout was one source of them.
220 // Some apps came to rely on them. :(
221 private boolean mAllowBrokenMeasureSpecs = false;
Romain Guy76d59a32013-04-08 10:51:35 -0700222 // Compatibility hack. Old versions of the platform would not take
223 // margins and padding into account when generating the height measure spec
224 // for children during the horizontal measure pass.
225 private boolean mMeasureVerticalWithPaddingMargin = false;
Adam Powell7da4b732012-12-07 15:28:33 -0800226
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800227 // A default width used for RTL measure pass
Philip Milneca2e9e12013-04-22 12:44:29 -0700228 /**
229 * Value reduced so as not to interfere with View's measurement spec. flags. See:
230 * {@link View#MEASURED_SIZE_MASK}.
231 * {@link View#MEASURED_STATE_TOO_SMALL}.
232 **/
Philip Milne3c647d22013-04-23 14:31:23 -0700233 private static final int DEFAULT_WIDTH = 0x00010000;
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 public RelativeLayout(Context context) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700236 this(context, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 }
238
239 public RelativeLayout(Context context, AttributeSet attrs) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700240 this(context, attrs, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 }
242
Alan Viverette617feb92013-09-09 18:09:13 -0700243 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
Alan Viveretted6479ec2013-09-10 17:03:02 -0700244 this(context, attrs, defStyleAttr, 0);
Alan Viverette617feb92013-09-09 18:09:13 -0700245 }
246
247 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
248 super(context, attrs, defStyleAttr, defStyleRes);
Alan Viveretted6479ec2013-09-10 17:03:02 -0700249 initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
Romain Guy76d59a32013-04-08 10:51:35 -0700250 queryCompatibilityModes(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 }
252
Alan Viveretted6479ec2013-09-10 17:03:02 -0700253 private void initFromAttributes(
254 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
255 final TypedArray a = context.obtainStyledAttributes(
256 attrs, R.styleable.RelativeLayout, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
258 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
259 a.recycle();
260 }
261
Romain Guy76d59a32013-04-08 10:51:35 -0700262 private void queryCompatibilityModes(Context context) {
263 int version = context.getApplicationInfo().targetSdkVersion;
264 mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
265 mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
266 }
267
Patrick Dubroye0a799a2011-05-04 16:19:22 -0700268 @Override
269 public boolean shouldDelayChildPressedState() {
270 return false;
271 }
272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 /**
274 * Defines which View is ignored when the gravity is applied. This setting has no
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700275 * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 *
277 * @param viewId The id of the View to be ignored by gravity, or 0 if no View
278 * should be ignored.
279 *
280 * @see #setGravity(int)
281 *
282 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
283 */
284 @android.view.RemotableViewMethod
285 public void setIgnoreGravity(int viewId) {
286 mIgnoreGravity = viewId;
287 }
288
289 /**
Philip Milne1018fb42012-03-13 12:00:04 -0700290 * Describes how the child views are positioned.
291 *
292 * @return the gravity.
293 *
294 * @see #setGravity(int)
295 * @see android.view.Gravity
296 *
297 * @attr ref android.R.styleable#RelativeLayout_gravity
298 */
299 public int getGravity() {
300 return mGravity;
301 }
302
303 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 * Describes how the child views are positioned. Defaults to
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700305 * <code>Gravity.START | Gravity.TOP</code>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 *
Adam Powell1fec24e2011-09-15 14:19:30 -0700307 * <p>Note that since RelativeLayout considers the positioning of each child
308 * relative to one another to be significant, setting gravity will affect
309 * the positioning of all children as a single unit within the parent.
310 * This happens after children have been relatively positioned.</p>
311 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 * @param gravity See {@link android.view.Gravity}
313 *
314 * @see #setHorizontalGravity(int)
315 * @see #setVerticalGravity(int)
316 *
317 * @attr ref android.R.styleable#RelativeLayout_gravity
318 */
319 @android.view.RemotableViewMethod
320 public void setGravity(int gravity) {
321 if (mGravity != gravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700322 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
Fabrice Di Meglio9e3b0022011-06-06 16:30:29 -0700323 gravity |= Gravity.START;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 }
325
326 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
327 gravity |= Gravity.TOP;
328 }
329
330 mGravity = gravity;
331 requestLayout();
332 }
333 }
334
335 @android.view.RemotableViewMethod
336 public void setHorizontalGravity(int horizontalGravity) {
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700337 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
338 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
339 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 requestLayout();
341 }
342 }
343
344 @android.view.RemotableViewMethod
345 public void setVerticalGravity(int verticalGravity) {
346 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
347 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
348 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
349 requestLayout();
350 }
351 }
352
353 @Override
354 public int getBaseline() {
355 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
356 }
357
358 @Override
Romain Guy725015a2009-06-23 14:27:34 -0700359 public void requestLayout() {
360 super.requestLayout();
361 mDirtyHierarchy = true;
362 }
363
364 private void sortChildren() {
Romain Guy6876b4f2013-06-03 14:25:56 -0700365 final int count = getChildCount();
366 if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
367 mSortedVerticalChildren = new View[count];
368 }
369
370 if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
371 mSortedHorizontalChildren = new View[count];
372 }
Romain Guy725015a2009-06-23 14:27:34 -0700373
374 final DependencyGraph graph = mGraph;
375 graph.clear();
376
377 for (int i = 0; i < count; i++) {
Romain Guy6876b4f2013-06-03 14:25:56 -0700378 graph.add(getChildAt(i));
Romain Guy725015a2009-06-23 14:27:34 -0700379 }
380
Romain Guybc5d8762012-01-06 16:40:49 -0800381 graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
382 graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
Romain Guy725015a2009-06-23 14:27:34 -0700383 }
384
385 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Romain Guy725015a2009-06-23 14:27:34 -0700387 if (mDirtyHierarchy) {
388 mDirtyHierarchy = false;
389 sortChildren();
390 }
391
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 int myWidth = -1;
393 int myHeight = -1;
394
395 int width = 0;
396 int height = 0;
397
Adam Powell132a7422012-09-30 12:39:56 -0700398 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
399 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
400 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
401 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402
403 // Record our dimensions if they are known;
404 if (widthMode != MeasureSpec.UNSPECIFIED) {
405 myWidth = widthSize;
406 }
407
408 if (heightMode != MeasureSpec.UNSPECIFIED) {
409 myHeight = heightSize;
410 }
411
412 if (widthMode == MeasureSpec.EXACTLY) {
413 width = myWidth;
414 }
415
416 if (heightMode == MeasureSpec.EXACTLY) {
417 height = myHeight;
418 }
419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 View ignore = null;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700421 int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700422 final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
424 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
425
426 int left = Integer.MAX_VALUE;
427 int top = Integer.MAX_VALUE;
428 int right = Integer.MIN_VALUE;
429 int bottom = Integer.MIN_VALUE;
430
Romain Guyf7dabb02009-06-25 14:47:14 -0700431 boolean offsetHorizontalAxis = false;
432 boolean offsetVerticalAxis = false;
433
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
435 ignore = findViewById(mIgnoreGravity);
436 }
437
Romain Guyf7dabb02009-06-25 14:47:14 -0700438 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
439 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
440
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800441 // We need to know our size for doing the correct computation of children positioning in RTL
442 // mode but there is no practical way to get it instead of running the code below.
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800443 // 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 -0800444 // before the computation and then, as a last pass, we will update their real position with
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800445 // an offset equals to "DEFAULT_WIDTH - width".
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800446 final int layoutDirection = getLayoutDirection();
447 if (isLayoutRtl() && myWidth == -1) {
Fabrice Di Megliod5ffc792013-02-19 15:59:21 -0800448 myWidth = DEFAULT_WIDTH;
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800449 }
450
Romain Guye24ef602009-06-25 13:01:55 -0700451 View[] views = mSortedHorizontalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700452 int count = views.length;
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -0800453
Romain Guy725015a2009-06-23 14:27:34 -0700454 for (int i = 0; i < count; i++) {
455 View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 if (child.getVisibility() != GONE) {
457 LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800458 int[] rules = params.getRules(layoutDirection);
Romain Guy95607032009-06-24 14:37:03 -0700459
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800460 applyHorizontalSizeRules(params, myWidth, rules);
Romain Guyf782e602009-06-25 15:26:49 -0700461 measureChildHorizontal(child, params, myWidth, myHeight);
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800462
Romain Guyf7dabb02009-06-25 14:47:14 -0700463 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
464 offsetHorizontalAxis = true;
465 }
Romain Guy725015a2009-06-23 14:27:34 -0700466 }
467 }
468
Romain Guye24ef602009-06-25 13:01:55 -0700469 views = mSortedVerticalChildren;
Romain Guy725015a2009-06-23 14:27:34 -0700470 count = views.length;
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700471 final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
Romain Guyf7dabb02009-06-25 14:47:14 -0700472
Romain Guy725015a2009-06-23 14:27:34 -0700473 for (int i = 0; i < count; i++) {
Alan Viverettee1a65712015-01-14 12:30:04 -0800474 final View child = views[i];
Romain Guy725015a2009-06-23 14:27:34 -0700475 if (child.getVisibility() != GONE) {
Alan Viverettee1a65712015-01-14 12:30:04 -0800476 final LayoutParams params = (LayoutParams) child.getLayoutParams();
477
478 applyVerticalSizeRules(params, myHeight, child.getBaseline());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 measureChild(child, params, myWidth, myHeight);
Romain Guyf7dabb02009-06-25 14:47:14 -0700480 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
481 offsetVerticalAxis = true;
482 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483
Romain Guyf7dabb02009-06-25 14:47:14 -0700484 if (isWrapContentWidth) {
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800485 if (isLayoutRtl()) {
Chet Haasee8222dd2013-09-05 07:44:18 -0700486 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700487 width = Math.max(width, myWidth - params.mLeft);
488 } else {
489 width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
490 }
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800491 } else {
Chet Haasee8222dd2013-09-05 07:44:18 -0700492 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700493 width = Math.max(width, params.mRight);
494 } else {
495 width = Math.max(width, params.mRight + params.rightMargin);
496 }
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800497 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700499
500 if (isWrapContentHeight) {
Chet Haasee8222dd2013-09-05 07:44:18 -0700501 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
Fabrice Di Meglio2bd961a2013-08-27 19:46:43 -0700502 height = Math.max(height, params.mBottom);
503 } else {
504 height = Math.max(height, params.mBottom + params.bottomMargin);
505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 }
507
508 if (child != ignore || verticalGravity) {
509 left = Math.min(left, params.mLeft - params.leftMargin);
510 top = Math.min(top, params.mTop - params.topMargin);
511 }
512
513 if (child != ignore || horizontalGravity) {
514 right = Math.max(right, params.mRight + params.rightMargin);
515 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
516 }
517 }
518 }
519
Alan Viverette0e14a852015-03-09 18:01:19 -0700520 // Use the top-start-most laid out view as the baseline. RTL offsets are
521 // applied later, so we can use the left-most edge as the starting edge.
522 View baselineView = null;
523 LayoutParams baselineParams = null;
524 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000525 final View child = views[i];
Alan Viverette0e14a852015-03-09 18:01:19 -0700526 if (child.getVisibility() != GONE) {
527 final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
528 if (baselineView == null || baselineParams == null
529 || compareLayoutPosition(childParams, baselineParams) < 0) {
530 baselineView = child;
531 baselineParams = childParams;
532 }
533 }
534 }
535 mBaselineView = baselineView;
536
Romain Guyf7dabb02009-06-25 14:47:14 -0700537 if (isWrapContentWidth) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700538 // Width already has left padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 // the right of each child view
540 width += mPaddingRight;
541
Adam Powell758d5a42013-09-13 09:22:30 -0700542 if (mLayoutParams != null && mLayoutParams.width >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 width = Math.max(width, mLayoutParams.width);
544 }
545
546 width = Math.max(width, getSuggestedMinimumWidth());
547 width = resolveSize(width, widthMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700548
549 if (offsetHorizontalAxis) {
Romain Guyd10a5762009-07-28 11:18:14 -0700550 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000551 final View child = views[i];
Romain Guyf7dabb02009-06-25 14:47:14 -0700552 if (child.getVisibility() != GONE) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000553 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700554 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700555 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
556 centerHorizontal(child, params, width);
Romain Guy42460ac2010-01-11 16:46:33 -0800557 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
558 final int childWidth = child.getMeasuredWidth();
559 params.mLeft = width - mPaddingRight - childWidth;
560 params.mRight = params.mLeft + childWidth;
Romain Guyf7dabb02009-06-25 14:47:14 -0700561 }
562 }
563 }
564 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 }
Romain Guyf7dabb02009-06-25 14:47:14 -0700566
567 if (isWrapContentHeight) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700568 // Height already has top padding in it since it was calculated by looking at
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 // the bottom of each child view
570 height += mPaddingBottom;
571
Adam Powell758d5a42013-09-13 09:22:30 -0700572 if (mLayoutParams != null && mLayoutParams.height >= 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 height = Math.max(height, mLayoutParams.height);
574 }
575
576 height = Math.max(height, getSuggestedMinimumHeight());
577 height = resolveSize(height, heightMeasureSpec);
Romain Guyf7dabb02009-06-25 14:47:14 -0700578
579 if (offsetVerticalAxis) {
580 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000581 final View child = views[i];
Romain Guyf7dabb02009-06-25 14:47:14 -0700582 if (child.getVisibility() != GONE) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000583 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700584 final int[] rules = params.getRules(layoutDirection);
Romain Guyf7dabb02009-06-25 14:47:14 -0700585 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
586 centerVertical(child, params, height);
Romain Guy42460ac2010-01-11 16:46:33 -0800587 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
588 final int childHeight = child.getMeasuredHeight();
589 params.mTop = height - mPaddingBottom - childHeight;
590 params.mBottom = params.mTop + childHeight;
Romain Guyf7dabb02009-06-25 14:47:14 -0700591 }
592 }
593 }
594 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 }
596
597 if (horizontalGravity || verticalGravity) {
598 final Rect selfBounds = mSelfBounds;
599 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
600 height - mPaddingBottom);
601
602 final Rect contentBounds = mContentBounds;
Fabrice Di Meglio6a036402011-05-23 14:43:23 -0700603 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
Fabrice Di Meglioc0053222011-06-13 12:16:51 -0700604 layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605
606 final int horizontalOffset = contentBounds.left - left;
607 final int verticalOffset = contentBounds.top - top;
608 if (horizontalOffset != 0 || verticalOffset != 0) {
Romain Guy725015a2009-06-23 14:27:34 -0700609 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000610 final View child = views[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 if (child.getVisibility() != GONE && child != ignore) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000612 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Romain Guyd10a5762009-07-28 11:18:14 -0700613 if (horizontalGravity) {
614 params.mLeft += horizontalOffset;
615 params.mRight += horizontalOffset;
616 }
617 if (verticalGravity) {
618 params.mTop += verticalOffset;
619 params.mBottom += verticalOffset;
620 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 }
622 }
623 }
624 }
625
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800626 if (isLayoutRtl()) {
627 final int offsetWidth = myWidth - width;
628 for (int i = 0; i < count; i++) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000629 final View child = views[i];
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800630 if (child.getVisibility() != GONE) {
Alan Viverette9554f9f2015-06-22 22:37:58 +0000631 final LayoutParams params = (LayoutParams) child.getLayoutParams();
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800632 params.mLeft -= offsetWidth;
633 params.mRight -= offsetWidth;
634 }
635 }
Fabrice Di Meglio306fe5c2013-01-23 18:46:25 -0800636 }
637
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 setMeasuredDimension(width, height);
639 }
640
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 /**
Alan Viverette0e14a852015-03-09 18:01:19 -0700642 * @return a negative number if the top of {@code p1} is above the top of
643 * {@code p2} or if they have identical top values and the left of
644 * {@code p1} is to the left of {@code p2}, or a positive number
645 * otherwise
646 */
647 private int compareLayoutPosition(LayoutParams p1, LayoutParams p2) {
648 final int topDiff = p1.mTop - p2.mTop;
649 if (topDiff != 0) {
650 return topDiff;
651 }
652 return p1.mLeft - p2.mLeft;
653 }
654
655 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 * Measure a child. The child should have left, top, right and bottom information
Chet Haase28308e82014-07-22 16:38:16 -0700657 * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
658 * that the view can extend up to the corresponding edge.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 *
660 * @param child Child to measure
661 * @param params LayoutParams associated with child
662 * @param myWidth Width of the the RelativeLayout
663 * @param myHeight Height of the RelativeLayout
664 */
Romain Guy725015a2009-06-23 14:27:34 -0700665 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
Romain Guye8fb03c2013-04-25 11:40:45 -0700666 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
667 params.mRight, params.width,
668 params.leftMargin, params.rightMargin,
669 mPaddingLeft, mPaddingRight,
670 myWidth);
Romain Guy1b7c7912013-04-25 14:55:28 -0700671 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
672 params.mBottom, params.height,
673 params.topMargin, params.bottomMargin,
674 mPaddingTop, mPaddingBottom,
675 myHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
677 }
678
Alan Viverette7a40d212015-07-29 15:55:20 -0400679 private void measureChildHorizontal(
680 View child, LayoutParams params, int myWidth, int myHeight) {
681 final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
682 params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
Romain Guy95607032009-06-24 14:37:03 -0700683 myWidth);
Alan Viverette7a40d212015-07-29 15:55:20 -0400684
685 final int childHeightMeasureSpec;
Adam Powell7da4b732012-12-07 15:28:33 -0800686 if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
Romain Guyf16c7a92013-02-11 17:43:59 -0800687 if (params.height >= 0) {
688 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
689 params.height, MeasureSpec.EXACTLY);
690 } else {
Alan Viverette7a40d212015-07-29 15:55:20 -0400691 // Negative values in a mySize/myWidth/myWidth value in
692 // RelativeLayout measurement is code for, "we got an
693 // unspecified mode in the RelativeLayout's measure spec."
Romain Guyf16c7a92013-02-11 17:43:59 -0800694 // Carry it forward.
695 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
696 }
Romain Guyf782e602009-06-25 15:26:49 -0700697 } else {
Alan Viverette7a40d212015-07-29 15:55:20 -0400698 final int maxHeight;
699 if (mMeasureVerticalWithPaddingMargin) {
700 maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
701 - params.topMargin - params.bottomMargin);
702 } else {
703 maxHeight = Math.max(0, myHeight);
704 }
705
706 final int heightMode;
Alan Viverette130ce742015-08-12 13:04:16 -0400707 if (params.height == LayoutParams.MATCH_PARENT) {
Alan Viverette7a40d212015-07-29 15:55:20 -0400708 heightMode = MeasureSpec.EXACTLY;
709 } else {
710 heightMode = MeasureSpec.AT_MOST;
711 }
712 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
Romain Guyf782e602009-06-25 15:26:49 -0700713 }
Alan Viverette7a40d212015-07-29 15:55:20 -0400714
Romain Guy725015a2009-06-23 14:27:34 -0700715 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
716 }
717
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 /**
719 * Get a measure spec that accounts for all of the constraints on this view.
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700720 * This includes size constraints imposed by the RelativeLayout as well as
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 * the View's desired dimension.
722 *
723 * @param childStart The left or top field of the child's layout params
724 * @param childEnd The right or bottom field of the child's layout params
725 * @param childSize The child's desired size (the width or height field of
726 * the child's layout params)
727 * @param startMargin The left or top margin
728 * @param endMargin The right or bottom margin
729 * @param startPadding mPaddingLeft or mPaddingTop
730 * @param endPadding mPaddingRight or mPaddingBottom
731 * @param mySize The width or height of this view (the RelativeLayout)
732 * @return MeasureSpec for the child
733 */
734 private int getChildMeasureSpec(int childStart, int childEnd,
735 int childSize, int startMargin, int endMargin, int startPadding,
736 int endPadding, int mySize) {
737 int childSpecMode = 0;
738 int childSpecSize = 0;
739
Chet Haase28308e82014-07-22 16:38:16 -0700740 // Negative values in a mySize value in RelativeLayout
Alan Viverette517a0042013-12-04 17:08:44 -0800741 // measurement is code for, "we got an unspecified mode in the
742 // RelativeLayout's measure spec."
Alan Viverette5b39ec82015-07-22 15:37:14 -0400743 final boolean isUnspecified = mySize < 0;
744 if (isUnspecified && !mAllowBrokenMeasureSpecs) {
Chet Haase28308e82014-07-22 16:38:16 -0700745 if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
Alan Viverette517a0042013-12-04 17:08:44 -0800746 // Constraints fixed both edges, so child has an exact size.
747 childSpecSize = Math.max(0, childEnd - childStart);
748 childSpecMode = MeasureSpec.EXACTLY;
Alan Viveretted5694f02014-02-12 15:49:18 -0800749 } else if (childSize >= 0) {
750 // The child specified an exact size.
751 childSpecSize = childSize;
752 childSpecMode = MeasureSpec.EXACTLY;
Alan Viverette517a0042013-12-04 17:08:44 -0800753 } else {
754 // Allow the child to be whatever size it wants.
755 childSpecSize = 0;
756 childSpecMode = MeasureSpec.UNSPECIFIED;
757 }
758
759 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
760 }
761
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 // Figure out start and end bounds.
763 int tempStart = childStart;
764 int tempEnd = childEnd;
765
766 // If the view did not express a layout constraint for an edge, use
767 // view's margins and our padding
Chet Haase28308e82014-07-22 16:38:16 -0700768 if (tempStart == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 tempStart = startPadding + startMargin;
770 }
Chet Haase28308e82014-07-22 16:38:16 -0700771 if (tempEnd == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 tempEnd = mySize - endPadding - endMargin;
773 }
774
775 // Figure out maximum size available to this view
Alan Viverette39310d32015-06-24 17:03:48 -0700776 final int maxAvailable = tempEnd - tempStart;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777
Chet Haase28308e82014-07-22 16:38:16 -0700778 if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
Alan Viverette39310d32015-06-24 17:03:48 -0700779 // Constraints fixed both edges, so child must be an exact size.
Alan Viverette5b39ec82015-07-22 15:37:14 -0400780 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
Alan Viverette39310d32015-06-24 17:03:48 -0700781 childSpecSize = Math.max(0, maxAvailable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 } else {
783 if (childSize >= 0) {
Alan Viverette39310d32015-06-24 17:03:48 -0700784 // Child wanted an exact size. Give as much as possible.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 childSpecMode = MeasureSpec.EXACTLY;
786
787 if (maxAvailable >= 0) {
Alan Viverette39310d32015-06-24 17:03:48 -0700788 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 childSpecSize = Math.min(maxAvailable, childSize);
790 } else {
791 // We can grow in this dimension.
792 childSpecSize = childSize;
793 }
Romain Guy980a9382010-01-08 15:06:28 -0800794 } else if (childSize == LayoutParams.MATCH_PARENT) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700795 // Child wanted to be as big as possible. Give all available
Alan Viverette39310d32015-06-24 17:03:48 -0700796 // space.
Alan Viverette5b39ec82015-07-22 15:37:14 -0400797 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
Alan Viverette39310d32015-06-24 17:03:48 -0700798 childSpecSize = Math.max(0, maxAvailable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 } else if (childSize == LayoutParams.WRAP_CONTENT) {
Alan Viverette39310d32015-06-24 17:03:48 -0700800 // Child wants to wrap content. Use AT_MOST to communicate
801 // available space if we know our max size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 if (maxAvailable >= 0) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700803 // We have a maximum size in this dimension.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 childSpecMode = MeasureSpec.AT_MOST;
805 childSpecSize = maxAvailable;
806 } else {
807 // We can grow in this dimension. Child can be as big as it
Alan Viverette39310d32015-06-24 17:03:48 -0700808 // wants.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 childSpecMode = MeasureSpec.UNSPECIFIED;
810 childSpecSize = 0;
811 }
812 }
813 }
814
815 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
816 }
817
Romain Guyf7dabb02009-06-25 14:47:14 -0700818 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
819 boolean wrapContent) {
820
Fabrice Di Meglioe56ffdc2012-09-23 14:51:16 -0700821 final int layoutDirection = getLayoutDirection();
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700822 int[] rules = params.getRules(layoutDirection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823
Chet Haase28308e82014-07-22 16:38:16 -0700824 if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 // Right is fixed, but left varies
826 params.mLeft = params.mRight - child.getMeasuredWidth();
Chet Haase28308e82014-07-22 16:38:16 -0700827 } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 // Left is fixed, but right varies
829 params.mRight = params.mLeft + child.getMeasuredWidth();
Chet Haase28308e82014-07-22 16:38:16 -0700830 } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 // Both left and right vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700832 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
833 if (!wrapContent) {
834 centerHorizontal(child, params, myWidth);
835 } else {
836 params.mLeft = mPaddingLeft + params.leftMargin;
837 params.mRight = params.mLeft + child.getMeasuredWidth();
838 }
839 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 } else {
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700841 // This is the default case. For RTL we start from the right and for LTR we start
842 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
843 if (isLayoutRtl()) {
844 params.mRight = myWidth - mPaddingRight- params.rightMargin;
845 params.mLeft = params.mRight - child.getMeasuredWidth();
846 } else {
847 params.mLeft = mPaddingLeft + params.leftMargin;
848 params.mRight = params.mLeft + child.getMeasuredWidth();
849 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 }
851 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -0700852 return rules[ALIGN_PARENT_END] != 0;
Romain Guy725015a2009-06-23 14:27:34 -0700853 }
854
Romain Guyf7dabb02009-06-25 14:47:14 -0700855 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
856 boolean wrapContent) {
857
Romain Guy725015a2009-06-23 14:27:34 -0700858 int[] rules = params.getRules();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859
Chet Haase28308e82014-07-22 16:38:16 -0700860 if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 // Bottom is fixed, but top varies
862 params.mTop = params.mBottom - child.getMeasuredHeight();
Chet Haase28308e82014-07-22 16:38:16 -0700863 } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 // Top is fixed, but bottom varies
865 params.mBottom = params.mTop + child.getMeasuredHeight();
Chet Haase28308e82014-07-22 16:38:16 -0700866 } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 // Both top and bottom vary
Romain Guyf7dabb02009-06-25 14:47:14 -0700868 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
869 if (!wrapContent) {
870 centerVertical(child, params, myHeight);
871 } else {
872 params.mTop = mPaddingTop + params.topMargin;
873 params.mBottom = params.mTop + child.getMeasuredHeight();
874 }
875 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 } else {
877 params.mTop = mPaddingTop + params.topMargin;
878 params.mBottom = params.mTop + child.getMeasuredHeight();
879 }
880 }
Romain Guy42460ac2010-01-11 16:46:33 -0800881 return rules[ALIGN_PARENT_BOTTOM] != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 }
883
Fabrice Di Meglio54726132013-01-18 18:36:45 -0800884 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 RelativeLayout.LayoutParams anchorParams;
886
Chet Haase28308e82014-07-22 16:38:16 -0700887 // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
888 // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
889 // wants to the right
890 // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
891 // wants to the left
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 // left=10, right=20 means the left and right ends are both fixed
Chet Haase28308e82014-07-22 16:38:16 -0700893 childParams.mLeft = VALUE_NOT_SET;
894 childParams.mRight = VALUE_NOT_SET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895
896 anchorParams = getRelatedViewParams(rules, LEFT_OF);
897 if (anchorParams != null) {
898 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
899 childParams.rightMargin);
900 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
901 if (myWidth >= 0) {
902 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
904 }
905
906 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
907 if (anchorParams != null) {
908 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
909 childParams.leftMargin);
910 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
911 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
912 }
913
914 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
915 if (anchorParams != null) {
916 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
917 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
918 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
919 }
920
921 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
922 if (anchorParams != null) {
923 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
924 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
925 if (myWidth >= 0) {
926 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 }
928 }
929
Romain Guy1b7c7912013-04-25 14:55:28 -0700930 if (0 != rules[ALIGN_PARENT_LEFT]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
932 }
933
Romain Guy1b7c7912013-04-25 14:55:28 -0700934 if (0 != rules[ALIGN_PARENT_RIGHT]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 if (myWidth >= 0) {
936 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 }
938 }
Romain Guy725015a2009-06-23 14:27:34 -0700939 }
940
Alan Viverettee1a65712015-01-14 12:30:04 -0800941 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {
942 final int[] rules = childParams.getRules();
943
944 // Baseline alignment overrides any explicitly specified top or bottom.
945 int baselineOffset = getRelatedViewBaselineOffset(rules);
946 if (baselineOffset != -1) {
947 if (myBaseline != -1) {
948 baselineOffset -= myBaseline;
949 }
950 childParams.mTop = baselineOffset;
951 childParams.mBottom = VALUE_NOT_SET;
952 return;
953 }
954
Romain Guy725015a2009-06-23 14:27:34 -0700955 RelativeLayout.LayoutParams anchorParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956
Chet Haase28308e82014-07-22 16:38:16 -0700957 childParams.mTop = VALUE_NOT_SET;
958 childParams.mBottom = VALUE_NOT_SET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959
960 anchorParams = getRelatedViewParams(rules, ABOVE);
961 if (anchorParams != null) {
962 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
963 childParams.bottomMargin);
964 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
965 if (myHeight >= 0) {
966 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 }
968 }
969
970 anchorParams = getRelatedViewParams(rules, BELOW);
971 if (anchorParams != null) {
972 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
973 childParams.topMargin);
974 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
975 childParams.mTop = mPaddingTop + childParams.topMargin;
976 }
977
978 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
979 if (anchorParams != null) {
980 childParams.mTop = anchorParams.mTop + childParams.topMargin;
981 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
982 childParams.mTop = mPaddingTop + childParams.topMargin;
983 }
984
985 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
986 if (anchorParams != null) {
987 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
988 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
989 if (myHeight >= 0) {
990 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 }
992 }
993
Romain Guy1b7c7912013-04-25 14:55:28 -0700994 if (0 != rules[ALIGN_PARENT_TOP]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 childParams.mTop = mPaddingTop + childParams.topMargin;
996 }
997
Romain Guy1b7c7912013-04-25 14:55:28 -0700998 if (0 != rules[ALIGN_PARENT_BOTTOM]) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 if (myHeight >= 0) {
1000 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 }
1002 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 }
1004
1005 private View getRelatedView(int[] rules, int relation) {
1006 int id = rules[relation];
1007 if (id != 0) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001008 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
Romain Guya0fd1d72009-06-24 14:25:43 -07001009 if (node == null) return null;
1010 View v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011
1012 // Find the first non-GONE view up the chain
1013 while (v.getVisibility() == View.GONE) {
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001014 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
Romain Guy1ab621e2009-06-25 13:31:57 -07001015 node = mGraph.mKeyNodes.get((rules[relation]));
Romain Guya0fd1d72009-06-24 14:25:43 -07001016 if (node == null) return null;
1017 v = node.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 }
1019
1020 return v;
1021 }
1022
1023 return null;
1024 }
1025
1026 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1027 View v = getRelatedView(rules, relation);
1028 if (v != null) {
1029 ViewGroup.LayoutParams params = v.getLayoutParams();
1030 if (params instanceof LayoutParams) {
1031 return (LayoutParams) v.getLayoutParams();
1032 }
1033 }
1034 return null;
1035 }
1036
Alan Viverettee1a65712015-01-14 12:30:04 -08001037 private int getRelatedViewBaselineOffset(int[] rules) {
1038 final View v = getRelatedView(rules, ALIGN_BASELINE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 if (v != null) {
Alan Viverettee1a65712015-01-14 12:30:04 -08001040 final int baseline = v.getBaseline();
1041 if (baseline != -1) {
1042 final ViewGroup.LayoutParams params = v.getLayoutParams();
1043 if (params instanceof LayoutParams) {
1044 final LayoutParams anchorParams = (LayoutParams) v.getLayoutParams();
1045 return anchorParams.mTop + baseline;
1046 }
1047 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 }
1049 return -1;
1050 }
1051
Romain Guyf16c7a92013-02-11 17:43:59 -08001052 private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 int childWidth = child.getMeasuredWidth();
1054 int left = (myWidth - childWidth) / 2;
1055
1056 params.mLeft = left;
1057 params.mRight = left + childWidth;
1058 }
1059
Romain Guyf16c7a92013-02-11 17:43:59 -08001060 private static void centerVertical(View child, LayoutParams params, int myHeight) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 int childHeight = child.getMeasuredHeight();
1062 int top = (myHeight - childHeight) / 2;
1063
1064 params.mTop = top;
1065 params.mBottom = top + childHeight;
1066 }
1067
1068 @Override
1069 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1070 // The layout has actually already been performed and the positions
1071 // cached. Apply the cached values to the children.
Fabrice Di Meglio13705ed2012-11-26 20:05:31 -08001072 final int count = getChildCount();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073
1074 for (int i = 0; i < count; i++) {
1075 View child = getChildAt(i);
1076 if (child.getVisibility() != GONE) {
1077 RelativeLayout.LayoutParams st =
1078 (RelativeLayout.LayoutParams) child.getLayoutParams();
1079 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 }
1081 }
1082 }
1083
1084 @Override
1085 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1086 return new RelativeLayout.LayoutParams(getContext(), attrs);
1087 }
1088
1089 /**
1090 * Returns a set of layout parameters with a width of
1091 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1092 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1093 */
1094 @Override
1095 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1096 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1097 }
1098
1099 // Override to allow type-checking of LayoutParams.
1100 @Override
1101 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1102 return p instanceof RelativeLayout.LayoutParams;
1103 }
1104
1105 @Override
Yigit Boyar885c50b2016-03-22 16:53:42 -07001106 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
Yigit Boyar2dd20a62016-08-01 17:42:28 -07001107 if (sPreserveMarginParamsInLayoutParamConversion) {
1108 if (lp instanceof LayoutParams) {
1109 return new LayoutParams((LayoutParams) lp);
1110 } else if (lp instanceof MarginLayoutParams) {
1111 return new LayoutParams((MarginLayoutParams) lp);
1112 }
Yigit Boyar885c50b2016-03-22 16:53:42 -07001113 }
Yigit Boyar2dd20a62016-08-01 17:42:28 -07001114 return new LayoutParams(lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 }
1116
Alan Viverettea54956a2015-01-07 16:05:02 -08001117 /** @hide */
svetoslavganov75986cf2009-05-14 22:28:01 -07001118 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -08001119 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001120 if (mTopToBottomLeftToRightSet == null) {
1121 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1122 }
1123
1124 // sort children top-to-bottom and left-to-right
1125 for (int i = 0, count = getChildCount(); i < count; i++) {
1126 mTopToBottomLeftToRightSet.add(getChildAt(i));
1127 }
1128
1129 for (View view : mTopToBottomLeftToRightSet) {
Svetoslav Ganov0b0a41d2011-09-07 18:06:03 -07001130 if (view.getVisibility() == View.VISIBLE
1131 && view.dispatchPopulateAccessibilityEvent(event)) {
svetoslavganov75986cf2009-05-14 22:28:01 -07001132 mTopToBottomLeftToRightSet.clear();
1133 return true;
1134 }
1135 }
1136
1137 mTopToBottomLeftToRightSet.clear();
1138 return false;
1139 }
1140
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001141 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -08001142 public CharSequence getAccessibilityClassName() {
1143 return RelativeLayout.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001144 }
1145
svetoslavganov75986cf2009-05-14 22:28:01 -07001146 /**
1147 * Compares two views in left-to-right and top-to-bottom fashion.
1148 */
1149 private class TopToBottomLeftToRightComparator implements Comparator<View> {
1150 public int compare(View first, View second) {
1151 // top - bottom
1152 int topDifference = first.getTop() - second.getTop();
1153 if (topDifference != 0) {
1154 return topDifference;
1155 }
1156 // left - right
1157 int leftDifference = first.getLeft() - second.getLeft();
1158 if (leftDifference != 0) {
1159 return leftDifference;
1160 }
1161 // break tie by height
1162 int heightDiference = first.getHeight() - second.getHeight();
1163 if (heightDiference != 0) {
1164 return heightDiference;
1165 }
1166 // break tie by width
1167 int widthDiference = first.getWidth() - second.getWidth();
1168 if (widthDiference != 0) {
1169 return widthDiference;
1170 }
1171 return 0;
1172 }
1173 }
1174
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 /**
1176 * Per-child layout information associated with RelativeLayout.
1177 *
1178 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1179 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1180 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1181 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1182 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1183 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1184 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1185 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1186 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1187 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1188 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1189 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1190 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1191 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1192 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1193 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1194 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001195 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1196 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1197 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1198 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1199 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1200 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 */
1202 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001203 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001204 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1205 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1206 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1207 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1208 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1209 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1210 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1211 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1212 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1213 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1214 @ViewDebug.IntToString(from = BELOW, to = "below"),
1215 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1216 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1217 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1218 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001219 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"),
1220 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"),
1221 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"),
1222 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"),
1223 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"),
1224 @ViewDebug.IntToString(from = START_OF, to = "startOf"),
1225 @ViewDebug.IntToString(from = END_OF, to = "endOf")
The Android Open Source Project10592532009-03-18 17:39:46 -07001226 }, mapping = {
1227 @ViewDebug.IntToString(from = TRUE, to = "true"),
Romain Guya1f3e4a2009-06-04 15:10:46 -07001228 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
The Android Open Source Project10592532009-03-18 17:39:46 -07001229 })
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 private int[] mRules = new int[VERB_COUNT];
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001232 private int[] mInitialRules = new int[VERB_COUNT];
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001233
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001234 private int mLeft, mTop, mRight, mBottom;
1235
Alan Viverette895615c2015-08-18 14:51:48 -04001236 /**
1237 * Whether this view had any relative rules modified following the most
1238 * recent resolution of layout direction.
1239 */
1240 private boolean mNeedsLayoutResolution;
1241
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001242 private boolean mRulesChanged = false;
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001243 private boolean mIsRtlCompatibilityMode = false;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 /**
1246 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1247 * the anchor's visibility is GONE.
1248 */
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001249 @ViewDebug.ExportedProperty(category = "layout")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 public boolean alignWithParent;
1251
1252 public LayoutParams(Context c, AttributeSet attrs) {
1253 super(c, attrs);
1254
1255 TypedArray a = c.obtainStyledAttributes(attrs,
1256 com.android.internal.R.styleable.RelativeLayout_Layout);
1257
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001258 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1259 mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1260 !c.getApplicationInfo().hasRtlSupport());
1261
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 final int[] rules = mRules;
Romain Guyf16c7a92013-02-11 17:43:59 -08001263 //noinspection MismatchedReadAndWriteOfArray
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001264 final int[] initialRules = mInitialRules;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265
1266 final int N = a.getIndexCount();
1267 for (int i = 0; i < N; i++) {
1268 int attr = a.getIndex(i);
1269 switch (attr) {
1270 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1271 alignWithParent = a.getBoolean(attr, false);
1272 break;
1273 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1274 rules[LEFT_OF] = a.getResourceId(attr, 0);
1275 break;
1276 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1277 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1278 break;
1279 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1280 rules[ABOVE] = a.getResourceId(attr, 0);
1281 break;
1282 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1283 rules[BELOW] = a.getResourceId(attr, 0);
1284 break;
1285 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1286 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1287 break;
1288 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1289 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1290 break;
1291 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1292 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1293 break;
1294 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1295 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1296 break;
1297 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1298 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1299 break;
1300 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1301 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1302 break;
1303 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1304 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1305 break;
1306 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1307 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1308 break;
1309 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1310 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1311 break;
1312 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1313 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1314 break;
1315 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1316 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1317 break;
1318 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1319 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1320 break;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001321 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1322 rules[START_OF] = a.getResourceId(attr, 0);
1323 break;
1324 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1325 rules[END_OF] = a.getResourceId(attr, 0);
1326 break;
1327 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1328 rules[ALIGN_START] = a.getResourceId(attr, 0);
1329 break;
1330 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1331 rules[ALIGN_END] = a.getResourceId(attr, 0);
1332 break;
1333 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1334 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1335 break;
1336 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1337 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1338 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 }
1340 }
Fabrice Di Meglio0072f642013-03-26 15:50:24 -07001341 mRulesChanged = true;
Romain Guyf16c7a92013-02-11 17:43:59 -08001342 System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001343
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 a.recycle();
1345 }
1346
1347 public LayoutParams(int w, int h) {
1348 super(w, h);
1349 }
1350
1351 /**
1352 * {@inheritDoc}
1353 */
1354 public LayoutParams(ViewGroup.LayoutParams source) {
1355 super(source);
1356 }
1357
1358 /**
1359 * {@inheritDoc}
1360 */
1361 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1362 super(source);
1363 }
1364
Alan Viverette0a0e1552013-08-07 13:24:09 -07001365 /**
1366 * Copy constructor. Clones the width, height, margin values, and rules
1367 * of the source.
1368 *
1369 * @param source The layout params to copy from.
1370 */
1371 public LayoutParams(LayoutParams source) {
1372 super(source);
1373
1374 this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
1375 this.mRulesChanged = source.mRulesChanged;
1376 this.alignWithParent = source.alignWithParent;
1377
1378 System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT);
1379 System.arraycopy(
1380 source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT);
1381 }
1382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 @Override
1384 public String debug(String output) {
1385 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1386 ", height=" + sizeToString(height) + " }";
1387 }
1388
1389 /**
Alan Viverette895615c2015-08-18 14:51:48 -04001390 * Adds a layout rule to be interpreted by the RelativeLayout.
1391 * <p>
1392 * This method should only be used for verbs that don't refer to a
1393 * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean
1394 * value ({@link #TRUE} for true or 0 for false). To
1395 * specify a verb that takes a subject, use {@link #addRule(int, int)}.
1396 * <p>
1397 * If the rule is relative to the layout direction (ex.
1398 * {@link #ALIGN_PARENT_START}), then the layout direction must be
1399 * resolved using {@link #resolveLayoutDirection(int)} before calling
1400 * {@link #getRule(int)} an absolute rule (ex.
1401 * {@link #ALIGN_PARENT_LEFT}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 *
Alan Viverette895615c2015-08-18 14:51:48 -04001403 * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 * @see #addRule(int, int)
Alan Viverette895615c2015-08-18 14:51:48 -04001405 * @see #removeRule(int)
Alan Viverette62c79e92015-02-26 09:47:10 -08001406 * @see #getRule(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 */
1408 public void addRule(int verb) {
Alan Viverette895615c2015-08-18 14:51:48 -04001409 addRule(verb, TRUE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 }
1411
1412 /**
Alan Viverette895615c2015-08-18 14:51:48 -04001413 * Adds a layout rule to be interpreted by the RelativeLayout.
1414 * <p>
1415 * Use this for verbs that refer to a sibling (ex.
1416 * {@link #ALIGN_RIGHT}) or take a boolean value (ex.
1417 * {@link #CENTER_IN_PARENT}).
1418 * <p>
1419 * If the rule is relative to the layout direction (ex.
1420 * {@link #START_OF}), then the layout direction must be resolved using
1421 * {@link #resolveLayoutDirection(int)} before calling
1422 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 *
Alan Viverette895615c2015-08-18 14:51:48 -04001424 * @param verb a layout verb, such as {@link #ALIGN_RIGHT}
1425 * @param subject the ID of another view to use as an anchor, or a
1426 * boolean value (represented as {@link #TRUE} for true
1427 * or 0 for false)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 * @see #addRule(int)
Alan Viverette895615c2015-08-18 14:51:48 -04001429 * @see #removeRule(int)
Alan Viverette62c79e92015-02-26 09:47:10 -08001430 * @see #getRule(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001431 */
Alan Viverette895615c2015-08-18 14:51:48 -04001432 public void addRule(int verb, int subject) {
1433 // If we're removing a relative rule, we'll need to force layout
1434 // resolution the next time it's requested.
1435 if (!mNeedsLayoutResolution && isRelativeRule(verb)
1436 && mInitialRules[verb] != 0 && subject == 0) {
1437 mNeedsLayoutResolution = true;
1438 }
1439
1440 mRules[verb] = subject;
1441 mInitialRules[verb] = subject;
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001442 mRulesChanged = true;
1443 }
1444
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001445 /**
1446 * Removes a layout rule to be interpreted by the RelativeLayout.
Alan Viverette895615c2015-08-18 14:51:48 -04001447 * <p>
1448 * If the rule is relative to the layout direction (ex.
1449 * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the
1450 * layout direction must be resolved using
1451 * {@link #resolveLayoutDirection(int)} before before calling
1452 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001453 *
1454 * @param verb One of the verbs defined by
1455 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1456 * ALIGN_WITH_PARENT_LEFT.
1457 * @see #addRule(int)
1458 * @see #addRule(int, int)
Alan Viverette62c79e92015-02-26 09:47:10 -08001459 * @see #getRule(int)
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001460 */
1461 public void removeRule(int verb) {
Alan Viverette895615c2015-08-18 14:51:48 -04001462 addRule(verb, 0);
Fabrice Di Meglioa4c10302012-07-26 12:27:56 -07001463 }
1464
Alan Viverette62c79e92015-02-26 09:47:10 -08001465 /**
1466 * Returns the layout rule associated with a specific verb.
1467 *
1468 * @param verb one of the verbs defined by {@link RelativeLayout}, such
1469 * as ALIGN_WITH_PARENT_LEFT
1470 * @return the id of another view to use as an anchor, a boolean value
1471 * (represented as {@link RelativeLayout#TRUE} for true
1472 * or 0 for false), or -1 for verbs that don't refer to another
1473 * sibling (for example, ALIGN_WITH_PARENT_BOTTOM)
1474 * @see #addRule(int)
1475 * @see #addRule(int, int)
1476 */
1477 public int getRule(int verb) {
1478 return mRules[verb];
1479 }
1480
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001481 private boolean hasRelativeRules() {
1482 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1483 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1484 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1485 }
1486
Alan Viverette895615c2015-08-18 14:51:48 -04001487 private boolean isRelativeRule(int rule) {
1488 return rule == START_OF || rule == END_OF
1489 || rule == ALIGN_START || rule == ALIGN_END
1490 || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END;
1491 }
1492
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001493 // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1494 // or not.
1495 //
1496 // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1497 // predominance over any "start/end" rules that could have been defined. A special case:
1498 // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1499 // resolve those "start"/"end" rules to "left"/"right" respectively.
1500 //
1501 // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1502 // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1503 //
1504 // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1505 // only the "left"/"right" rules at the end.
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001506 private void resolveRules(int layoutDirection) {
1507 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001508
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001509 // Reset to initial state
Romain Guyf16c7a92013-02-11 17:43:59 -08001510 System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001511
1512 // Apply rules depending on direction and if we are in RTL compatibility mode
1513 if (mIsRtlCompatibilityMode) {
1514 if (mRules[ALIGN_START] != 0) {
1515 if (mRules[ALIGN_LEFT] == 0) {
1516 // "left" rule is not defined but "start" rule is: use the "start" rule as
1517 // the "left" rule
1518 mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1519 }
1520 mRules[ALIGN_START] = 0;
1521 }
1522
1523 if (mRules[ALIGN_END] != 0) {
1524 if (mRules[ALIGN_RIGHT] == 0) {
1525 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1526 // "right" rule
1527 mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1528 }
1529 mRules[ALIGN_END] = 0;
1530 }
1531
1532 if (mRules[START_OF] != 0) {
1533 if (mRules[LEFT_OF] == 0) {
1534 // "left" rule is not defined but "start" rule is: use the "start" rule as
1535 // the "left" rule
1536 mRules[LEFT_OF] = mRules[START_OF];
1537 }
1538 mRules[START_OF] = 0;
1539 }
1540
1541 if (mRules[END_OF] != 0) {
1542 if (mRules[RIGHT_OF] == 0) {
1543 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1544 // "right" rule
1545 mRules[RIGHT_OF] = mRules[END_OF];
1546 }
1547 mRules[END_OF] = 0;
1548 }
1549
1550 if (mRules[ALIGN_PARENT_START] != 0) {
1551 if (mRules[ALIGN_PARENT_LEFT] == 0) {
1552 // "left" rule is not defined but "start" rule is: use the "start" rule as
1553 // the "left" rule
1554 mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1555 }
1556 mRules[ALIGN_PARENT_START] = 0;
1557 }
1558
Alan Viverette567e4472014-08-19 15:41:51 -07001559 if (mRules[ALIGN_PARENT_END] != 0) {
Fabrice Di Meglioc44d8802013-02-19 17:42:13 -08001560 if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1561 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1562 // "right" rule
1563 mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1564 }
1565 mRules[ALIGN_PARENT_END] = 0;
1566 }
1567 } else {
1568 // JB MR1+ case
1569 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1570 (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1571 // "start"/"end" rules take precedence over "left"/"right" rules
1572 mRules[ALIGN_LEFT] = 0;
1573 mRules[ALIGN_RIGHT] = 0;
1574 }
1575 if (mRules[ALIGN_START] != 0) {
1576 // "start" rule resolved to "left" or "right" depending on the direction
1577 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1578 mRules[ALIGN_START] = 0;
1579 }
1580 if (mRules[ALIGN_END] != 0) {
1581 // "end" rule resolved to "left" or "right" depending on the direction
1582 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1583 mRules[ALIGN_END] = 0;
1584 }
1585
1586 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1587 (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1588 // "start"/"end" rules take precedence over "left"/"right" rules
1589 mRules[LEFT_OF] = 0;
1590 mRules[RIGHT_OF] = 0;
1591 }
1592 if (mRules[START_OF] != 0) {
1593 // "start" rule resolved to "left" or "right" depending on the direction
1594 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1595 mRules[START_OF] = 0;
1596 }
1597 if (mRules[END_OF] != 0) {
1598 // "end" rule resolved to "left" or "right" depending on the direction
1599 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1600 mRules[END_OF] = 0;
1601 }
1602
1603 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1604 (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1605 // "start"/"end" rules take precedence over "left"/"right" rules
1606 mRules[ALIGN_PARENT_LEFT] = 0;
1607 mRules[ALIGN_PARENT_RIGHT] = 0;
1608 }
1609 if (mRules[ALIGN_PARENT_START] != 0) {
1610 // "start" rule resolved to "left" or "right" depending on the direction
1611 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1612 mRules[ALIGN_PARENT_START] = 0;
1613 }
1614 if (mRules[ALIGN_PARENT_END] != 0) {
1615 // "end" rule resolved to "left" or "right" depending on the direction
1616 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1617 mRules[ALIGN_PARENT_END] = 0;
1618 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001619 }
Alan Viverette895615c2015-08-18 14:51:48 -04001620
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001621 mRulesChanged = false;
Alan Viverette895615c2015-08-18 14:51:48 -04001622 mNeedsLayoutResolution = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 }
1624
1625 /**
1626 * Retrieves a complete list of all supported rules, where the index is the rule
1627 * verb, and the element value is the value specified, or "false" if it was never
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001628 * set. If there are relative rules defined (*_START / *_END), they will be resolved
1629 * depending on the layout direction.
1630 *
1631 * @param layoutDirection the direction of the layout.
1632 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
1633 * or {@link View#LAYOUT_DIRECTION_RTL}
1634 * @return the supported rules
1635 * @see #addRule(int, int)
1636 *
1637 * @hide
1638 */
1639 public int[] getRules(int layoutDirection) {
Alan Viverette895615c2015-08-18 14:51:48 -04001640 resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001641 return mRules;
1642 }
1643
1644 /**
1645 * Retrieves a complete list of all supported rules, where the index is the rule
1646 * verb, and the element value is the value specified, or "false" if it was never
1647 * set. There will be no resolution of relative rules done.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 *
1649 * @return the supported rules
1650 * @see #addRule(int, int)
1651 */
1652 public int[] getRules() {
1653 return mRules;
1654 }
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001655
Alan Viverette895615c2015-08-18 14:51:48 -04001656 /**
1657 * This will be called by {@link android.view.View#requestLayout()} to
1658 * resolve layout parameters that are relative to the layout direction.
1659 * <p>
1660 * After this method is called, any rules using layout-relative verbs
1661 * (ex. {@link #START_OF}) previously added via {@link #addRule(int)}
1662 * may only be accessed via their resolved absolute verbs (ex.
1663 * {@link #LEFT_OF}).
1664 */
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001665 @Override
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001666 public void resolveLayoutDirection(int layoutDirection) {
Alan Viverette895615c2015-08-18 14:51:48 -04001667 if (shouldResolveLayoutDirection(layoutDirection)) {
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001668 resolveRules(layoutDirection);
1669 }
Alan Viverette895615c2015-08-18 14:51:48 -04001670
1671 // This will set the layout direction.
Fabrice Di Meglio2918ab62012-10-10 16:39:25 -07001672 super.resolveLayoutDirection(layoutDirection);
Fabrice Di Megliof443f982012-07-13 20:24:03 -07001673 }
Siva Velusamy94a6d152015-05-05 15:07:00 -07001674
Alan Viverette895615c2015-08-18 14:51:48 -04001675 private boolean shouldResolveLayoutDirection(int layoutDirection) {
1676 return (mNeedsLayoutResolution || hasRelativeRules())
1677 && (mRulesChanged || layoutDirection != getLayoutDirection());
1678 }
1679
Siva Velusamy94a6d152015-05-05 15:07:00 -07001680 /** @hide */
1681 @Override
1682 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1683 super.encodeProperties(encoder);
1684 encoder.addProperty("layout:alignWithParent", alignWithParent);
1685 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001686 }
Romain Guy725015a2009-06-23 14:27:34 -07001687
1688 private static class DependencyGraph {
1689 /**
Romain Guy1ab621e2009-06-25 13:31:57 -07001690 * List of all views in the graph.
1691 */
1692 private ArrayList<Node> mNodes = new ArrayList<Node>();
1693
1694 /**
Romain Guy725015a2009-06-23 14:27:34 -07001695 * List of nodes in the graph. Each node is identified by its
1696 * view id (see View#getId()).
1697 */
Romain Guy1ab621e2009-06-25 13:31:57 -07001698 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001699
1700 /**
1701 * Temporary data structure used to build the list of roots
1702 * for this graph.
1703 */
Romain Guybc5d8762012-01-06 16:40:49 -08001704 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
Romain Guy725015a2009-06-23 14:27:34 -07001705
1706 /**
1707 * Clears the graph.
1708 */
1709 void clear() {
Romain Guy1ab621e2009-06-25 13:31:57 -07001710 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001711 final int count = nodes.size();
1712
1713 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001714 nodes.get(i).release();
Romain Guy725015a2009-06-23 14:27:34 -07001715 }
1716 nodes.clear();
1717
Romain Guy1ab621e2009-06-25 13:31:57 -07001718 mKeyNodes.clear();
Romain Guy725015a2009-06-23 14:27:34 -07001719 mRoots.clear();
1720 }
1721
1722 /**
1723 * Adds a view to the graph.
1724 *
1725 * @param view The view to be added as a node to the graph.
1726 */
1727 void add(View view) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001728 final int id = view.getId();
1729 final Node node = Node.acquire(view);
1730
1731 if (id != View.NO_ID) {
1732 mKeyNodes.put(id, node);
1733 }
1734
1735 mNodes.add(node);
Romain Guy725015a2009-06-23 14:27:34 -07001736 }
1737
1738 /**
1739 * Builds a sorted list of views. The sorting order depends on the dependencies
1740 * between the view. For instance, if view C needs view A to be processed first
1741 * and view A needs view B to be processed first, the dependency graph
1742 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1743 *
1744 * @param sorted The sorted list of views. The length of this array must
1745 * be equal to getChildCount().
1746 * @param rules The list of rules to take into account.
1747 */
1748 void getSortedViews(View[] sorted, int... rules) {
Romain Guybc5d8762012-01-06 16:40:49 -08001749 final ArrayDeque<Node> roots = findRoots(rules);
Romain Guy725015a2009-06-23 14:27:34 -07001750 int index = 0;
1751
Romain Guybc5d8762012-01-06 16:40:49 -08001752 Node node;
1753 while ((node = roots.pollLast()) != null) {
Romain Guy725015a2009-06-23 14:27:34 -07001754 final View view = node.view;
1755 final int key = view.getId();
1756
1757 sorted[index++] = view;
1758
Romain Guy6876b4f2013-06-03 14:25:56 -07001759 final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
1760 final int count = dependents.size();
1761 for (int i = 0; i < count; i++) {
1762 final Node dependent = dependents.keyAt(i);
Romain Guy725015a2009-06-23 14:27:34 -07001763 final SparseArray<Node> dependencies = dependent.dependencies;
1764
1765 dependencies.remove(key);
1766 if (dependencies.size() == 0) {
1767 roots.add(dependent);
1768 }
1769 }
1770 }
1771
1772 if (index < sorted.length) {
1773 throw new IllegalStateException("Circular dependencies cannot exist"
1774 + " in RelativeLayout");
1775 }
1776 }
1777
1778 /**
1779 * Finds the roots of the graph. A root is a node with no dependency and
1780 * with [0..n] dependents.
1781 *
1782 * @param rulesFilter The list of rules to consider when building the
1783 * dependencies
1784 *
1785 * @return A list of node, each being a root of the graph
1786 */
Romain Guybc5d8762012-01-06 16:40:49 -08001787 private ArrayDeque<Node> findRoots(int[] rulesFilter) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001788 final SparseArray<Node> keyNodes = mKeyNodes;
1789 final ArrayList<Node> nodes = mNodes;
Romain Guy725015a2009-06-23 14:27:34 -07001790 final int count = nodes.size();
1791
1792 // Find roots can be invoked several times, so make sure to clear
1793 // all dependents and dependencies before running the algorithm
1794 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001795 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001796 node.dependents.clear();
1797 node.dependencies.clear();
1798 }
1799
1800 // Builds up the dependents and dependencies for each node of the graph
1801 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001802 final Node node = nodes.get(i);
Romain Guy725015a2009-06-23 14:27:34 -07001803
1804 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1805 final int[] rules = layoutParams.mRules;
1806 final int rulesCount = rulesFilter.length;
1807
1808 // Look only the the rules passed in parameter, this way we build only the
1809 // dependencies for a specific set of rules
1810 for (int j = 0; j < rulesCount; j++) {
1811 final int rule = rules[rulesFilter[j]];
1812 if (rule > 0) {
1813 // The node this node depends on
Romain Guy1ab621e2009-06-25 13:31:57 -07001814 final Node dependency = keyNodes.get(rule);
Romain Guyda3003e2009-07-19 19:47:42 -07001815 // Skip unknowns and self dependencies
1816 if (dependency == null || dependency == node) {
Romain Guyb8f8de82009-06-25 12:03:56 -07001817 continue;
1818 }
Romain Guy725015a2009-06-23 14:27:34 -07001819 // Add the current node as a dependent
Romain Guybc5d8762012-01-06 16:40:49 -08001820 dependency.dependents.put(node, this);
Romain Guy725015a2009-06-23 14:27:34 -07001821 // Add a dependency to the current node
1822 node.dependencies.put(rule, dependency);
1823 }
1824 }
1825 }
1826
Romain Guybc5d8762012-01-06 16:40:49 -08001827 final ArrayDeque<Node> roots = mRoots;
Romain Guy725015a2009-06-23 14:27:34 -07001828 roots.clear();
1829
1830 // Finds all the roots in the graph: all nodes with no dependencies
1831 for (int i = 0; i < count; i++) {
Romain Guy1ab621e2009-06-25 13:31:57 -07001832 final Node node = nodes.get(i);
Romain Guybc5d8762012-01-06 16:40:49 -08001833 if (node.dependencies.size() == 0) roots.addLast(node);
Romain Guy725015a2009-06-23 14:27:34 -07001834 }
1835
1836 return roots;
1837 }
1838
1839 /**
Romain Guy725015a2009-06-23 14:27:34 -07001840 * A node in the dependency graph. A node is a view, its list of dependencies
1841 * and its list of dependents.
1842 *
1843 * A node with no dependent is considered a root of the graph.
1844 */
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001845 static class Node {
Romain Guy725015a2009-06-23 14:27:34 -07001846 /**
1847 * The view representing this node in the layout.
1848 */
1849 View view;
1850
1851 /**
1852 * The list of dependents for this node; a dependent is a node
1853 * that needs this node to be processed first.
1854 */
Romain Guy6876b4f2013-06-03 14:25:56 -07001855 final ArrayMap<Node, DependencyGraph> dependents =
1856 new ArrayMap<Node, DependencyGraph>();
Romain Guy725015a2009-06-23 14:27:34 -07001857
1858 /**
1859 * The list of dependencies for this node.
1860 */
1861 final SparseArray<Node> dependencies = new SparseArray<Node>();
1862
1863 /*
1864 * START POOL IMPLEMENTATION
1865 */
Romain Guybaac4632009-06-29 14:28:29 -07001866 // The pool is static, so all nodes instances are shared across
1867 // activities, that's why we give it a rather high limit
1868 private static final int POOL_LIMIT = 100;
Romain Guy6876b4f2013-06-03 14:25:56 -07001869 private static final SynchronizedPool<Node> sPool =
1870 new SynchronizedPool<Node>(POOL_LIMIT);
Svetoslav Ganov8643aa02011-04-20 12:12:33 -07001871
Romain Guy725015a2009-06-23 14:27:34 -07001872 static Node acquire(View view) {
Svetoslav Ganovabae2a12012-11-27 16:59:37 -08001873 Node node = sPool.acquire();
1874 if (node == null) {
1875 node = new Node();
1876 }
Romain Guy725015a2009-06-23 14:27:34 -07001877 node.view = view;
Romain Guy725015a2009-06-23 14:27:34 -07001878 return node;
1879 }
1880
1881 void release() {
1882 view = null;
1883 dependents.clear();
1884 dependencies.clear();
1885
1886 sPool.release(this);
1887 }
1888 /*
1889 * END POOL IMPLEMENTATION
1890 */
1891 }
1892 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001893}