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