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