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