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