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