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