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