blob: d085edae74c6aa8f6974031ac47638e099b2dcc1 [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
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
19import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
20
21import android.annotation.NonNull;
22import android.compat.annotation.UnsupportedAppUsage;
23import android.content.Context;
24import android.content.res.ResourceId;
25import android.content.res.TypedArray;
26import android.graphics.Rect;
27import android.os.Build;
28import android.util.ArrayMap;
29import android.util.AttributeSet;
30import android.util.Pools.SynchronizedPool;
31import android.util.SparseArray;
32import android.view.Gravity;
33import android.view.View;
34import android.view.ViewDebug;
35import android.view.ViewGroup;
36import android.view.ViewHierarchyEncoder;
37import android.view.accessibility.AccessibilityEvent;
38import android.view.inspector.InspectableProperty;
39import android.view.inspector.InspectionCompanion;
40import android.view.inspector.PropertyMapper;
41import android.view.inspector.PropertyReader;
42import android.widget.RemoteViews.RemoteView;
43
44import com.android.internal.R;
45
46import java.util.ArrayDeque;
47import java.util.ArrayList;
48import java.util.Comparator;
49import java.util.SortedSet;
50import java.util.TreeSet;
51
52/**
53 * A Layout where the positions of the children can be described in relation to each other or to the
54 * parent.
55 *
56 * <p>
57 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the
58 * position of its children. For example, you cannot have a RelativeLayout whose height is set to
59 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to
60 * {@link #ALIGN_PARENT_BOTTOM}.
61 * </p>
62 *
63 * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
64 * a measurement bug that could cause child views to be measured with incorrect
65 * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
66 * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
67 * for more details.) This was triggered when a RelativeLayout container was placed in
68 * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
69 * not equipped to properly measure with the MeasureSpec mode
70 * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
71 * this would silently work anyway as RelativeLayout would pass a very large
72 * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
73 *
74 * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
75 * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
76 * version 18 or newer will receive the correct behavior.</p>
77 *
78 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
79 * Layout</a> guide.</p>
80 *
81 * <p>
82 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
83 * layout attributes
84 * </p>
85 *
86 * @attr ref android.R.styleable#RelativeLayout_gravity
87 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
88 */
89@RemoteView
90public class RelativeLayout extends ViewGroup {
91 public static final int TRUE = -1;
92
93 /**
94 * Rule that aligns a child's right edge with another child's left edge.
95 */
96 public static final int LEFT_OF = 0;
97 /**
98 * Rule that aligns a child's left edge with another child's right edge.
99 */
100 public static final int RIGHT_OF = 1;
101 /**
102 * Rule that aligns a child's bottom edge with another child's top edge.
103 */
104 public static final int ABOVE = 2;
105 /**
106 * Rule that aligns a child's top edge with another child's bottom edge.
107 */
108 public static final int BELOW = 3;
109
110 /**
111 * Rule that aligns a child's baseline with another child's baseline.
112 */
113 public static final int ALIGN_BASELINE = 4;
114 /**
115 * Rule that aligns a child's left edge with another child's left edge.
116 */
117 public static final int ALIGN_LEFT = 5;
118 /**
119 * Rule that aligns a child's top edge with another child's top edge.
120 */
121 public static final int ALIGN_TOP = 6;
122 /**
123 * Rule that aligns a child's right edge with another child's right edge.
124 */
125 public static final int ALIGN_RIGHT = 7;
126 /**
127 * Rule that aligns a child's bottom edge with another child's bottom edge.
128 */
129 public static final int ALIGN_BOTTOM = 8;
130
131 /**
132 * Rule that aligns the child's left edge with its RelativeLayout
133 * parent's left edge.
134 */
135 public static final int ALIGN_PARENT_LEFT = 9;
136 /**
137 * Rule that aligns the child's top edge with its RelativeLayout
138 * parent's top edge.
139 */
140 public static final int ALIGN_PARENT_TOP = 10;
141 /**
142 * Rule that aligns the child's right edge with its RelativeLayout
143 * parent's right edge.
144 */
145 public static final int ALIGN_PARENT_RIGHT = 11;
146 /**
147 * Rule that aligns the child's bottom edge with its RelativeLayout
148 * parent's bottom edge.
149 */
150 public static final int ALIGN_PARENT_BOTTOM = 12;
151
152 /**
153 * Rule that centers the child with respect to the bounds of its
154 * RelativeLayout parent.
155 */
156 public static final int CENTER_IN_PARENT = 13;
157 /**
158 * Rule that centers the child horizontally with respect to the
159 * bounds of its RelativeLayout parent.
160 */
161 public static final int CENTER_HORIZONTAL = 14;
162 /**
163 * Rule that centers the child vertically with respect to the
164 * bounds of its RelativeLayout parent.
165 */
166 public static final int CENTER_VERTICAL = 15;
167 /**
168 * Rule that aligns a child's end edge with another child's start edge.
169 */
170 public static final int START_OF = 16;
171 /**
172 * Rule that aligns a child's start edge with another child's end edge.
173 */
174 public static final int END_OF = 17;
175 /**
176 * Rule that aligns a child's start edge with another child's start edge.
177 */
178 public static final int ALIGN_START = 18;
179 /**
180 * Rule that aligns a child's end edge with another child's end edge.
181 */
182 public static final int ALIGN_END = 19;
183 /**
184 * Rule that aligns the child's start edge with its RelativeLayout
185 * parent's start edge.
186 */
187 public static final int ALIGN_PARENT_START = 20;
188 /**
189 * Rule that aligns the child's end edge with its RelativeLayout
190 * parent's end edge.
191 */
192 public static final int ALIGN_PARENT_END = 21;
193
194 private static final int VERB_COUNT = 22;
195
196
197 private static final int[] RULES_VERTICAL = {
198 ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM
199 };
200
201 private static final int[] RULES_HORIZONTAL = {
202 LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT, START_OF, END_OF, ALIGN_START, ALIGN_END
203 };
204
205 /**
206 * Used to indicate left/right/top/bottom should be inferred from constraints
207 */
208 private static final int VALUE_NOT_SET = Integer.MIN_VALUE;
209
210 private View mBaselineView = null;
211
212 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
213 private int mGravity = Gravity.START | Gravity.TOP;
214 private final Rect mContentBounds = new Rect();
215 private final Rect mSelfBounds = new Rect();
216 private int mIgnoreGravity;
217
218 private SortedSet<View> mTopToBottomLeftToRightSet = null;
219
220 private boolean mDirtyHierarchy;
221 private View[] mSortedHorizontalChildren;
222 private View[] mSortedVerticalChildren;
223 private final DependencyGraph mGraph = new DependencyGraph();
224
225 // Compatibility hack. Old versions of the platform had problems
226 // with MeasureSpec value overflow and RelativeLayout was one source of them.
227 // Some apps came to rely on them. :(
228 private boolean mAllowBrokenMeasureSpecs = false;
229 // Compatibility hack. Old versions of the platform would not take
230 // margins and padding into account when generating the height measure spec
231 // for children during the horizontal measure pass.
232 private boolean mMeasureVerticalWithPaddingMargin = false;
233
234 // A default width used for RTL measure pass
235 /**
236 * Value reduced so as not to interfere with View's measurement spec. flags. See:
237 * {@link View#MEASURED_SIZE_MASK}.
238 * {@link View#MEASURED_STATE_TOO_SMALL}.
239 **/
240 private static final int DEFAULT_WIDTH = 0x00010000;
241
242 public RelativeLayout(Context context) {
243 this(context, null);
244 }
245
246 public RelativeLayout(Context context, AttributeSet attrs) {
247 this(context, attrs, 0);
248 }
249
250 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
251 this(context, attrs, defStyleAttr, 0);
252 }
253
254 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
255 super(context, attrs, defStyleAttr, defStyleRes);
256 initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
257 queryCompatibilityModes(context);
258 }
259
260 private void initFromAttributes(
261 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
262 final TypedArray a = context.obtainStyledAttributes(
263 attrs, R.styleable.RelativeLayout, defStyleAttr, defStyleRes);
264 saveAttributeDataForStyleable(context, R.styleable.RelativeLayout,
265 attrs, a, defStyleAttr, defStyleRes);
266 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID);
267 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity);
268 a.recycle();
269 }
270
271 private void queryCompatibilityModes(Context context) {
272 int version = context.getApplicationInfo().targetSdkVersion;
273 mAllowBrokenMeasureSpecs = version <= Build.VERSION_CODES.JELLY_BEAN_MR1;
274 mMeasureVerticalWithPaddingMargin = version >= Build.VERSION_CODES.JELLY_BEAN_MR2;
275 }
276
277 @Override
278 public boolean shouldDelayChildPressedState() {
279 return false;
280 }
281
282 /**
283 * Defines which View is ignored when the gravity is applied. This setting has no
284 * effect if the gravity is <code>Gravity.START | Gravity.TOP</code>.
285 *
286 * @param viewId The id of the View to be ignored by gravity, or 0 if no View
287 * should be ignored.
288 *
289 * @see #setGravity(int)
290 *
291 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
292 */
293 @android.view.RemotableViewMethod
294 public void setIgnoreGravity(int viewId) {
295 mIgnoreGravity = viewId;
296 }
297
298 /**
299 * Get the id of the View to be ignored by gravity
300 *
301 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity
302 */
303 @InspectableProperty
304 public int getIgnoreGravity() {
305 return mIgnoreGravity;
306 }
307
308 /**
309 * Describes how the child views are positioned.
310 *
311 * @return the gravity.
312 *
313 * @see #setGravity(int)
314 * @see android.view.Gravity
315 *
316 * @attr ref android.R.styleable#RelativeLayout_gravity
317 */
318 @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY)
319 public int getGravity() {
320 return mGravity;
321 }
322
323 /**
324 * Describes how the child views are positioned. Defaults to
325 * <code>Gravity.START | Gravity.TOP</code>.
326 *
327 * <p>Note that since RelativeLayout considers the positioning of each child
328 * relative to one another to be significant, setting gravity will affect
329 * the positioning of all children as a single unit within the parent.
330 * This happens after children have been relatively positioned.</p>
331 *
332 * @param gravity See {@link android.view.Gravity}
333 *
334 * @see #setHorizontalGravity(int)
335 * @see #setVerticalGravity(int)
336 *
337 * @attr ref android.R.styleable#RelativeLayout_gravity
338 */
339 @android.view.RemotableViewMethod
340 public void setGravity(int gravity) {
341 if (mGravity != gravity) {
342 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
343 gravity |= Gravity.START;
344 }
345
346 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
347 gravity |= Gravity.TOP;
348 }
349
350 mGravity = gravity;
351 requestLayout();
352 }
353 }
354
355 @android.view.RemotableViewMethod
356 public void setHorizontalGravity(int horizontalGravity) {
357 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
358 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
359 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
360 requestLayout();
361 }
362 }
363
364 @android.view.RemotableViewMethod
365 public void setVerticalGravity(int verticalGravity) {
366 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
367 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
368 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
369 requestLayout();
370 }
371 }
372
373 @Override
374 public int getBaseline() {
375 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline();
376 }
377
378 @Override
379 public void requestLayout() {
380 super.requestLayout();
381 mDirtyHierarchy = true;
382 }
383
384 private void sortChildren() {
385 final int count = getChildCount();
386 if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
387 mSortedVerticalChildren = new View[count];
388 }
389
390 if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
391 mSortedHorizontalChildren = new View[count];
392 }
393
394 final DependencyGraph graph = mGraph;
395 graph.clear();
396
397 for (int i = 0; i < count; i++) {
398 graph.add(getChildAt(i));
399 }
400
401 graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
402 graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
403 }
404
405 @Override
406 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
407 if (mDirtyHierarchy) {
408 mDirtyHierarchy = false;
409 sortChildren();
410 }
411
412 int myWidth = -1;
413 int myHeight = -1;
414
415 int width = 0;
416 int height = 0;
417
418 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
419 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
420 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
421 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
422
423 // Record our dimensions if they are known;
424 if (widthMode != MeasureSpec.UNSPECIFIED) {
425 myWidth = widthSize;
426 }
427
428 if (heightMode != MeasureSpec.UNSPECIFIED) {
429 myHeight = heightSize;
430 }
431
432 if (widthMode == MeasureSpec.EXACTLY) {
433 width = myWidth;
434 }
435
436 if (heightMode == MeasureSpec.EXACTLY) {
437 height = myHeight;
438 }
439
440 View ignore = null;
441 int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
442 final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
443 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
444 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
445
446 int left = Integer.MAX_VALUE;
447 int top = Integer.MAX_VALUE;
448 int right = Integer.MIN_VALUE;
449 int bottom = Integer.MIN_VALUE;
450
451 boolean offsetHorizontalAxis = false;
452 boolean offsetVerticalAxis = false;
453
454 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
455 ignore = findViewById(mIgnoreGravity);
456 }
457
458 final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
459 final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
460
461 // We need to know our size for doing the correct computation of children positioning in RTL
462 // mode but there is no practical way to get it instead of running the code below.
463 // So, instead of running the code twice, we just set the width to a "default display width"
464 // before the computation and then, as a last pass, we will update their real position with
465 // an offset equals to "DEFAULT_WIDTH - width".
466 final int layoutDirection = getLayoutDirection();
467 if (isLayoutRtl() && myWidth == -1) {
468 myWidth = DEFAULT_WIDTH;
469 }
470
471 View[] views = mSortedHorizontalChildren;
472 int count = views.length;
473
474 for (int i = 0; i < count; i++) {
475 View child = views[i];
476 if (child.getVisibility() != GONE) {
477 LayoutParams params = (LayoutParams) child.getLayoutParams();
478 int[] rules = params.getRules(layoutDirection);
479
480 applyHorizontalSizeRules(params, myWidth, rules);
481 measureChildHorizontal(child, params, myWidth, myHeight);
482
483 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
484 offsetHorizontalAxis = true;
485 }
486 }
487 }
488
489 views = mSortedVerticalChildren;
490 count = views.length;
491 final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
492
493 for (int i = 0; i < count; i++) {
494 final View child = views[i];
495 if (child.getVisibility() != GONE) {
496 final LayoutParams params = (LayoutParams) child.getLayoutParams();
497
498 applyVerticalSizeRules(params, myHeight, child.getBaseline());
499 measureChild(child, params, myWidth, myHeight);
500 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
501 offsetVerticalAxis = true;
502 }
503
504 if (isWrapContentWidth) {
505 if (isLayoutRtl()) {
506 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
507 width = Math.max(width, myWidth - params.mLeft);
508 } else {
509 width = Math.max(width, myWidth - params.mLeft + params.leftMargin);
510 }
511 } else {
512 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
513 width = Math.max(width, params.mRight);
514 } else {
515 width = Math.max(width, params.mRight + params.rightMargin);
516 }
517 }
518 }
519
520 if (isWrapContentHeight) {
521 if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
522 height = Math.max(height, params.mBottom);
523 } else {
524 height = Math.max(height, params.mBottom + params.bottomMargin);
525 }
526 }
527
528 if (child != ignore || verticalGravity) {
529 left = Math.min(left, params.mLeft - params.leftMargin);
530 top = Math.min(top, params.mTop - params.topMargin);
531 }
532
533 if (child != ignore || horizontalGravity) {
534 right = Math.max(right, params.mRight + params.rightMargin);
535 bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
536 }
537 }
538 }
539
540 // Use the top-start-most laid out view as the baseline. RTL offsets are
541 // applied later, so we can use the left-most edge as the starting edge.
542 View baselineView = null;
543 LayoutParams baselineParams = null;
544 for (int i = 0; i < count; i++) {
545 final View child = views[i];
546 if (child.getVisibility() != GONE) {
547 final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
548 if (baselineView == null || baselineParams == null
549 || compareLayoutPosition(childParams, baselineParams) < 0) {
550 baselineView = child;
551 baselineParams = childParams;
552 }
553 }
554 }
555 mBaselineView = baselineView;
556
557 if (isWrapContentWidth) {
558 // Width already has left padding in it since it was calculated by looking at
559 // the right of each child view
560 width += mPaddingRight;
561
562 if (mLayoutParams != null && mLayoutParams.width >= 0) {
563 width = Math.max(width, mLayoutParams.width);
564 }
565
566 width = Math.max(width, getSuggestedMinimumWidth());
567 width = resolveSize(width, widthMeasureSpec);
568
569 if (offsetHorizontalAxis) {
570 for (int i = 0; i < count; i++) {
571 final View child = views[i];
572 if (child.getVisibility() != GONE) {
573 final LayoutParams params = (LayoutParams) child.getLayoutParams();
574 final int[] rules = params.getRules(layoutDirection);
575 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
576 centerHorizontal(child, params, width);
577 } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
578 final int childWidth = child.getMeasuredWidth();
579 params.mLeft = width - mPaddingRight - childWidth;
580 params.mRight = params.mLeft + childWidth;
581 }
582 }
583 }
584 }
585 }
586
587 if (isWrapContentHeight) {
588 // Height already has top padding in it since it was calculated by looking at
589 // the bottom of each child view
590 height += mPaddingBottom;
591
592 if (mLayoutParams != null && mLayoutParams.height >= 0) {
593 height = Math.max(height, mLayoutParams.height);
594 }
595
596 height = Math.max(height, getSuggestedMinimumHeight());
597 height = resolveSize(height, heightMeasureSpec);
598
599 if (offsetVerticalAxis) {
600 for (int i = 0; i < count; i++) {
601 final View child = views[i];
602 if (child.getVisibility() != GONE) {
603 final LayoutParams params = (LayoutParams) child.getLayoutParams();
604 final int[] rules = params.getRules(layoutDirection);
605 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
606 centerVertical(child, params, height);
607 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
608 final int childHeight = child.getMeasuredHeight();
609 params.mTop = height - mPaddingBottom - childHeight;
610 params.mBottom = params.mTop + childHeight;
611 }
612 }
613 }
614 }
615 }
616
617 if (horizontalGravity || verticalGravity) {
618 final Rect selfBounds = mSelfBounds;
619 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
620 height - mPaddingBottom);
621
622 final Rect contentBounds = mContentBounds;
623 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
624 layoutDirection);
625
626 final int horizontalOffset = contentBounds.left - left;
627 final int verticalOffset = contentBounds.top - top;
628 if (horizontalOffset != 0 || verticalOffset != 0) {
629 for (int i = 0; i < count; i++) {
630 final View child = views[i];
631 if (child.getVisibility() != GONE && child != ignore) {
632 final LayoutParams params = (LayoutParams) child.getLayoutParams();
633 if (horizontalGravity) {
634 params.mLeft += horizontalOffset;
635 params.mRight += horizontalOffset;
636 }
637 if (verticalGravity) {
638 params.mTop += verticalOffset;
639 params.mBottom += verticalOffset;
640 }
641 }
642 }
643 }
644 }
645
646 if (isLayoutRtl()) {
647 final int offsetWidth = myWidth - width;
648 for (int i = 0; i < count; i++) {
649 final View child = views[i];
650 if (child.getVisibility() != GONE) {
651 final LayoutParams params = (LayoutParams) child.getLayoutParams();
652 params.mLeft -= offsetWidth;
653 params.mRight -= offsetWidth;
654 }
655 }
656 }
657
658 setMeasuredDimension(width, height);
659 }
660
661 /**
662 * @return a negative number if the top of {@code p1} is above the top of
663 * {@code p2} or if they have identical top values and the left of
664 * {@code p1} is to the left of {@code p2}, or a positive number
665 * otherwise
666 */
667 private int compareLayoutPosition(LayoutParams p1, LayoutParams p2) {
668 final int topDiff = p1.mTop - p2.mTop;
669 if (topDiff != 0) {
670 return topDiff;
671 }
672 return p1.mLeft - p2.mLeft;
673 }
674
675 /**
676 * Measure a child. The child should have left, top, right and bottom information
677 * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
678 * that the view can extend up to the corresponding edge.
679 *
680 * @param child Child to measure
681 * @param params LayoutParams associated with child
682 * @param myWidth Width of the the RelativeLayout
683 * @param myHeight Height of the RelativeLayout
684 */
685 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
686 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
687 params.mRight, params.width,
688 params.leftMargin, params.rightMargin,
689 mPaddingLeft, mPaddingRight,
690 myWidth);
691 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
692 params.mBottom, params.height,
693 params.topMargin, params.bottomMargin,
694 mPaddingTop, mPaddingBottom,
695 myHeight);
696 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
697 }
698
699 private void measureChildHorizontal(
700 View child, LayoutParams params, int myWidth, int myHeight) {
701 final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
702 params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
703 myWidth);
704
705 final int childHeightMeasureSpec;
706 if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
707 if (params.height >= 0) {
708 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
709 params.height, MeasureSpec.EXACTLY);
710 } else {
711 // Negative values in a mySize/myWidth/myWidth value in
712 // RelativeLayout measurement is code for, "we got an
713 // unspecified mode in the RelativeLayout's measure spec."
714 // Carry it forward.
715 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
716 }
717 } else {
718 final int maxHeight;
719 if (mMeasureVerticalWithPaddingMargin) {
720 maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
721 - params.topMargin - params.bottomMargin);
722 } else {
723 maxHeight = Math.max(0, myHeight);
724 }
725
726 final int heightMode;
727 if (params.height == LayoutParams.MATCH_PARENT) {
728 heightMode = MeasureSpec.EXACTLY;
729 } else {
730 heightMode = MeasureSpec.AT_MOST;
731 }
732 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
733 }
734
735 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
736 }
737
738 /**
739 * Get a measure spec that accounts for all of the constraints on this view.
740 * This includes size constraints imposed by the RelativeLayout as well as
741 * the View's desired dimension.
742 *
743 * @param childStart The left or top field of the child's layout params
744 * @param childEnd The right or bottom field of the child's layout params
745 * @param childSize The child's desired size (the width or height field of
746 * the child's layout params)
747 * @param startMargin The left or top margin
748 * @param endMargin The right or bottom margin
749 * @param startPadding mPaddingLeft or mPaddingTop
750 * @param endPadding mPaddingRight or mPaddingBottom
751 * @param mySize The width or height of this view (the RelativeLayout)
752 * @return MeasureSpec for the child
753 */
754 private int getChildMeasureSpec(int childStart, int childEnd,
755 int childSize, int startMargin, int endMargin, int startPadding,
756 int endPadding, int mySize) {
757 int childSpecMode = 0;
758 int childSpecSize = 0;
759
760 // Negative values in a mySize value in RelativeLayout
761 // measurement is code for, "we got an unspecified mode in the
762 // RelativeLayout's measure spec."
763 final boolean isUnspecified = mySize < 0;
764 if (isUnspecified && !mAllowBrokenMeasureSpecs) {
765 if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
766 // Constraints fixed both edges, so child has an exact size.
767 childSpecSize = Math.max(0, childEnd - childStart);
768 childSpecMode = MeasureSpec.EXACTLY;
769 } else if (childSize >= 0) {
770 // The child specified an exact size.
771 childSpecSize = childSize;
772 childSpecMode = MeasureSpec.EXACTLY;
773 } else {
774 // Allow the child to be whatever size it wants.
775 childSpecSize = 0;
776 childSpecMode = MeasureSpec.UNSPECIFIED;
777 }
778
779 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
780 }
781
782 // Figure out start and end bounds.
783 int tempStart = childStart;
784 int tempEnd = childEnd;
785
786 // If the view did not express a layout constraint for an edge, use
787 // view's margins and our padding
788 if (tempStart == VALUE_NOT_SET) {
789 tempStart = startPadding + startMargin;
790 }
791 if (tempEnd == VALUE_NOT_SET) {
792 tempEnd = mySize - endPadding - endMargin;
793 }
794
795 // Figure out maximum size available to this view
796 final int maxAvailable = tempEnd - tempStart;
797
798 if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
799 // Constraints fixed both edges, so child must be an exact size.
800 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
801 childSpecSize = Math.max(0, maxAvailable);
802 } else {
803 if (childSize >= 0) {
804 // Child wanted an exact size. Give as much as possible.
805 childSpecMode = MeasureSpec.EXACTLY;
806
807 if (maxAvailable >= 0) {
808 // We have a maximum size in this dimension.
809 childSpecSize = Math.min(maxAvailable, childSize);
810 } else {
811 // We can grow in this dimension.
812 childSpecSize = childSize;
813 }
814 } else if (childSize == LayoutParams.MATCH_PARENT) {
815 // Child wanted to be as big as possible. Give all available
816 // space.
817 childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
818 childSpecSize = Math.max(0, maxAvailable);
819 } else if (childSize == LayoutParams.WRAP_CONTENT) {
820 // Child wants to wrap content. Use AT_MOST to communicate
821 // available space if we know our max size.
822 if (maxAvailable >= 0) {
823 // We have a maximum size in this dimension.
824 childSpecMode = MeasureSpec.AT_MOST;
825 childSpecSize = maxAvailable;
826 } else {
827 // We can grow in this dimension. Child can be as big as it
828 // wants.
829 childSpecMode = MeasureSpec.UNSPECIFIED;
830 childSpecSize = 0;
831 }
832 }
833 }
834
835 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
836 }
837
838 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
839 boolean wrapContent) {
840
841 final int layoutDirection = getLayoutDirection();
842 int[] rules = params.getRules(layoutDirection);
843
844 if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
845 // Right is fixed, but left varies
846 params.mLeft = params.mRight - child.getMeasuredWidth();
847 } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
848 // Left is fixed, but right varies
849 params.mRight = params.mLeft + child.getMeasuredWidth();
850 } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
851 // Both left and right vary
852 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
853 if (!wrapContent) {
854 centerHorizontal(child, params, myWidth);
855 } else {
856 positionAtEdge(child, params, myWidth);
857 }
858 return true;
859 } else {
860 // This is the default case. For RTL we start from the right and for LTR we start
861 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
862 positionAtEdge(child, params, myWidth);
863 }
864 }
865 return rules[ALIGN_PARENT_END] != 0;
866 }
867
868 private void positionAtEdge(View child, LayoutParams params, int myWidth) {
869 if (isLayoutRtl()) {
870 params.mRight = myWidth - mPaddingRight - params.rightMargin;
871 params.mLeft = params.mRight - child.getMeasuredWidth();
872 } else {
873 params.mLeft = mPaddingLeft + params.leftMargin;
874 params.mRight = params.mLeft + child.getMeasuredWidth();
875 }
876 }
877
878 private boolean positionChildVertical(View child, LayoutParams params, int myHeight,
879 boolean wrapContent) {
880
881 int[] rules = params.getRules();
882
883 if (params.mTop == VALUE_NOT_SET && params.mBottom != VALUE_NOT_SET) {
884 // Bottom is fixed, but top varies
885 params.mTop = params.mBottom - child.getMeasuredHeight();
886 } else if (params.mTop != VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
887 // Top is fixed, but bottom varies
888 params.mBottom = params.mTop + child.getMeasuredHeight();
889 } else if (params.mTop == VALUE_NOT_SET && params.mBottom == VALUE_NOT_SET) {
890 // Both top and bottom vary
891 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
892 if (!wrapContent) {
893 centerVertical(child, params, myHeight);
894 } else {
895 params.mTop = mPaddingTop + params.topMargin;
896 params.mBottom = params.mTop + child.getMeasuredHeight();
897 }
898 return true;
899 } else {
900 params.mTop = mPaddingTop + params.topMargin;
901 params.mBottom = params.mTop + child.getMeasuredHeight();
902 }
903 }
904 return rules[ALIGN_PARENT_BOTTOM] != 0;
905 }
906
907 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
908 RelativeLayout.LayoutParams anchorParams;
909
910 // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
911 // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
912 // wants to the right
913 // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
914 // wants to the left
915 // left=10, right=20 means the left and right ends are both fixed
916 childParams.mLeft = VALUE_NOT_SET;
917 childParams.mRight = VALUE_NOT_SET;
918
919 anchorParams = getRelatedViewParams(rules, LEFT_OF);
920 if (anchorParams != null) {
921 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
922 childParams.rightMargin);
923 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
924 if (myWidth >= 0) {
925 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
926 }
927 }
928
929 anchorParams = getRelatedViewParams(rules, RIGHT_OF);
930 if (anchorParams != null) {
931 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
932 childParams.leftMargin);
933 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
934 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
935 }
936
937 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
938 if (anchorParams != null) {
939 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
940 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
941 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
942 }
943
944 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
945 if (anchorParams != null) {
946 childParams.mRight = anchorParams.mRight - childParams.rightMargin;
947 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
948 if (myWidth >= 0) {
949 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
950 }
951 }
952
953 if (0 != rules[ALIGN_PARENT_LEFT]) {
954 childParams.mLeft = mPaddingLeft + childParams.leftMargin;
955 }
956
957 if (0 != rules[ALIGN_PARENT_RIGHT]) {
958 if (myWidth >= 0) {
959 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
960 }
961 }
962 }
963
964 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {
965 final int[] rules = childParams.getRules();
966
967 // Baseline alignment overrides any explicitly specified top or bottom.
968 int baselineOffset = getRelatedViewBaselineOffset(rules);
969 if (baselineOffset != -1) {
970 if (myBaseline != -1) {
971 baselineOffset -= myBaseline;
972 }
973 childParams.mTop = baselineOffset;
974 childParams.mBottom = VALUE_NOT_SET;
975 return;
976 }
977
978 RelativeLayout.LayoutParams anchorParams;
979
980 childParams.mTop = VALUE_NOT_SET;
981 childParams.mBottom = VALUE_NOT_SET;
982
983 anchorParams = getRelatedViewParams(rules, ABOVE);
984 if (anchorParams != null) {
985 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
986 childParams.bottomMargin);
987 } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
988 if (myHeight >= 0) {
989 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
990 }
991 }
992
993 anchorParams = getRelatedViewParams(rules, BELOW);
994 if (anchorParams != null) {
995 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
996 childParams.topMargin);
997 } else if (childParams.alignWithParent && rules[BELOW] != 0) {
998 childParams.mTop = mPaddingTop + childParams.topMargin;
999 }
1000
1001 anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
1002 if (anchorParams != null) {
1003 childParams.mTop = anchorParams.mTop + childParams.topMargin;
1004 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
1005 childParams.mTop = mPaddingTop + childParams.topMargin;
1006 }
1007
1008 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
1009 if (anchorParams != null) {
1010 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
1011 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
1012 if (myHeight >= 0) {
1013 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
1014 }
1015 }
1016
1017 if (0 != rules[ALIGN_PARENT_TOP]) {
1018 childParams.mTop = mPaddingTop + childParams.topMargin;
1019 }
1020
1021 if (0 != rules[ALIGN_PARENT_BOTTOM]) {
1022 if (myHeight >= 0) {
1023 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
1024 }
1025 }
1026 }
1027
1028 private View getRelatedView(int[] rules, int relation) {
1029 int id = rules[relation];
1030 if (id != 0) {
1031 DependencyGraph.Node node = mGraph.mKeyNodes.get(id);
1032 if (node == null) return null;
1033 View v = node.view;
1034
1035 // Find the first non-GONE view up the chain
1036 while (v.getVisibility() == View.GONE) {
1037 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection());
1038 node = mGraph.mKeyNodes.get((rules[relation]));
1039 // ignore self dependency. for more info look in git commit: da3003
1040 if (node == null || v == node.view) return null;
1041 v = node.view;
1042 }
1043
1044 return v;
1045 }
1046
1047 return null;
1048 }
1049
1050 private LayoutParams getRelatedViewParams(int[] rules, int relation) {
1051 View v = getRelatedView(rules, relation);
1052 if (v != null) {
1053 ViewGroup.LayoutParams params = v.getLayoutParams();
1054 if (params instanceof LayoutParams) {
1055 return (LayoutParams) v.getLayoutParams();
1056 }
1057 }
1058 return null;
1059 }
1060
1061 private int getRelatedViewBaselineOffset(int[] rules) {
1062 final View v = getRelatedView(rules, ALIGN_BASELINE);
1063 if (v != null) {
1064 final int baseline = v.getBaseline();
1065 if (baseline != -1) {
1066 final ViewGroup.LayoutParams params = v.getLayoutParams();
1067 if (params instanceof LayoutParams) {
1068 final LayoutParams anchorParams = (LayoutParams) v.getLayoutParams();
1069 return anchorParams.mTop + baseline;
1070 }
1071 }
1072 }
1073 return -1;
1074 }
1075
1076 private static void centerHorizontal(View child, LayoutParams params, int myWidth) {
1077 int childWidth = child.getMeasuredWidth();
1078 int left = (myWidth - childWidth) / 2;
1079
1080 params.mLeft = left;
1081 params.mRight = left + childWidth;
1082 }
1083
1084 private static void centerVertical(View child, LayoutParams params, int myHeight) {
1085 int childHeight = child.getMeasuredHeight();
1086 int top = (myHeight - childHeight) / 2;
1087
1088 params.mTop = top;
1089 params.mBottom = top + childHeight;
1090 }
1091
1092 @Override
1093 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1094 // The layout has actually already been performed and the positions
1095 // cached. Apply the cached values to the children.
1096 final int count = getChildCount();
1097
1098 for (int i = 0; i < count; i++) {
1099 View child = getChildAt(i);
1100 if (child.getVisibility() != GONE) {
1101 RelativeLayout.LayoutParams st =
1102 (RelativeLayout.LayoutParams) child.getLayoutParams();
1103 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
1104 }
1105 }
1106 }
1107
1108 @Override
1109 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1110 return new RelativeLayout.LayoutParams(getContext(), attrs);
1111 }
1112
1113 /**
1114 * Returns a set of layout parameters with a width of
1115 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT},
1116 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning.
1117 */
1118 @Override
1119 protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1120 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1121 }
1122
1123 // Override to allow type-checking of LayoutParams.
1124 @Override
1125 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1126 return p instanceof RelativeLayout.LayoutParams;
1127 }
1128
1129 @Override
1130 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
1131 if (sPreserveMarginParamsInLayoutParamConversion) {
1132 if (lp instanceof LayoutParams) {
1133 return new LayoutParams((LayoutParams) lp);
1134 } else if (lp instanceof MarginLayoutParams) {
1135 return new LayoutParams((MarginLayoutParams) lp);
1136 }
1137 }
1138 return new LayoutParams(lp);
1139 }
1140
1141 /** @hide */
1142 @Override
1143 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
1144 if (mTopToBottomLeftToRightSet == null) {
1145 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator());
1146 }
1147
1148 // sort children top-to-bottom and left-to-right
1149 for (int i = 0, count = getChildCount(); i < count; i++) {
1150 mTopToBottomLeftToRightSet.add(getChildAt(i));
1151 }
1152
1153 for (View view : mTopToBottomLeftToRightSet) {
1154 if (view.getVisibility() == View.VISIBLE
1155 && view.dispatchPopulateAccessibilityEvent(event)) {
1156 mTopToBottomLeftToRightSet.clear();
1157 return true;
1158 }
1159 }
1160
1161 mTopToBottomLeftToRightSet.clear();
1162 return false;
1163 }
1164
1165 @Override
1166 public CharSequence getAccessibilityClassName() {
1167 return RelativeLayout.class.getName();
1168 }
1169
1170 /**
1171 * Compares two views in left-to-right and top-to-bottom fashion.
1172 */
1173 private class TopToBottomLeftToRightComparator implements Comparator<View> {
1174 public int compare(View first, View second) {
1175 // top - bottom
1176 int topDifference = first.getTop() - second.getTop();
1177 if (topDifference != 0) {
1178 return topDifference;
1179 }
1180 // left - right
1181 int leftDifference = first.getLeft() - second.getLeft();
1182 if (leftDifference != 0) {
1183 return leftDifference;
1184 }
1185 // break tie by height
1186 int heightDiference = first.getHeight() - second.getHeight();
1187 if (heightDiference != 0) {
1188 return heightDiference;
1189 }
1190 // break tie by width
1191 int widthDiference = first.getWidth() - second.getWidth();
1192 if (widthDiference != 0) {
1193 return widthDiference;
1194 }
1195 return 0;
1196 }
1197 }
1198
1199 /**
1200 * Specifies how a view is positioned within a {@link RelativeLayout}.
1201 * The relative layout containing the view uses the value of these layout parameters to
1202 * determine where to position the view on the screen. If the view is not contained
1203 * within a relative layout, these attributes are ignored.
1204 *
1205 * See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
1206 * Layout</a> guide for example code demonstrating how to use relative layout's
1207 * layout parameters in a layout XML.
1208 *
1209 * To learn more about layout parameters and how they differ from typical view attributes,
1210 * see the <a href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Layouts
1211 * guide</a>.
1212 *
1213 *
1214 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing
1215 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf
1216 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf
1217 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above
1218 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below
1219 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline
1220 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft
1221 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop
1222 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight
1223 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom
1224 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft
1225 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop
1226 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight
1227 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom
1228 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent
1229 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal
1230 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
1231 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf
1232 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf
1233 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart
1234 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd
1235 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart
1236 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd
1237 */
1238 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
1239 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
1240 @ViewDebug.IntToString(from = ABOVE, to = "above"),
1241 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
1242 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
1243 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"),
1244 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
1245 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"),
1246 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"),
1247 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"),
1248 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"),
1249 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"),
1250 @ViewDebug.IntToString(from = BELOW, to = "below"),
1251 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"),
1252 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"),
1253 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"),
1254 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"),
1255 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"),
1256 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"),
1257 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"),
1258 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"),
1259 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"),
1260 @ViewDebug.IntToString(from = START_OF, to = "startOf"),
1261 @ViewDebug.IntToString(from = END_OF, to = "endOf")
1262 }, mapping = {
1263 @ViewDebug.IntToString(from = TRUE, to = "true"),
1264 @ViewDebug.IntToString(from = 0, to = "false/NO_ID")
1265 })
1266
1267 private int[] mRules = new int[VERB_COUNT];
1268 private int[] mInitialRules = new int[VERB_COUNT];
1269
1270 @UnsupportedAppUsage
1271 private int mLeft;
1272 @UnsupportedAppUsage
1273 private int mTop;
1274 @UnsupportedAppUsage
1275 private int mRight;
1276 @UnsupportedAppUsage
1277 private int mBottom;
1278
1279 /**
1280 * Whether this view had any relative rules modified following the most
1281 * recent resolution of layout direction.
1282 */
1283 private boolean mNeedsLayoutResolution;
1284
1285 private boolean mRulesChanged = false;
1286 private boolean mIsRtlCompatibilityMode = false;
1287
1288 /**
1289 * When true, uses the parent as the anchor if the anchor doesn't exist or if
1290 * the anchor's visibility is GONE.
1291 */
1292 @ViewDebug.ExportedProperty(category = "layout")
1293 public boolean alignWithParent;
1294
1295 public LayoutParams(Context c, AttributeSet attrs) {
1296 super(c, attrs);
1297
1298 TypedArray a = c.obtainStyledAttributes(attrs,
1299 com.android.internal.R.styleable.RelativeLayout_Layout);
1300
1301 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
1302 mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
1303 !c.getApplicationInfo().hasRtlSupport());
1304
1305 final int[] rules = mRules;
1306 //noinspection MismatchedReadAndWriteOfArray
1307 final int[] initialRules = mInitialRules;
1308
1309 final int N = a.getIndexCount();
1310 for (int i = 0; i < N; i++) {
1311 int attr = a.getIndex(i);
1312 switch (attr) {
1313 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
1314 alignWithParent = a.getBoolean(attr, false);
1315 break;
1316 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
1317 rules[LEFT_OF] = a.getResourceId(attr, 0);
1318 break;
1319 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
1320 rules[RIGHT_OF] = a.getResourceId(attr, 0);
1321 break;
1322 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
1323 rules[ABOVE] = a.getResourceId(attr, 0);
1324 break;
1325 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
1326 rules[BELOW] = a.getResourceId(attr, 0);
1327 break;
1328 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
1329 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
1330 break;
1331 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
1332 rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
1333 break;
1334 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
1335 rules[ALIGN_TOP] = a.getResourceId(attr, 0);
1336 break;
1337 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
1338 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
1339 break;
1340 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
1341 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
1342 break;
1343 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
1344 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
1345 break;
1346 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
1347 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
1348 break;
1349 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
1350 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
1351 break;
1352 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
1353 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
1354 break;
1355 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
1356 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
1357 break;
1358 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
1359 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
1360 break;
1361 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
1362 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
1363 break;
1364 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
1365 rules[START_OF] = a.getResourceId(attr, 0);
1366 break;
1367 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
1368 rules[END_OF] = a.getResourceId(attr, 0);
1369 break;
1370 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
1371 rules[ALIGN_START] = a.getResourceId(attr, 0);
1372 break;
1373 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
1374 rules[ALIGN_END] = a.getResourceId(attr, 0);
1375 break;
1376 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
1377 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
1378 break;
1379 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
1380 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
1381 break;
1382 }
1383 }
1384 mRulesChanged = true;
1385 System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
1386
1387 a.recycle();
1388 }
1389
1390 public LayoutParams(int w, int h) {
1391 super(w, h);
1392 }
1393
1394 /**
1395 * {@inheritDoc}
1396 */
1397 public LayoutParams(ViewGroup.LayoutParams source) {
1398 super(source);
1399 }
1400
1401 /**
1402 * {@inheritDoc}
1403 */
1404 public LayoutParams(ViewGroup.MarginLayoutParams source) {
1405 super(source);
1406 }
1407
1408 /**
1409 * Copy constructor. Clones the width, height, margin values, and rules
1410 * of the source.
1411 *
1412 * @param source The layout params to copy from.
1413 */
1414 public LayoutParams(LayoutParams source) {
1415 super(source);
1416
1417 this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
1418 this.mRulesChanged = source.mRulesChanged;
1419 this.alignWithParent = source.alignWithParent;
1420
1421 System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT);
1422 System.arraycopy(
1423 source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT);
1424 }
1425
1426 @Override
1427 public String debug(String output) {
1428 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) +
1429 ", height=" + sizeToString(height) + " }";
1430 }
1431
1432 /**
1433 * Adds a layout rule to be interpreted by the RelativeLayout.
1434 * <p>
1435 * This method should only be used for verbs that don't refer to a
1436 * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean
1437 * value ({@link #TRUE} for true or 0 for false). To
1438 * specify a verb that takes a subject, use {@link #addRule(int, int)}.
1439 * <p>
1440 * If the rule is relative to the layout direction (ex.
1441 * {@link #ALIGN_PARENT_START}), then the layout direction must be
1442 * resolved using {@link #resolveLayoutDirection(int)} before calling
1443 * {@link #getRule(int)} an absolute rule (ex.
1444 * {@link #ALIGN_PARENT_LEFT}.
1445 *
1446 * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT}
1447 * @see #addRule(int, int)
1448 * @see #removeRule(int)
1449 * @see #getRule(int)
1450 */
1451 public void addRule(int verb) {
1452 addRule(verb, TRUE);
1453 }
1454
1455 /**
1456 * Adds a layout rule to be interpreted by the RelativeLayout.
1457 * <p>
1458 * Use this for verbs that refer to a sibling (ex.
1459 * {@link #ALIGN_RIGHT}) or take a boolean value (ex.
1460 * {@link #CENTER_IN_PARENT}).
1461 * <p>
1462 * If the rule is relative to the layout direction (ex.
1463 * {@link #START_OF}), then the layout direction must be resolved using
1464 * {@link #resolveLayoutDirection(int)} before calling
1465 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
1466 *
1467 * @param verb a layout verb, such as {@link #ALIGN_RIGHT}
1468 * @param subject the ID of another view to use as an anchor, or a
1469 * boolean value (represented as {@link #TRUE} for true
1470 * or 0 for false)
1471 * @see #addRule(int)
1472 * @see #removeRule(int)
1473 * @see #getRule(int)
1474 */
1475 public void addRule(int verb, int subject) {
1476 // If we're removing a relative rule, we'll need to force layout
1477 // resolution the next time it's requested.
1478 if (!mNeedsLayoutResolution && isRelativeRule(verb)
1479 && mInitialRules[verb] != 0 && subject == 0) {
1480 mNeedsLayoutResolution = true;
1481 }
1482
1483 mRules[verb] = subject;
1484 mInitialRules[verb] = subject;
1485 mRulesChanged = true;
1486 }
1487
1488 /**
1489 * Removes a layout rule to be interpreted by the RelativeLayout.
1490 * <p>
1491 * If the rule is relative to the layout direction (ex.
1492 * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the
1493 * layout direction must be resolved using
1494 * {@link #resolveLayoutDirection(int)} before before calling
1495 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}.
1496 *
1497 * @param verb One of the verbs defined by
1498 * {@link android.widget.RelativeLayout RelativeLayout}, such as
1499 * ALIGN_WITH_PARENT_LEFT.
1500 * @see #addRule(int)
1501 * @see #addRule(int, int)
1502 * @see #getRule(int)
1503 */
1504 public void removeRule(int verb) {
1505 addRule(verb, 0);
1506 }
1507
1508 /**
1509 * Returns the layout rule associated with a specific verb.
1510 *
1511 * @param verb one of the verbs defined by {@link RelativeLayout}, such
1512 * as ALIGN_WITH_PARENT_LEFT
1513 * @return the id of another view to use as an anchor, a boolean value
1514 * (represented as {@link RelativeLayout#TRUE} for true
1515 * or 0 for false), or -1 for verbs that don't refer to another
1516 * sibling (for example, ALIGN_WITH_PARENT_BOTTOM)
1517 * @see #addRule(int)
1518 * @see #addRule(int, int)
1519 */
1520 public int getRule(int verb) {
1521 return mRules[verb];
1522 }
1523
1524 private boolean hasRelativeRules() {
1525 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 ||
1526 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 ||
1527 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0);
1528 }
1529
1530 private boolean isRelativeRule(int rule) {
1531 return rule == START_OF || rule == END_OF
1532 || rule == ALIGN_START || rule == ALIGN_END
1533 || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END;
1534 }
1535
1536 // The way we are resolving rules depends on the layout direction and if we are pre JB MR1
1537 // or not.
1538 //
1539 // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having
1540 // predominance over any "start/end" rules that could have been defined. A special case:
1541 // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we
1542 // resolve those "start"/"end" rules to "left"/"right" respectively.
1543 //
1544 // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right"
1545 // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules.
1546 //
1547 // In all cases, the result of the resolution should clear the "start"/"end" rules to leave
1548 // only the "left"/"right" rules at the end.
1549 private void resolveRules(int layoutDirection) {
1550 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL);
1551
1552 // Reset to initial state
1553 System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT);
1554
1555 // Apply rules depending on direction and if we are in RTL compatibility mode
1556 if (mIsRtlCompatibilityMode) {
1557 if (mRules[ALIGN_START] != 0) {
1558 if (mRules[ALIGN_LEFT] == 0) {
1559 // "left" rule is not defined but "start" rule is: use the "start" rule as
1560 // the "left" rule
1561 mRules[ALIGN_LEFT] = mRules[ALIGN_START];
1562 }
1563 mRules[ALIGN_START] = 0;
1564 }
1565
1566 if (mRules[ALIGN_END] != 0) {
1567 if (mRules[ALIGN_RIGHT] == 0) {
1568 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1569 // "right" rule
1570 mRules[ALIGN_RIGHT] = mRules[ALIGN_END];
1571 }
1572 mRules[ALIGN_END] = 0;
1573 }
1574
1575 if (mRules[START_OF] != 0) {
1576 if (mRules[LEFT_OF] == 0) {
1577 // "left" rule is not defined but "start" rule is: use the "start" rule as
1578 // the "left" rule
1579 mRules[LEFT_OF] = mRules[START_OF];
1580 }
1581 mRules[START_OF] = 0;
1582 }
1583
1584 if (mRules[END_OF] != 0) {
1585 if (mRules[RIGHT_OF] == 0) {
1586 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1587 // "right" rule
1588 mRules[RIGHT_OF] = mRules[END_OF];
1589 }
1590 mRules[END_OF] = 0;
1591 }
1592
1593 if (mRules[ALIGN_PARENT_START] != 0) {
1594 if (mRules[ALIGN_PARENT_LEFT] == 0) {
1595 // "left" rule is not defined but "start" rule is: use the "start" rule as
1596 // the "left" rule
1597 mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1598 }
1599 mRules[ALIGN_PARENT_START] = 0;
1600 }
1601
1602 if (mRules[ALIGN_PARENT_END] != 0) {
1603 if (mRules[ALIGN_PARENT_RIGHT] == 0) {
1604 // "right" rule is not defined but "end" rule is: use the "end" rule as the
1605 // "right" rule
1606 mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1607 }
1608 mRules[ALIGN_PARENT_END] = 0;
1609 }
1610 } else {
1611 // JB MR1+ case
1612 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) &&
1613 (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) {
1614 // "start"/"end" rules take precedence over "left"/"right" rules
1615 mRules[ALIGN_LEFT] = 0;
1616 mRules[ALIGN_RIGHT] = 0;
1617 }
1618 if (mRules[ALIGN_START] != 0) {
1619 // "start" rule resolved to "left" or "right" depending on the direction
1620 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START];
1621 mRules[ALIGN_START] = 0;
1622 }
1623 if (mRules[ALIGN_END] != 0) {
1624 // "end" rule resolved to "left" or "right" depending on the direction
1625 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END];
1626 mRules[ALIGN_END] = 0;
1627 }
1628
1629 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) &&
1630 (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) {
1631 // "start"/"end" rules take precedence over "left"/"right" rules
1632 mRules[LEFT_OF] = 0;
1633 mRules[RIGHT_OF] = 0;
1634 }
1635 if (mRules[START_OF] != 0) {
1636 // "start" rule resolved to "left" or "right" depending on the direction
1637 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF];
1638 mRules[START_OF] = 0;
1639 }
1640 if (mRules[END_OF] != 0) {
1641 // "end" rule resolved to "left" or "right" depending on the direction
1642 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF];
1643 mRules[END_OF] = 0;
1644 }
1645
1646 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) &&
1647 (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) {
1648 // "start"/"end" rules take precedence over "left"/"right" rules
1649 mRules[ALIGN_PARENT_LEFT] = 0;
1650 mRules[ALIGN_PARENT_RIGHT] = 0;
1651 }
1652 if (mRules[ALIGN_PARENT_START] != 0) {
1653 // "start" rule resolved to "left" or "right" depending on the direction
1654 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START];
1655 mRules[ALIGN_PARENT_START] = 0;
1656 }
1657 if (mRules[ALIGN_PARENT_END] != 0) {
1658 // "end" rule resolved to "left" or "right" depending on the direction
1659 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END];
1660 mRules[ALIGN_PARENT_END] = 0;
1661 }
1662 }
1663
1664 mRulesChanged = false;
1665 mNeedsLayoutResolution = false;
1666 }
1667
1668 /**
1669 * Retrieves a complete list of all supported rules, where the index is the rule
1670 * verb, and the element value is the value specified, or "false" if it was never
1671 * set. If there are relative rules defined (*_START / *_END), they will be resolved
1672 * depending on the layout direction.
1673 *
1674 * @param layoutDirection the direction of the layout.
1675 * Should be either {@link View#LAYOUT_DIRECTION_LTR}
1676 * or {@link View#LAYOUT_DIRECTION_RTL}
1677 * @return the supported rules
1678 * @see #addRule(int, int)
1679 *
1680 * @hide
1681 */
1682 public int[] getRules(int layoutDirection) {
1683 resolveLayoutDirection(layoutDirection);
1684 return mRules;
1685 }
1686
1687 /**
1688 * Retrieves a complete list of all supported rules, where the index is the rule
1689 * verb, and the element value is the value specified, or "false" if it was never
1690 * set. There will be no resolution of relative rules done.
1691 *
1692 * @return the supported rules
1693 * @see #addRule(int, int)
1694 */
1695 public int[] getRules() {
1696 return mRules;
1697 }
1698
1699 /**
1700 * This will be called by {@link android.view.View#requestLayout()} to
1701 * resolve layout parameters that are relative to the layout direction.
1702 * <p>
1703 * After this method is called, any rules using layout-relative verbs
1704 * (ex. {@link #START_OF}) previously added via {@link #addRule(int)}
1705 * may only be accessed via their resolved absolute verbs (ex.
1706 * {@link #LEFT_OF}).
1707 */
1708 @Override
1709 public void resolveLayoutDirection(int layoutDirection) {
1710 if (shouldResolveLayoutDirection(layoutDirection)) {
1711 resolveRules(layoutDirection);
1712 }
1713
1714 // This will set the layout direction.
1715 super.resolveLayoutDirection(layoutDirection);
1716 }
1717
1718 private boolean shouldResolveLayoutDirection(int layoutDirection) {
1719 return (mNeedsLayoutResolution || hasRelativeRules())
1720 && (mRulesChanged || layoutDirection != getLayoutDirection());
1721 }
1722
1723 /** @hide */
1724 @Override
1725 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1726 super.encodeProperties(encoder);
1727 encoder.addProperty("layout:alignWithParent", alignWithParent);
1728 }
1729
1730 /** @hide */
1731 public static final class InspectionCompanion
1732 implements android.view.inspector.InspectionCompanion<LayoutParams> {
1733 private boolean mPropertiesMapped;
1734
1735 private int mAboveId;
1736 private int mAlignBaselineId;
1737 private int mAlignBottomId;
1738 private int mAlignEndId;
1739 private int mAlignLeftId;
1740 private int mAlignParentBottomId;
1741 private int mAlignParentEndId;
1742 private int mAlignParentLeftId;
1743 private int mAlignParentRightId;
1744 private int mAlignParentStartId;
1745 private int mAlignParentTopId;
1746 private int mAlignRightId;
1747 private int mAlignStartId;
1748 private int mAlignTopId;
1749 private int mAlignWithParentIfMissingId;
1750 private int mBelowId;
1751 private int mCenterHorizontalId;
1752 private int mCenterInParentId;
1753 private int mCenterVerticalId;
1754 private int mToEndOfId;
1755 private int mToLeftOfId;
1756 private int mToRightOfId;
1757 private int mToStartOfId;
1758
1759 @Override
1760 public void mapProperties(@NonNull PropertyMapper propertyMapper) {
1761 mPropertiesMapped = true;
1762
1763 mAboveId = propertyMapper.mapResourceId("layout_above", R.attr.layout_above);
1764
1765 mAlignBaselineId = propertyMapper.mapResourceId(
1766 "layout_alignBaseline", R.attr.layout_alignBaseline);
1767
1768 mAlignBottomId = propertyMapper.mapResourceId(
1769 "layout_alignBottom", R.attr.layout_alignBottom);
1770
1771 mAlignEndId = propertyMapper.mapResourceId(
1772 "layout_alignEnd", R.attr.layout_alignEnd);
1773
1774 mAlignLeftId = propertyMapper.mapResourceId(
1775 "layout_alignLeft", R.attr.layout_alignLeft);
1776
1777 mAlignParentBottomId = propertyMapper.mapBoolean(
1778 "layout_alignParentBottom", R.attr.layout_alignParentBottom);
1779
1780 mAlignParentEndId = propertyMapper.mapBoolean(
1781 "layout_alignParentEnd", R.attr.layout_alignParentEnd);
1782
1783 mAlignParentLeftId = propertyMapper.mapBoolean(
1784 "layout_alignParentLeft", R.attr.layout_alignParentLeft);
1785
1786 mAlignParentRightId = propertyMapper.mapBoolean(
1787 "layout_alignParentRight", R.attr.layout_alignParentRight);
1788
1789 mAlignParentStartId = propertyMapper.mapBoolean(
1790 "layout_alignParentStart", R.attr.layout_alignParentStart);
1791
1792 mAlignParentTopId = propertyMapper.mapBoolean(
1793 "layout_alignParentTop", R.attr.layout_alignParentTop);
1794
1795 mAlignRightId = propertyMapper.mapResourceId(
1796 "layout_alignRight", R.attr.layout_alignRight);
1797
1798 mAlignStartId = propertyMapper.mapResourceId(
1799 "layout_alignStart", R.attr.layout_alignStart);
1800
1801 mAlignTopId = propertyMapper.mapResourceId(
1802 "layout_alignTop", R.attr.layout_alignTop);
1803
1804 mAlignWithParentIfMissingId = propertyMapper.mapBoolean(
1805 "layout_alignWithParentIfMissing",
1806 R.attr.layout_alignWithParentIfMissing);
1807
1808 mBelowId = propertyMapper.mapResourceId("layout_below", R.attr.layout_below);
1809
1810 mCenterHorizontalId = propertyMapper.mapBoolean(
1811 "layout_centerHorizontal", R.attr.layout_centerHorizontal);
1812
1813 mCenterInParentId = propertyMapper.mapBoolean(
1814 "layout_centerInParent", R.attr.layout_centerInParent);
1815
1816 mCenterVerticalId = propertyMapper.mapBoolean(
1817 "layout_centerVertical", R.attr.layout_centerVertical);
1818
1819 mToEndOfId = propertyMapper.mapResourceId(
1820 "layout_toEndOf", R.attr.layout_toEndOf);
1821
1822 mToLeftOfId = propertyMapper.mapResourceId(
1823 "layout_toLeftOf", R.attr.layout_toLeftOf);
1824
1825 mToRightOfId = propertyMapper.mapResourceId(
1826 "layout_toRightOf", R.attr.layout_toRightOf);
1827
1828 mToStartOfId = propertyMapper.mapResourceId(
1829 "layout_toStartOf", R.attr.layout_toStartOf);
1830 }
1831
1832 @Override
1833 public void readProperties(
1834 @NonNull LayoutParams node,
1835 @NonNull PropertyReader propertyReader
1836 ) {
1837 if (!mPropertiesMapped) {
1838 throw new UninitializedPropertyMapException();
1839 }
1840
1841 final int[] rules = node.getRules();
1842
1843 propertyReader.readResourceId(mAboveId, rules[ABOVE]);
1844 propertyReader.readResourceId(mAlignBaselineId, rules[ALIGN_BASELINE]);
1845 propertyReader.readResourceId(mAlignBottomId, rules[ALIGN_BOTTOM]);
1846 propertyReader.readResourceId(mAlignEndId, rules[ALIGN_END]);
1847 propertyReader.readResourceId(mAlignLeftId, rules[ALIGN_LEFT]);
1848 propertyReader.readBoolean(
1849 mAlignParentBottomId, rules[ALIGN_PARENT_BOTTOM] == TRUE);
1850 propertyReader.readBoolean(mAlignParentEndId, rules[ALIGN_PARENT_END] == TRUE);
1851 propertyReader.readBoolean(mAlignParentLeftId, rules[ALIGN_PARENT_LEFT] == TRUE);
1852 propertyReader.readBoolean(mAlignParentRightId, rules[ALIGN_PARENT_RIGHT] == TRUE);
1853 propertyReader.readBoolean(mAlignParentStartId, rules[ALIGN_PARENT_START] == TRUE);
1854 propertyReader.readBoolean(mAlignParentTopId, rules[ALIGN_PARENT_TOP] == TRUE);
1855 propertyReader.readResourceId(mAlignRightId, rules[ALIGN_RIGHT]);
1856 propertyReader.readResourceId(mAlignStartId, rules[ALIGN_START]);
1857 propertyReader.readResourceId(mAlignTopId, rules[ALIGN_TOP]);
1858 propertyReader.readBoolean(mAlignWithParentIfMissingId, node.alignWithParent);
1859 propertyReader.readResourceId(mBelowId, rules[BELOW]);
1860 propertyReader.readBoolean(mCenterHorizontalId, rules[CENTER_HORIZONTAL] == TRUE);
1861 propertyReader.readBoolean(mCenterInParentId, rules[CENTER_IN_PARENT] == TRUE);
1862 propertyReader.readBoolean(mCenterVerticalId, rules[CENTER_VERTICAL] == TRUE);
1863 propertyReader.readResourceId(mToEndOfId, rules[END_OF]);
1864 propertyReader.readResourceId(mToLeftOfId, rules[LEFT_OF]);
1865 propertyReader.readResourceId(mToRightOfId, rules[RIGHT_OF]);
1866 propertyReader.readResourceId(mToStartOfId, rules[START_OF]);
1867 }
1868 }
1869 }
1870
1871 private static class DependencyGraph {
1872 /**
1873 * List of all views in the graph.
1874 */
1875 private ArrayList<Node> mNodes = new ArrayList<Node>();
1876
1877 /**
1878 * List of nodes in the graph. Each node is identified by its
1879 * view id (see View#getId()).
1880 */
1881 private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
1882
1883 /**
1884 * Temporary data structure used to build the list of roots
1885 * for this graph.
1886 */
1887 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
1888
1889 /**
1890 * Clears the graph.
1891 */
1892 void clear() {
1893 final ArrayList<Node> nodes = mNodes;
1894 final int count = nodes.size();
1895
1896 for (int i = 0; i < count; i++) {
1897 nodes.get(i).release();
1898 }
1899 nodes.clear();
1900
1901 mKeyNodes.clear();
1902 mRoots.clear();
1903 }
1904
1905 /**
1906 * Adds a view to the graph.
1907 *
1908 * @param view The view to be added as a node to the graph.
1909 */
1910 void add(View view) {
1911 final int id = view.getId();
1912 final Node node = Node.acquire(view);
1913
1914 if (id != View.NO_ID) {
1915 mKeyNodes.put(id, node);
1916 }
1917
1918 mNodes.add(node);
1919 }
1920
1921 /**
1922 * Builds a sorted list of views. The sorting order depends on the dependencies
1923 * between the view. For instance, if view C needs view A to be processed first
1924 * and view A needs view B to be processed first, the dependency graph
1925 * is: B -> A -> C. The sorted array will contain views B, A and C in this order.
1926 *
1927 * @param sorted The sorted list of views. The length of this array must
1928 * be equal to getChildCount().
1929 * @param rules The list of rules to take into account.
1930 */
1931 void getSortedViews(View[] sorted, int... rules) {
1932 final ArrayDeque<Node> roots = findRoots(rules);
1933 int index = 0;
1934
1935 Node node;
1936 while ((node = roots.pollLast()) != null) {
1937 final View view = node.view;
1938 final int key = view.getId();
1939
1940 sorted[index++] = view;
1941
1942 final ArrayMap<Node, DependencyGraph> dependents = node.dependents;
1943 final int count = dependents.size();
1944 for (int i = 0; i < count; i++) {
1945 final Node dependent = dependents.keyAt(i);
1946 final SparseArray<Node> dependencies = dependent.dependencies;
1947
1948 dependencies.remove(key);
1949 if (dependencies.size() == 0) {
1950 roots.add(dependent);
1951 }
1952 }
1953 }
1954
1955 if (index < sorted.length) {
1956 throw new IllegalStateException("Circular dependencies cannot exist"
1957 + " in RelativeLayout");
1958 }
1959 }
1960
1961 /**
1962 * Finds the roots of the graph. A root is a node with no dependency and
1963 * with [0..n] dependents.
1964 *
1965 * @param rulesFilter The list of rules to consider when building the
1966 * dependencies
1967 *
1968 * @return A list of node, each being a root of the graph
1969 */
1970 private ArrayDeque<Node> findRoots(int[] rulesFilter) {
1971 final SparseArray<Node> keyNodes = mKeyNodes;
1972 final ArrayList<Node> nodes = mNodes;
1973 final int count = nodes.size();
1974
1975 // Find roots can be invoked several times, so make sure to clear
1976 // all dependents and dependencies before running the algorithm
1977 for (int i = 0; i < count; i++) {
1978 final Node node = nodes.get(i);
1979 node.dependents.clear();
1980 node.dependencies.clear();
1981 }
1982
1983 // Builds up the dependents and dependencies for each node of the graph
1984 for (int i = 0; i < count; i++) {
1985 final Node node = nodes.get(i);
1986
1987 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
1988 final int[] rules = layoutParams.mRules;
1989 final int rulesCount = rulesFilter.length;
1990
1991 // Look only the the rules passed in parameter, this way we build only the
1992 // dependencies for a specific set of rules
1993 for (int j = 0; j < rulesCount; j++) {
1994 final int rule = rules[rulesFilter[j]];
1995 if (rule > 0 || ResourceId.isValid(rule)) {
1996 // The node this node depends on
1997 final Node dependency = keyNodes.get(rule);
1998 // Skip unknowns and self dependencies
1999 if (dependency == null || dependency == node) {
2000 continue;
2001 }
2002 // Add the current node as a dependent
2003 dependency.dependents.put(node, this);
2004 // Add a dependency to the current node
2005 node.dependencies.put(rule, dependency);
2006 }
2007 }
2008 }
2009
2010 final ArrayDeque<Node> roots = mRoots;
2011 roots.clear();
2012
2013 // Finds all the roots in the graph: all nodes with no dependencies
2014 for (int i = 0; i < count; i++) {
2015 final Node node = nodes.get(i);
2016 if (node.dependencies.size() == 0) roots.addLast(node);
2017 }
2018
2019 return roots;
2020 }
2021
2022 /**
2023 * A node in the dependency graph. A node is a view, its list of dependencies
2024 * and its list of dependents.
2025 *
2026 * A node with no dependent is considered a root of the graph.
2027 */
2028 static class Node {
2029
2030 @UnsupportedAppUsage
2031 Node() {
2032 }
2033
2034 /**
2035 * The view representing this node in the layout.
2036 */
2037 View view;
2038
2039 /**
2040 * The list of dependents for this node; a dependent is a node
2041 * that needs this node to be processed first.
2042 */
2043 final ArrayMap<Node, DependencyGraph> dependents =
2044 new ArrayMap<Node, DependencyGraph>();
2045
2046 /**
2047 * The list of dependencies for this node.
2048 */
2049 final SparseArray<Node> dependencies = new SparseArray<Node>();
2050
2051 /*
2052 * START POOL IMPLEMENTATION
2053 */
2054 // The pool is static, so all nodes instances are shared across
2055 // activities, that's why we give it a rather high limit
2056 private static final int POOL_LIMIT = 100;
2057 private static final SynchronizedPool<Node> sPool =
2058 new SynchronizedPool<Node>(POOL_LIMIT);
2059
2060 static Node acquire(View view) {
2061 Node node = sPool.acquire();
2062 if (node == null) {
2063 node = new Node();
2064 }
2065 node.view = view;
2066 return node;
2067 }
2068
2069 void release() {
2070 view = null;
2071 dependents.clear();
2072 dependencies.clear();
2073
2074 sPool.release(this);
2075 }
2076 /*
2077 * END POOL IMPLEMENTATION
2078 */
2079 }
2080 }
2081}