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