RelativeLayout.java revision 13705eda8cc065ee98feb94af649615579cd4a58
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 myWidth = 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 myWidth += child.getMeasuredWidth(); 440 myWidth += params.leftMargin + params.rightMargin; 441 } 442 } 443 } 444 445 for (int i = 0; i < count; i++) { 446 View child = views[i]; 447 if (child.getVisibility() != GONE) { 448 LayoutParams params = (LayoutParams) child.getLayoutParams(); 449 450 applyHorizontalSizeRules(params, myWidth); 451 measureChildHorizontal(child, params, myWidth, myHeight); 452 if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { 453 offsetHorizontalAxis = true; 454 } 455 } 456 } 457 458 views = mSortedVerticalChildren; 459 count = views.length; 460 461 for (int i = 0; i < count; i++) { 462 View child = views[i]; 463 if (child.getVisibility() != GONE) { 464 LayoutParams params = (LayoutParams) child.getLayoutParams(); 465 466 applyVerticalSizeRules(params, myHeight); 467 measureChild(child, params, myWidth, myHeight); 468 if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) { 469 offsetVerticalAxis = true; 470 } 471 472 if (isWrapContentWidth) { 473 width = Math.max(width, params.mRight); 474 } 475 476 if (isWrapContentHeight) { 477 height = Math.max(height, params.mBottom); 478 } 479 480 if (child != ignore || verticalGravity) { 481 left = Math.min(left, params.mLeft - params.leftMargin); 482 top = Math.min(top, params.mTop - params.topMargin); 483 } 484 485 if (child != ignore || horizontalGravity) { 486 right = Math.max(right, params.mRight + params.rightMargin); 487 bottom = Math.max(bottom, params.mBottom + params.bottomMargin); 488 } 489 } 490 } 491 492 if (mHasBaselineAlignedChild) { 493 for (int i = 0; i < count; i++) { 494 View child = getChildAt(i); 495 if (child.getVisibility() != GONE) { 496 LayoutParams params = (LayoutParams) child.getLayoutParams(); 497 alignBaseline(child, params); 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 512 final int layoutDirection = getLayoutDirection(); 513 514 if (isWrapContentWidth) { 515 // Width already has left padding in it since it was calculated by looking at 516 // the right of each child view 517 width += mPaddingRight; 518 519 if (mLayoutParams.width >= 0) { 520 width = Math.max(width, mLayoutParams.width); 521 } 522 523 width = Math.max(width, getSuggestedMinimumWidth()); 524 width = resolveSize(width, widthMeasureSpec); 525 526 if (offsetHorizontalAxis) { 527 for (int i = 0; i < count; i++) { 528 View child = getChildAt(i); 529 if (child.getVisibility() != GONE) { 530 LayoutParams params = (LayoutParams) child.getLayoutParams(); 531 final int[] rules = params.getRules(layoutDirection); 532 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { 533 centerHorizontal(child, params, width); 534 } else if (rules[ALIGN_PARENT_RIGHT] != 0) { 535 final int childWidth = child.getMeasuredWidth(); 536 params.mLeft = width - mPaddingRight - childWidth; 537 params.mRight = params.mLeft + childWidth; 538 } 539 } 540 } 541 } 542 } 543 544 if (isWrapContentHeight) { 545 // Height already has top padding in it since it was calculated by looking at 546 // the bottom of each child view 547 height += mPaddingBottom; 548 549 if (mLayoutParams.height >= 0) { 550 height = Math.max(height, mLayoutParams.height); 551 } 552 553 height = Math.max(height, getSuggestedMinimumHeight()); 554 height = resolveSize(height, heightMeasureSpec); 555 556 if (offsetVerticalAxis) { 557 for (int i = 0; i < count; i++) { 558 View child = getChildAt(i); 559 if (child.getVisibility() != GONE) { 560 LayoutParams params = (LayoutParams) child.getLayoutParams(); 561 final int[] rules = params.getRules(layoutDirection); 562 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { 563 centerVertical(child, params, height); 564 } else if (rules[ALIGN_PARENT_BOTTOM] != 0) { 565 final int childHeight = child.getMeasuredHeight(); 566 params.mTop = height - mPaddingBottom - childHeight; 567 params.mBottom = params.mTop + childHeight; 568 } 569 } 570 } 571 } 572 } 573 574 if (horizontalGravity || verticalGravity) { 575 final Rect selfBounds = mSelfBounds; 576 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight, 577 height - mPaddingBottom); 578 579 final Rect contentBounds = mContentBounds; 580 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds, 581 layoutDirection); 582 583 final int horizontalOffset = contentBounds.left - left; 584 final int verticalOffset = contentBounds.top - top; 585 if (horizontalOffset != 0 || verticalOffset != 0) { 586 for (int i = 0; i < count; i++) { 587 View child = getChildAt(i); 588 if (child.getVisibility() != GONE && child != ignore) { 589 LayoutParams params = (LayoutParams) child.getLayoutParams(); 590 if (horizontalGravity) { 591 params.mLeft += horizontalOffset; 592 params.mRight += horizontalOffset; 593 } 594 if (verticalGravity) { 595 params.mTop += verticalOffset; 596 params.mBottom += verticalOffset; 597 } 598 } 599 } 600 } 601 } 602 603 setMeasuredDimension(width, height); 604 } 605 606 private void alignBaseline(View child, LayoutParams params) { 607 final int layoutDirection = getLayoutDirection(); 608 int[] rules = params.getRules(layoutDirection); 609 int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE); 610 611 if (anchorBaseline != -1) { 612 LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE); 613 if (anchorParams != null) { 614 int offset = anchorParams.mTop + anchorBaseline; 615 int baseline = child.getBaseline(); 616 if (baseline != -1) { 617 offset -= baseline; 618 } 619 int height = params.mBottom - params.mTop; 620 params.mTop = offset; 621 params.mBottom = params.mTop + height; 622 } 623 } 624 625 if (mBaselineView == null) { 626 mBaselineView = child; 627 } else { 628 LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams(); 629 if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) { 630 mBaselineView = child; 631 } 632 } 633 } 634 635 /** 636 * Measure a child. The child should have left, top, right and bottom information 637 * stored in its LayoutParams. If any of these values is -1 it means that the view 638 * can extend up to the corresponding edge. 639 * 640 * @param child Child to measure 641 * @param params LayoutParams associated with child 642 * @param myWidth Width of the the RelativeLayout 643 * @param myHeight Height of the RelativeLayout 644 */ 645 private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) { 646 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, 647 params.mRight, params.width, 648 params.leftMargin, params.rightMargin, 649 mPaddingLeft, mPaddingRight, 650 myWidth); 651 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop, 652 params.mBottom, params.height, 653 params.topMargin, params.bottomMargin, 654 mPaddingTop, mPaddingBottom, 655 myHeight); 656 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 657 } 658 659 private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) { 660 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, 661 params.mRight, params.width, 662 params.leftMargin, params.rightMargin, 663 mPaddingLeft, mPaddingRight, 664 myWidth); 665 int childHeightMeasureSpec; 666 if (params.width == LayoutParams.MATCH_PARENT) { 667 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); 668 } else { 669 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); 670 } 671 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 672 } 673 674 /** 675 * Get a measure spec that accounts for all of the constraints on this view. 676 * This includes size constraints imposed by the RelativeLayout as well as 677 * the View's desired dimension. 678 * 679 * @param childStart The left or top field of the child's layout params 680 * @param childEnd The right or bottom field of the child's layout params 681 * @param childSize The child's desired size (the width or height field of 682 * the child's layout params) 683 * @param startMargin The left or top margin 684 * @param endMargin The right or bottom margin 685 * @param startPadding mPaddingLeft or mPaddingTop 686 * @param endPadding mPaddingRight or mPaddingBottom 687 * @param mySize The width or height of this view (the RelativeLayout) 688 * @return MeasureSpec for the child 689 */ 690 private int getChildMeasureSpec(int childStart, int childEnd, 691 int childSize, int startMargin, int endMargin, int startPadding, 692 int endPadding, int mySize) { 693 int childSpecMode = 0; 694 int childSpecSize = 0; 695 696 // Figure out start and end bounds. 697 int tempStart = childStart; 698 int tempEnd = childEnd; 699 700 // If the view did not express a layout constraint for an edge, use 701 // view's margins and our padding 702 if (tempStart < 0) { 703 tempStart = startPadding + startMargin; 704 } 705 if (tempEnd < 0) { 706 tempEnd = mySize - endPadding - endMargin; 707 } 708 709 // Figure out maximum size available to this view 710 int maxAvailable = tempEnd - tempStart; 711 712 if (childStart >= 0 && childEnd >= 0) { 713 // Constraints fixed both edges, so child must be an exact size 714 childSpecMode = MeasureSpec.EXACTLY; 715 childSpecSize = maxAvailable; 716 } else { 717 if (childSize >= 0) { 718 // Child wanted an exact size. Give as much as possible 719 childSpecMode = MeasureSpec.EXACTLY; 720 721 if (maxAvailable >= 0) { 722 // We have a maxmum size in this dimension. 723 childSpecSize = Math.min(maxAvailable, childSize); 724 } else { 725 // We can grow in this dimension. 726 childSpecSize = childSize; 727 } 728 } else if (childSize == LayoutParams.MATCH_PARENT) { 729 // Child wanted to be as big as possible. Give all available 730 // space 731 childSpecMode = MeasureSpec.EXACTLY; 732 childSpecSize = maxAvailable; 733 } else if (childSize == LayoutParams.WRAP_CONTENT) { 734 // Child wants to wrap content. Use AT_MOST 735 // to communicate available space if we know 736 // our max size 737 if (maxAvailable >= 0) { 738 // We have a maximum size in this dimension. 739 childSpecMode = MeasureSpec.AT_MOST; 740 childSpecSize = maxAvailable; 741 } else { 742 // We can grow in this dimension. Child can be as big as it 743 // wants 744 childSpecMode = MeasureSpec.UNSPECIFIED; 745 childSpecSize = 0; 746 } 747 } 748 } 749 750 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); 751 } 752 753 private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth, 754 boolean wrapContent) { 755 756 final int layoutDirection = getLayoutDirection(); 757 int[] rules = params.getRules(layoutDirection); 758 759 if (params.mLeft < 0 && params.mRight >= 0) { 760 // Right is fixed, but left varies 761 params.mLeft = params.mRight - child.getMeasuredWidth(); 762 } else if (params.mLeft >= 0 && params.mRight < 0) { 763 // Left is fixed, but right varies 764 params.mRight = params.mLeft + child.getMeasuredWidth(); 765 } else if (params.mLeft < 0 && params.mRight < 0) { 766 // Both left and right vary 767 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { 768 if (!wrapContent) { 769 centerHorizontal(child, params, myWidth); 770 } else { 771 params.mLeft = mPaddingLeft + params.leftMargin; 772 params.mRight = params.mLeft + child.getMeasuredWidth(); 773 } 774 return true; 775 } else { 776 // This is the default case. For RTL we start from the right and for LTR we start 777 // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL. 778 if (isLayoutRtl()) { 779 params.mRight = myWidth - mPaddingRight- params.rightMargin; 780 params.mLeft = params.mRight - child.getMeasuredWidth(); 781 } else { 782 params.mLeft = mPaddingLeft + params.leftMargin; 783 params.mRight = params.mLeft + child.getMeasuredWidth(); 784 } 785 } 786 } 787 return rules[ALIGN_PARENT_END] != 0; 788 } 789 790 private boolean positionChildVertical(View child, LayoutParams params, int myHeight, 791 boolean wrapContent) { 792 793 int[] rules = params.getRules(); 794 795 if (params.mTop < 0 && params.mBottom >= 0) { 796 // Bottom is fixed, but top varies 797 params.mTop = params.mBottom - child.getMeasuredHeight(); 798 } else if (params.mTop >= 0 && params.mBottom < 0) { 799 // Top is fixed, but bottom varies 800 params.mBottom = params.mTop + child.getMeasuredHeight(); 801 } else if (params.mTop < 0 && params.mBottom < 0) { 802 // Both top and bottom vary 803 if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { 804 if (!wrapContent) { 805 centerVertical(child, params, myHeight); 806 } else { 807 params.mTop = mPaddingTop + params.topMargin; 808 params.mBottom = params.mTop + child.getMeasuredHeight(); 809 } 810 return true; 811 } else { 812 params.mTop = mPaddingTop + params.topMargin; 813 params.mBottom = params.mTop + child.getMeasuredHeight(); 814 } 815 } 816 return rules[ALIGN_PARENT_BOTTOM] != 0; 817 } 818 819 private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) { 820 final int layoutDirection = getLayoutDirection(); 821 int[] rules = childParams.getRules(layoutDirection); 822 RelativeLayout.LayoutParams anchorParams; 823 824 // -1 indicated a "soft requirement" in that direction. For example: 825 // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right 826 // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left 827 // left=10, right=20 means the left and right ends are both fixed 828 childParams.mLeft = -1; 829 childParams.mRight = -1; 830 831 anchorParams = getRelatedViewParams(rules, LEFT_OF); 832 if (anchorParams != null) { 833 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin + 834 childParams.rightMargin); 835 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) { 836 if (myWidth >= 0) { 837 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; 838 } else { 839 // FIXME uh oh... 840 } 841 } 842 843 anchorParams = getRelatedViewParams(rules, RIGHT_OF); 844 if (anchorParams != null) { 845 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin + 846 childParams.leftMargin); 847 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) { 848 childParams.mLeft = mPaddingLeft + childParams.leftMargin; 849 } 850 851 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT); 852 if (anchorParams != null) { 853 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin; 854 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) { 855 childParams.mLeft = mPaddingLeft + childParams.leftMargin; 856 } 857 858 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT); 859 if (anchorParams != null) { 860 childParams.mRight = anchorParams.mRight - childParams.rightMargin; 861 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) { 862 if (myWidth >= 0) { 863 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; 864 } else { 865 // FIXME uh oh... 866 } 867 } 868 869 if (0 != rules[ALIGN_PARENT_LEFT]) { 870 childParams.mLeft = mPaddingLeft + childParams.leftMargin; 871 } 872 873 if (0 != rules[ALIGN_PARENT_RIGHT]) { 874 if (myWidth >= 0) { 875 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; 876 } else { 877 // FIXME uh oh... 878 } 879 } 880 } 881 882 private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) { 883 int[] rules = childParams.getRules(); 884 RelativeLayout.LayoutParams anchorParams; 885 886 childParams.mTop = -1; 887 childParams.mBottom = -1; 888 889 anchorParams = getRelatedViewParams(rules, ABOVE); 890 if (anchorParams != null) { 891 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin + 892 childParams.bottomMargin); 893 } else if (childParams.alignWithParent && rules[ABOVE] != 0) { 894 if (myHeight >= 0) { 895 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; 896 } else { 897 // FIXME uh oh... 898 } 899 } 900 901 anchorParams = getRelatedViewParams(rules, BELOW); 902 if (anchorParams != null) { 903 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin + 904 childParams.topMargin); 905 } else if (childParams.alignWithParent && rules[BELOW] != 0) { 906 childParams.mTop = mPaddingTop + childParams.topMargin; 907 } 908 909 anchorParams = getRelatedViewParams(rules, ALIGN_TOP); 910 if (anchorParams != null) { 911 childParams.mTop = anchorParams.mTop + childParams.topMargin; 912 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) { 913 childParams.mTop = mPaddingTop + childParams.topMargin; 914 } 915 916 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM); 917 if (anchorParams != null) { 918 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin; 919 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) { 920 if (myHeight >= 0) { 921 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; 922 } else { 923 // FIXME uh oh... 924 } 925 } 926 927 if (0 != rules[ALIGN_PARENT_TOP]) { 928 childParams.mTop = mPaddingTop + childParams.topMargin; 929 } 930 931 if (0 != rules[ALIGN_PARENT_BOTTOM]) { 932 if (myHeight >= 0) { 933 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; 934 } else { 935 // FIXME uh oh... 936 } 937 } 938 939 if (rules[ALIGN_BASELINE] != 0) { 940 mHasBaselineAlignedChild = true; 941 } 942 } 943 944 private View getRelatedView(int[] rules, int relation) { 945 int id = rules[relation]; 946 if (id != 0) { 947 DependencyGraph.Node node = mGraph.mKeyNodes.get(id); 948 if (node == null) return null; 949 View v = node.view; 950 951 // Find the first non-GONE view up the chain 952 while (v.getVisibility() == View.GONE) { 953 rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection()); 954 node = mGraph.mKeyNodes.get((rules[relation])); 955 if (node == null) return null; 956 v = node.view; 957 } 958 959 return v; 960 } 961 962 return null; 963 } 964 965 private LayoutParams getRelatedViewParams(int[] rules, int relation) { 966 View v = getRelatedView(rules, relation); 967 if (v != null) { 968 ViewGroup.LayoutParams params = v.getLayoutParams(); 969 if (params instanceof LayoutParams) { 970 return (LayoutParams) v.getLayoutParams(); 971 } 972 } 973 return null; 974 } 975 976 private int getRelatedViewBaseline(int[] rules, int relation) { 977 View v = getRelatedView(rules, relation); 978 if (v != null) { 979 return v.getBaseline(); 980 } 981 return -1; 982 } 983 984 private void centerHorizontal(View child, LayoutParams params, int myWidth) { 985 int childWidth = child.getMeasuredWidth(); 986 int left = (myWidth - childWidth) / 2; 987 988 params.mLeft = left; 989 params.mRight = left + childWidth; 990 } 991 992 private void centerVertical(View child, LayoutParams params, int myHeight) { 993 int childHeight = child.getMeasuredHeight(); 994 int top = (myHeight - childHeight) / 2; 995 996 params.mTop = top; 997 params.mBottom = top + childHeight; 998 } 999 1000 @Override 1001 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1002 // The layout has actually already been performed and the positions 1003 // cached. Apply the cached values to the children. 1004 final int count = getChildCount(); 1005 1006 for (int i = 0; i < count; i++) { 1007 View child = getChildAt(i); 1008 if (child.getVisibility() != GONE) { 1009 RelativeLayout.LayoutParams st = 1010 (RelativeLayout.LayoutParams) child.getLayoutParams(); 1011 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom); 1012 } 1013 } 1014 } 1015 1016 @Override 1017 public LayoutParams generateLayoutParams(AttributeSet attrs) { 1018 return new RelativeLayout.LayoutParams(getContext(), attrs); 1019 } 1020 1021 /** 1022 * Returns a set of layout parameters with a width of 1023 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, 1024 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning. 1025 */ 1026 @Override 1027 protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 1028 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 1029 } 1030 1031 // Override to allow type-checking of LayoutParams. 1032 @Override 1033 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1034 return p instanceof RelativeLayout.LayoutParams; 1035 } 1036 1037 @Override 1038 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1039 return new LayoutParams(p); 1040 } 1041 1042 @Override 1043 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 1044 if (mTopToBottomLeftToRightSet == null) { 1045 mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator()); 1046 } 1047 1048 // sort children top-to-bottom and left-to-right 1049 for (int i = 0, count = getChildCount(); i < count; i++) { 1050 mTopToBottomLeftToRightSet.add(getChildAt(i)); 1051 } 1052 1053 for (View view : mTopToBottomLeftToRightSet) { 1054 if (view.getVisibility() == View.VISIBLE 1055 && view.dispatchPopulateAccessibilityEvent(event)) { 1056 mTopToBottomLeftToRightSet.clear(); 1057 return true; 1058 } 1059 } 1060 1061 mTopToBottomLeftToRightSet.clear(); 1062 return false; 1063 } 1064 1065 @Override 1066 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1067 super.onInitializeAccessibilityEvent(event); 1068 event.setClassName(RelativeLayout.class.getName()); 1069 } 1070 1071 @Override 1072 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1073 super.onInitializeAccessibilityNodeInfo(info); 1074 info.setClassName(RelativeLayout.class.getName()); 1075 } 1076 1077 /** 1078 * Compares two views in left-to-right and top-to-bottom fashion. 1079 */ 1080 private class TopToBottomLeftToRightComparator implements Comparator<View> { 1081 public int compare(View first, View second) { 1082 // top - bottom 1083 int topDifference = first.getTop() - second.getTop(); 1084 if (topDifference != 0) { 1085 return topDifference; 1086 } 1087 // left - right 1088 int leftDifference = first.getLeft() - second.getLeft(); 1089 if (leftDifference != 0) { 1090 return leftDifference; 1091 } 1092 // break tie by height 1093 int heightDiference = first.getHeight() - second.getHeight(); 1094 if (heightDiference != 0) { 1095 return heightDiference; 1096 } 1097 // break tie by width 1098 int widthDiference = first.getWidth() - second.getWidth(); 1099 if (widthDiference != 0) { 1100 return widthDiference; 1101 } 1102 return 0; 1103 } 1104 } 1105 1106 /** 1107 * Per-child layout information associated with RelativeLayout. 1108 * 1109 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing 1110 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf 1111 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf 1112 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above 1113 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below 1114 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline 1115 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft 1116 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop 1117 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight 1118 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom 1119 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft 1120 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop 1121 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight 1122 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom 1123 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent 1124 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal 1125 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical 1126 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toStartOf 1127 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toEndOf 1128 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignStart 1129 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignEnd 1130 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentStart 1131 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentEnd 1132 */ 1133 public static class LayoutParams extends ViewGroup.MarginLayoutParams { 1134 @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = { 1135 @ViewDebug.IntToString(from = ABOVE, to = "above"), 1136 @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"), 1137 @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"), 1138 @ViewDebug.IntToString(from = ALIGN_LEFT, to = "alignLeft"), 1139 @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"), 1140 @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT, to = "alignParentLeft"), 1141 @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT, to = "alignParentRight"), 1142 @ViewDebug.IntToString(from = ALIGN_PARENT_TOP, to = "alignParentTop"), 1143 @ViewDebug.IntToString(from = ALIGN_RIGHT, to = "alignRight"), 1144 @ViewDebug.IntToString(from = ALIGN_TOP, to = "alignTop"), 1145 @ViewDebug.IntToString(from = BELOW, to = "below"), 1146 @ViewDebug.IntToString(from = CENTER_HORIZONTAL, to = "centerHorizontal"), 1147 @ViewDebug.IntToString(from = CENTER_IN_PARENT, to = "center"), 1148 @ViewDebug.IntToString(from = CENTER_VERTICAL, to = "centerVertical"), 1149 @ViewDebug.IntToString(from = LEFT_OF, to = "leftOf"), 1150 @ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf"), 1151 @ViewDebug.IntToString(from = ALIGN_START, to = "alignStart"), 1152 @ViewDebug.IntToString(from = ALIGN_END, to = "alignEnd"), 1153 @ViewDebug.IntToString(from = ALIGN_PARENT_START, to = "alignParentStart"), 1154 @ViewDebug.IntToString(from = ALIGN_PARENT_END, to = "alignParentEnd"), 1155 @ViewDebug.IntToString(from = START_OF, to = "startOf"), 1156 @ViewDebug.IntToString(from = END_OF, to = "endOf") 1157 }, mapping = { 1158 @ViewDebug.IntToString(from = TRUE, to = "true"), 1159 @ViewDebug.IntToString(from = 0, to = "false/NO_ID") 1160 }) 1161 1162 private int[] mRules = new int[VERB_COUNT]; 1163 private int[] mInitialRules = new int[VERB_COUNT]; 1164 1165 private int mLeft, mTop, mRight, mBottom; 1166 1167 private int mStart = DEFAULT_RELATIVE; 1168 private int mEnd = DEFAULT_RELATIVE; 1169 1170 private boolean mRulesChanged = false; 1171 1172 /** 1173 * When true, uses the parent as the anchor if the anchor doesn't exist or if 1174 * the anchor's visibility is GONE. 1175 */ 1176 @ViewDebug.ExportedProperty(category = "layout") 1177 public boolean alignWithParent; 1178 1179 public LayoutParams(Context c, AttributeSet attrs) { 1180 super(c, attrs); 1181 1182 TypedArray a = c.obtainStyledAttributes(attrs, 1183 com.android.internal.R.styleable.RelativeLayout_Layout); 1184 1185 final int[] rules = mRules; 1186 final int[] initialRules = mInitialRules; 1187 1188 final int N = a.getIndexCount(); 1189 for (int i = 0; i < N; i++) { 1190 int attr = a.getIndex(i); 1191 switch (attr) { 1192 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing: 1193 alignWithParent = a.getBoolean(attr, false); 1194 break; 1195 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf: 1196 rules[LEFT_OF] = a.getResourceId(attr, 0); 1197 break; 1198 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf: 1199 rules[RIGHT_OF] = a.getResourceId(attr, 0); 1200 break; 1201 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above: 1202 rules[ABOVE] = a.getResourceId(attr, 0); 1203 break; 1204 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below: 1205 rules[BELOW] = a.getResourceId(attr, 0); 1206 break; 1207 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline: 1208 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0); 1209 break; 1210 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft: 1211 rules[ALIGN_LEFT] = a.getResourceId(attr, 0); 1212 break; 1213 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop: 1214 rules[ALIGN_TOP] = a.getResourceId(attr, 0); 1215 break; 1216 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight: 1217 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0); 1218 break; 1219 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom: 1220 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0); 1221 break; 1222 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft: 1223 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0; 1224 break; 1225 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop: 1226 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0; 1227 break; 1228 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight: 1229 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0; 1230 break; 1231 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom: 1232 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0; 1233 break; 1234 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent: 1235 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0; 1236 break; 1237 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal: 1238 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0; 1239 break; 1240 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical: 1241 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0; 1242 break; 1243 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf: 1244 rules[START_OF] = a.getResourceId(attr, 0); 1245 break; 1246 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf: 1247 rules[END_OF] = a.getResourceId(attr, 0); 1248 break; 1249 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart: 1250 rules[ALIGN_START] = a.getResourceId(attr, 0); 1251 break; 1252 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd: 1253 rules[ALIGN_END] = a.getResourceId(attr, 0); 1254 break; 1255 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart: 1256 rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0; 1257 break; 1258 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd: 1259 rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0; 1260 break; 1261 } 1262 } 1263 1264 for (int n = LEFT_OF; n < VERB_COUNT; n++) { 1265 initialRules[n] = rules[n]; 1266 } 1267 1268 a.recycle(); 1269 } 1270 1271 public LayoutParams(int w, int h) { 1272 super(w, h); 1273 } 1274 1275 /** 1276 * {@inheritDoc} 1277 */ 1278 public LayoutParams(ViewGroup.LayoutParams source) { 1279 super(source); 1280 } 1281 1282 /** 1283 * {@inheritDoc} 1284 */ 1285 public LayoutParams(ViewGroup.MarginLayoutParams source) { 1286 super(source); 1287 } 1288 1289 @Override 1290 public String debug(String output) { 1291 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) + 1292 ", height=" + sizeToString(height) + " }"; 1293 } 1294 1295 /** 1296 * Adds a layout rule to be interpreted by the RelativeLayout. This 1297 * method should only be used for constraints that don't refer to another sibling 1298 * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE} 1299 * for true or 0 for false). To specify a verb that takes a subject, use 1300 * {@link #addRule(int, int)} instead. 1301 * 1302 * @param verb One of the verbs defined by 1303 * {@link android.widget.RelativeLayout RelativeLayout}, such as 1304 * ALIGN_WITH_PARENT_LEFT. 1305 * @see #addRule(int, int) 1306 */ 1307 public void addRule(int verb) { 1308 mRules[verb] = TRUE; 1309 mInitialRules[verb] = TRUE; 1310 mRulesChanged = true; 1311 } 1312 1313 /** 1314 * Adds a layout rule to be interpreted by the RelativeLayout. Use this for 1315 * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean 1316 * value (VISIBLE). 1317 * 1318 * @param verb One of the verbs defined by 1319 * {@link android.widget.RelativeLayout RelativeLayout}, such as 1320 * ALIGN_WITH_PARENT_LEFT. 1321 * @param anchor The id of another view to use as an anchor, 1322 * or a boolean value(represented as {@link RelativeLayout#TRUE}) 1323 * for true or 0 for false). For verbs that don't refer to another sibling 1324 * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1. 1325 * @see #addRule(int) 1326 */ 1327 public void addRule(int verb, int anchor) { 1328 mRules[verb] = anchor; 1329 mInitialRules[verb] = anchor; 1330 mRulesChanged = true; 1331 } 1332 1333 /** 1334 * Removes a layout rule to be interpreted by the RelativeLayout. 1335 * 1336 * @param verb One of the verbs defined by 1337 * {@link android.widget.RelativeLayout RelativeLayout}, such as 1338 * ALIGN_WITH_PARENT_LEFT. 1339 * @see #addRule(int) 1340 * @see #addRule(int, int) 1341 */ 1342 public void removeRule(int verb) { 1343 mRules[verb] = 0; 1344 mInitialRules[verb] = 0; 1345 mRulesChanged = true; 1346 } 1347 1348 private boolean hasRelativeRules() { 1349 return (mInitialRules[START_OF] != 0 || mInitialRules[END_OF] != 0 || 1350 mInitialRules[ALIGN_START] != 0 || mInitialRules[ALIGN_END] != 0 || 1351 mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0); 1352 } 1353 1354 private void resolveRules(int layoutDirection) { 1355 final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL); 1356 // Reset to initial state 1357 for (int n = LEFT_OF; n < VERB_COUNT; n++) { 1358 mRules[n] = mInitialRules[n]; 1359 } 1360 // Apply rules depending on direction 1361 if (mRules[ALIGN_START] != 0) { 1362 mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START]; 1363 } 1364 if (mRules[ALIGN_END] != 0) { 1365 mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END]; 1366 } 1367 if (mRules[START_OF] != 0) { 1368 mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF]; 1369 } 1370 if (mRules[END_OF] != 0) { 1371 mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF]; 1372 } 1373 if (mRules[ALIGN_PARENT_START] != 0) { 1374 mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; 1375 } 1376 if (mRules[ALIGN_PARENT_END] != 0) { 1377 mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; 1378 } 1379 mRulesChanged = false; 1380 } 1381 1382 /** 1383 * Retrieves a complete list of all supported rules, where the index is the rule 1384 * verb, and the element value is the value specified, or "false" if it was never 1385 * set. If there are relative rules defined (*_START / *_END), they will be resolved 1386 * depending on the layout direction. 1387 * 1388 * @param layoutDirection the direction of the layout. 1389 * Should be either {@link View#LAYOUT_DIRECTION_LTR} 1390 * or {@link View#LAYOUT_DIRECTION_RTL} 1391 * @return the supported rules 1392 * @see #addRule(int, int) 1393 * 1394 * @hide 1395 */ 1396 public int[] getRules(int layoutDirection) { 1397 if (hasRelativeRules() && 1398 (mRulesChanged || layoutDirection != getLayoutDirection())) { 1399 resolveRules(layoutDirection); 1400 if (layoutDirection != getLayoutDirection()) { 1401 setLayoutDirection(layoutDirection); 1402 } 1403 } 1404 return mRules; 1405 } 1406 1407 /** 1408 * Retrieves a complete list of all supported rules, where the index is the rule 1409 * verb, and the element value is the value specified, or "false" if it was never 1410 * set. There will be no resolution of relative rules done. 1411 * 1412 * @return the supported rules 1413 * @see #addRule(int, int) 1414 */ 1415 public int[] getRules() { 1416 return mRules; 1417 } 1418 1419 @Override 1420 public void resolveLayoutDirection(int layoutDirection) { 1421 final boolean isLayoutRtl = isLayoutRtl(); 1422 if (isLayoutRtl) { 1423 if (mStart != DEFAULT_RELATIVE) mRight = mStart; 1424 if (mEnd != DEFAULT_RELATIVE) mLeft = mEnd; 1425 } else { 1426 if (mStart != DEFAULT_RELATIVE) mLeft = mStart; 1427 if (mEnd != DEFAULT_RELATIVE) mRight = mEnd; 1428 } 1429 1430 if (hasRelativeRules() && layoutDirection != getLayoutDirection()) { 1431 resolveRules(layoutDirection); 1432 } 1433 // This will set the layout direction 1434 super.resolveLayoutDirection(layoutDirection); 1435 } 1436 } 1437 1438 private static class DependencyGraph { 1439 /** 1440 * List of all views in the graph. 1441 */ 1442 private ArrayList<Node> mNodes = new ArrayList<Node>(); 1443 1444 /** 1445 * List of nodes in the graph. Each node is identified by its 1446 * view id (see View#getId()). 1447 */ 1448 private SparseArray<Node> mKeyNodes = new SparseArray<Node>(); 1449 1450 /** 1451 * Temporary data structure used to build the list of roots 1452 * for this graph. 1453 */ 1454 private ArrayDeque<Node> mRoots = new ArrayDeque<Node>(); 1455 1456 /** 1457 * Clears the graph. 1458 */ 1459 void clear() { 1460 final ArrayList<Node> nodes = mNodes; 1461 final int count = nodes.size(); 1462 1463 for (int i = 0; i < count; i++) { 1464 nodes.get(i).release(); 1465 } 1466 nodes.clear(); 1467 1468 mKeyNodes.clear(); 1469 mRoots.clear(); 1470 } 1471 1472 /** 1473 * Adds a view to the graph. 1474 * 1475 * @param view The view to be added as a node to the graph. 1476 */ 1477 void add(View view) { 1478 final int id = view.getId(); 1479 final Node node = Node.acquire(view); 1480 1481 if (id != View.NO_ID) { 1482 mKeyNodes.put(id, node); 1483 } 1484 1485 mNodes.add(node); 1486 } 1487 1488 /** 1489 * Builds a sorted list of views. The sorting order depends on the dependencies 1490 * between the view. For instance, if view C needs view A to be processed first 1491 * and view A needs view B to be processed first, the dependency graph 1492 * is: B -> A -> C. The sorted array will contain views B, A and C in this order. 1493 * 1494 * @param sorted The sorted list of views. The length of this array must 1495 * be equal to getChildCount(). 1496 * @param rules The list of rules to take into account. 1497 */ 1498 void getSortedViews(View[] sorted, int... rules) { 1499 final ArrayDeque<Node> roots = findRoots(rules); 1500 int index = 0; 1501 1502 Node node; 1503 while ((node = roots.pollLast()) != null) { 1504 final View view = node.view; 1505 final int key = view.getId(); 1506 1507 sorted[index++] = view; 1508 1509 final HashMap<Node, DependencyGraph> dependents = node.dependents; 1510 for (Node dependent : dependents.keySet()) { 1511 final SparseArray<Node> dependencies = dependent.dependencies; 1512 1513 dependencies.remove(key); 1514 if (dependencies.size() == 0) { 1515 roots.add(dependent); 1516 } 1517 } 1518 } 1519 1520 if (index < sorted.length) { 1521 throw new IllegalStateException("Circular dependencies cannot exist" 1522 + " in RelativeLayout"); 1523 } 1524 } 1525 1526 /** 1527 * Finds the roots of the graph. A root is a node with no dependency and 1528 * with [0..n] dependents. 1529 * 1530 * @param rulesFilter The list of rules to consider when building the 1531 * dependencies 1532 * 1533 * @return A list of node, each being a root of the graph 1534 */ 1535 private ArrayDeque<Node> findRoots(int[] rulesFilter) { 1536 final SparseArray<Node> keyNodes = mKeyNodes; 1537 final ArrayList<Node> nodes = mNodes; 1538 final int count = nodes.size(); 1539 1540 // Find roots can be invoked several times, so make sure to clear 1541 // all dependents and dependencies before running the algorithm 1542 for (int i = 0; i < count; i++) { 1543 final Node node = nodes.get(i); 1544 node.dependents.clear(); 1545 node.dependencies.clear(); 1546 } 1547 1548 // Builds up the dependents and dependencies for each node of the graph 1549 for (int i = 0; i < count; i++) { 1550 final Node node = nodes.get(i); 1551 1552 final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); 1553 final int[] rules = layoutParams.mRules; 1554 final int rulesCount = rulesFilter.length; 1555 1556 // Look only the the rules passed in parameter, this way we build only the 1557 // dependencies for a specific set of rules 1558 for (int j = 0; j < rulesCount; j++) { 1559 final int rule = rules[rulesFilter[j]]; 1560 if (rule > 0) { 1561 // The node this node depends on 1562 final Node dependency = keyNodes.get(rule); 1563 // Skip unknowns and self dependencies 1564 if (dependency == null || dependency == node) { 1565 continue; 1566 } 1567 // Add the current node as a dependent 1568 dependency.dependents.put(node, this); 1569 // Add a dependency to the current node 1570 node.dependencies.put(rule, dependency); 1571 } 1572 } 1573 } 1574 1575 final ArrayDeque<Node> roots = mRoots; 1576 roots.clear(); 1577 1578 // Finds all the roots in the graph: all nodes with no dependencies 1579 for (int i = 0; i < count; i++) { 1580 final Node node = nodes.get(i); 1581 if (node.dependencies.size() == 0) roots.addLast(node); 1582 } 1583 1584 return roots; 1585 } 1586 1587 /** 1588 * Prints the dependency graph for the specified rules. 1589 * 1590 * @param resources The context's resources to print the ids. 1591 * @param rules The list of rules to take into account. 1592 */ 1593 void log(Resources resources, int... rules) { 1594 final ArrayDeque<Node> roots = findRoots(rules); 1595 for (Node node : roots) { 1596 printNode(resources, node); 1597 } 1598 } 1599 1600 static void printViewId(Resources resources, View view) { 1601 if (view.getId() != View.NO_ID) { 1602 d(LOG_TAG, resources.getResourceEntryName(view.getId())); 1603 } else { 1604 d(LOG_TAG, "NO_ID"); 1605 } 1606 } 1607 1608 private static void appendViewId(Resources resources, Node node, StringBuilder buffer) { 1609 if (node.view.getId() != View.NO_ID) { 1610 buffer.append(resources.getResourceEntryName(node.view.getId())); 1611 } else { 1612 buffer.append("NO_ID"); 1613 } 1614 } 1615 1616 private static void printNode(Resources resources, Node node) { 1617 if (node.dependents.size() == 0) { 1618 printViewId(resources, node.view); 1619 } else { 1620 for (Node dependent : node.dependents.keySet()) { 1621 StringBuilder buffer = new StringBuilder(); 1622 appendViewId(resources, node, buffer); 1623 printdependents(resources, dependent, buffer); 1624 } 1625 } 1626 } 1627 1628 private static void printdependents(Resources resources, Node node, StringBuilder buffer) { 1629 buffer.append(" -> "); 1630 appendViewId(resources, node, buffer); 1631 1632 if (node.dependents.size() == 0) { 1633 d(LOG_TAG, buffer.toString()); 1634 } else { 1635 for (Node dependent : node.dependents.keySet()) { 1636 StringBuilder subBuffer = new StringBuilder(buffer); 1637 printdependents(resources, dependent, subBuffer); 1638 } 1639 } 1640 } 1641 1642 /** 1643 * A node in the dependency graph. A node is a view, its list of dependencies 1644 * and its list of dependents. 1645 * 1646 * A node with no dependent is considered a root of the graph. 1647 */ 1648 static class Node implements Poolable<Node> { 1649 /** 1650 * The view representing this node in the layout. 1651 */ 1652 View view; 1653 1654 /** 1655 * The list of dependents for this node; a dependent is a node 1656 * that needs this node to be processed first. 1657 */ 1658 final HashMap<Node, DependencyGraph> dependents = new HashMap<Node, DependencyGraph>(); 1659 1660 /** 1661 * The list of dependencies for this node. 1662 */ 1663 final SparseArray<Node> dependencies = new SparseArray<Node>(); 1664 1665 /* 1666 * START POOL IMPLEMENTATION 1667 */ 1668 // The pool is static, so all nodes instances are shared across 1669 // activities, that's why we give it a rather high limit 1670 private static final int POOL_LIMIT = 100; 1671 private static final Pool<Node> sPool = Pools.synchronizedPool( 1672 Pools.finitePool(new PoolableManager<Node>() { 1673 public Node newInstance() { 1674 return new Node(); 1675 } 1676 1677 public void onAcquired(Node element) { 1678 } 1679 1680 public void onReleased(Node element) { 1681 } 1682 }, POOL_LIMIT) 1683 ); 1684 1685 private Node mNext; 1686 private boolean mIsPooled; 1687 1688 public void setNextPoolable(Node element) { 1689 mNext = element; 1690 } 1691 1692 public Node getNextPoolable() { 1693 return mNext; 1694 } 1695 1696 public boolean isPooled() { 1697 return mIsPooled; 1698 } 1699 1700 public void setPooled(boolean isPooled) { 1701 mIsPooled = isPooled; 1702 } 1703 1704 static Node acquire(View view) { 1705 final Node node = sPool.acquire(); 1706 node.view = view; 1707 1708 return node; 1709 } 1710 1711 void release() { 1712 view = null; 1713 dependents.clear(); 1714 dependencies.clear(); 1715 1716 sPool.release(this); 1717 } 1718 /* 1719 * END POOL IMPLEMENTATION 1720 */ 1721 } 1722 } 1723} 1724