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 (sPreserveMarginParamsInLayoutParamConversion) { 1108 if (lp instanceof LayoutParams) { 1109 return new LayoutParams((LayoutParams) lp); 1110 } else if (lp instanceof MarginLayoutParams) { 1111 return new LayoutParams((MarginLayoutParams) lp); 1112 } 1113 } 1114 return new LayoutParams(lp); 1115 } 1116 1117 /** @hide */ 1118 @Override dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)1119 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 1120 if (mTopToBottomLeftToRightSet == null) { 1121 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator()); 1122 } 1123 1124 // sort children top-to-bottom and left-to-right 1125 for (int i = 0, count = getChildCount(); i < count; i++) { 1126 mTopToBottomLeftToRightSet.add(getChildAt(i)); 1127 } 1128 1129 for (View view : mTopToBottomLeftToRightSet) { 1130 if (view.getVisibility() == View.VISIBLE 1131 && view.dispatchPopulateAccessibilityEvent(event)) { 1132 mTopToBottomLeftToRightSet.clear(); 1133 return true; 1134 } 1135 } 1136 1137 mTopToBottomLeftToRightSet.clear(); 1138 return false; 1139 } 1140 1141 @Override getAccessibilityClassName()1142 public CharSequence getAccessibilityClassName() { 1143 return RelativeLayout.class.getName(); 1144 } 1145 1146 /** 1147 * Compares two views in left-to-right and top-to-bottom fashion. 1148 */ 1149 private class TopToBottomLeftToRightComparator implements Comparator<View> { compare(View first, View second)1150 public int compare(View first, View second) { 1151 // top - bottom 1152 int topDifference = first.getTop() - second.getTop(); 1153 if (topDifference != 0) { 1154 return topDifference; 1155 } 1156 // left - right 1157 int leftDifference = first.getLeft() - second.getLeft(); 1158 if (leftDifference != 0) { 1159 return leftDifference; 1160 } 1161 // break tie by height 1162 int heightDiference = first.getHeight() - second.getHeight(); 1163 if (heightDiference != 0) { 1164 return heightDiference; 1165 } 1166 // break tie by width 1167 int widthDiference = first.getWidth() - second.getWidth(); 1168 if (widthDiference != 0) { 1169 return widthDiference; 1170 } 1171 return 0; 1172 } 1173 } 1174 1175 /** 1176 * Per-child layout information associated with RelativeLayout. 1177 * 1178 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing 1179 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf 1180 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf 1181 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above 1182 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below 1183 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline 1184 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft 1185 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop 1186 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight 1187 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom 1188 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft 1189 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop 1190 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight 1191 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom 1192 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent 1193 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal 1194 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical 1195 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf 1196 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf 1197 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart 1198 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd 1199 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart 1200 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd 1201 */ 1202 public static class LayoutParams extends ViewGroup.MarginLayoutParams { 1203 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = { 1204 @ViewDebug.IntToString(from = ABOVE, to = "above"), 1205 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"), 1206 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"), 1207 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"), 1208 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"), 1209 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"), 1210 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"), 1211 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"), 1212 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"), 1213 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"), 1214 @ViewDebug.IntToString(from = BELOW, to = "below"), 1215 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"), 1216 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"), 1217 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"), 1218 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"), 1219 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"), 1220 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"), 1221 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"), 1222 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"), 1223 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"), 1224 @ViewDebug.IntToString(from = START_OF, to = "startOf"), 1225 @ViewDebug.IntToString(from = END_OF, to = "endOf") 1226 }, mapping = { 1227 @ViewDebug.IntToString(from = TRUE, to = "true"), 1228 @ViewDebug.IntToString(from = 0, to = "false/NO_ID") 1229 }) 1230 1231 private int[] mRules = new int[VERB_COUNT]; 1232 private int[] mInitialRules = new int[VERB_COUNT]; 1233 1234 private int mLeft, mTop, mRight, mBottom; 1235 1236 /** 1237 * Whether this view had any relative rules modified following the most 1238 * recent resolution of layout direction. 1239 */ 1240 private boolean mNeedsLayoutResolution; 1241 1242 private boolean mRulesChanged = false; 1243 private boolean mIsRtlCompatibilityMode = false; 1244 1245 /** 1246 * When true, uses the parent as the anchor if the anchor doesn't exist or if 1247 * the anchor's visibility is GONE. 1248 */ 1249 @ViewDebug.ExportedProperty(category = "layout") 1250 public boolean alignWithParent; 1251 LayoutParams(Context c, AttributeSet attrs)1252 public LayoutParams(Context c, AttributeSet attrs) { 1253 super(c, attrs); 1254 1255 TypedArray a = c.obtainStyledAttributes(attrs, 1256 com.android.internal.R.styleable.RelativeLayout_Layout); 1257 1258 final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; 1259 mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 || 1260 !c.getApplicationInfo().hasRtlSupport()); 1261 1262 final int[] rules = mRules; 1263 //noinspection MismatchedReadAndWriteOfArray 1264 final int[] initialRules = mInitialRules; 1265 1266 final int N = a.getIndexCount(); 1267 for (int i = 0; i < N; i++) { 1268 int attr = a.getIndex(i); 1269 switch (attr) { 1270 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing: 1271 alignWithParent = a.getBoolean(attr, false); 1272 break; 1273 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf: 1274 rules[LEFT_OF] = a.getResourceId(attr, 0); 1275 break; 1276 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf: 1277 rules[RIGHT_OF] = a.getResourceId(attr, 0); 1278 break; 1279 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above: 1280 rules[ABOVE] = a.getResourceId(attr, 0); 1281 break; 1282 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below: 1283 rules[BELOW] = a.getResourceId(attr, 0); 1284 break; 1285 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline: 1286 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0); 1287 break; 1288 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft: 1289 rules[ALIGN_LEFT] = a.getResourceId(attr, 0); 1290 break; 1291 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop: 1292 rules[ALIGN_TOP] = a.getResourceId(attr, 0); 1293 break; 1294 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight: 1295 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0); 1296 break; 1297 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom: 1298 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0); 1299 break; 1300 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft: 1301 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0; 1302 break; 1303 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop: 1304 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0; 1305 break; 1306 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight: 1307 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0; 1308 break; 1309 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom: 1310 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0; 1311 break; 1312 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent: 1313 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0; 1314 break; 1315 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal: 1316 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0; 1317 break; 1318 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical: 1319 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0; 1320 break; 1321 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf: 1322 rules[START_OF] = a.getResourceId(attr, 0); 1323 break; 1324 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf: 1325 rules[END_OF] = a.getResourceId(attr, 0); 1326 break; 1327 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart: 1328 rules[ALIGN_START] = a.getResourceId(attr, 0); 1329 break; 1330 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd: 1331 rules[ALIGN_END] = a.getResourceId(attr, 0); 1332 break; 1333 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart: 1334 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0; 1335 break; 1336 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd: 1337 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0; 1338 break; 1339 } 1340 } 1341 mRulesChanged = true; 1342 System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT); 1343 1344 a.recycle(); 1345 } 1346 LayoutParams(int w, int h)1347 public LayoutParams(int w, int h) { 1348 super(w, h); 1349 } 1350 1351 /** 1352 * {@inheritDoc} 1353 */ LayoutParams(ViewGroup.LayoutParams source)1354 public LayoutParams(ViewGroup.LayoutParams source) { 1355 super(source); 1356 } 1357 1358 /** 1359 * {@inheritDoc} 1360 */ LayoutParams(ViewGroup.MarginLayoutParams source)1361 public LayoutParams(ViewGroup.MarginLayoutParams source) { 1362 super(source); 1363 } 1364 1365 /** 1366 * Copy constructor. Clones the width, height, margin values, and rules 1367 * of the source. 1368 * 1369 * @param source The layout params to copy from. 1370 */ LayoutParams(LayoutParams source)1371 public LayoutParams(LayoutParams source) { 1372 super(source); 1373 1374 this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode; 1375 this.mRulesChanged = source.mRulesChanged; 1376 this.alignWithParent = source.alignWithParent; 1377 1378 System.arraycopy(source.mRules, LEFT_OF, this.mRules, LEFT_OF, VERB_COUNT); 1379 System.arraycopy( 1380 source.mInitialRules, LEFT_OF, this.mInitialRules, LEFT_OF, VERB_COUNT); 1381 } 1382 1383 @Override debug(String output)1384 public String debug(String output) { 1385 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) + 1386 ", height=" + sizeToString(height) + " }"; 1387 } 1388 1389 /** 1390 * Adds a layout rule to be interpreted by the RelativeLayout. 1391 * <p> 1392 * This method should only be used for verbs that don't refer to a 1393 * sibling (ex. {@link #ALIGN_RIGHT}) or take a boolean 1394 * value ({@link #TRUE} for true or 0 for false). To 1395 * specify a verb that takes a subject, use {@link #addRule(int, int)}. 1396 * <p> 1397 * If the rule is relative to the layout direction (ex. 1398 * {@link #ALIGN_PARENT_START}), then the layout direction must be 1399 * resolved using {@link #resolveLayoutDirection(int)} before calling 1400 * {@link #getRule(int)} an absolute rule (ex. 1401 * {@link #ALIGN_PARENT_LEFT}. 1402 * 1403 * @param verb a layout verb, such as {@link #ALIGN_PARENT_LEFT} 1404 * @see #addRule(int, int) 1405 * @see #removeRule(int) 1406 * @see #getRule(int) 1407 */ addRule(int verb)1408 public void addRule(int verb) { 1409 addRule(verb, TRUE); 1410 } 1411 1412 /** 1413 * Adds a layout rule to be interpreted by the RelativeLayout. 1414 * <p> 1415 * Use this for verbs that refer to a sibling (ex. 1416 * {@link #ALIGN_RIGHT}) or take a boolean value (ex. 1417 * {@link #CENTER_IN_PARENT}). 1418 * <p> 1419 * If the rule is relative to the layout direction (ex. 1420 * {@link #START_OF}), then the layout direction must be resolved using 1421 * {@link #resolveLayoutDirection(int)} before calling 1422 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}. 1423 * 1424 * @param verb a layout verb, such as {@link #ALIGN_RIGHT} 1425 * @param subject the ID of another view to use as an anchor, or a 1426 * boolean value (represented as {@link #TRUE} for true 1427 * or 0 for false) 1428 * @see #addRule(int) 1429 * @see #removeRule(int) 1430 * @see #getRule(int) 1431 */ addRule(int verb, int subject)1432 public void addRule(int verb, int subject) { 1433 // If we're removing a relative rule, we'll need to force layout 1434 // resolution the next time it's requested. 1435 if (!mNeedsLayoutResolution && isRelativeRule(verb) 1436 && mInitialRules[verb] != 0 && subject == 0) { 1437 mNeedsLayoutResolution = true; 1438 } 1439 1440 mRules[verb] = subject; 1441 mInitialRules[verb] = subject; 1442 mRulesChanged = true; 1443 } 1444 1445 /** 1446 * Removes a layout rule to be interpreted by the RelativeLayout. 1447 * <p> 1448 * If the rule is relative to the layout direction (ex. 1449 * {@link #START_OF}, {@link #ALIGN_PARENT_START}, etc.) then the 1450 * layout direction must be resolved using 1451 * {@link #resolveLayoutDirection(int)} before before calling 1452 * {@link #getRule(int)} with an absolute rule (ex. {@link #LEFT_OF}. 1453 * 1454 * @param verb One of the verbs defined by 1455 * {@link android.widget.RelativeLayout RelativeLayout}, such as 1456 * ALIGN_WITH_PARENT_LEFT. 1457 * @see #addRule(int) 1458 * @see #addRule(int, int) 1459 * @see #getRule(int) 1460 */ removeRule(int verb)1461 public void removeRule(int verb) { 1462 addRule(verb, 0); 1463 } 1464 1465 /** 1466 * Returns the layout rule associated with a specific verb. 1467 * 1468 * @param verb one of the verbs defined by {@link RelativeLayout}, such 1469 * as ALIGN_WITH_PARENT_LEFT 1470 * @return the id of another view to use as an anchor, a boolean value 1471 * (represented as {@link RelativeLayout#TRUE} for true 1472 * or 0 for false), or -1 for verbs that don't refer to another 1473 * sibling (for example, ALIGN_WITH_PARENT_BOTTOM) 1474 * @see #addRule(int) 1475 * @see #addRule(int, int) 1476 */ getRule(int verb)1477 public int getRule(int verb) { 1478 return mRules[verb]; 1479 } 1480 hasRelativeRules()1481 private boolean hasRelativeRules() { 1482 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 || 1483 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 || 1484 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0); 1485 } 1486 isRelativeRule(int rule)1487 private boolean isRelativeRule(int rule) { 1488 return rule == START_OF || rule == END_OF 1489 || rule == ALIGN_START || rule == ALIGN_END 1490 || rule == ALIGN_PARENT_START || rule == ALIGN_PARENT_END; 1491 } 1492 1493 // The way we are resolving rules depends on the layout direction and if we are pre JB MR1 1494 // or not. 1495 // 1496 // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having 1497 // predominance over any "start/end" rules that could have been defined. A special case: 1498 // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we 1499 // resolve those "start"/"end" rules to "left"/"right" respectively. 1500 // 1501 // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right" 1502 // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules. 1503 // 1504 // In all cases, the result of the resolution should clear the "start"/"end" rules to leave 1505 // only the "left"/"right" rules at the end. resolveRules(int layoutDirection)1506 private void resolveRules(int layoutDirection) { 1507 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL); 1508 1509 // Reset to initial state 1510 System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT); 1511 1512 // Apply rules depending on direction and if we are in RTL compatibility mode 1513 if (mIsRtlCompatibilityMode) { 1514 if (mRules[ALIGN_START] != 0) { 1515 if (mRules[ALIGN_LEFT] == 0) { 1516 // "left" rule is not defined but "start" rule is: use the "start" rule as 1517 // the "left" rule 1518 mRules[ALIGN_LEFT] = mRules[ALIGN_START]; 1519 } 1520 mRules[ALIGN_START] = 0; 1521 } 1522 1523 if (mRules[ALIGN_END] != 0) { 1524 if (mRules[ALIGN_RIGHT] == 0) { 1525 // "right" rule is not defined but "end" rule is: use the "end" rule as the 1526 // "right" rule 1527 mRules[ALIGN_RIGHT] = mRules[ALIGN_END]; 1528 } 1529 mRules[ALIGN_END] = 0; 1530 } 1531 1532 if (mRules[START_OF] != 0) { 1533 if (mRules[LEFT_OF] == 0) { 1534 // "left" rule is not defined but "start" rule is: use the "start" rule as 1535 // the "left" rule 1536 mRules[LEFT_OF] = mRules[START_OF]; 1537 } 1538 mRules[START_OF] = 0; 1539 } 1540 1541 if (mRules[END_OF] != 0) { 1542 if (mRules[RIGHT_OF] == 0) { 1543 // "right" rule is not defined but "end" rule is: use the "end" rule as the 1544 // "right" rule 1545 mRules[RIGHT_OF] = mRules[END_OF]; 1546 } 1547 mRules[END_OF] = 0; 1548 } 1549 1550 if (mRules[ALIGN_PARENT_START] != 0) { 1551 if (mRules[ALIGN_PARENT_LEFT] == 0) { 1552 // "left" rule is not defined but "start" rule is: use the "start" rule as 1553 // the "left" rule 1554 mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; 1555 } 1556 mRules[ALIGN_PARENT_START] = 0; 1557 } 1558 1559 if (mRules[ALIGN_PARENT_END] != 0) { 1560 if (mRules[ALIGN_PARENT_RIGHT] == 0) { 1561 // "right" rule is not defined but "end" rule is: use the "end" rule as the 1562 // "right" rule 1563 mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; 1564 } 1565 mRules[ALIGN_PARENT_END] = 0; 1566 } 1567 } else { 1568 // JB MR1+ case 1569 if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) && 1570 (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) { 1571 // "start"/"end" rules take precedence over "left"/"right" rules 1572 mRules[ALIGN_LEFT] = 0; 1573 mRules[ALIGN_RIGHT] = 0; 1574 } 1575 if (mRules[ALIGN_START] != 0) { 1576 // "start" rule resolved to "left" or "right" depending on the direction 1577 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START]; 1578 mRules[ALIGN_START] = 0; 1579 } 1580 if (mRules[ALIGN_END] != 0) { 1581 // "end" rule resolved to "left" or "right" depending on the direction 1582 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END]; 1583 mRules[ALIGN_END] = 0; 1584 } 1585 1586 if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) && 1587 (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) { 1588 // "start"/"end" rules take precedence over "left"/"right" rules 1589 mRules[LEFT_OF] = 0; 1590 mRules[RIGHT_OF] = 0; 1591 } 1592 if (mRules[START_OF] != 0) { 1593 // "start" rule resolved to "left" or "right" depending on the direction 1594 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF]; 1595 mRules[START_OF] = 0; 1596 } 1597 if (mRules[END_OF] != 0) { 1598 // "end" rule resolved to "left" or "right" depending on the direction 1599 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF]; 1600 mRules[END_OF] = 0; 1601 } 1602 1603 if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) && 1604 (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) { 1605 // "start"/"end" rules take precedence over "left"/"right" rules 1606 mRules[ALIGN_PARENT_LEFT] = 0; 1607 mRules[ALIGN_PARENT_RIGHT] = 0; 1608 } 1609 if (mRules[ALIGN_PARENT_START] != 0) { 1610 // "start" rule resolved to "left" or "right" depending on the direction 1611 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; 1612 mRules[ALIGN_PARENT_START] = 0; 1613 } 1614 if (mRules[ALIGN_PARENT_END] != 0) { 1615 // "end" rule resolved to "left" or "right" depending on the direction 1616 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; 1617 mRules[ALIGN_PARENT_END] = 0; 1618 } 1619 } 1620 1621 mRulesChanged = false; 1622 mNeedsLayoutResolution = false; 1623 } 1624 1625 /** 1626 * Retrieves a complete list of all supported rules, where the index is the rule 1627 * verb, and the element value is the value specified, or "false" if it was never 1628 * set. If there are relative rules defined (*_START / *_END), they will be resolved 1629 * depending on the layout direction. 1630 * 1631 * @param layoutDirection the direction of the layout. 1632 * Should be either {@link View#LAYOUT_DIRECTION_LTR} 1633 * or {@link View#LAYOUT_DIRECTION_RTL} 1634 * @return the supported rules 1635 * @see #addRule(int, int) 1636 * 1637 * @hide 1638 */ getRules(int layoutDirection)1639 public int[] getRules(int layoutDirection) { 1640 resolveLayoutDirection(layoutDirection); 1641 return mRules; 1642 } 1643 1644 /** 1645 * Retrieves a complete list of all supported rules, where the index is the rule 1646 * verb, and the element value is the value specified, or "false" if it was never 1647 * set. There will be no resolution of relative rules done. 1648 * 1649 * @return the supported rules 1650 * @see #addRule(int, int) 1651 */ getRules()1652 public int[] getRules() { 1653 return mRules; 1654 } 1655 1656 /** 1657 * This will be called by {@link android.view.View#requestLayout()} to 1658 * resolve layout parameters that are relative to the layout direction. 1659 * <p> 1660 * After this method is called, any rules using layout-relative verbs 1661 * (ex. {@link #START_OF}) previously added via {@link #addRule(int)} 1662 * may only be accessed via their resolved absolute verbs (ex. 1663 * {@link #LEFT_OF}). 1664 */ 1665 @Override resolveLayoutDirection(int layoutDirection)1666 public void resolveLayoutDirection(int layoutDirection) { 1667 if (shouldResolveLayoutDirection(layoutDirection)) { 1668 resolveRules(layoutDirection); 1669 } 1670 1671 // This will set the layout direction. 1672 super.resolveLayoutDirection(layoutDirection); 1673 } 1674 shouldResolveLayoutDirection(int layoutDirection)1675 private boolean shouldResolveLayoutDirection(int layoutDirection) { 1676 return (mNeedsLayoutResolution || hasRelativeRules()) 1677 && (mRulesChanged || layoutDirection != getLayoutDirection()); 1678 } 1679 1680 /** @hide */ 1681 @Override encodeProperties(@onNull ViewHierarchyEncoder encoder)1682 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 1683 super.encodeProperties(encoder); 1684 encoder.addProperty("layout:alignWithParent", alignWithParent); 1685 } 1686 } 1687 1688 private static class DependencyGraph { 1689 /** 1690 * List of all views in the graph. 1691 */ 1692 private ArrayList<Node> mNodes = new ArrayList<Node>(); 1693 1694 /** 1695 * List of nodes in the graph. Each node is identified by its 1696 * view id (see View#getId()). 1697 */ 1698 private SparseArray<Node> mKeyNodes = new SparseArray<Node>(); 1699 1700 /** 1701 * Temporary data structure used to build the list of roots 1702 * for this graph. 1703 */ 1704 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>(); 1705 1706 /** 1707 * Clears the graph. 1708 */ clear()1709 void clear() { 1710 final ArrayList<Node> nodes = mNodes; 1711 final int count = nodes.size(); 1712 1713 for (int i = 0; i < count; i++) { 1714 nodes.get(i).release(); 1715 } 1716 nodes.clear(); 1717 1718 mKeyNodes.clear(); 1719 mRoots.clear(); 1720 } 1721 1722 /** 1723 * Adds a view to the graph. 1724 * 1725 * @param view The view to be added as a node to the graph. 1726 */ add(View view)1727 void add(View view) { 1728 final int id = view.getId(); 1729 final Node node = Node.acquire(view); 1730 1731 if (id != View.NO_ID) { 1732 mKeyNodes.put(id, node); 1733 } 1734 1735 mNodes.add(node); 1736 } 1737 1738 /** 1739 * Builds a sorted list of views. The sorting order depends on the dependencies 1740 * between the view. For instance, if view C needs view A to be processed first 1741 * and view A needs view B to be processed first, the dependency graph 1742 * is: B -> A -> C. The sorted array will contain views B, A and C in this order. 1743 * 1744 * @param sorted The sorted list of views. The length of this array must 1745 * be equal to getChildCount(). 1746 * @param rules The list of rules to take into account. 1747 */ getSortedViews(View[] sorted, int... rules)1748 void getSortedViews(View[] sorted, int... rules) { 1749 final ArrayDeque<Node> roots = findRoots(rules); 1750 int index = 0; 1751 1752 Node node; 1753 while ((node = roots.pollLast()) != null) { 1754 final View view = node.view; 1755 final int key = view.getId(); 1756 1757 sorted[index++] = view; 1758 1759 final ArrayMap<Node, DependencyGraph> dependents = node.dependents; 1760 final int count = dependents.size(); 1761 for (int i = 0; i < count; i++) { 1762 final Node dependent = dependents.keyAt(i); 1763 final SparseArray<Node> dependencies = dependent.dependencies; 1764 1765 dependencies.remove(key); 1766 if (dependencies.size() == 0) { 1767 roots.add(dependent); 1768 } 1769 } 1770 } 1771 1772 if (index < sorted.length) { 1773 throw new IllegalStateException("Circular dependencies cannot exist" 1774 + " in RelativeLayout"); 1775 } 1776 } 1777 1778 /** 1779 * Finds the roots of the graph. A root is a node with no dependency and 1780 * with [0..n] dependents. 1781 * 1782 * @param rulesFilter The list of rules to consider when building the 1783 * dependencies 1784 * 1785 * @return A list of node, each being a root of the graph 1786 */ findRoots(int[] rulesFilter)1787 private ArrayDeque<Node> findRoots(int[] rulesFilter) { 1788 final SparseArray<Node> keyNodes = mKeyNodes; 1789 final ArrayList<Node> nodes = mNodes; 1790 final int count = nodes.size(); 1791 1792 // Find roots can be invoked several times, so make sure to clear 1793 // all dependents and dependencies before running the algorithm 1794 for (int i = 0; i < count; i++) { 1795 final Node node = nodes.get(i); 1796 node.dependents.clear(); 1797 node.dependencies.clear(); 1798 } 1799 1800 // Builds up the dependents and dependencies for each node of the graph 1801 for (int i = 0; i < count; i++) { 1802 final Node node = nodes.get(i); 1803 1804 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); 1805 final int[] rules = layoutParams.mRules; 1806 final int rulesCount = rulesFilter.length; 1807 1808 // Look only the the rules passed in parameter, this way we build only the 1809 // dependencies for a specific set of rules 1810 for (int j = 0; j < rulesCount; j++) { 1811 final int rule = rules[rulesFilter[j]]; 1812 if (rule > 0) { 1813 // The node this node depends on 1814 final Node dependency = keyNodes.get(rule); 1815 // Skip unknowns and self dependencies 1816 if (dependency == null || dependency == node) { 1817 continue; 1818 } 1819 // Add the current node as a dependent 1820 dependency.dependents.put(node, this); 1821 // Add a dependency to the current node 1822 node.dependencies.put(rule, dependency); 1823 } 1824 } 1825 } 1826 1827 final ArrayDeque<Node> roots = mRoots; 1828 roots.clear(); 1829 1830 // Finds all the roots in the graph: all nodes with no dependencies 1831 for (int i = 0; i < count; i++) { 1832 final Node node = nodes.get(i); 1833 if (node.dependencies.size() == 0) roots.addLast(node); 1834 } 1835 1836 return roots; 1837 } 1838 1839 /** 1840 * A node in the dependency graph. A node is a view, its list of dependencies 1841 * and its list of dependents. 1842 * 1843 * A node with no dependent is considered a root of the graph. 1844 */ 1845 static class Node { 1846 /** 1847 * The view representing this node in the layout. 1848 */ 1849 View view; 1850 1851 /** 1852 * The list of dependents for this node; a dependent is a node 1853 * that needs this node to be processed first. 1854 */ 1855 final ArrayMap<Node, DependencyGraph> dependents = 1856 new ArrayMap<Node, DependencyGraph>(); 1857 1858 /** 1859 * The list of dependencies for this node. 1860 */ 1861 final SparseArray<Node> dependencies = new SparseArray<Node>(); 1862 1863 /* 1864 * START POOL IMPLEMENTATION 1865 */ 1866 // The pool is static, so all nodes instances are shared across 1867 // activities, that's why we give it a rather high limit 1868 private static final int POOL_LIMIT = 100; 1869 private static final SynchronizedPool<Node> sPool = 1870 new SynchronizedPool<Node>(POOL_LIMIT); 1871 acquire(View view)1872 static Node acquire(View view) { 1873 Node node = sPool.acquire(); 1874 if (node == null) { 1875 node = new Node(); 1876 } 1877 node.view = view; 1878 return node; 1879 } 1880 release()1881 void release() { 1882 view = null; 1883 dependents.clear(); 1884 dependencies.clear(); 1885 1886 sPool.release(this); 1887 } 1888 /* 1889 * END POOL IMPLEMENTATION 1890 */ 1891 } 1892 } 1893 } 1894