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 230 public RelativeLayout(Context context) { 231 super(context); 232 queryCompatibilityModes(context); 233 } 234 235 public RelativeLayout(Context context, AttributeSet attrs) { 236 super(context, attrs); 237 initFromAttributes(context, attrs); 238 queryCompatibilityModes(context); 239 } 240 241 public RelativeLayout(Context context, AttributeSet attrs, int defStyle) { 242 super(context, attrs, defStyle); 243 initFromAttributes(context, attrs); 244 queryCompatibilityModes(context); 245 } 246 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 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 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 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 */ 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 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 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 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 346 public int getBaseline() { 347 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline(); 348 } 349 350 @Override 351 public void requestLayout() { 352 super.requestLayout(); 353 mDirtyHierarchy = true; 354 } 355 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 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 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 */ 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 1077 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1078 return p instanceof RelativeLayout.LayoutParams; 1079 } 1080 1081 @Override 1082 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1083 return new LayoutParams(p); 1084 } 1085 1086 @Override 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 1110 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1111 super.onInitializeAccessibilityEvent(event); 1112 event.setClassName(RelativeLayout.class.getName()); 1113 } 1114 1115 @Override 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> { 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 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 1319 public LayoutParams(int w, int h) { 1320 super(w, h); 1321 } 1322 1323 /** 1324 * {@inheritDoc} 1325 */ 1326 public LayoutParams(ViewGroup.LayoutParams source) { 1327 super(source); 1328 } 1329 1330 /** 1331 * {@inheritDoc} 1332 */ 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 */ 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 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 */ 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 */ 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 */ 1408 public void removeRule(int verb) { 1409 mRules[verb] = 0; 1410 mInitialRules[verb] = 0; 1411 mRulesChanged = true; 1412 } 1413 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. 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 */ 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 */ 1583 public int[] getRules() { 1584 return mRules; 1585 } 1586 1587 @Override 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 */ 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 */ 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 */ 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 */ 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 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 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