LinearLayout.java revision c0053223bedf33581b0830fb87be32c1f26e5372
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 { 566 return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0; 567 } 568 } 569 570 /** 571 * Measures the children when the orientation of this LinearLayout is set 572 * to {@link #VERTICAL}. 573 * 574 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 575 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 576 * 577 * @see #getOrientation() 578 * @see #setOrientation(int) 579 * @see #onMeasure(int, int) 580 */ 581 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { 582 mTotalLength = 0; 583 int maxWidth = 0; 584 int childState = 0; 585 int alternativeMaxWidth = 0; 586 int weightedMaxWidth = 0; 587 boolean allFillParent = true; 588 float totalWeight = 0; 589 590 final int count = getVirtualChildCount(); 591 592 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 593 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 594 595 boolean matchWidth = false; 596 597 final int baselineChildIndex = mBaselineAlignedChildIndex; 598 final boolean useLargestChild = mUseLargestChild; 599 600 int largestChildHeight = Integer.MIN_VALUE; 601 602 // See how tall everyone is. Also remember max width. 603 for (int i = 0; i < count; ++i) { 604 final View child = getVirtualChildAt(i); 605 606 if (child == null) { 607 mTotalLength += measureNullChild(i); 608 continue; 609 } 610 611 if (child.getVisibility() == View.GONE) { 612 i += getChildrenSkipCount(child, i); 613 continue; 614 } 615 616 if (hasDividerBeforeChildAt(i)) { 617 mTotalLength += mDividerHeight; 618 } 619 620 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 621 622 totalWeight += lp.weight; 623 624 if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) { 625 // Optimization: don't bother measuring children who are going to use 626 // leftover space. These views will get measured again down below if 627 // there is any leftover space. 628 final int totalLength = mTotalLength; 629 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin); 630 } else { 631 int oldHeight = Integer.MIN_VALUE; 632 633 if (lp.height == 0 && lp.weight > 0) { 634 // heightMode is either UNSPECIFIED or AT_MOST, and this 635 // child wanted to stretch to fill available space. 636 // Translate that to WRAP_CONTENT so that it does not end up 637 // with a height of 0 638 oldHeight = 0; 639 lp.height = LayoutParams.WRAP_CONTENT; 640 } 641 642 // Determine how big this child would like to be. If this or 643 // previous children have given a weight, then we allow it to 644 // use all available space (and we will shrink things later 645 // if needed). 646 measureChildBeforeLayout( 647 child, i, widthMeasureSpec, 0, heightMeasureSpec, 648 totalWeight == 0 ? mTotalLength : 0); 649 650 if (oldHeight != Integer.MIN_VALUE) { 651 lp.height = oldHeight; 652 } 653 654 final int childHeight = child.getMeasuredHeight(); 655 final int totalLength = mTotalLength; 656 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + 657 lp.bottomMargin + getNextLocationOffset(child)); 658 659 if (useLargestChild) { 660 largestChildHeight = Math.max(childHeight, largestChildHeight); 661 } 662 } 663 664 /** 665 * If applicable, compute the additional offset to the child's baseline 666 * we'll need later when asked {@link #getBaseline}. 667 */ 668 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) { 669 mBaselineChildTop = mTotalLength; 670 } 671 672 // if we are trying to use a child index for our baseline, the above 673 // book keeping only works if there are no children above it with 674 // weight. fail fast to aid the developer. 675 if (i < baselineChildIndex && lp.weight > 0) { 676 throw new RuntimeException("A child of LinearLayout with index " 677 + "less than mBaselineAlignedChildIndex has weight > 0, which " 678 + "won't work. Either remove the weight, or don't set " 679 + "mBaselineAlignedChildIndex."); 680 } 681 682 boolean matchWidthLocally = false; 683 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) { 684 // The width of the linear layout will scale, and at least one 685 // child said it wanted to match our width. Set a flag 686 // indicating that we need to remeasure at least that view when 687 // we know our width. 688 matchWidth = true; 689 matchWidthLocally = true; 690 } 691 692 final int margin = lp.leftMargin + lp.rightMargin; 693 final int measuredWidth = child.getMeasuredWidth() + margin; 694 maxWidth = Math.max(maxWidth, measuredWidth); 695 childState = combineMeasuredStates(childState, child.getMeasuredState()); 696 697 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 698 if (lp.weight > 0) { 699 /* 700 * Widths of weighted Views are bogus if we end up 701 * remeasuring, so keep them separate. 702 */ 703 weightedMaxWidth = Math.max(weightedMaxWidth, 704 matchWidthLocally ? margin : measuredWidth); 705 } else { 706 alternativeMaxWidth = Math.max(alternativeMaxWidth, 707 matchWidthLocally ? margin : measuredWidth); 708 } 709 710 i += getChildrenSkipCount(child, i); 711 } 712 713 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) { 714 mTotalLength += mDividerHeight; 715 } 716 717 if (useLargestChild && 718 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) { 719 mTotalLength = 0; 720 721 for (int i = 0; i < count; ++i) { 722 final View child = getVirtualChildAt(i); 723 724 if (child == null) { 725 mTotalLength += measureNullChild(i); 726 continue; 727 } 728 729 if (child.getVisibility() == GONE) { 730 i += getChildrenSkipCount(child, i); 731 continue; 732 } 733 734 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 735 child.getLayoutParams(); 736 // Account for negative margins 737 final int totalLength = mTotalLength; 738 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight + 739 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 740 } 741 } 742 743 // Add in our padding 744 mTotalLength += mPaddingTop + mPaddingBottom; 745 746 int heightSize = mTotalLength; 747 748 // Check against our minimum height 749 heightSize = Math.max(heightSize, getSuggestedMinimumHeight()); 750 751 // Reconcile our calculated size with the heightMeasureSpec 752 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0); 753 heightSize = heightSizeAndState & MEASURED_SIZE_MASK; 754 755 // Either expand children with weight to take up available space or 756 // shrink them if they extend beyond our current bounds 757 int delta = heightSize - mTotalLength; 758 if (delta != 0 && totalWeight > 0.0f) { 759 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 760 761 mTotalLength = 0; 762 763 for (int i = 0; i < count; ++i) { 764 final View child = getVirtualChildAt(i); 765 766 if (child.getVisibility() == View.GONE) { 767 continue; 768 } 769 770 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 771 772 float childExtra = lp.weight; 773 if (childExtra > 0) { 774 // Child said it could absorb extra space -- give him his share 775 int share = (int) (childExtra * delta / weightSum); 776 weightSum -= childExtra; 777 delta -= share; 778 779 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 780 mPaddingLeft + mPaddingRight + 781 lp.leftMargin + lp.rightMargin, lp.width); 782 783 // TODO: Use a field like lp.isMeasured to figure out if this 784 // child has been previously measured 785 if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) { 786 // child was measured once already above... 787 // base new measurement on stored values 788 int childHeight = child.getMeasuredHeight() + share; 789 if (childHeight < 0) { 790 childHeight = 0; 791 } 792 793 child.measure(childWidthMeasureSpec, 794 MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)); 795 } else { 796 // child was skipped in the loop above. 797 // Measure for this first time here 798 child.measure(childWidthMeasureSpec, 799 MeasureSpec.makeMeasureSpec(share > 0 ? share : 0, 800 MeasureSpec.EXACTLY)); 801 } 802 803 // Child may now not fit in vertical dimension. 804 childState = combineMeasuredStates(childState, child.getMeasuredState() 805 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); 806 } 807 808 final int margin = lp.leftMargin + lp.rightMargin; 809 final int measuredWidth = child.getMeasuredWidth() + margin; 810 maxWidth = Math.max(maxWidth, measuredWidth); 811 812 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY && 813 lp.width == LayoutParams.MATCH_PARENT; 814 815 alternativeMaxWidth = Math.max(alternativeMaxWidth, 816 matchWidthLocally ? margin : measuredWidth); 817 818 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 819 820 final int totalLength = mTotalLength; 821 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() + 822 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 823 } 824 825 // Add in our padding 826 mTotalLength += mPaddingTop + mPaddingBottom; 827 // TODO: Should we recompute the heightSpec based on the new total length? 828 } else { 829 alternativeMaxWidth = Math.max(alternativeMaxWidth, 830 weightedMaxWidth); 831 832 833 // We have no limit, so make all weighted views as tall as the largest child. 834 // Children will have already been measured once. 835 if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { 836 for (int i = 0; i < count; i++) { 837 final View child = getVirtualChildAt(i); 838 839 if (child == null || child.getVisibility() == View.GONE) { 840 continue; 841 } 842 843 final LinearLayout.LayoutParams lp = 844 (LinearLayout.LayoutParams) child.getLayoutParams(); 845 846 float childExtra = lp.weight; 847 if (childExtra > 0) { 848 child.measure( 849 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), 850 MeasureSpec.EXACTLY), 851 MeasureSpec.makeMeasureSpec(largestChildHeight, 852 MeasureSpec.EXACTLY)); 853 } 854 } 855 } 856 } 857 858 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) { 859 maxWidth = alternativeMaxWidth; 860 } 861 862 maxWidth += mPaddingLeft + mPaddingRight; 863 864 // Check against our minimum width 865 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 866 867 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 868 heightSizeAndState); 869 870 if (matchWidth) { 871 forceUniformWidth(count, heightMeasureSpec); 872 } 873 } 874 875 private void forceUniformWidth(int count, int heightMeasureSpec) { 876 // Pretend that the linear layout has an exact size. 877 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), 878 MeasureSpec.EXACTLY); 879 for (int i = 0; i< count; ++i) { 880 final View child = getVirtualChildAt(i); 881 if (child.getVisibility() != GONE) { 882 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams()); 883 884 if (lp.width == LayoutParams.MATCH_PARENT) { 885 // Temporarily force children to reuse their old measured height 886 // FIXME: this may not be right for something like wrapping text? 887 int oldHeight = lp.height; 888 lp.height = child.getMeasuredHeight(); 889 890 // Remeasue with new dimensions 891 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0); 892 lp.height = oldHeight; 893 } 894 } 895 } 896 } 897 898 /** 899 * Measures the children when the orientation of this LinearLayout is set 900 * to {@link #HORIZONTAL}. 901 * 902 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 903 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 904 * 905 * @see #getOrientation() 906 * @see #setOrientation(int) 907 * @see #onMeasure(int, int) 908 */ 909 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { 910 mTotalLength = 0; 911 int maxHeight = 0; 912 int childState = 0; 913 int alternativeMaxHeight = 0; 914 int weightedMaxHeight = 0; 915 boolean allFillParent = true; 916 float totalWeight = 0; 917 918 final int count = getVirtualChildCount(); 919 920 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 921 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 922 923 boolean matchHeight = false; 924 925 if (mMaxAscent == null || mMaxDescent == null) { 926 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT]; 927 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT]; 928 } 929 930 final int[] maxAscent = mMaxAscent; 931 final int[] maxDescent = mMaxDescent; 932 933 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 934 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 935 936 final boolean baselineAligned = mBaselineAligned; 937 final boolean useLargestChild = mUseLargestChild; 938 939 final boolean isExactly = widthMode == MeasureSpec.EXACTLY; 940 941 int largestChildWidth = Integer.MIN_VALUE; 942 943 // See how wide everyone is. Also remember max height. 944 for (int i = 0; i < count; ++i) { 945 final View child = getVirtualChildAt(i); 946 947 if (child == null) { 948 mTotalLength += measureNullChild(i); 949 continue; 950 } 951 952 if (child.getVisibility() == GONE) { 953 i += getChildrenSkipCount(child, i); 954 continue; 955 } 956 957 if (hasDividerBeforeChildAt(i)) { 958 mTotalLength += mDividerWidth; 959 } 960 961 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 962 child.getLayoutParams(); 963 964 totalWeight += lp.weight; 965 966 if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) { 967 // Optimization: don't bother measuring children who are going to use 968 // leftover space. These views will get measured again down below if 969 // there is any leftover space. 970 if (isExactly) { 971 mTotalLength += lp.leftMargin + lp.rightMargin; 972 } else { 973 final int totalLength = mTotalLength; 974 mTotalLength = Math.max(totalLength, totalLength + 975 lp.leftMargin + lp.rightMargin); 976 } 977 978 // Baseline alignment requires to measure widgets to obtain the 979 // baseline offset (in particular for TextViews). The following 980 // defeats the optimization mentioned above. Allow the child to 981 // use as much space as it wants because we can shrink things 982 // later (and re-measure). 983 if (baselineAligned) { 984 final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 985 child.measure(freeSpec, freeSpec); 986 } 987 } else { 988 int oldWidth = Integer.MIN_VALUE; 989 990 if (lp.width == 0 && lp.weight > 0) { 991 // widthMode is either UNSPECIFIED or AT_MOST, and this 992 // child 993 // wanted to stretch to fill available space. Translate that to 994 // WRAP_CONTENT so that it does not end up with a width of 0 995 oldWidth = 0; 996 lp.width = LayoutParams.WRAP_CONTENT; 997 } 998 999 // Determine how big this child would like to be. If this or 1000 // previous children have given a weight, then we allow it to 1001 // use all available space (and we will shrink things later 1002 // if needed). 1003 measureChildBeforeLayout(child, i, widthMeasureSpec, 1004 totalWeight == 0 ? mTotalLength : 0, 1005 heightMeasureSpec, 0); 1006 1007 if (oldWidth != Integer.MIN_VALUE) { 1008 lp.width = oldWidth; 1009 } 1010 1011 final int childWidth = child.getMeasuredWidth(); 1012 if (isExactly) { 1013 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin + 1014 getNextLocationOffset(child); 1015 } else { 1016 final int totalLength = mTotalLength; 1017 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin + 1018 lp.rightMargin + getNextLocationOffset(child)); 1019 } 1020 1021 if (useLargestChild) { 1022 largestChildWidth = Math.max(childWidth, largestChildWidth); 1023 } 1024 } 1025 1026 boolean matchHeightLocally = false; 1027 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) { 1028 // The height of the linear layout will scale, and at least one 1029 // child said it wanted to match our height. Set a flag indicating that 1030 // we need to remeasure at least that view when we know our height. 1031 matchHeight = true; 1032 matchHeightLocally = true; 1033 } 1034 1035 final int margin = lp.topMargin + lp.bottomMargin; 1036 final int childHeight = child.getMeasuredHeight() + margin; 1037 childState = combineMeasuredStates(childState, child.getMeasuredState()); 1038 1039 if (baselineAligned) { 1040 final int childBaseline = child.getBaseline(); 1041 if (childBaseline != -1) { 1042 // Translates the child's vertical gravity into an index 1043 // in the range 0..VERTICAL_GRAVITY_COUNT 1044 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1045 & Gravity.VERTICAL_GRAVITY_MASK; 1046 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1047 & ~Gravity.AXIS_SPECIFIED) >> 1; 1048 1049 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1050 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline); 1051 } 1052 } 1053 1054 maxHeight = Math.max(maxHeight, childHeight); 1055 1056 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1057 if (lp.weight > 0) { 1058 /* 1059 * Heights of weighted Views are bogus if we end up 1060 * remeasuring, so keep them separate. 1061 */ 1062 weightedMaxHeight = Math.max(weightedMaxHeight, 1063 matchHeightLocally ? margin : childHeight); 1064 } else { 1065 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1066 matchHeightLocally ? margin : childHeight); 1067 } 1068 1069 i += getChildrenSkipCount(child, i); 1070 } 1071 1072 if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) { 1073 mTotalLength += mDividerWidth; 1074 } 1075 1076 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1077 // the most common case 1078 if (maxAscent[INDEX_TOP] != -1 || 1079 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1080 maxAscent[INDEX_BOTTOM] != -1 || 1081 maxAscent[INDEX_FILL] != -1) { 1082 final int ascent = Math.max(maxAscent[INDEX_FILL], 1083 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1084 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1085 final int descent = Math.max(maxDescent[INDEX_FILL], 1086 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1087 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1088 maxHeight = Math.max(maxHeight, ascent + descent); 1089 } 1090 1091 if (useLargestChild && 1092 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { 1093 mTotalLength = 0; 1094 1095 for (int i = 0; i < count; ++i) { 1096 final View child = getVirtualChildAt(i); 1097 1098 if (child == null) { 1099 mTotalLength += measureNullChild(i); 1100 continue; 1101 } 1102 1103 if (child.getVisibility() == GONE) { 1104 i += getChildrenSkipCount(child, i); 1105 continue; 1106 } 1107 1108 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 1109 child.getLayoutParams(); 1110 if (isExactly) { 1111 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin + 1112 getNextLocationOffset(child); 1113 } else { 1114 final int totalLength = mTotalLength; 1115 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth + 1116 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1117 } 1118 } 1119 } 1120 1121 // Add in our padding 1122 mTotalLength += mPaddingLeft + mPaddingRight; 1123 1124 int widthSize = mTotalLength; 1125 1126 // Check against our minimum width 1127 widthSize = Math.max(widthSize, getSuggestedMinimumWidth()); 1128 1129 // Reconcile our calculated size with the widthMeasureSpec 1130 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0); 1131 widthSize = widthSizeAndState & MEASURED_SIZE_MASK; 1132 1133 // Either expand children with weight to take up available space or 1134 // shrink them if they extend beyond our current bounds 1135 int delta = widthSize - mTotalLength; 1136 if (delta != 0 && totalWeight > 0.0f) { 1137 float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 1138 1139 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 1140 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 1141 maxHeight = -1; 1142 1143 mTotalLength = 0; 1144 1145 for (int i = 0; i < count; ++i) { 1146 final View child = getVirtualChildAt(i); 1147 1148 if (child == null || child.getVisibility() == View.GONE) { 1149 continue; 1150 } 1151 1152 final LinearLayout.LayoutParams lp = 1153 (LinearLayout.LayoutParams) child.getLayoutParams(); 1154 1155 float childExtra = lp.weight; 1156 if (childExtra > 0) { 1157 // Child said it could absorb extra space -- give him his share 1158 int share = (int) (childExtra * delta / weightSum); 1159 weightSum -= childExtra; 1160 delta -= share; 1161 1162 final int childHeightMeasureSpec = getChildMeasureSpec( 1163 heightMeasureSpec, 1164 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin, 1165 lp.height); 1166 1167 // TODO: Use a field like lp.isMeasured to figure out if this 1168 // child has been previously measured 1169 if ((lp.width != 0) || (widthMode != MeasureSpec.EXACTLY)) { 1170 // child was measured once already above ... base new measurement 1171 // on stored values 1172 int childWidth = child.getMeasuredWidth() + share; 1173 if (childWidth < 0) { 1174 childWidth = 0; 1175 } 1176 1177 child.measure( 1178 MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), 1179 childHeightMeasureSpec); 1180 } else { 1181 // child was skipped in the loop above. Measure for this first time here 1182 child.measure(MeasureSpec.makeMeasureSpec( 1183 share > 0 ? share : 0, MeasureSpec.EXACTLY), 1184 childHeightMeasureSpec); 1185 } 1186 1187 // Child may now not fit in horizontal dimension. 1188 childState = combineMeasuredStates(childState, 1189 child.getMeasuredState() & MEASURED_STATE_MASK); 1190 } 1191 1192 if (isExactly) { 1193 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin + 1194 getNextLocationOffset(child); 1195 } else { 1196 final int totalLength = mTotalLength; 1197 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() + 1198 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1199 } 1200 1201 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY && 1202 lp.height == LayoutParams.MATCH_PARENT; 1203 1204 final int margin = lp.topMargin + lp .bottomMargin; 1205 int childHeight = child.getMeasuredHeight() + margin; 1206 maxHeight = Math.max(maxHeight, childHeight); 1207 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1208 matchHeightLocally ? margin : childHeight); 1209 1210 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1211 1212 if (baselineAligned) { 1213 final int childBaseline = child.getBaseline(); 1214 if (childBaseline != -1) { 1215 // Translates the child's vertical gravity into an index in the range 0..2 1216 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1217 & Gravity.VERTICAL_GRAVITY_MASK; 1218 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1219 & ~Gravity.AXIS_SPECIFIED) >> 1; 1220 1221 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1222 maxDescent[index] = Math.max(maxDescent[index], 1223 childHeight - childBaseline); 1224 } 1225 } 1226 } 1227 1228 // Add in our padding 1229 mTotalLength += mPaddingLeft + mPaddingRight; 1230 // TODO: Should we update widthSize with the new total length? 1231 1232 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1233 // the most common case 1234 if (maxAscent[INDEX_TOP] != -1 || 1235 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1236 maxAscent[INDEX_BOTTOM] != -1 || 1237 maxAscent[INDEX_FILL] != -1) { 1238 final int ascent = Math.max(maxAscent[INDEX_FILL], 1239 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1240 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1241 final int descent = Math.max(maxDescent[INDEX_FILL], 1242 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1243 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1244 maxHeight = Math.max(maxHeight, ascent + descent); 1245 } 1246 } else { 1247 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); 1248 1249 // We have no limit, so make all weighted views as wide as the largest child. 1250 // Children will have already been measured once. 1251 if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { 1252 for (int i = 0; i < count; i++) { 1253 final View child = getVirtualChildAt(i); 1254 1255 if (child == null || child.getVisibility() == View.GONE) { 1256 continue; 1257 } 1258 1259 final LinearLayout.LayoutParams lp = 1260 (LinearLayout.LayoutParams) child.getLayoutParams(); 1261 1262 float childExtra = lp.weight; 1263 if (childExtra > 0) { 1264 child.measure( 1265 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY), 1266 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), 1267 MeasureSpec.EXACTLY)); 1268 } 1269 } 1270 } 1271 } 1272 1273 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { 1274 maxHeight = alternativeMaxHeight; 1275 } 1276 1277 maxHeight += mPaddingTop + mPaddingBottom; 1278 1279 // Check against our minimum height 1280 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 1281 1282 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK), 1283 resolveSizeAndState(maxHeight, heightMeasureSpec, 1284 (childState<<MEASURED_HEIGHT_STATE_SHIFT))); 1285 1286 if (matchHeight) { 1287 forceUniformHeight(count, widthMeasureSpec); 1288 } 1289 } 1290 1291 private void forceUniformHeight(int count, int widthMeasureSpec) { 1292 // Pretend that the linear layout has an exact size. This is the measured height of 1293 // ourselves. The measured height should be the max height of the children, changed 1294 // to accomodate the heightMesureSpec from the parent 1295 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), 1296 MeasureSpec.EXACTLY); 1297 for (int i = 0; i < count; ++i) { 1298 final View child = getVirtualChildAt(i); 1299 if (child.getVisibility() != GONE) { 1300 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 1301 1302 if (lp.height == LayoutParams.MATCH_PARENT) { 1303 // Temporarily force children to reuse their old measured width 1304 // FIXME: this may not be right for something like wrapping text? 1305 int oldWidth = lp.width; 1306 lp.width = child.getMeasuredWidth(); 1307 1308 // Remeasure with new dimensions 1309 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0); 1310 lp.width = oldWidth; 1311 } 1312 } 1313 } 1314 } 1315 1316 /** 1317 * <p>Returns the number of children to skip after measuring/laying out 1318 * the specified child.</p> 1319 * 1320 * @param child the child after which we want to skip children 1321 * @param index the index of the child after which we want to skip children 1322 * @return the number of children to skip, 0 by default 1323 */ 1324 int getChildrenSkipCount(View child, int index) { 1325 return 0; 1326 } 1327 1328 /** 1329 * <p>Returns the size (width or height) that should be occupied by a null 1330 * child.</p> 1331 * 1332 * @param childIndex the index of the null child 1333 * @return the width or height of the child depending on the orientation 1334 */ 1335 int measureNullChild(int childIndex) { 1336 return 0; 1337 } 1338 1339 /** 1340 * <p>Measure the child according to the parent's measure specs. This 1341 * method should be overriden by subclasses to force the sizing of 1342 * children. This method is called by {@link #measureVertical(int, int)} and 1343 * {@link #measureHorizontal(int, int)}.</p> 1344 * 1345 * @param child the child to measure 1346 * @param childIndex the index of the child in this view 1347 * @param widthMeasureSpec horizontal space requirements as imposed by the parent 1348 * @param totalWidth extra space that has been used up by the parent horizontally 1349 * @param heightMeasureSpec vertical space requirements as imposed by the parent 1350 * @param totalHeight extra space that has been used up by the parent vertically 1351 */ 1352 void measureChildBeforeLayout(View child, int childIndex, 1353 int widthMeasureSpec, int totalWidth, int heightMeasureSpec, 1354 int totalHeight) { 1355 measureChildWithMargins(child, widthMeasureSpec, totalWidth, 1356 heightMeasureSpec, totalHeight); 1357 } 1358 1359 /** 1360 * <p>Return the location offset of the specified child. This can be used 1361 * by subclasses to change the location of a given widget.</p> 1362 * 1363 * @param child the child for which to obtain the location offset 1364 * @return the location offset in pixels 1365 */ 1366 int getLocationOffset(View child) { 1367 return 0; 1368 } 1369 1370 /** 1371 * <p>Return the size offset of the next sibling of the specified child. 1372 * This can be used by subclasses to change the location of the widget 1373 * following <code>child</code>.</p> 1374 * 1375 * @param child the child whose next sibling will be moved 1376 * @return the location offset of the next child in pixels 1377 */ 1378 int getNextLocationOffset(View child) { 1379 return 0; 1380 } 1381 1382 @Override 1383 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1384 if (mOrientation == VERTICAL) { 1385 layoutVertical(); 1386 } else { 1387 layoutHorizontal(); 1388 } 1389 } 1390 1391 /** 1392 * Position the children during a layout pass if the orientation of this 1393 * LinearLayout is set to {@link #VERTICAL}. 1394 * 1395 * @see #getOrientation() 1396 * @see #setOrientation(int) 1397 * @see #onLayout(boolean, int, int, int, int) 1398 */ 1399 void layoutVertical() { 1400 final int paddingLeft = mPaddingLeft; 1401 1402 int childTop; 1403 int childLeft; 1404 1405 // Where right end of child should go 1406 final int width = mRight - mLeft; 1407 int childRight = width - mPaddingRight; 1408 1409 // Space available for child 1410 int childSpace = width - paddingLeft - mPaddingRight; 1411 1412 final int count = getVirtualChildCount(); 1413 1414 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1415 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1416 1417 switch (majorGravity) { 1418 case Gravity.BOTTOM: 1419 // mTotalLength contains the padding already 1420 childTop = mPaddingTop + mBottom - mTop - mTotalLength; 1421 break; 1422 1423 // mTotalLength contains the padding already 1424 case Gravity.CENTER_VERTICAL: 1425 childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2; 1426 break; 1427 1428 case Gravity.TOP: 1429 default: 1430 childTop = mPaddingTop; 1431 break; 1432 } 1433 1434 for (int i = 0; i < count; i++) { 1435 final View child = getVirtualChildAt(i); 1436 if (child == null) { 1437 childTop += measureNullChild(i); 1438 } else if (child.getVisibility() != GONE) { 1439 final int childWidth = child.getMeasuredWidth(); 1440 final int childHeight = child.getMeasuredHeight(); 1441 1442 final LinearLayout.LayoutParams lp = 1443 (LinearLayout.LayoutParams) child.getLayoutParams(); 1444 1445 int gravity = lp.gravity; 1446 if (gravity < 0) { 1447 gravity = minorGravity; 1448 } 1449 final int layoutDirection = getResolvedLayoutDirection(); 1450 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 1451 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 1452 case Gravity.CENTER_HORIZONTAL: 1453 childLeft = paddingLeft + ((childSpace - childWidth) / 2) 1454 + lp.leftMargin - lp.rightMargin; 1455 break; 1456 1457 case Gravity.RIGHT: 1458 childLeft = childRight - childWidth - lp.rightMargin; 1459 break; 1460 1461 case Gravity.LEFT: 1462 default: 1463 childLeft = paddingLeft + lp.leftMargin; 1464 break; 1465 } 1466 1467 if (hasDividerBeforeChildAt(i)) { 1468 childTop += mDividerHeight; 1469 } 1470 1471 childTop += lp.topMargin; 1472 setChildFrame(child, childLeft, childTop + getLocationOffset(child), 1473 childWidth, childHeight); 1474 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); 1475 1476 i += getChildrenSkipCount(child, i); 1477 } 1478 } 1479 } 1480 1481 /** 1482 * Position the children during a layout pass if the orientation of this 1483 * LinearLayout is set to {@link #HORIZONTAL}. 1484 * 1485 * @see #getOrientation() 1486 * @see #setOrientation(int) 1487 * @see #onLayout(boolean, int, int, int, int) 1488 */ 1489 void layoutHorizontal() { 1490 final boolean isLayoutRtl = isLayoutRtl(); 1491 final int paddingTop = mPaddingTop; 1492 1493 int childTop; 1494 int childLeft; 1495 1496 // Where bottom of child should go 1497 final int height = mBottom - mTop; 1498 int childBottom = height - mPaddingBottom; 1499 1500 // Space available for child 1501 int childSpace = height - paddingTop - mPaddingBottom; 1502 1503 final int count = getVirtualChildCount(); 1504 1505 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1506 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1507 1508 final boolean baselineAligned = mBaselineAligned; 1509 1510 final int[] maxAscent = mMaxAscent; 1511 final int[] maxDescent = mMaxDescent; 1512 1513 final int layoutDirection = getResolvedLayoutDirection(); 1514 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) { 1515 case Gravity.RIGHT: 1516 // mTotalLength contains the padding already 1517 childLeft = mPaddingLeft + mRight - mLeft - mTotalLength; 1518 break; 1519 1520 case Gravity.CENTER_HORIZONTAL: 1521 // mTotalLength contains the padding already 1522 childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2; 1523 break; 1524 1525 case Gravity.LEFT: 1526 default: 1527 childLeft = mPaddingLeft; 1528 break; 1529 } 1530 1531 int start = 0; 1532 int dir = 1; 1533 //In case of RTL, start drawing from the last child. 1534 if (isLayoutRtl) { 1535 start = count - 1; 1536 dir = -1; 1537 } 1538 1539 for (int i = 0; i < count; i++) { 1540 int childIndex = start + dir * i; 1541 final View child = getVirtualChildAt(childIndex); 1542 1543 if (child == null) { 1544 childLeft += measureNullChild(childIndex); 1545 } else if (child.getVisibility() != GONE) { 1546 final int childWidth = child.getMeasuredWidth(); 1547 final int childHeight = child.getMeasuredHeight(); 1548 int childBaseline = -1; 1549 1550 final LinearLayout.LayoutParams lp = 1551 (LinearLayout.LayoutParams) child.getLayoutParams(); 1552 1553 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) { 1554 childBaseline = child.getBaseline(); 1555 } 1556 1557 int gravity = lp.gravity; 1558 if (gravity < 0) { 1559 gravity = minorGravity; 1560 } 1561 1562 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 1563 case Gravity.TOP: 1564 childTop = paddingTop + lp.topMargin; 1565 if (childBaseline != -1) { 1566 childTop += maxAscent[INDEX_TOP] - childBaseline; 1567 } 1568 break; 1569 1570 case Gravity.CENTER_VERTICAL: 1571 // Removed support for baseline alignment when layout_gravity or 1572 // gravity == center_vertical. See bug #1038483. 1573 // Keep the code around if we need to re-enable this feature 1574 // if (childBaseline != -1) { 1575 // // Align baselines vertically only if the child is smaller than us 1576 // if (childSpace - childHeight > 0) { 1577 // childTop = paddingTop + (childSpace / 2) - childBaseline; 1578 // } else { 1579 // childTop = paddingTop + (childSpace - childHeight) / 2; 1580 // } 1581 // } else { 1582 childTop = paddingTop + ((childSpace - childHeight) / 2) 1583 + lp.topMargin - lp.bottomMargin; 1584 break; 1585 1586 case Gravity.BOTTOM: 1587 childTop = childBottom - childHeight - lp.bottomMargin; 1588 if (childBaseline != -1) { 1589 int descent = child.getMeasuredHeight() - childBaseline; 1590 childTop -= (maxDescent[INDEX_BOTTOM] - descent); 1591 } 1592 break; 1593 default: 1594 childTop = paddingTop; 1595 break; 1596 } 1597 1598 if (hasDividerBeforeChildAt(childIndex)) { 1599 childLeft += mDividerWidth; 1600 } 1601 1602 childLeft += lp.leftMargin; 1603 setChildFrame(child, childLeft + getLocationOffset(child), childTop, 1604 childWidth, childHeight); 1605 childLeft += childWidth + lp.rightMargin + 1606 getNextLocationOffset(child); 1607 1608 i += getChildrenSkipCount(child, childIndex); 1609 } 1610 } 1611 } 1612 1613 private void setChildFrame(View child, int left, int top, int width, int height) { 1614 child.layout(left, top, left + width, top + height); 1615 } 1616 1617 /** 1618 * Should the layout be a column or a row. 1619 * @param orientation Pass HORIZONTAL or VERTICAL. Default 1620 * value is HORIZONTAL. 1621 * 1622 * @attr ref android.R.styleable#LinearLayout_orientation 1623 */ 1624 public void setOrientation(int orientation) { 1625 if (mOrientation != orientation) { 1626 mOrientation = orientation; 1627 requestLayout(); 1628 } 1629 } 1630 1631 /** 1632 * Returns the current orientation. 1633 * 1634 * @return either {@link #HORIZONTAL} or {@link #VERTICAL} 1635 */ 1636 public int getOrientation() { 1637 return mOrientation; 1638 } 1639 1640 /** 1641 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If 1642 * this layout has a VERTICAL orientation, this controls where all the child 1643 * views are placed if there is extra vertical space. If this layout has a 1644 * HORIZONTAL orientation, this controls the alignment of the children. 1645 * 1646 * @param gravity See {@link android.view.Gravity} 1647 * 1648 * @attr ref android.R.styleable#LinearLayout_gravity 1649 */ 1650 @android.view.RemotableViewMethod 1651 public void setGravity(int gravity) { 1652 if (mGravity != gravity) { 1653 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { 1654 gravity |= Gravity.START; 1655 } 1656 1657 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 1658 gravity |= Gravity.TOP; 1659 } 1660 1661 mGravity = gravity; 1662 requestLayout(); 1663 } 1664 } 1665 1666 @android.view.RemotableViewMethod 1667 public void setHorizontalGravity(int horizontalGravity) { 1668 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1669 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) { 1670 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity; 1671 requestLayout(); 1672 } 1673 } 1674 1675 @android.view.RemotableViewMethod 1676 public void setVerticalGravity(int verticalGravity) { 1677 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK; 1678 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) { 1679 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity; 1680 requestLayout(); 1681 } 1682 } 1683 1684 @Override 1685 public LayoutParams generateLayoutParams(AttributeSet attrs) { 1686 return new LinearLayout.LayoutParams(getContext(), attrs); 1687 } 1688 1689 /** 1690 * Returns a set of layout parameters with a width of 1691 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} 1692 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} 1693 * when the layout's orientation is {@link #VERTICAL}. When the orientation is 1694 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT} 1695 * and the height to {@link LayoutParams#WRAP_CONTENT}. 1696 */ 1697 @Override 1698 protected LayoutParams generateDefaultLayoutParams() { 1699 if (mOrientation == HORIZONTAL) { 1700 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 1701 } else if (mOrientation == VERTICAL) { 1702 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 1703 } 1704 return null; 1705 } 1706 1707 @Override 1708 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1709 return new LayoutParams(p); 1710 } 1711 1712 1713 // Override to allow type-checking of LayoutParams. 1714 @Override 1715 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1716 return p instanceof LinearLayout.LayoutParams; 1717 } 1718 1719 /** 1720 * Per-child layout information associated with ViewLinearLayout. 1721 * 1722 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight 1723 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity 1724 */ 1725 public static class LayoutParams extends ViewGroup.MarginLayoutParams { 1726 /** 1727 * Indicates how much of the extra space in the LinearLayout will be 1728 * allocated to the view associated with these LayoutParams. Specify 1729 * 0 if the view should not be stretched. Otherwise the extra pixels 1730 * will be pro-rated among all views whose weight is greater than 0. 1731 */ 1732 @ViewDebug.ExportedProperty(category = "layout") 1733 public float weight; 1734 1735 /** 1736 * Gravity for the view associated with these LayoutParams. 1737 * 1738 * @see android.view.Gravity 1739 */ 1740 @ViewDebug.ExportedProperty(category = "layout", mapping = { 1741 @ViewDebug.IntToString(from = -1, to = "NONE"), 1742 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), 1743 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), 1744 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), 1745 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), 1746 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), 1747 @ViewDebug.IntToString(from = Gravity.START, to = "START"), 1748 @ViewDebug.IntToString(from = Gravity.END, to = "END"), 1749 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), 1750 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), 1751 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), 1752 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), 1753 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), 1754 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") 1755 }) 1756 public int gravity = -1; 1757 1758 /** 1759 * {@inheritDoc} 1760 */ 1761 public LayoutParams(Context c, AttributeSet attrs) { 1762 super(c, attrs); 1763 TypedArray a = 1764 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout); 1765 1766 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0); 1767 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1); 1768 1769 a.recycle(); 1770 } 1771 1772 /** 1773 * {@inheritDoc} 1774 */ 1775 public LayoutParams(int width, int height) { 1776 super(width, height); 1777 weight = 0; 1778 } 1779 1780 /** 1781 * Creates a new set of layout parameters with the specified width, height 1782 * and weight. 1783 * 1784 * @param width the width, either {@link #MATCH_PARENT}, 1785 * {@link #WRAP_CONTENT} or a fixed size in pixels 1786 * @param height the height, either {@link #MATCH_PARENT}, 1787 * {@link #WRAP_CONTENT} or a fixed size in pixels 1788 * @param weight the weight 1789 */ 1790 public LayoutParams(int width, int height, float weight) { 1791 super(width, height); 1792 this.weight = weight; 1793 } 1794 1795 /** 1796 * {@inheritDoc} 1797 */ 1798 public LayoutParams(ViewGroup.LayoutParams p) { 1799 super(p); 1800 } 1801 1802 /** 1803 * {@inheritDoc} 1804 */ 1805 public LayoutParams(MarginLayoutParams source) { 1806 super(source); 1807 } 1808 1809 @Override 1810 public String debug(String output) { 1811 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) + 1812 ", height=" + sizeToString(height) + " weight=" + weight + "}"; 1813 } 1814 } 1815} 1816