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