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