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