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