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