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.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.InspectionCompanion; 40 import android.view.inspector.PropertyMapper; 41 import android.view.inspector.PropertyReader; 42 import android.widget.RemoteViews.RemoteView; 43 44 import com.android.internal.R; 45 46 import java.util.ArrayDeque; 47 import java.util.ArrayList; 48 import java.util.Comparator; 49 import java.util.SortedSet; 50 import 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 90 public 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 RelativeLayout(Context context)242 public RelativeLayout(Context context) { 243 this(context, null); 244 } 245 RelativeLayout(Context context, AttributeSet attrs)246 public RelativeLayout(Context context, AttributeSet attrs) { 247 this(context, attrs, 0); 248 } 249 RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr)250 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 251 this(context, attrs, defStyleAttr, 0); 252 } 253 RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 initFromAttributes( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 queryCompatibilityModes(Context context)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 shouldDelayChildPressedState()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 setIgnoreGravity(int viewId)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 getIgnoreGravity()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) getGravity()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 setGravity(int gravity)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 setHorizontalGravity(int horizontalGravity)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 setVerticalGravity(int verticalGravity)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 getBaseline()374 public int getBaseline() { 375 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline(); 376 } 377 378 @Override requestLayout()379 public void requestLayout() { 380 super.requestLayout(); 381 mDirtyHierarchy = true; 382 } 383 sortChildren()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 onMeasure(int widthMeasureSpec, int heightMeasureSpec)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 */ compareLayoutPosition(LayoutParams p1, LayoutParams p2)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 */ measureChild(View child, LayoutParams params, int myWidth, int myHeight)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 measureChildHorizontal( View child, LayoutParams params, int myWidth, int myHeight)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 */ getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize)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 positionChildHorizontal(View child, LayoutParams params, int myWidth, boolean wrapContent)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 positionAtEdge(View child, LayoutParams params, int myWidth)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 positionChildVertical(View child, LayoutParams params, int myHeight, boolean wrapContent)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 applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules)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 applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline)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 getRelatedView(int[] rules, int relation)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 getRelatedViewParams(int[] rules, int relation)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 getRelatedViewBaselineOffset(int[] rules)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 centerHorizontal(View child, LayoutParams params, int myWidth)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 centerVertical(View child, LayoutParams params, int myHeight)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 onLayout(boolean changed, int l, int t, int r, int b)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 generateLayoutParams(AttributeSet attrs)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 generateDefaultLayoutParams()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 checkLayoutParams(ViewGroup.LayoutParams p)1125 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1126 return p instanceof RelativeLayout.LayoutParams; 1127 } 1128 1129 @Override generateLayoutParams(ViewGroup.LayoutParams lp)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 dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)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 getAccessibilityClassName()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> { compare(View first, View second)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="/guide/topics/ui/layout/relative.html"> 1206 * Relative 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="/guide/topics/ui/declaring-layout.html#attributes"> 1211 * Layouts 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 LayoutParams(Context c, AttributeSet attrs)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 LayoutParams(int w, int h)1390 public LayoutParams(int w, int h) { 1391 super(w, h); 1392 } 1393 1394 /** 1395 * {@inheritDoc} 1396 */ LayoutParams(ViewGroup.LayoutParams source)1397 public LayoutParams(ViewGroup.LayoutParams source) { 1398 super(source); 1399 } 1400 1401 /** 1402 * {@inheritDoc} 1403 */ LayoutParams(ViewGroup.MarginLayoutParams source)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 */ LayoutParams(LayoutParams source)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 debug(String output)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 */ addRule(int verb)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 */ addRule(int verb, int subject)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 */ removeRule(int verb)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 */ getRule(int verb)1520 public int getRule(int verb) { 1521 return mRules[verb]; 1522 } 1523 hasRelativeRules()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 isRelativeRule(int rule)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. resolveRules(int layoutDirection)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 */ getRules(int layoutDirection)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 */ getRules()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 resolveLayoutDirection(int layoutDirection)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 shouldResolveLayoutDirection(int layoutDirection)1718 private boolean shouldResolveLayoutDirection(int layoutDirection) { 1719 return (mNeedsLayoutResolution || hasRelativeRules()) 1720 && (mRulesChanged || layoutDirection != getLayoutDirection()); 1721 } 1722 1723 /** @hide */ 1724 @Override encodeProperties(@onNull ViewHierarchyEncoder encoder)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 mapProperties(@onNull PropertyMapper propertyMapper)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 readProperties( @onNull LayoutParams node, @NonNull PropertyReader propertyReader )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 */ clear()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 */ add(View view)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 */ getSortedViews(View[] sorted, int... rules)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 */ findRoots(int[] rulesFilter)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 * The view representing this node in the layout. 2031 */ 2032 View view; 2033 2034 /** 2035 * The list of dependents for this node; a dependent is a node 2036 * that needs this node to be processed first. 2037 */ 2038 final ArrayMap<Node, DependencyGraph> dependents = 2039 new ArrayMap<Node, DependencyGraph>(); 2040 2041 /** 2042 * The list of dependencies for this node. 2043 */ 2044 final SparseArray<Node> dependencies = new SparseArray<Node>(); 2045 2046 /* 2047 * START POOL IMPLEMENTATION 2048 */ 2049 // The pool is static, so all nodes instances are shared across 2050 // activities, that's why we give it a rather high limit 2051 private static final int POOL_LIMIT = 100; 2052 private static final SynchronizedPool<Node> sPool = 2053 new SynchronizedPool<Node>(POOL_LIMIT); 2054 acquire(View view)2055 static Node acquire(View view) { 2056 Node node = sPool.acquire(); 2057 if (node == null) { 2058 node = new Node(); 2059 } 2060 node.view = view; 2061 return node; 2062 } 2063 release()2064 void release() { 2065 view = null; 2066 dependents.clear(); 2067 dependencies.clear(); 2068 2069 sPool.release(this); 2070 } 2071 /* 2072 * END POOL IMPLEMENTATION 2073 */ 2074 } 2075 } 2076 } 2077