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