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 17 package android.widget; 18 19 import com.android.internal.R; 20 21 import android.content.Context; 22 import android.content.res.TypedArray; 23 import android.graphics.Canvas; 24 import android.graphics.drawable.Drawable; 25 import android.util.AttributeSet; 26 import android.view.Gravity; 27 import android.view.View; 28 import android.view.ViewDebug; 29 import android.view.ViewGroup; 30 import android.view.accessibility.AccessibilityEvent; 31 import android.view.accessibility.AccessibilityNodeInfo; 32 import android.widget.RemoteViews.RemoteView; 33 34 35 /** 36 * A Layout that arranges its children in a single column or a single row. The direction of 37 * the row can be set by calling {@link #setOrientation(int) setOrientation()}. 38 * You can also specify gravity, which specifies the alignment of all the child elements by 39 * calling {@link #setGravity(int) setGravity()} or specify that specific children 40 * grow to fill up any remaining space in the layout by setting the <em>weight</em> member of 41 * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}. 42 * The default orientation is horizontal. 43 * 44 * <p>See the <a href="{@docRoot}guide/topics/ui/layout/linear.html">Linear Layout</a> 45 * guide.</p> 46 * 47 * <p> 48 * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams} 49 * for layout attributes </p> 50 * 51 * @attr ref android.R.styleable#LinearLayout_baselineAligned 52 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex 53 * @attr ref android.R.styleable#LinearLayout_gravity 54 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 55 * @attr ref android.R.styleable#LinearLayout_orientation 56 * @attr ref android.R.styleable#LinearLayout_weightSum 57 */ 58 @RemoteView 59 public class LinearLayout extends ViewGroup { 60 public static final int HORIZONTAL = 0; 61 public static final int VERTICAL = 1; 62 63 /** 64 * Don't show any dividers. 65 */ 66 public static final int SHOW_DIVIDER_NONE = 0; 67 /** 68 * Show a divider at the beginning of the group. 69 */ 70 public static final int SHOW_DIVIDER_BEGINNING = 1; 71 /** 72 * Show dividers between each item in the group. 73 */ 74 public static final int SHOW_DIVIDER_MIDDLE = 2; 75 /** 76 * Show a divider at the end of the group. 77 */ 78 public static final int SHOW_DIVIDER_END = 4; 79 80 /** 81 * Whether the children of this layout are baseline aligned. Only applicable 82 * if {@link #mOrientation} is horizontal. 83 */ 84 @ViewDebug.ExportedProperty(category = "layout") 85 private boolean mBaselineAligned = true; 86 87 /** 88 * If this layout is part of another layout that is baseline aligned, 89 * use the child at this index as the baseline. 90 * 91 * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned 92 * with whether the children of this layout are baseline aligned. 93 */ 94 @ViewDebug.ExportedProperty(category = "layout") 95 private int mBaselineAlignedChildIndex = -1; 96 97 /** 98 * The additional offset to the child's baseline. 99 * We'll calculate the baseline of this layout as we measure vertically; for 100 * horizontal linear layouts, the offset of 0 is appropriate. 101 */ 102 @ViewDebug.ExportedProperty(category = "measurement") 103 private int mBaselineChildTop = 0; 104 105 @ViewDebug.ExportedProperty(category = "measurement") 106 private int mOrientation; 107 108 @ViewDebug.ExportedProperty(category = "measurement", flagMapping = { 109 @ViewDebug.FlagToString(mask = -1, 110 equals = -1, name = "NONE"), 111 @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY, 112 equals = Gravity.NO_GRAVITY,name = "NONE"), 113 @ViewDebug.FlagToString(mask = Gravity.TOP, 114 equals = Gravity.TOP, name = "TOP"), 115 @ViewDebug.FlagToString(mask = Gravity.BOTTOM, 116 equals = Gravity.BOTTOM, name = "BOTTOM"), 117 @ViewDebug.FlagToString(mask = Gravity.LEFT, 118 equals = Gravity.LEFT, name = "LEFT"), 119 @ViewDebug.FlagToString(mask = Gravity.RIGHT, 120 equals = Gravity.RIGHT, name = "RIGHT"), 121 @ViewDebug.FlagToString(mask = Gravity.START, 122 equals = Gravity.START, name = "START"), 123 @ViewDebug.FlagToString(mask = Gravity.END, 124 equals = Gravity.END, name = "END"), 125 @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL, 126 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"), 127 @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL, 128 equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"), 129 @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL, 130 equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"), 131 @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL, 132 equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"), 133 @ViewDebug.FlagToString(mask = Gravity.CENTER, 134 equals = Gravity.CENTER, name = "CENTER"), 135 @ViewDebug.FlagToString(mask = Gravity.FILL, 136 equals = Gravity.FILL, name = "FILL"), 137 @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION, 138 equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE") 139 }) 140 private int mGravity = Gravity.START | Gravity.TOP; 141 142 @ViewDebug.ExportedProperty(category = "measurement") 143 private int mTotalLength; 144 145 @ViewDebug.ExportedProperty(category = "layout") 146 private float mWeightSum; 147 148 @ViewDebug.ExportedProperty(category = "layout") 149 private boolean mUseLargestChild; 150 151 private int[] mMaxAscent; 152 private int[] mMaxDescent; 153 154 private static final int VERTICAL_GRAVITY_COUNT = 4; 155 156 private static final int INDEX_CENTER_VERTICAL = 0; 157 private static final int INDEX_TOP = 1; 158 private static final int INDEX_BOTTOM = 2; 159 private static final int INDEX_FILL = 3; 160 161 private Drawable mDivider; 162 private int mDividerWidth; 163 private int mDividerHeight; 164 private int mShowDividers; 165 private int mDividerPadding; 166 167 public LinearLayout(Context context) { 168 super(context); 169 } 170 171 public LinearLayout(Context context, AttributeSet attrs) { 172 this(context, attrs, 0); 173 } 174 175 public LinearLayout(Context context, AttributeSet attrs, int defStyle) { 176 super(context, attrs, defStyle); 177 178 TypedArray a = context.obtainStyledAttributes(attrs, 179 com.android.internal.R.styleable.LinearLayout, defStyle, 0); 180 181 int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1); 182 if (index >= 0) { 183 setOrientation(index); 184 } 185 186 index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1); 187 if (index >= 0) { 188 setGravity(index); 189 } 190 191 boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true); 192 if (!baselineAligned) { 193 setBaselineAligned(baselineAligned); 194 } 195 196 mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f); 197 198 mBaselineAlignedChildIndex = 199 a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1); 200 201 mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false); 202 203 setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider)); 204 mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE); 205 mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0); 206 207 a.recycle(); 208 } 209 210 /** 211 * Set how dividers should be shown between items in this layout 212 * 213 * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING}, 214 * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}, 215 * or {@link #SHOW_DIVIDER_NONE} to show no dividers. 216 */ 217 public void setShowDividers(int showDividers) { 218 if (showDividers != mShowDividers) { 219 requestLayout(); 220 } 221 mShowDividers = showDividers; 222 } 223 224 @Override 225 public boolean shouldDelayChildPressedState() { 226 return false; 227 } 228 229 /** 230 * @return A flag set indicating how dividers should be shown around items. 231 * @see #setShowDividers(int) 232 */ 233 public int getShowDividers() { 234 return mShowDividers; 235 } 236 237 /** 238 * @return the divider Drawable that will divide each item. 239 * 240 * @see #setDividerDrawable(Drawable) 241 * 242 * @attr ref android.R.styleable#LinearLayout_divider 243 */ 244 public Drawable getDividerDrawable() { 245 return mDivider; 246 } 247 248 /** 249 * Set a drawable to be used as a divider between items. 250 * 251 * @param divider Drawable that will divide each item. 252 * 253 * @see #setShowDividers(int) 254 * 255 * @attr ref android.R.styleable#LinearLayout_divider 256 */ 257 public void setDividerDrawable(Drawable divider) { 258 if (divider == mDivider) { 259 return; 260 } 261 mDivider = divider; 262 if (divider != null) { 263 mDividerWidth = divider.getIntrinsicWidth(); 264 mDividerHeight = divider.getIntrinsicHeight(); 265 } else { 266 mDividerWidth = 0; 267 mDividerHeight = 0; 268 } 269 setWillNotDraw(divider == null); 270 requestLayout(); 271 } 272 273 /** 274 * Set padding displayed on both ends of dividers. 275 * 276 * @param padding Padding value in pixels that will be applied to each end 277 * 278 * @see #setShowDividers(int) 279 * @see #setDividerDrawable(Drawable) 280 * @see #getDividerPadding() 281 */ 282 public void setDividerPadding(int padding) { 283 mDividerPadding = padding; 284 } 285 286 /** 287 * Get the padding size used to inset dividers in pixels 288 * 289 * @see #setShowDividers(int) 290 * @see #setDividerDrawable(Drawable) 291 * @see #setDividerPadding(int) 292 */ 293 public int getDividerPadding() { 294 return mDividerPadding; 295 } 296 297 /** 298 * Get the width of the current divider drawable. 299 * 300 * @hide Used internally by framework. 301 */ 302 public int getDividerWidth() { 303 return mDividerWidth; 304 } 305 306 @Override 307 protected void onDraw(Canvas canvas) { 308 if (mDivider == null) { 309 return; 310 } 311 312 if (mOrientation == VERTICAL) { 313 drawDividersVertical(canvas); 314 } else { 315 drawDividersHorizontal(canvas); 316 } 317 } 318 319 void drawDividersVertical(Canvas canvas) { 320 final int count = getVirtualChildCount(); 321 for (int i = 0; i < count; i++) { 322 final View child = getVirtualChildAt(i); 323 324 if (child != null && child.getVisibility() != GONE) { 325 if (hasDividerBeforeChildAt(i)) { 326 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 327 final int top = child.getTop() - lp.topMargin - mDividerHeight; 328 drawHorizontalDivider(canvas, top); 329 } 330 } 331 } 332 333 if (hasDividerBeforeChildAt(count)) { 334 final View child = getVirtualChildAt(count - 1); 335 int bottom = 0; 336 if (child == null) { 337 bottom = getHeight() - getPaddingBottom() - mDividerHeight; 338 } else { 339 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 340 bottom = child.getBottom() + lp.bottomMargin; 341 } 342 drawHorizontalDivider(canvas, bottom); 343 } 344 } 345 346 void drawDividersHorizontal(Canvas canvas) { 347 final int count = getVirtualChildCount(); 348 for (int i = 0; i < count; i++) { 349 final View child = getVirtualChildAt(i); 350 351 if (child != null && child.getVisibility() != GONE) { 352 if (hasDividerBeforeChildAt(i)) { 353 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 354 final int left = child.getLeft() - lp.leftMargin - mDividerWidth; 355 drawVerticalDivider(canvas, left); 356 } 357 } 358 } 359 360 if (hasDividerBeforeChildAt(count)) { 361 final View child = getVirtualChildAt(count - 1); 362 int right = 0; 363 if (child == null) { 364 right = getWidth() - getPaddingRight() - mDividerWidth; 365 } else { 366 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 367 right = child.getRight() + lp.rightMargin; 368 } 369 drawVerticalDivider(canvas, right); 370 } 371 } 372 373 void drawHorizontalDivider(Canvas canvas, int top) { 374 mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, 375 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); 376 mDivider.draw(canvas); 377 } 378 379 void drawVerticalDivider(Canvas canvas, int left) { 380 mDivider.setBounds(left, getPaddingTop() + mDividerPadding, 381 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); 382 mDivider.draw(canvas); 383 } 384 385 /** 386 * <p>Indicates whether widgets contained within this layout are aligned 387 * on their baseline or not.</p> 388 * 389 * @return true when widgets are baseline-aligned, false otherwise 390 */ 391 public boolean isBaselineAligned() { 392 return mBaselineAligned; 393 } 394 395 /** 396 * <p>Defines whether widgets contained in this layout are 397 * baseline-aligned or not.</p> 398 * 399 * @param baselineAligned true to align widgets on their baseline, 400 * false otherwise 401 * 402 * @attr ref android.R.styleable#LinearLayout_baselineAligned 403 */ 404 @android.view.RemotableViewMethod 405 public void setBaselineAligned(boolean baselineAligned) { 406 mBaselineAligned = baselineAligned; 407 } 408 409 /** 410 * When true, all children with a weight will be considered having 411 * the minimum size of the largest child. If false, all children are 412 * measured normally. 413 * 414 * @return True to measure children with a weight using the minimum 415 * size of the largest child, false otherwise. 416 * 417 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 418 */ 419 public boolean isMeasureWithLargestChildEnabled() { 420 return mUseLargestChild; 421 } 422 423 /** 424 * When set to true, all children with a weight will be considered having 425 * the minimum size of the largest child. If false, all children are 426 * measured normally. 427 * 428 * Disabled by default. 429 * 430 * @param enabled True to measure children with a weight using the 431 * minimum size of the largest child, false otherwise. 432 * 433 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 434 */ 435 @android.view.RemotableViewMethod 436 public void setMeasureWithLargestChildEnabled(boolean enabled) { 437 mUseLargestChild = enabled; 438 } 439 440 @Override 441 public int getBaseline() { 442 if (mBaselineAlignedChildIndex < 0) { 443 return super.getBaseline(); 444 } 445 446 if (getChildCount() <= mBaselineAlignedChildIndex) { 447 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout " 448 + "set to an index that is out of bounds."); 449 } 450 451 final View child = getChildAt(mBaselineAlignedChildIndex); 452 final int childBaseline = child.getBaseline(); 453 454 if (childBaseline == -1) { 455 if (mBaselineAlignedChildIndex == 0) { 456 // this is just the default case, safe to return -1 457 return -1; 458 } 459 // the user picked an index that points to something that doesn't 460 // know how to calculate its baseline. 461 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout " 462 + "points to a View that doesn't know how to get its baseline."); 463 } 464 465 // TODO: This should try to take into account the virtual offsets 466 // (See getNextLocationOffset and getLocationOffset) 467 // We should add to childTop: 468 // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex]) 469 // and also add: 470 // getLocationOffset(child) 471 int childTop = mBaselineChildTop; 472 473 if (mOrientation == VERTICAL) { 474 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 475 if (majorGravity != Gravity.TOP) { 476 switch (majorGravity) { 477 case Gravity.BOTTOM: 478 childTop = mBottom - mTop - mPaddingBottom - mTotalLength; 479 break; 480 481 case Gravity.CENTER_VERTICAL: 482 childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) - 483 mTotalLength) / 2; 484 break; 485 } 486 } 487 } 488 489 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 490 return childTop + lp.topMargin + childBaseline; 491 } 492 493 /** 494 * @return The index of the child that will be used if this layout is 495 * part of a larger layout that is baseline aligned, or -1 if none has 496 * been set. 497 */ 498 public int getBaselineAlignedChildIndex() { 499 return mBaselineAlignedChildIndex; 500 } 501 502 /** 503 * @param i The index of the child that will be used if this layout is 504 * part of a larger layout that is baseline aligned. 505 * 506 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex 507 */ 508 @android.view.RemotableViewMethod 509 public void setBaselineAlignedChildIndex(int i) { 510 if ((i < 0) || (i >= getChildCount())) { 511 throw new IllegalArgumentException("base aligned child index out " 512 + "of range (0, " + getChildCount() + ")"); 513 } 514 mBaselineAlignedChildIndex = i; 515 } 516 517 /** 518 * <p>Returns the view at the specified index. This method can be overriden 519 * to take into account virtual children. Refer to 520 * {@link android.widget.TableLayout} and {@link android.widget.TableRow} 521 * for an example.</p> 522 * 523 * @param index the child's index 524 * @return the child at the specified index 525 */ 526 View getVirtualChildAt(int index) { 527 return getChildAt(index); 528 } 529 530 /** 531 * <p>Returns the virtual number of children. This number might be different 532 * than the actual number of children if the layout can hold virtual 533 * children. Refer to 534 * {@link android.widget.TableLayout} and {@link android.widget.TableRow} 535 * for an example.</p> 536 * 537 * @return the virtual number of children 538 */ 539 int getVirtualChildCount() { 540 return getChildCount(); 541 } 542 543 /** 544 * Returns the desired weights sum. 545 * 546 * @return A number greater than 0.0f if the weight sum is defined, or 547 * a number lower than or equals to 0.0f if not weight sum is 548 * to be used. 549 */ 550 public float getWeightSum() { 551 return mWeightSum; 552 } 553 554 /** 555 * Defines the desired weights sum. If unspecified the weights sum is computed 556 * at layout time by adding the layout_weight of each child. 557 * 558 * This can be used for instance to give a single child 50% of the total 559 * available space by giving it a layout_weight of 0.5 and setting the 560 * weightSum to 1.0. 561 * 562 * @param weightSum a number greater than 0.0f, or a number lower than or equals 563 * to 0.0f if the weight sum should be computed from the children's 564 * layout_weight 565 */ 566 @android.view.RemotableViewMethod 567 public void setWeightSum(float weightSum) { 568 mWeightSum = Math.max(0.0f, weightSum); 569 } 570 571 @Override 572 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 573 if (mOrientation == VERTICAL) { 574 measureVertical(widthMeasureSpec, heightMeasureSpec); 575 } else { 576 measureHorizontal(widthMeasureSpec, heightMeasureSpec); 577 } 578 } 579 580 /** 581 * Determines where to position dividers between children. 582 * 583 * @param childIndex Index of child to check for preceding divider 584 * @return true if there should be a divider before the child at childIndex 585 * @hide Pending API consideration. Currently only used internally by the system. 586 */ 587 protected boolean hasDividerBeforeChildAt(int childIndex) { 588 if (childIndex == 0) { 589 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0; 590 } else if (childIndex == getChildCount()) { 591 return (mShowDividers & SHOW_DIVIDER_END) != 0; 592 } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) { 593 boolean hasVisibleViewBefore = false; 594 for (int i = childIndex - 1; i >= 0; i--) { 595 if (getChildAt(i).getVisibility() != GONE) { 596 hasVisibleViewBefore = true; 597 break; 598 } 599 } 600 return hasVisibleViewBefore; 601 } 602 return false; 603 } 604 605 /** 606 * Measures the children when the orientation of this LinearLayout is set 607 * to {@link #VERTICAL}. 608 * 609 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 610 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 611 * 612 * @see #getOrientation() 613 * @see #setOrientation(int) 614 * @see #onMeasure(int, int) 615 */ 616 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { 617 mTotalLength = 0; 618 int maxWidth = 0; 619 int childState = 0; 620 int alternativeMaxWidth = 0; 621 int weightedMaxWidth = 0; 622 boolean allFillParent = true; 623 float totalWeight = 0; 624 625 final int count = getVirtualChildCount(); 626 627 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 628 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 629 630 boolean matchWidth = false; 631 632 final int baselineChildIndex = mBaselineAlignedChildIndex; 633 final boolean useLargestChild = mUseLargestChild; 634 635 int largestChildHeight = Integer.MIN_VALUE; 636 637 // See how tall everyone is. Also remember max width. 638 for (int i = 0; i < count; ++i) { 639 final View child = getVirtualChildAt(i); 640 641 if (child == null) { 642 mTotalLength += measureNullChild(i); 643 continue; 644 } 645 646 if (child.getVisibility() == View.GONE) { 647 i += getChildrenSkipCount(child, i); 648 continue; 649 } 650 651 if (hasDividerBeforeChildAt(i)) { 652 mTotalLength += mDividerHeight; 653 } 654 655 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 656 657 totalWeight += lp.weight; 658 659 if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) { 660 // Optimization: don't bother measuring children who are going to use 661 // leftover space. These views will get measured again down below if 662 // there is any leftover space. 663 final int totalLength = mTotalLength; 664 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin); 665 } else { 666 int oldHeight = Integer.MIN_VALUE; 667 668 if (lp.height == 0 && lp.weight > 0) { 669 // heightMode is either UNSPECIFIED or AT_MOST, and this 670 // child wanted to stretch to fill available space. 671 // Translate that to WRAP_CONTENT so that it does not end up 672 // with a height of 0 673 oldHeight = 0; 674 lp.height = LayoutParams.WRAP_CONTENT; 675 } 676 677 // Determine how big this child would like to be. If this or 678 // previous children have given a weight, then we allow it to 679 // use all available space (and we will shrink things later 680 // if needed). 681 measureChildBeforeLayout( 682 child, i, widthMeasureSpec, 0, heightMeasureSpec, 683 totalWeight == 0 ? mTotalLength : 0); 684 685 if (oldHeight != Integer.MIN_VALUE) { 686 lp.height = oldHeight; 687 } 688 689 final int childHeight = child.getMeasuredHeight(); 690 final int totalLength = mTotalLength; 691 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + 692 lp.bottomMargin + getNextLocationOffset(child)); 693 694 if (useLargestChild) { 695 largestChildHeight = Math.max(childHeight, largestChildHeight); 696 } 697 } 698 699 /** 700 * If applicable, compute the additional offset to the child's baseline 701 * we'll need later when asked {@link #getBaseline}. 702 */ 703 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) { 704 mBaselineChildTop = mTotalLength; 705 } 706 707 // if we are trying to use a child index for our baseline, the above 708 // book keeping only works if there are no children above it with 709 // weight. fail fast to aid the developer. 710 if (i < baselineChildIndex && lp.weight > 0) { 711 throw new RuntimeException("A child of LinearLayout with index " 712 + "less than mBaselineAlignedChildIndex has weight > 0, which " 713 + "won't work. Either remove the weight, or don't set " 714 + "mBaselineAlignedChildIndex."); 715 } 716 717 boolean matchWidthLocally = false; 718 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) { 719 // The width of the linear layout will scale, and at least one 720 // child said it wanted to match our width. Set a flag 721 // indicating that we need to remeasure at least that view when 722 // we know our width. 723 matchWidth = true; 724 matchWidthLocally = true; 725 } 726 727 final int margin = lp.leftMargin + lp.rightMargin; 728 final int measuredWidth = child.getMeasuredWidth() + margin; 729 maxWidth = Math.max(maxWidth, measuredWidth); 730 childState = combineMeasuredStates(childState, child.getMeasuredState()); 731 732 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 733 if (lp.weight > 0) { 734 /* 735 * Widths of weighted Views are bogus if we end up 736 * remeasuring, so keep them separate. 737 */ 738 weightedMaxWidth = Math.max(weightedMaxWidth, 739 matchWidthLocally ? margin : measuredWidth); 740 } else { 741 alternativeMaxWidth = Math.max(alternativeMaxWidth, 742 matchWidthLocally ? margin : measuredWidth); 743 } 744 745 i += getChildrenSkipCount(child, i); 746 } 747 748 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) { 749 mTotalLength += mDividerHeight; 750 } 751 752 if (useLargestChild && 753 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) { 754 mTotalLength = 0; 755 756 for (int i = 0; i < count; ++i) { 757 final View child = getVirtualChildAt(i); 758 759 if (child == null) { 760 mTotalLength += measureNullChild(i); 761 continue; 762 } 763 764 if (child.getVisibility() == GONE) { 765 i += getChildrenSkipCount(child, i); 766 continue; 767 } 768 769 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 770 child.getLayoutParams(); 771 // Account for negative margins 772 final int totalLength = mTotalLength; 773 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight + 774 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 775 } 776 } 777 778 // Add in our padding 779 mTotalLength += mPaddingTop + mPaddingBottom; 780 781 int heightSize = mTotalLength; 782 783 // Check against our minimum height 784 heightSize = Math.max(heightSize, getSuggestedMinimumHeight()); 785 786 // Reconcile our calculated size with the heightMeasureSpec 787 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0); 788 heightSize = heightSizeAndState & MEASURED_SIZE_MASK; 789 790 // Either expand children with weight to take up available space or 791 // shrink them if they extend beyond our current bounds 792 int delta = heightSize - mTotalLength; 793 if (delta != 0 && totalWeight > 0.0f) { 794 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 795 796 mTotalLength = 0; 797 798 for (int i = 0; i < count; ++i) { 799 final View child = getVirtualChildAt(i); 800 801 if (child.getVisibility() == View.GONE) { 802 continue; 803 } 804 805 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 806 807 float childExtra = lp.weight; 808 if (childExtra > 0) { 809 // Child said it could absorb extra space -- give him his share 810 int share = (int) (childExtra * delta / weightSum); 811 weightSum -= childExtra; 812 delta -= share; 813 814 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 815 mPaddingLeft + mPaddingRight + 816 lp.leftMargin + lp.rightMargin, lp.width); 817 818 // TODO: Use a field like lp.isMeasured to figure out if this 819 // child has been previously measured 820 if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) { 821 // child was measured once already above... 822 // base new measurement on stored values 823 int childHeight = child.getMeasuredHeight() + share; 824 if (childHeight < 0) { 825 childHeight = 0; 826 } 827 828 child.measure(childWidthMeasureSpec, 829 MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)); 830 } else { 831 // child was skipped in the loop above. 832 // Measure for this first time here 833 child.measure(childWidthMeasureSpec, 834 MeasureSpec.makeMeasureSpec(share > 0 ? share : 0, 835 MeasureSpec.EXACTLY)); 836 } 837 838 // Child may now not fit in vertical dimension. 839 childState = combineMeasuredStates(childState, child.getMeasuredState() 840 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); 841 } 842 843 final int margin = lp.leftMargin + lp.rightMargin; 844 final int measuredWidth = child.getMeasuredWidth() + margin; 845 maxWidth = Math.max(maxWidth, measuredWidth); 846 847 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY && 848 lp.width == LayoutParams.MATCH_PARENT; 849 850 alternativeMaxWidth = Math.max(alternativeMaxWidth, 851 matchWidthLocally ? margin : measuredWidth); 852 853 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 854 855 final int totalLength = mTotalLength; 856 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() + 857 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 858 } 859 860 // Add in our padding 861 mTotalLength += mPaddingTop + mPaddingBottom; 862 // TODO: Should we recompute the heightSpec based on the new total length? 863 } else { 864 alternativeMaxWidth = Math.max(alternativeMaxWidth, 865 weightedMaxWidth); 866 867 868 // We have no limit, so make all weighted views as tall as the largest child. 869 // Children will have already been measured once. 870 if (useLargestChild && heightMode != MeasureSpec.EXACTLY) { 871 for (int i = 0; i < count; i++) { 872 final View child = getVirtualChildAt(i); 873 874 if (child == null || child.getVisibility() == View.GONE) { 875 continue; 876 } 877 878 final LinearLayout.LayoutParams lp = 879 (LinearLayout.LayoutParams) child.getLayoutParams(); 880 881 float childExtra = lp.weight; 882 if (childExtra > 0) { 883 child.measure( 884 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), 885 MeasureSpec.EXACTLY), 886 MeasureSpec.makeMeasureSpec(largestChildHeight, 887 MeasureSpec.EXACTLY)); 888 } 889 } 890 } 891 } 892 893 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) { 894 maxWidth = alternativeMaxWidth; 895 } 896 897 maxWidth += mPaddingLeft + mPaddingRight; 898 899 // Check against our minimum width 900 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 901 902 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 903 heightSizeAndState); 904 905 if (matchWidth) { 906 forceUniformWidth(count, heightMeasureSpec); 907 } 908 } 909 910 private void forceUniformWidth(int count, int heightMeasureSpec) { 911 // Pretend that the linear layout has an exact size. 912 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), 913 MeasureSpec.EXACTLY); 914 for (int i = 0; i< count; ++i) { 915 final View child = getVirtualChildAt(i); 916 if (child.getVisibility() != GONE) { 917 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams()); 918 919 if (lp.width == LayoutParams.MATCH_PARENT) { 920 // Temporarily force children to reuse their old measured height 921 // FIXME: this may not be right for something like wrapping text? 922 int oldHeight = lp.height; 923 lp.height = child.getMeasuredHeight(); 924 925 // Remeasue with new dimensions 926 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0); 927 lp.height = oldHeight; 928 } 929 } 930 } 931 } 932 933 /** 934 * Measures the children when the orientation of this LinearLayout is set 935 * to {@link #HORIZONTAL}. 936 * 937 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 938 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 939 * 940 * @see #getOrientation() 941 * @see #setOrientation(int) 942 * @see #onMeasure(int, int) 943 */ 944 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { 945 mTotalLength = 0; 946 int maxHeight = 0; 947 int childState = 0; 948 int alternativeMaxHeight = 0; 949 int weightedMaxHeight = 0; 950 boolean allFillParent = true; 951 float totalWeight = 0; 952 953 final int count = getVirtualChildCount(); 954 955 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 956 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 957 958 boolean matchHeight = false; 959 960 if (mMaxAscent == null || mMaxDescent == null) { 961 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT]; 962 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT]; 963 } 964 965 final int[] maxAscent = mMaxAscent; 966 final int[] maxDescent = mMaxDescent; 967 968 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 969 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 970 971 final boolean baselineAligned = mBaselineAligned; 972 final boolean useLargestChild = mUseLargestChild; 973 974 final boolean isExactly = widthMode == MeasureSpec.EXACTLY; 975 976 int largestChildWidth = Integer.MIN_VALUE; 977 978 // See how wide everyone is. Also remember max height. 979 for (int i = 0; i < count; ++i) { 980 final View child = getVirtualChildAt(i); 981 982 if (child == null) { 983 mTotalLength += measureNullChild(i); 984 continue; 985 } 986 987 if (child.getVisibility() == GONE) { 988 i += getChildrenSkipCount(child, i); 989 continue; 990 } 991 992 if (hasDividerBeforeChildAt(i)) { 993 mTotalLength += mDividerWidth; 994 } 995 996 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 997 child.getLayoutParams(); 998 999 totalWeight += lp.weight; 1000 1001 if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) { 1002 // Optimization: don't bother measuring children who are going to use 1003 // leftover space. These views will get measured again down below if 1004 // there is any leftover space. 1005 if (isExactly) { 1006 mTotalLength += lp.leftMargin + lp.rightMargin; 1007 } else { 1008 final int totalLength = mTotalLength; 1009 mTotalLength = Math.max(totalLength, totalLength + 1010 lp.leftMargin + lp.rightMargin); 1011 } 1012 1013 // Baseline alignment requires to measure widgets to obtain the 1014 // baseline offset (in particular for TextViews). The following 1015 // defeats the optimization mentioned above. Allow the child to 1016 // use as much space as it wants because we can shrink things 1017 // later (and re-measure). 1018 if (baselineAligned) { 1019 final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 1020 child.measure(freeSpec, freeSpec); 1021 } 1022 } else { 1023 int oldWidth = Integer.MIN_VALUE; 1024 1025 if (lp.width == 0 && lp.weight > 0) { 1026 // widthMode is either UNSPECIFIED or AT_MOST, and this 1027 // child 1028 // wanted to stretch to fill available space. Translate that to 1029 // WRAP_CONTENT so that it does not end up with a width of 0 1030 oldWidth = 0; 1031 lp.width = LayoutParams.WRAP_CONTENT; 1032 } 1033 1034 // Determine how big this child would like to be. If this or 1035 // previous children have given a weight, then we allow it to 1036 // use all available space (and we will shrink things later 1037 // if needed). 1038 measureChildBeforeLayout(child, i, widthMeasureSpec, 1039 totalWeight == 0 ? mTotalLength : 0, 1040 heightMeasureSpec, 0); 1041 1042 if (oldWidth != Integer.MIN_VALUE) { 1043 lp.width = oldWidth; 1044 } 1045 1046 final int childWidth = child.getMeasuredWidth(); 1047 if (isExactly) { 1048 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin + 1049 getNextLocationOffset(child); 1050 } else { 1051 final int totalLength = mTotalLength; 1052 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin + 1053 lp.rightMargin + getNextLocationOffset(child)); 1054 } 1055 1056 if (useLargestChild) { 1057 largestChildWidth = Math.max(childWidth, largestChildWidth); 1058 } 1059 } 1060 1061 boolean matchHeightLocally = false; 1062 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) { 1063 // The height of the linear layout will scale, and at least one 1064 // child said it wanted to match our height. Set a flag indicating that 1065 // we need to remeasure at least that view when we know our height. 1066 matchHeight = true; 1067 matchHeightLocally = true; 1068 } 1069 1070 final int margin = lp.topMargin + lp.bottomMargin; 1071 final int childHeight = child.getMeasuredHeight() + margin; 1072 childState = combineMeasuredStates(childState, child.getMeasuredState()); 1073 1074 if (baselineAligned) { 1075 final int childBaseline = child.getBaseline(); 1076 if (childBaseline != -1) { 1077 // Translates the child's vertical gravity into an index 1078 // in the range 0..VERTICAL_GRAVITY_COUNT 1079 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1080 & Gravity.VERTICAL_GRAVITY_MASK; 1081 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1082 & ~Gravity.AXIS_SPECIFIED) >> 1; 1083 1084 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1085 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline); 1086 } 1087 } 1088 1089 maxHeight = Math.max(maxHeight, childHeight); 1090 1091 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1092 if (lp.weight > 0) { 1093 /* 1094 * Heights of weighted Views are bogus if we end up 1095 * remeasuring, so keep them separate. 1096 */ 1097 weightedMaxHeight = Math.max(weightedMaxHeight, 1098 matchHeightLocally ? margin : childHeight); 1099 } else { 1100 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1101 matchHeightLocally ? margin : childHeight); 1102 } 1103 1104 i += getChildrenSkipCount(child, i); 1105 } 1106 1107 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) { 1108 mTotalLength += mDividerWidth; 1109 } 1110 1111 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1112 // the most common case 1113 if (maxAscent[INDEX_TOP] != -1 || 1114 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1115 maxAscent[INDEX_BOTTOM] != -1 || 1116 maxAscent[INDEX_FILL] != -1) { 1117 final int ascent = Math.max(maxAscent[INDEX_FILL], 1118 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1119 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1120 final int descent = Math.max(maxDescent[INDEX_FILL], 1121 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1122 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1123 maxHeight = Math.max(maxHeight, ascent + descent); 1124 } 1125 1126 if (useLargestChild && 1127 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { 1128 mTotalLength = 0; 1129 1130 for (int i = 0; i < count; ++i) { 1131 final View child = getVirtualChildAt(i); 1132 1133 if (child == null) { 1134 mTotalLength += measureNullChild(i); 1135 continue; 1136 } 1137 1138 if (child.getVisibility() == GONE) { 1139 i += getChildrenSkipCount(child, i); 1140 continue; 1141 } 1142 1143 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 1144 child.getLayoutParams(); 1145 if (isExactly) { 1146 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin + 1147 getNextLocationOffset(child); 1148 } else { 1149 final int totalLength = mTotalLength; 1150 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth + 1151 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1152 } 1153 } 1154 } 1155 1156 // Add in our padding 1157 mTotalLength += mPaddingLeft + mPaddingRight; 1158 1159 int widthSize = mTotalLength; 1160 1161 // Check against our minimum width 1162 widthSize = Math.max(widthSize, getSuggestedMinimumWidth()); 1163 1164 // Reconcile our calculated size with the widthMeasureSpec 1165 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0); 1166 widthSize = widthSizeAndState & MEASURED_SIZE_MASK; 1167 1168 // Either expand children with weight to take up available space or 1169 // shrink them if they extend beyond our current bounds 1170 int delta = widthSize - mTotalLength; 1171 if (delta != 0 && totalWeight > 0.0f) { 1172 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 1173 1174 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 1175 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 1176 maxHeight = -1; 1177 1178 mTotalLength = 0; 1179 1180 for (int i = 0; i < count; ++i) { 1181 final View child = getVirtualChildAt(i); 1182 1183 if (child == null || child.getVisibility() == View.GONE) { 1184 continue; 1185 } 1186 1187 final LinearLayout.LayoutParams lp = 1188 (LinearLayout.LayoutParams) child.getLayoutParams(); 1189 1190 float childExtra = lp.weight; 1191 if (childExtra > 0) { 1192 // Child said it could absorb extra space -- give him his share 1193 int share = (int) (childExtra * delta / weightSum); 1194 weightSum -= childExtra; 1195 delta -= share; 1196 1197 final int childHeightMeasureSpec = getChildMeasureSpec( 1198 heightMeasureSpec, 1199 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin, 1200 lp.height); 1201 1202 // TODO: Use a field like lp.isMeasured to figure out if this 1203 // child has been previously measured 1204 if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) { 1205 // child was measured once already above ... base new measurement 1206 // on stored values 1207 int childWidth = child.getMeasuredWidth() + share; 1208 if (childWidth < 0) { 1209 childWidth = 0; 1210 } 1211 1212 child.measure( 1213 MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), 1214 childHeightMeasureSpec); 1215 } else { 1216 // child was skipped in the loop above. Measure for this first time here 1217 child.measure(MeasureSpec.makeMeasureSpec( 1218 share > 0 ? share : 0, MeasureSpec.EXACTLY), 1219 childHeightMeasureSpec); 1220 } 1221 1222 // Child may now not fit in horizontal dimension. 1223 childState = combineMeasuredStates(childState, 1224 child.getMeasuredState() & MEASURED_STATE_MASK); 1225 } 1226 1227 if (isExactly) { 1228 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin + 1229 getNextLocationOffset(child); 1230 } else { 1231 final int totalLength = mTotalLength; 1232 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() + 1233 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1234 } 1235 1236 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY && 1237 lp.height == LayoutParams.MATCH_PARENT; 1238 1239 final int margin = lp.topMargin + lp .bottomMargin; 1240 int childHeight = child.getMeasuredHeight() + margin; 1241 maxHeight = Math.max(maxHeight, childHeight); 1242 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1243 matchHeightLocally ? margin : childHeight); 1244 1245 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1246 1247 if (baselineAligned) { 1248 final int childBaseline = child.getBaseline(); 1249 if (childBaseline != -1) { 1250 // Translates the child's vertical gravity into an index in the range 0..2 1251 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1252 & Gravity.VERTICAL_GRAVITY_MASK; 1253 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1254 & ~Gravity.AXIS_SPECIFIED) >> 1; 1255 1256 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1257 maxDescent[index] = Math.max(maxDescent[index], 1258 childHeight - childBaseline); 1259 } 1260 } 1261 } 1262 1263 // Add in our padding 1264 mTotalLength += mPaddingLeft + mPaddingRight; 1265 // TODO: Should we update widthSize with the new total length? 1266 1267 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1268 // the most common case 1269 if (maxAscent[INDEX_TOP] != -1 || 1270 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1271 maxAscent[INDEX_BOTTOM] != -1 || 1272 maxAscent[INDEX_FILL] != -1) { 1273 final int ascent = Math.max(maxAscent[INDEX_FILL], 1274 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1275 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1276 final int descent = Math.max(maxDescent[INDEX_FILL], 1277 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1278 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1279 maxHeight = Math.max(maxHeight, ascent + descent); 1280 } 1281 } else { 1282 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); 1283 1284 // We have no limit, so make all weighted views as wide as the largest child. 1285 // Children will have already been measured once. 1286 if (useLargestChild && widthMode != MeasureSpec.EXACTLY) { 1287 for (int i = 0; i < count; i++) { 1288 final View child = getVirtualChildAt(i); 1289 1290 if (child == null || child.getVisibility() == View.GONE) { 1291 continue; 1292 } 1293 1294 final LinearLayout.LayoutParams lp = 1295 (LinearLayout.LayoutParams) child.getLayoutParams(); 1296 1297 float childExtra = lp.weight; 1298 if (childExtra > 0) { 1299 child.measure( 1300 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY), 1301 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), 1302 MeasureSpec.EXACTLY)); 1303 } 1304 } 1305 } 1306 } 1307 1308 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { 1309 maxHeight = alternativeMaxHeight; 1310 } 1311 1312 maxHeight += mPaddingTop + mPaddingBottom; 1313 1314 // Check against our minimum height 1315 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 1316 1317 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK), 1318 resolveSizeAndState(maxHeight, heightMeasureSpec, 1319 (childState<<MEASURED_HEIGHT_STATE_SHIFT))); 1320 1321 if (matchHeight) { 1322 forceUniformHeight(count, widthMeasureSpec); 1323 } 1324 } 1325 1326 private void forceUniformHeight(int count, int widthMeasureSpec) { 1327 // Pretend that the linear layout has an exact size. This is the measured height of 1328 // ourselves. The measured height should be the max height of the children, changed 1329 // to accomodate the heightMesureSpec from the parent 1330 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), 1331 MeasureSpec.EXACTLY); 1332 for (int i = 0; i < count; ++i) { 1333 final View child = getVirtualChildAt(i); 1334 if (child.getVisibility() != GONE) { 1335 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 1336 1337 if (lp.height == LayoutParams.MATCH_PARENT) { 1338 // Temporarily force children to reuse their old measured width 1339 // FIXME: this may not be right for something like wrapping text? 1340 int oldWidth = lp.width; 1341 lp.width = child.getMeasuredWidth(); 1342 1343 // Remeasure with new dimensions 1344 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0); 1345 lp.width = oldWidth; 1346 } 1347 } 1348 } 1349 } 1350 1351 /** 1352 * <p>Returns the number of children to skip after measuring/laying out 1353 * the specified child.</p> 1354 * 1355 * @param child the child after which we want to skip children 1356 * @param index the index of the child after which we want to skip children 1357 * @return the number of children to skip, 0 by default 1358 */ 1359 int getChildrenSkipCount(View child, int index) { 1360 return 0; 1361 } 1362 1363 /** 1364 * <p>Returns the size (width or height) that should be occupied by a null 1365 * child.</p> 1366 * 1367 * @param childIndex the index of the null child 1368 * @return the width or height of the child depending on the orientation 1369 */ 1370 int measureNullChild(int childIndex) { 1371 return 0; 1372 } 1373 1374 /** 1375 * <p>Measure the child according to the parent's measure specs. This 1376 * method should be overriden by subclasses to force the sizing of 1377 * children. This method is called by {@link #measureVertical(int, int)} and 1378 * {@link #measureHorizontal(int, int)}.</p> 1379 * 1380 * @param child the child to measure 1381 * @param childIndex the index of the child in this view 1382 * @param widthMeasureSpec horizontal space requirements as imposed by the parent 1383 * @param totalWidth extra space that has been used up by the parent horizontally 1384 * @param heightMeasureSpec vertical space requirements as imposed by the parent 1385 * @param totalHeight extra space that has been used up by the parent vertically 1386 */ 1387 void measureChildBeforeLayout(View child, int childIndex, 1388 int widthMeasureSpec, int totalWidth, int heightMeasureSpec, 1389 int totalHeight) { 1390 measureChildWithMargins(child, widthMeasureSpec, totalWidth, 1391 heightMeasureSpec, totalHeight); 1392 } 1393 1394 /** 1395 * <p>Return the location offset of the specified child. This can be used 1396 * by subclasses to change the location of a given widget.</p> 1397 * 1398 * @param child the child for which to obtain the location offset 1399 * @return the location offset in pixels 1400 */ 1401 int getLocationOffset(View child) { 1402 return 0; 1403 } 1404 1405 /** 1406 * <p>Return the size offset of the next sibling of the specified child. 1407 * This can be used by subclasses to change the location of the widget 1408 * following <code>child</code>.</p> 1409 * 1410 * @param child the child whose next sibling will be moved 1411 * @return the location offset of the next child in pixels 1412 */ 1413 int getNextLocationOffset(View child) { 1414 return 0; 1415 } 1416 1417 @Override 1418 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1419 if (mOrientation == VERTICAL) { 1420 layoutVertical(); 1421 } else { 1422 layoutHorizontal(); 1423 } 1424 } 1425 1426 /** 1427 * Position the children during a layout pass if the orientation of this 1428 * LinearLayout is set to {@link #VERTICAL}. 1429 * 1430 * @see #getOrientation() 1431 * @see #setOrientation(int) 1432 * @see #onLayout(boolean, int, int, int, int) 1433 */ 1434 void layoutVertical() { 1435 final int paddingLeft = mPaddingLeft; 1436 1437 int childTop; 1438 int childLeft; 1439 1440 // Where right end of child should go 1441 final int width = mRight - mLeft; 1442 int childRight = width - mPaddingRight; 1443 1444 // Space available for child 1445 int childSpace = width - paddingLeft - mPaddingRight; 1446 1447 final int count = getVirtualChildCount(); 1448 1449 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1450 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1451 1452 switch (majorGravity) { 1453 case Gravity.BOTTOM: 1454 // mTotalLength contains the padding already 1455 childTop = mPaddingTop + mBottom - mTop - mTotalLength; 1456 break; 1457 1458 // mTotalLength contains the padding already 1459 case Gravity.CENTER_VERTICAL: 1460 childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2; 1461 break; 1462 1463 case Gravity.TOP: 1464 default: 1465 childTop = mPaddingTop; 1466 break; 1467 } 1468 1469 for (int i = 0; i < count; i++) { 1470 final View child = getVirtualChildAt(i); 1471 if (child == null) { 1472 childTop += measureNullChild(i); 1473 } else if (child.getVisibility() != GONE) { 1474 final int childWidth = child.getMeasuredWidth(); 1475 final int childHeight = child.getMeasuredHeight(); 1476 1477 final LinearLayout.LayoutParams lp = 1478 (LinearLayout.LayoutParams) child.getLayoutParams(); 1479 1480 int gravity = lp.gravity; 1481 if (gravity < 0) { 1482 gravity = minorGravity; 1483 } 1484 final int layoutDirection = getResolvedLayoutDirection(); 1485 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 1486 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 1487 case Gravity.CENTER_HORIZONTAL: 1488 childLeft = paddingLeft + ((childSpace - childWidth) / 2) 1489 + lp.leftMargin - lp.rightMargin; 1490 break; 1491 1492 case Gravity.RIGHT: 1493 childLeft = childRight - childWidth - lp.rightMargin; 1494 break; 1495 1496 case Gravity.LEFT: 1497 default: 1498 childLeft = paddingLeft + lp.leftMargin; 1499 break; 1500 } 1501 1502 if (hasDividerBeforeChildAt(i)) { 1503 childTop += mDividerHeight; 1504 } 1505 1506 childTop += lp.topMargin; 1507 setChildFrame(child, childLeft, childTop + getLocationOffset(child), 1508 childWidth, childHeight); 1509 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); 1510 1511 i += getChildrenSkipCount(child, i); 1512 } 1513 } 1514 } 1515 1516 /** 1517 * Position the children during a layout pass if the orientation of this 1518 * LinearLayout is set to {@link #HORIZONTAL}. 1519 * 1520 * @see #getOrientation() 1521 * @see #setOrientation(int) 1522 * @see #onLayout(boolean, int, int, int, int) 1523 */ 1524 void layoutHorizontal() { 1525 final boolean isLayoutRtl = isLayoutRtl(); 1526 final int paddingTop = mPaddingTop; 1527 1528 int childTop; 1529 int childLeft; 1530 1531 // Where bottom of child should go 1532 final int height = mBottom - mTop; 1533 int childBottom = height - mPaddingBottom; 1534 1535 // Space available for child 1536 int childSpace = height - paddingTop - mPaddingBottom; 1537 1538 final int count = getVirtualChildCount(); 1539 1540 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1541 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1542 1543 final boolean baselineAligned = mBaselineAligned; 1544 1545 final int[] maxAscent = mMaxAscent; 1546 final int[] maxDescent = mMaxDescent; 1547 1548 final int layoutDirection = getResolvedLayoutDirection(); 1549 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) { 1550 case Gravity.RIGHT: 1551 // mTotalLength contains the padding already 1552 childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; 1553 break; 1554 1555 case Gravity.CENTER_HORIZONTAL: 1556 // mTotalLength contains the padding already 1557 childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2; 1558 break; 1559 1560 case Gravity.LEFT: 1561 default: 1562 childLeft = mPaddingLeft; 1563 break; 1564 } 1565 1566 int start = 0; 1567 int dir = 1; 1568 //In case of RTL, start drawing from the last child. 1569 if (isLayoutRtl) { 1570 start = count - 1; 1571 dir = -1; 1572 } 1573 1574 for (int i = 0; i < count; i++) { 1575 int childIndex = start + dir * i; 1576 final View child = getVirtualChildAt(childIndex); 1577 1578 if (child == null) { 1579 childLeft += measureNullChild(childIndex); 1580 } else if (child.getVisibility() != GONE) { 1581 final int childWidth = child.getMeasuredWidth(); 1582 final int childHeight = child.getMeasuredHeight(); 1583 int childBaseline = -1; 1584 1585 final LinearLayout.LayoutParams lp = 1586 (LinearLayout.LayoutParams) child.getLayoutParams(); 1587 1588 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) { 1589 childBaseline = child.getBaseline(); 1590 } 1591 1592 int gravity = lp.gravity; 1593 if (gravity < 0) { 1594 gravity = minorGravity; 1595 } 1596 1597 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 1598 case Gravity.TOP: 1599 childTop = paddingTop + lp.topMargin; 1600 if (childBaseline != -1) { 1601 childTop += maxAscent[INDEX_TOP] - childBaseline; 1602 } 1603 break; 1604 1605 case Gravity.CENTER_VERTICAL: 1606 // Removed support for baseline alignment when layout_gravity or 1607 // gravity == center_vertical. See bug #1038483. 1608 // Keep the code around if we need to re-enable this feature 1609 // if (childBaseline != -1) { 1610 // // Align baselines vertically only if the child is smaller than us 1611 // if (childSpace - childHeight > 0) { 1612 // childTop = paddingTop + (childSpace / 2) - childBaseline; 1613 // } else { 1614 // childTop = paddingTop + (childSpace - childHeight) / 2; 1615 // } 1616 // } else { 1617 childTop = paddingTop + ((childSpace - childHeight) / 2) 1618 + lp.topMargin - lp.bottomMargin; 1619 break; 1620 1621 case Gravity.BOTTOM: 1622 childTop = childBottom - childHeight - lp.bottomMargin; 1623 if (childBaseline != -1) { 1624 int descent = child.getMeasuredHeight() - childBaseline; 1625 childTop -= (maxDescent[INDEX_BOTTOM] - descent); 1626 } 1627 break; 1628 default: 1629 childTop = paddingTop; 1630 break; 1631 } 1632 1633 if (hasDividerBeforeChildAt(childIndex)) { 1634 childLeft += mDividerWidth; 1635 } 1636 1637 childLeft += lp.leftMargin; 1638 setChildFrame(child, childLeft + getLocationOffset(child), childTop, 1639 childWidth, childHeight); 1640 childLeft += childWidth + lp.rightMargin + 1641 getNextLocationOffset(child); 1642 1643 i += getChildrenSkipCount(child, childIndex); 1644 } 1645 } 1646 } 1647 1648 private void setChildFrame(View child, int left, int top, int width, int height) { 1649 child.layout(left, top, left + width, top + height); 1650 } 1651 1652 /** 1653 * Should the layout be a column or a row. 1654 * @param orientation Pass HORIZONTAL or VERTICAL. Default 1655 * value is HORIZONTAL. 1656 * 1657 * @attr ref android.R.styleable#LinearLayout_orientation 1658 */ 1659 public void setOrientation(int orientation) { 1660 if (mOrientation != orientation) { 1661 mOrientation = orientation; 1662 requestLayout(); 1663 } 1664 } 1665 1666 /** 1667 * Returns the current orientation. 1668 * 1669 * @return either {@link #HORIZONTAL} or {@link #VERTICAL} 1670 */ 1671 public int getOrientation() { 1672 return mOrientation; 1673 } 1674 1675 /** 1676 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If 1677 * this layout has a VERTICAL orientation, this controls where all the child 1678 * views are placed if there is extra vertical space. If this layout has a 1679 * HORIZONTAL orientation, this controls the alignment of the children. 1680 * 1681 * @param gravity See {@link android.view.Gravity} 1682 * 1683 * @attr ref android.R.styleable#LinearLayout_gravity 1684 */ 1685 @android.view.RemotableViewMethod 1686 public void setGravity(int gravity) { 1687 if (mGravity != gravity) { 1688 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { 1689 gravity |= Gravity.START; 1690 } 1691 1692 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 1693 gravity |= Gravity.TOP; 1694 } 1695 1696 mGravity = gravity; 1697 requestLayout(); 1698 } 1699 } 1700 1701 @android.view.RemotableViewMethod 1702 public void setHorizontalGravity(int horizontalGravity) { 1703 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1704 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) { 1705 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity; 1706 requestLayout(); 1707 } 1708 } 1709 1710 @android.view.RemotableViewMethod 1711 public void setVerticalGravity(int verticalGravity) { 1712 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK; 1713 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) { 1714 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity; 1715 requestLayout(); 1716 } 1717 } 1718 1719 @Override 1720 public LayoutParams generateLayoutParams(AttributeSet attrs) { 1721 return new LinearLayout.LayoutParams(getContext(), attrs); 1722 } 1723 1724 /** 1725 * Returns a set of layout parameters with a width of 1726 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} 1727 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} 1728 * when the layout's orientation is {@link #VERTICAL}. When the orientation is 1729 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT} 1730 * and the height to {@link LayoutParams#WRAP_CONTENT}. 1731 */ 1732 @Override 1733 protected LayoutParams generateDefaultLayoutParams() { 1734 if (mOrientation == HORIZONTAL) { 1735 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 1736 } else if (mOrientation == VERTICAL) { 1737 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 1738 } 1739 return null; 1740 } 1741 1742 @Override 1743 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1744 return new LayoutParams(p); 1745 } 1746 1747 1748 // Override to allow type-checking of LayoutParams. 1749 @Override 1750 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1751 return p instanceof LinearLayout.LayoutParams; 1752 } 1753 1754 @Override 1755 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1756 super.onInitializeAccessibilityEvent(event); 1757 event.setClassName(LinearLayout.class.getName()); 1758 } 1759 1760 @Override 1761 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1762 super.onInitializeAccessibilityNodeInfo(info); 1763 info.setClassName(LinearLayout.class.getName()); 1764 } 1765 1766 /** 1767 * Per-child layout information associated with ViewLinearLayout. 1768 * 1769 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight 1770 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity 1771 */ 1772 public static class LayoutParams extends ViewGroup.MarginLayoutParams { 1773 /** 1774 * Indicates how much of the extra space in the LinearLayout will be 1775 * allocated to the view associated with these LayoutParams. Specify 1776 * 0 if the view should not be stretched. Otherwise the extra pixels 1777 * will be pro-rated among all views whose weight is greater than 0. 1778 */ 1779 @ViewDebug.ExportedProperty(category = "layout") 1780 public float weight; 1781 1782 /** 1783 * Gravity for the view associated with these LayoutParams. 1784 * 1785 * @see android.view.Gravity 1786 */ 1787 @ViewDebug.ExportedProperty(category = "layout", mapping = { 1788 @ViewDebug.IntToString(from = -1, to = "NONE"), 1789 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), 1790 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), 1791 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), 1792 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), 1793 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), 1794 @ViewDebug.IntToString(from = Gravity.START, to = "START"), 1795 @ViewDebug.IntToString(from = Gravity.END, to = "END"), 1796 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), 1797 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), 1798 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), 1799 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), 1800 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), 1801 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") 1802 }) 1803 public int gravity = -1; 1804 1805 /** 1806 * {@inheritDoc} 1807 */ 1808 public LayoutParams(Context c, AttributeSet attrs) { 1809 super(c, attrs); 1810 TypedArray a = 1811 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout); 1812 1813 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0); 1814 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1); 1815 1816 a.recycle(); 1817 } 1818 1819 /** 1820 * {@inheritDoc} 1821 */ 1822 public LayoutParams(int width, int height) { 1823 super(width, height); 1824 weight = 0; 1825 } 1826 1827 /** 1828 * Creates a new set of layout parameters with the specified width, height 1829 * and weight. 1830 * 1831 * @param width the width, either {@link #MATCH_PARENT}, 1832 * {@link #WRAP_CONTENT} or a fixed size in pixels 1833 * @param height the height, either {@link #MATCH_PARENT}, 1834 * {@link #WRAP_CONTENT} or a fixed size in pixels 1835 * @param weight the weight 1836 */ 1837 public LayoutParams(int width, int height, float weight) { 1838 super(width, height); 1839 this.weight = weight; 1840 } 1841 1842 /** 1843 * {@inheritDoc} 1844 */ 1845 public LayoutParams(ViewGroup.LayoutParams p) { 1846 super(p); 1847 } 1848 1849 /** 1850 * {@inheritDoc} 1851 */ 1852 public LayoutParams(MarginLayoutParams source) { 1853 super(source); 1854 } 1855 1856 @Override 1857 public String debug(String output) { 1858 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) + 1859 ", height=" + sizeToString(height) + " weight=" + weight + "}"; 1860 } 1861 } 1862 } 1863