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