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