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