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