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