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