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