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