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