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 RelativeLayout(Context context)235 public RelativeLayout(Context context) { 236 this(context, null); 237 } 238 RelativeLayout(Context context, AttributeSet attrs)239 public RelativeLayout(Context context, AttributeSet attrs) { 240 this(context, attrs, 0); 241 } 242 RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr)243 public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 244 this(context, attrs, defStyleAttr, 0); 245 } 246 RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 initFromAttributes( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 queryCompatibilityModes(Context context)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 shouldDelayChildPressedState()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 setIgnoreGravity(int viewId)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 */ getGravity()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 setGravity(int gravity)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 setHorizontalGravity(int horizontalGravity)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 setVerticalGravity(int verticalGravity)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 getBaseline()354 public int getBaseline() { 355 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline(); 356 } 357 358 @Override requestLayout()359 public void requestLayout() { 360 super.requestLayout(); 361 mDirtyHierarchy = true; 362 } 363 sortChildren()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 onMeasure(int widthMeasureSpec, int heightMeasureSpec)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 alignBaseline(View child, LayoutParams params)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 */ measureChild(View child, LayoutParams params, int myWidth, int myHeight)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 measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight)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 */ getChildMeasureSpec(int childStart, int childEnd, int childSize, int startMargin, int endMargin, int startPadding, int endPadding, int mySize)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 positionChildHorizontal(View child, LayoutParams params, int myWidth, boolean wrapContent)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 positionChildVertical(View child, LayoutParams params, int myHeight, boolean wrapContent)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 applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules)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 applyVerticalSizeRules(LayoutParams childParams, int myHeight)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 getRelatedView(int[] rules, int relation)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 getRelatedViewParams(int[] rules, int relation)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 getRelatedViewBaseline(int[] rules, int relation)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 centerHorizontal(View child, LayoutParams params, int myWidth)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 centerVertical(View child, LayoutParams params, int myHeight)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 onLayout(boolean changed, int l, int t, int r, int b)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 generateLayoutParams(AttributeSet attrs)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 generateDefaultLayoutParams()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 checkLayoutParams(ViewGroup.LayoutParams p)1098 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1099 return p instanceof RelativeLayout.LayoutParams; 1100 } 1101 1102 @Override generateLayoutParams(ViewGroup.LayoutParams p)1103 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1104 return new LayoutParams(p); 1105 } 1106 1107 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)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 onInitializeAccessibilityEvent(AccessibilityEvent event)1131 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1132 super.onInitializeAccessibilityEvent(event); 1133 event.setClassName(RelativeLayout.class.getName()); 1134 } 1135 1136 @Override onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)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> { compare(View first, View second)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 LayoutParams(Context c, AttributeSet attrs)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 LayoutParams(int w, int h)1337 public LayoutParams(int w, int h) { 1338 super(w, h); 1339 } 1340 1341 /** 1342 * {@inheritDoc} 1343 */ LayoutParams(ViewGroup.LayoutParams source)1344 public LayoutParams(ViewGroup.LayoutParams source) { 1345 super(source); 1346 } 1347 1348 /** 1349 * {@inheritDoc} 1350 */ LayoutParams(ViewGroup.MarginLayoutParams source)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 */ LayoutParams(LayoutParams source)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 debug(String output)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 */ addRule(int verb)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 */ addRule(int verb, int anchor)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 */ removeRule(int verb)1426 public void removeRule(int verb) { 1427 mRules[verb] = 0; 1428 mInitialRules[verb] = 0; 1429 mRulesChanged = true; 1430 } 1431 hasRelativeRules()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. resolveRules(int layoutDirection)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 */ getRules(int layoutDirection)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 */ getRules()1601 public int[] getRules() { 1602 return mRules; 1603 } 1604 1605 @Override resolveLayoutDirection(int layoutDirection)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 */ clear()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 */ add(View view)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 */ getSortedViews(View[] sorted, int... rules)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 */ findRoots(int[] rulesFilter)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 acquire(View view)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 release()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