RelativeLayout.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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 android.content.Context; 20import android.content.res.TypedArray; 21import android.util.AttributeSet; 22import android.view.View; 23import android.view.ViewGroup; 24import android.view.Gravity; 25import android.widget.RemoteViews.RemoteView; 26import android.graphics.Rect; 27import com.android.internal.R; 28 29 30/** 31 * A Layout where the positions of the children can be described in relation to each other or to the 32 * parent. For the sake of efficiency, the relations between views are evaluated in one pass, so if 33 * view Y is dependent on the position of view X, make sure the view X comes first in the layout. 34 * 35 * <p> 36 * Note that you cannot have a circular dependency between the size of the RelativeLayout and the 37 * position of its children. For example, you cannot have a RelativeLayout whose height is set to 38 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT WRAP_CONTENT} and a child set to 39 * {@link #ALIGN_PARENT_BOTTOM}. 40 * </p> 41 * 42 * <p> 43 * Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for 44 * layout attributes 45 * </p> 46 * 47 * @attr ref android.R.styleable#RelativeLayout_gravity 48 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity 49 */ 50@RemoteView 51public class RelativeLayout extends ViewGroup { 52 public static final int TRUE = -1; 53 54 /** 55 * Rule that aligns a child's right edge with another child's left edge. 56 */ 57 public static final int LEFT_OF = 0; 58 /** 59 * Rule that aligns a child's left edge with another child's right edge. 60 */ 61 public static final int RIGHT_OF = 1; 62 /** 63 * Rule that aligns a child's bottom edge with another child's top edge. 64 */ 65 public static final int ABOVE = 2; 66 /** 67 * Rule that aligns a child's top edge with another child's bottom edge. 68 */ 69 public static final int BELOW = 3; 70 71 /** 72 * Rule that aligns a child's baseline with another child's baseline. 73 */ 74 public static final int ALIGN_BASELINE = 4; 75 /** 76 * Rule that aligns a child's left edge with another child's left edge. 77 */ 78 public static final int ALIGN_LEFT = 5; 79 /** 80 * Rule that aligns a child's top edge with another child's top edge. 81 */ 82 public static final int ALIGN_TOP = 6; 83 /** 84 * Rule that aligns a child's right edge with another child's right edge. 85 */ 86 public static final int ALIGN_RIGHT = 7; 87 /** 88 * Rule that aligns a child's bottom edge with another child's bottom edge. 89 */ 90 public static final int ALIGN_BOTTOM = 8; 91 92 /** 93 * Rule that aligns the child's left edge with its RelativeLayout 94 * parent's left edge. 95 */ 96 public static final int ALIGN_PARENT_LEFT = 9; 97 /** 98 * Rule that aligns the child's top edge with its RelativeLayout 99 * parent's top edge. 100 */ 101 public static final int ALIGN_PARENT_TOP = 10; 102 /** 103 * Rule that aligns the child's right edge with its RelativeLayout 104 * parent's right edge. 105 */ 106 public static final int ALIGN_PARENT_RIGHT = 11; 107 /** 108 * Rule that aligns the child's bottom edge with its RelativeLayout 109 * parent's bottom edge. 110 */ 111 public static final int ALIGN_PARENT_BOTTOM = 12; 112 113 /** 114 * Rule that centers the child with respect to the bounds of its 115 * RelativeLayout parent. 116 */ 117 public static final int CENTER_IN_PARENT = 13; 118 /** 119 * Rule that centers the child horizontally with respect to the 120 * bounds of its RelativeLayout parent. 121 */ 122 public static final int CENTER_HORIZONTAL = 14; 123 /** 124 * Rule that centers the child vertically with respect to the 125 * bounds of its RelativeLayout parent. 126 */ 127 public static final int CENTER_VERTICAL = 15; 128 129 private static final int VERB_COUNT = 16; 130 131 private View mBaselineView = null; 132 private boolean mHasBaselineAlignedChild; 133 134 private int mGravity = Gravity.LEFT | Gravity.TOP; 135 private final Rect mContentBounds = new Rect(); 136 private final Rect mSelfBounds = new Rect(); 137 private int mIgnoreGravity; 138 139 public RelativeLayout(Context context) { 140 super(context); 141 } 142 143 public RelativeLayout(Context context, AttributeSet attrs) { 144 super(context, attrs); 145 initFromAttributes(context, attrs); 146 } 147 148 public RelativeLayout(Context context, AttributeSet attrs, int defStyle) { 149 super(context, attrs, defStyle); 150 initFromAttributes(context, attrs); 151 } 152 153 private void initFromAttributes(Context context, AttributeSet attrs) { 154 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RelativeLayout); 155 mIgnoreGravity = a.getResourceId(R.styleable.RelativeLayout_ignoreGravity, View.NO_ID); 156 mGravity = a.getInt(R.styleable.RelativeLayout_gravity, mGravity); 157 a.recycle(); 158 } 159 160 /** 161 * Defines which View is ignored when the gravity is applied. This setting has no 162 * effect if the gravity is <code>Gravity.LEFT | Gravity.TOP</code>. 163 * 164 * @param viewId The id of the View to be ignored by gravity, or 0 if no View 165 * should be ignored. 166 * 167 * @see #setGravity(int) 168 * 169 * @attr ref android.R.styleable#RelativeLayout_ignoreGravity 170 */ 171 @android.view.RemotableViewMethod 172 public void setIgnoreGravity(int viewId) { 173 mIgnoreGravity = viewId; 174 } 175 176 /** 177 * Describes how the child views are positioned. Defaults to 178 * <code>Gravity.LEFT | Gravity.TOP</code>. 179 * 180 * @param gravity See {@link android.view.Gravity} 181 * 182 * @see #setHorizontalGravity(int) 183 * @see #setVerticalGravity(int) 184 * 185 * @attr ref android.R.styleable#RelativeLayout_gravity 186 */ 187 @android.view.RemotableViewMethod 188 public void setGravity(int gravity) { 189 if (mGravity != gravity) { 190 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { 191 gravity |= Gravity.LEFT; 192 } 193 194 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 195 gravity |= Gravity.TOP; 196 } 197 198 mGravity = gravity; 199 requestLayout(); 200 } 201 } 202 203 @android.view.RemotableViewMethod 204 public void setHorizontalGravity(int horizontalGravity) { 205 final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK; 206 if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) { 207 mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity; 208 requestLayout(); 209 } 210 } 211 212 @android.view.RemotableViewMethod 213 public void setVerticalGravity(int verticalGravity) { 214 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK; 215 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) { 216 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity; 217 requestLayout(); 218 } 219 } 220 221 @Override 222 public int getBaseline() { 223 return mBaselineView != null ? mBaselineView.getBaseline() : super.getBaseline(); 224 } 225 226 @Override 227 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 228 int myWidth = -1; 229 int myHeight = -1; 230 231 int width = 0; 232 int height = 0; 233 234 int widthMode = MeasureSpec.getMode(widthMeasureSpec); 235 int heightMode = MeasureSpec.getMode(heightMeasureSpec); 236 int widthSize = MeasureSpec.getSize(widthMeasureSpec); 237 int heightSize = MeasureSpec.getSize(heightMeasureSpec); 238 239 // Record our dimensions if they are known; 240 if (widthMode != MeasureSpec.UNSPECIFIED) { 241 myWidth = widthSize; 242 } 243 244 if (heightMode != MeasureSpec.UNSPECIFIED) { 245 myHeight = heightSize; 246 } 247 248 if (widthMode == MeasureSpec.EXACTLY) { 249 width = myWidth; 250 } 251 252 if (heightMode == MeasureSpec.EXACTLY) { 253 height = myHeight; 254 } 255 256 int len = this.getChildCount(); 257 mHasBaselineAlignedChild = false; 258 259 View ignore = null; 260 int gravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK; 261 final boolean horizontalGravity = gravity != Gravity.LEFT && gravity != 0; 262 gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 263 final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0; 264 265 int left = Integer.MAX_VALUE; 266 int top = Integer.MAX_VALUE; 267 int right = Integer.MIN_VALUE; 268 int bottom = Integer.MIN_VALUE; 269 270 if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { 271 ignore = findViewById(mIgnoreGravity); 272 } 273 274 for (int i = 0; i < len; i++) { 275 View child = getChildAt(i); 276 if (child.getVisibility() != GONE) { 277 LayoutParams params = (LayoutParams) child.getLayoutParams(); 278 applySizeRules(params, myWidth, myHeight); 279 measureChild(child, params, myWidth, myHeight); 280 positionChild(child, params, myWidth, myHeight); 281 282 if (widthMode != MeasureSpec.EXACTLY) { 283 width = Math.max(width, params.mRight); 284 } 285 if (heightMode != MeasureSpec.EXACTLY) { 286 height = Math.max(height, params.mBottom); 287 } 288 289 if (child != ignore || verticalGravity) { 290 left = Math.min(left, params.mLeft - params.leftMargin); 291 top = Math.min(top, params.mTop - params.topMargin); 292 } 293 294 if (child != ignore || horizontalGravity) { 295 right = Math.max(right, params.mRight + params.rightMargin); 296 bottom = Math.max(bottom, params.mBottom + params.bottomMargin); 297 } 298 } 299 } 300 301 if (mHasBaselineAlignedChild) { 302 for (int i = 0; i < len; i++) { 303 View child = getChildAt(i); 304 if (child.getVisibility() != GONE) { 305 LayoutParams params = (LayoutParams) child.getLayoutParams(); 306 alignBaseline(child, params); 307 308 if (child != ignore || verticalGravity) { 309 left = Math.min(left, params.mLeft - params.leftMargin); 310 top = Math.min(top, params.mTop - params.topMargin); 311 } 312 313 if (child != ignore || horizontalGravity) { 314 right = Math.max(right, params.mRight + params.rightMargin); 315 bottom = Math.max(bottom, params.mBottom + params.bottomMargin); 316 } 317 } 318 } 319 } 320 321 if (widthMode != MeasureSpec.EXACTLY) { 322 // Width already has left padding in it since it was calculated by looking at 323 // the right of each child view 324 width += mPaddingRight; 325 326 if (mLayoutParams.width >= 0) { 327 width = Math.max(width, mLayoutParams.width); 328 } 329 330 width = Math.max(width, getSuggestedMinimumWidth()); 331 width = resolveSize(width, widthMeasureSpec); 332 } 333 if (heightMode != MeasureSpec.EXACTLY) { 334 // Height already has top padding in it since it was calculated by looking at 335 // the bottom of each child view 336 height += mPaddingBottom; 337 338 if (mLayoutParams.height >= 0) { 339 height = Math.max(height, mLayoutParams.height); 340 } 341 342 height = Math.max(height, getSuggestedMinimumHeight()); 343 height = resolveSize(height, heightMeasureSpec); 344 } 345 346 if (horizontalGravity || verticalGravity) { 347 final Rect selfBounds = mSelfBounds; 348 selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight, 349 height - mPaddingBottom); 350 351 final Rect contentBounds = mContentBounds; 352 Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds); 353 354 final int horizontalOffset = contentBounds.left - left; 355 final int verticalOffset = contentBounds.top - top; 356 if (horizontalOffset != 0 || verticalOffset != 0) { 357 for (int i = 0; i < len; i++) { 358 View child = getChildAt(i); 359 if (child.getVisibility() != GONE && child != ignore) { 360 LayoutParams params = (LayoutParams) child.getLayoutParams(); 361 params.mLeft += horizontalOffset; 362 params.mRight += horizontalOffset; 363 params.mTop += verticalOffset; 364 params.mBottom += verticalOffset; 365 } 366 } 367 } 368 } 369 370 setMeasuredDimension(width, height); 371 } 372 373 private void alignBaseline(View child, LayoutParams params) { 374 int[] rules = params.getRules(); 375 int anchorBaseline = getRelatedViewBaseline(rules, ALIGN_BASELINE); 376 377 if (anchorBaseline != -1) { 378 LayoutParams anchorParams = getRelatedViewParams(rules, ALIGN_BASELINE); 379 if (anchorParams != null) { 380 int offset = anchorParams.mTop + anchorBaseline; 381 int baseline = child.getBaseline(); 382 if (baseline != -1) { 383 offset -= baseline; 384 } 385 int height = params.mBottom - params.mTop; 386 params.mTop = offset; 387 params.mBottom = params.mTop + height; 388 } 389 } 390 391 if (mBaselineView == null) { 392 mBaselineView = child; 393 } else { 394 LayoutParams lp = (LayoutParams) mBaselineView.getLayoutParams(); 395 if (params.mTop < lp.mTop || (params.mTop == lp.mTop && params.mLeft < lp.mLeft)) { 396 mBaselineView = child; 397 } 398 } 399 } 400 401 /** 402 * Measure a child. The child should have left, top, right and bottom information 403 * stored in its LayoutParams. If any of these values is -1 it means that the view 404 * can extend up to the corresponding edge. 405 * 406 * @param child Child to measure 407 * @param params LayoutParams associated with child 408 * @param myWidth Width of the the RelativeLayout 409 * @param myHeight Height of the RelativeLayout 410 */ 411 private void measureChild(View child, LayoutParams params, int myWidth, 412 int myHeight) { 413 414 int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, 415 params.mRight, params.width, 416 params.leftMargin, params.rightMargin, 417 mPaddingLeft, mPaddingRight, 418 myWidth); 419 int childHeightMeasureSpec = getChildMeasureSpec(params.mTop, 420 params.mBottom, params.height, 421 params.topMargin, params.bottomMargin, 422 mPaddingTop, mPaddingBottom, 423 myHeight); 424 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 425 } 426 427 /** 428 * Get a measure spec that accounts for all of the constraints on this view. 429 * This includes size contstraints imposed by the RelativeLayout as well as 430 * the View's desired dimension. 431 * 432 * @param childStart The left or top field of the child's layout params 433 * @param childEnd The right or bottom field of the child's layout params 434 * @param childSize The child's desired size (the width or height field of 435 * the child's layout params) 436 * @param startMargin The left or top margin 437 * @param endMargin The right or bottom margin 438 * @param startPadding mPaddingLeft or mPaddingTop 439 * @param endPadding mPaddingRight or mPaddingBottom 440 * @param mySize The width or height of this view (the RelativeLayout) 441 * @return MeasureSpec for the child 442 */ 443 private int getChildMeasureSpec(int childStart, int childEnd, 444 int childSize, int startMargin, int endMargin, int startPadding, 445 int endPadding, int mySize) { 446 int childSpecMode = 0; 447 int childSpecSize = 0; 448 449 // Figure out start and end bounds. 450 int tempStart = childStart; 451 int tempEnd = childEnd; 452 453 // If the view did not express a layout constraint for an edge, use 454 // view's margins and our padding 455 if (tempStart < 0) { 456 tempStart = startPadding + startMargin; 457 } 458 if (tempEnd < 0) { 459 tempEnd = mySize - endPadding - endMargin; 460 } 461 462 // Figure out maximum size available to this view 463 int maxAvailable = tempEnd - tempStart; 464 465 if (childStart >= 0 && childEnd >= 0) { 466 // Constraints fixed both edges, so child must be an exact size 467 childSpecMode = MeasureSpec.EXACTLY; 468 childSpecSize = maxAvailable; 469 } else { 470 if (childSize >= 0) { 471 // Child wanted an exact size. Give as much as possible 472 childSpecMode = MeasureSpec.EXACTLY; 473 474 if (maxAvailable >= 0) { 475 // We have a maxmum size in this dimension. 476 childSpecSize = Math.min(maxAvailable, childSize); 477 } else { 478 // We can grow in this dimension. 479 childSpecSize = childSize; 480 } 481 } else if (childSize == LayoutParams.FILL_PARENT) { 482 // Child wanted to be as big as possible. Give all availble 483 // space 484 childSpecMode = MeasureSpec.EXACTLY; 485 childSpecSize = maxAvailable; 486 } else if (childSize == LayoutParams.WRAP_CONTENT) { 487 // Child wants to wrap content. Use AT_MOST 488 // to communicate available space if we know 489 // our max size 490 if (maxAvailable >= 0) { 491 // We have a maxmum size in this dimension. 492 childSpecMode = MeasureSpec.AT_MOST; 493 childSpecSize = maxAvailable; 494 } else { 495 // We can grow in this dimension. Child can be as big as it 496 // wants 497 childSpecMode = MeasureSpec.UNSPECIFIED; 498 childSpecSize = 0; 499 } 500 } 501 } 502 503 return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); 504 } 505 506 /** 507 * After the child has been measured, assign it a position. Some views may 508 * already have final values for l,t,r,b. Others may have one or both edges 509 * unfixed (i.e. set to -1) in each dimension. These will get positioned 510 * based on which edge is fixed, the view's desired dimension, and whether 511 * or not it is centered. 512 * 513 * @param child Child to position 514 * @param params LayoutParams associated with child 515 * @param myWidth Width of the the RelativeLayout 516 * @param myHeight Height of the RelativeLayout 517 */ 518 private void positionChild(View child, LayoutParams params, int myWidth, int myHeight) { 519 int[] rules = params.getRules(); 520 521 if (params.mLeft < 0 && params.mRight >= 0) { 522 // Right is fixed, but left varies 523 params.mLeft = params.mRight - child.getMeasuredWidth(); 524 } else if (params.mLeft >= 0 && params.mRight < 0) { 525 // Left is fixed, but right varies 526 params.mRight = params.mLeft + child.getMeasuredWidth(); 527 } else if (params.mLeft < 0 && params.mRight < 0) { 528 // Both left and right vary 529 if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) { 530 centerHorizontal(child, params, myWidth); 531 } else { 532 params.mLeft = mPaddingLeft + params.leftMargin; 533 params.mRight = params.mLeft + child.getMeasuredWidth(); 534 } 535 } 536 537 if (params.mTop < 0 && params.mBottom >= 0) { 538 // Bottom is fixed, but top varies 539 params.mTop = params.mBottom - child.getMeasuredHeight(); 540 } else if (params.mTop >= 0 && params.mBottom < 0) { 541 // Top is fixed, but bottom varies 542 params.mBottom = params.mTop + child.getMeasuredHeight(); 543 } else if (params.mTop < 0 && params.mBottom < 0) { 544 // Both top and bottom vary 545 if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) { 546 centerVertical(child, params, myHeight); 547 } else { 548 params.mTop = mPaddingTop + params.topMargin; 549 params.mBottom = params.mTop + child.getMeasuredHeight(); 550 } 551 } 552 } 553 554 /** 555 * Set l,t,r,b values in the LayoutParams for one view based on its layout rules. 556 * Big assumption #1: All antecedents of this view have been sized & positioned 557 * Big assumption #2: The dimensions of the parent view (the RelativeLayout) 558 * are already known if they are needed. 559 * 560 * @param childParams LayoutParams for the view being positioned 561 * @param myWidth Width of the the RelativeLayout 562 * @param myHeight Height of the RelativeLayout 563 */ 564 private void applySizeRules(LayoutParams childParams, int myWidth, int myHeight) { 565 int[] rules = childParams.getRules(); 566 RelativeLayout.LayoutParams anchorParams; 567 568 // -1 indicated a "soft requirement" in that direction. For example: 569 // left=10, right=-1 means the view must start at 10, but can go as far as it wants to the right 570 // left =-1, right=10 means the view must end at 10, but can go as far as it wants to the left 571 // left=10, right=20 means the left and right ends are both fixed 572 childParams.mLeft = -1; 573 childParams.mRight = -1; 574 575 anchorParams = getRelatedViewParams(rules, LEFT_OF); 576 if (anchorParams != null) { 577 childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin + 578 childParams.rightMargin); 579 } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) { 580 if (myWidth >= 0) { 581 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; 582 } else { 583 // FIXME uh oh... 584 } 585 } 586 587 anchorParams = getRelatedViewParams(rules, RIGHT_OF); 588 if (anchorParams != null) { 589 childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin + 590 childParams.leftMargin); 591 } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) { 592 childParams.mLeft = mPaddingLeft + childParams.leftMargin; 593 } 594 595 anchorParams = getRelatedViewParams(rules, ALIGN_LEFT); 596 if (anchorParams != null) { 597 childParams.mLeft = anchorParams.mLeft + childParams.leftMargin; 598 } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) { 599 childParams.mLeft = mPaddingLeft + childParams.leftMargin; 600 } 601 602 anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT); 603 if (anchorParams != null) { 604 childParams.mRight = anchorParams.mRight - childParams.rightMargin; 605 } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) { 606 if (myWidth >= 0) { 607 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; 608 } else { 609 // FIXME uh oh... 610 } 611 } 612 613 if (0 != rules[ALIGN_PARENT_LEFT]) { 614 childParams.mLeft = mPaddingLeft + childParams.leftMargin; 615 } 616 617 if (0 != rules[ALIGN_PARENT_RIGHT]) { 618 if (myWidth >= 0) { 619 childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin; 620 } else { 621 // FIXME uh oh... 622 } 623 } 624 625 childParams.mTop = -1; 626 childParams.mBottom = -1; 627 628 anchorParams = getRelatedViewParams(rules, ABOVE); 629 if (anchorParams != null) { 630 childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin + 631 childParams.bottomMargin); 632 } else if (childParams.alignWithParent && rules[ABOVE] != 0) { 633 if (myHeight >= 0) { 634 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; 635 } else { 636 // FIXME uh oh... 637 } 638 } 639 640 anchorParams = getRelatedViewParams(rules, BELOW); 641 if (anchorParams != null) { 642 childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin + 643 childParams.topMargin); 644 } else if (childParams.alignWithParent && rules[BELOW] != 0) { 645 childParams.mTop = mPaddingTop + childParams.topMargin; 646 } 647 648 anchorParams = getRelatedViewParams(rules, ALIGN_TOP); 649 if (anchorParams != null) { 650 childParams.mTop = anchorParams.mTop + childParams.topMargin; 651 } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) { 652 childParams.mTop = mPaddingTop + childParams.topMargin; 653 } 654 655 anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM); 656 if (anchorParams != null) { 657 childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin; 658 } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) { 659 if (myHeight >= 0) { 660 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; 661 } else { 662 // FIXME uh oh... 663 } 664 } 665 666 if (0 != rules[ALIGN_PARENT_TOP]) { 667 childParams.mTop = mPaddingTop + childParams.topMargin; 668 } 669 670 if (0 != rules[ALIGN_PARENT_BOTTOM]) { 671 if (myHeight >= 0) { 672 childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin; 673 } else { 674 // FIXME uh oh... 675 } 676 } 677 678 if (rules[ALIGN_BASELINE] != 0) { 679 mHasBaselineAlignedChild = true; 680 } 681 } 682 683 private View getRelatedView(int[] rules, int relation) { 684 int id = rules[relation]; 685 if (id != 0) { 686 View v = findViewById(id); 687 if (v == null) { 688 return null; 689 } 690 691 // Find the first non-GONE view up the chain 692 while (v.getVisibility() == View.GONE) { 693 rules = ((LayoutParams) v.getLayoutParams()).getRules(); 694 v = v.findViewById(rules[relation]); 695 if (v == null) { 696 return null; 697 } 698 } 699 700 return v; 701 } 702 703 return null; 704 } 705 706 private LayoutParams getRelatedViewParams(int[] rules, int relation) { 707 View v = getRelatedView(rules, relation); 708 if (v != null) { 709 ViewGroup.LayoutParams params = v.getLayoutParams(); 710 if (params instanceof LayoutParams) { 711 return (LayoutParams) v.getLayoutParams(); 712 } 713 } 714 return null; 715 } 716 717 private int getRelatedViewBaseline(int[] rules, int relation) { 718 View v = getRelatedView(rules, relation); 719 if (v != null) { 720 return v.getBaseline(); 721 } 722 return -1; 723 } 724 725 private void centerHorizontal(View child, LayoutParams params, int myWidth) { 726 int childWidth = child.getMeasuredWidth(); 727 int left = (myWidth - childWidth) / 2; 728 729 params.mLeft = left; 730 params.mRight = left + childWidth; 731 } 732 733 private void centerVertical(View child, LayoutParams params, int myHeight) { 734 int childHeight = child.getMeasuredHeight(); 735 int top = (myHeight - childHeight) / 2; 736 737 params.mTop = top; 738 params.mBottom = top + childHeight; 739 } 740 741 @Override 742 protected void onLayout(boolean changed, int l, int t, int r, int b) { 743 // The layout has actually already been performed and the positions 744 // cached. Apply the cached values to the children. 745 int count = getChildCount(); 746 747 for (int i = 0; i < count; i++) { 748 View child = getChildAt(i); 749 if (child.getVisibility() != GONE) { 750 RelativeLayout.LayoutParams st = 751 (RelativeLayout.LayoutParams) child.getLayoutParams(); 752 child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom); 753 754 } 755 } 756 } 757 758 @Override 759 public LayoutParams generateLayoutParams(AttributeSet attrs) { 760 return new RelativeLayout.LayoutParams(getContext(), attrs); 761 } 762 763 /** 764 * Returns a set of layout parameters with a width of 765 * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}, 766 * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning. 767 */ 768 @Override 769 protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 770 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 771 } 772 773 // Override to allow type-checking of LayoutParams. 774 @Override 775 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 776 return p instanceof RelativeLayout.LayoutParams; 777 } 778 779 @Override 780 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 781 return new LayoutParams(p); 782 } 783 784 /** 785 * Per-child layout information associated with RelativeLayout. 786 * 787 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing 788 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toLeftOf 789 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_toRightOf 790 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_above 791 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_below 792 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBaseline 793 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignLeft 794 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignTop 795 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignRight 796 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignBottom 797 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentLeft 798 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentTop 799 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentRight 800 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignParentBottom 801 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerInParent 802 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerHorizontal 803 * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical 804 */ 805 public static class LayoutParams extends ViewGroup.MarginLayoutParams { 806 private int[] mRules = new int[VERB_COUNT]; 807 private int mLeft, mTop, mRight, mBottom; 808 809 /** 810 * When true, uses the parent as the anchor if the anchor doesn't exist or if 811 * the anchor's visibility is GONE. 812 */ 813 public boolean alignWithParent; 814 815 public LayoutParams(Context c, AttributeSet attrs) { 816 super(c, attrs); 817 818 TypedArray a = c.obtainStyledAttributes(attrs, 819 com.android.internal.R.styleable.RelativeLayout_Layout); 820 821 final int[] rules = mRules; 822 823 final int N = a.getIndexCount(); 824 for (int i = 0; i < N; i++) { 825 int attr = a.getIndex(i); 826 switch (attr) { 827 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing: 828 alignWithParent = a.getBoolean(attr, false); 829 break; 830 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf: 831 rules[LEFT_OF] = a.getResourceId(attr, 0); 832 break; 833 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf: 834 rules[RIGHT_OF] = a.getResourceId(attr, 0); 835 break; 836 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above: 837 rules[ABOVE] = a.getResourceId(attr, 0); 838 break; 839 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below: 840 rules[BELOW] = a.getResourceId(attr, 0); 841 break; 842 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline: 843 rules[ALIGN_BASELINE] = a.getResourceId(attr, 0); 844 break; 845 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft: 846 rules[ALIGN_LEFT] = a.getResourceId(attr, 0); 847 break; 848 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop: 849 rules[ALIGN_TOP] = a.getResourceId(attr, 0); 850 break; 851 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight: 852 rules[ALIGN_RIGHT] = a.getResourceId(attr, 0); 853 break; 854 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom: 855 rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0); 856 break; 857 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft: 858 rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0; 859 break; 860 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop: 861 rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0; 862 break; 863 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight: 864 rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0; 865 break; 866 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom: 867 rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0; 868 break; 869 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent: 870 rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0; 871 break; 872 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal: 873 rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0; 874 break; 875 case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical: 876 rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0; 877 break; 878 } 879 } 880 881 a.recycle(); 882 } 883 884 public LayoutParams(int w, int h) { 885 super(w, h); 886 } 887 888 /** 889 * {@inheritDoc} 890 */ 891 public LayoutParams(ViewGroup.LayoutParams source) { 892 super(source); 893 } 894 895 /** 896 * {@inheritDoc} 897 */ 898 public LayoutParams(ViewGroup.MarginLayoutParams source) { 899 super(source); 900 } 901 902 @Override 903 public String debug(String output) { 904 return output + "ViewGroup.LayoutParams={ width=" + sizeToString(width) + 905 ", height=" + sizeToString(height) + " }"; 906 } 907 908 /** 909 * Adds a layout rule to be interpreted by the RelativeLayout. This 910 * method should only be used for constraints that don't refer to another sibling 911 * (e.g., CENTER_IN_PARENT) or take a boolean value ({@link RelativeLayout#TRUE} 912 * for true or - for false). To specify a verb that takes a subject, use 913 * {@link #addRule(int, int)} instead. 914 * 915 * @param verb One of the verbs defined by 916 * {@link android.widget.RelativeLayout RelativeLayout}, such as 917 * ALIGN_WITH_PARENT_LEFT. 918 * @see #addRule(int, int) 919 */ 920 public void addRule(int verb) { 921 mRules[verb] = TRUE; 922 } 923 924 /** 925 * Adds a layout rule to be interpreted by the RelativeLayout. Use this for 926 * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean 927 * value (VISIBLE). 928 * 929 * @param verb One of the verbs defined by 930 * {@link android.widget.RelativeLayout RelativeLayout}, such as 931 * ALIGN_WITH_PARENT_LEFT. 932 * @param anchor The id of another view to use as an anchor, 933 * or a boolean value(represented as {@link RelativeLayout#TRUE}) 934 * for true or 0 for false). For verbs that don't refer to another sibling 935 * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1. 936 * @see #addRule(int) 937 */ 938 public void addRule(int verb, int anchor) { 939 mRules[verb] = anchor; 940 } 941 942 /** 943 * Retrieves a complete list of all supported rules, where the index is the rule 944 * verb, and the element value is the value specified, or "false" if it was never 945 * set. 946 * 947 * @return the supported rules 948 * @see #addRule(int, int) 949 */ 950 public int[] getRules() { 951 return mRules; 952 } 953 } 954} 955