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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.content.res.TypedArray; 24 import android.graphics.Canvas; 25 import android.graphics.drawable.Drawable; 26 import android.os.Build; 27 import android.util.AttributeSet; 28 import android.view.Gravity; 29 import android.view.View; 30 import android.view.ViewDebug; 31 import android.view.ViewGroup; 32 import android.view.ViewHierarchyEncoder; 33 import android.widget.RemoteViews.RemoteView; 34 35 import com.android.internal.R; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 40 41 /** 42 * A layout that arranges other views either horizontally in a single column 43 * or vertically in a single row. 44 * 45 * <p>The following snippet shows how to include a linear layout in your layout XML file:</p> 46 * 47 * <pre><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 48 * android:layout_width="match_parent" 49 * android:layout_height="match_parent" 50 * android:paddingLeft="16dp" 51 * android:paddingRight="16dp" 52 * android:orientation="horizontal" 53 * android:gravity="center"> 54 * 55 * <!-- Include other widget or layout tags here. These are considered 56 * "child views" or "children" of the linear layout --> 57 * 58 * </LinearLayout></pre> 59 * 60 * <p>Set {@link android.R.styleable#LinearLayout_orientation android:orientation} to specify 61 * whether child views are displayed in a row or column.</p> 62 * 63 * <p>To control how linear layout aligns all the views it contains, set a value for 64 * {@link android.R.styleable#LinearLayout_gravity android:gravity}. For example, the 65 * snippet above sets android:gravity to "center". The value you set affects 66 * both horizontal and vertical alignment of all child views within the single row or column.</p> 67 * 68 * <p>You can set 69 * {@link android.R.styleable#LinearLayout_Layout_layout_weight android:layout_weight} 70 * on individual child views to specify how linear layout divides remaining space amongst 71 * the views it contains. See the 72 * <a href="https://developer.android.com/guide/topics/ui/layout/linear.html">Linear Layout</a> 73 * guide for an example.</p> 74 * 75 * <p>See 76 * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} 77 * to learn about other attributes you can set on a child view to affect its 78 * position and size in the containing linear layout.</p> 79 * 80 * @attr ref android.R.styleable#LinearLayout_baselineAligned 81 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex 82 * @attr ref android.R.styleable#LinearLayout_gravity 83 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 84 * @attr ref android.R.styleable#LinearLayout_orientation 85 * @attr ref android.R.styleable#LinearLayout_weightSum 86 */ 87 @RemoteView 88 public class LinearLayout extends ViewGroup { 89 /** @hide */ 90 @IntDef({HORIZONTAL, VERTICAL}) 91 @Retention(RetentionPolicy.SOURCE) 92 public @interface OrientationMode {} 93 94 public static final int HORIZONTAL = 0; 95 public static final int VERTICAL = 1; 96 97 /** @hide */ 98 @IntDef(flag = true, prefix = { "SHOW_DIVIDER_" }, value = { 99 SHOW_DIVIDER_NONE, 100 SHOW_DIVIDER_BEGINNING, 101 SHOW_DIVIDER_MIDDLE, 102 SHOW_DIVIDER_END 103 }) 104 @Retention(RetentionPolicy.SOURCE) 105 public @interface DividerMode {} 106 107 /** 108 * Don't show any dividers. 109 */ 110 public static final int SHOW_DIVIDER_NONE = 0; 111 /** 112 * Show a divider at the beginning of the group. 113 */ 114 public static final int SHOW_DIVIDER_BEGINNING = 1; 115 /** 116 * Show dividers between each item in the group. 117 */ 118 public static final int SHOW_DIVIDER_MIDDLE = 2; 119 /** 120 * Show a divider at the end of the group. 121 */ 122 public static final int SHOW_DIVIDER_END = 4; 123 124 /** 125 * Compatibility check. Old versions of the platform would give different 126 * results from measurement passes using EXACTLY and non-EXACTLY modes, 127 * even when the resulting size was the same. 128 */ 129 private final boolean mAllowInconsistentMeasurement; 130 131 /** 132 * Whether the children of this layout are baseline aligned. Only applicable 133 * if {@link #mOrientation} is horizontal. 134 */ 135 @ViewDebug.ExportedProperty(category = "layout") 136 private boolean mBaselineAligned = true; 137 138 /** 139 * If this layout is part of another layout that is baseline aligned, 140 * use the child at this index as the baseline. 141 * 142 * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned 143 * with whether the children of this layout are baseline aligned. 144 */ 145 @ViewDebug.ExportedProperty(category = "layout") 146 private int mBaselineAlignedChildIndex = -1; 147 148 /** 149 * The additional offset to the child's baseline. 150 * We'll calculate the baseline of this layout as we measure vertically; for 151 * horizontal linear layouts, the offset of 0 is appropriate. 152 */ 153 @ViewDebug.ExportedProperty(category = "measurement") 154 private int mBaselineChildTop = 0; 155 156 @ViewDebug.ExportedProperty(category = "measurement") 157 private int mOrientation; 158 159 @ViewDebug.ExportedProperty(category = "measurement", flagMapping = { 160 @ViewDebug.FlagToString(mask = -1, 161 equals = -1, name = "NONE"), 162 @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY, 163 equals = Gravity.NO_GRAVITY,name = "NONE"), 164 @ViewDebug.FlagToString(mask = Gravity.TOP, 165 equals = Gravity.TOP, name = "TOP"), 166 @ViewDebug.FlagToString(mask = Gravity.BOTTOM, 167 equals = Gravity.BOTTOM, name = "BOTTOM"), 168 @ViewDebug.FlagToString(mask = Gravity.LEFT, 169 equals = Gravity.LEFT, name = "LEFT"), 170 @ViewDebug.FlagToString(mask = Gravity.RIGHT, 171 equals = Gravity.RIGHT, name = "RIGHT"), 172 @ViewDebug.FlagToString(mask = Gravity.START, 173 equals = Gravity.START, name = "START"), 174 @ViewDebug.FlagToString(mask = Gravity.END, 175 equals = Gravity.END, name = "END"), 176 @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL, 177 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"), 178 @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL, 179 equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"), 180 @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL, 181 equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"), 182 @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL, 183 equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"), 184 @ViewDebug.FlagToString(mask = Gravity.CENTER, 185 equals = Gravity.CENTER, name = "CENTER"), 186 @ViewDebug.FlagToString(mask = Gravity.FILL, 187 equals = Gravity.FILL, name = "FILL"), 188 @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION, 189 equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE") 190 }, formatToHexString = true) 191 private int mGravity = Gravity.START | Gravity.TOP; 192 193 @ViewDebug.ExportedProperty(category = "measurement") 194 private int mTotalLength; 195 196 @ViewDebug.ExportedProperty(category = "layout") 197 private float mWeightSum; 198 199 @ViewDebug.ExportedProperty(category = "layout") 200 private boolean mUseLargestChild; 201 202 private int[] mMaxAscent; 203 private int[] mMaxDescent; 204 205 private static final int VERTICAL_GRAVITY_COUNT = 4; 206 207 private static final int INDEX_CENTER_VERTICAL = 0; 208 private static final int INDEX_TOP = 1; 209 private static final int INDEX_BOTTOM = 2; 210 private static final int INDEX_FILL = 3; 211 212 private Drawable mDivider; 213 private int mDividerWidth; 214 private int mDividerHeight; 215 private int mShowDividers; 216 private int mDividerPadding; 217 218 private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED; 219 220 /** 221 * Signals that compatibility booleans have been initialized according to 222 * target SDK versions. 223 */ 224 private static boolean sCompatibilityDone = false; 225 226 /** 227 * Behavior change in P; always remeasure weighted children, regardless of excess space. 228 */ 229 private static boolean sRemeasureWeightedChildren = true; 230 231 public LinearLayout(Context context) { 232 this(context, null); 233 } 234 235 public LinearLayout(Context context, @Nullable AttributeSet attrs) { 236 this(context, attrs, 0); 237 } 238 239 public LinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 240 this(context, attrs, defStyleAttr, 0); 241 } 242 243 public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 244 super(context, attrs, defStyleAttr, defStyleRes); 245 246 if (!sCompatibilityDone && context != null) { 247 final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; 248 249 // Older apps only remeasure non-zero children 250 sRemeasureWeightedChildren = targetSdkVersion >= Build.VERSION_CODES.P; 251 252 sCompatibilityDone = true; 253 } 254 255 final TypedArray a = context.obtainStyledAttributes( 256 attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes); 257 258 int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1); 259 if (index >= 0) { 260 setOrientation(index); 261 } 262 263 index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1); 264 if (index >= 0) { 265 setGravity(index); 266 } 267 268 boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true); 269 if (!baselineAligned) { 270 setBaselineAligned(baselineAligned); 271 } 272 273 mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f); 274 275 mBaselineAlignedChildIndex = 276 a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1); 277 278 mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false); 279 280 mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE); 281 mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0); 282 setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider)); 283 284 final int version = context.getApplicationInfo().targetSdkVersion; 285 mAllowInconsistentMeasurement = version <= Build.VERSION_CODES.M; 286 287 a.recycle(); 288 } 289 290 /** 291 * Returns <code>true</code> if this layout is currently configured to show at least one 292 * divider. 293 */ 294 private boolean isShowingDividers() { 295 return (mShowDividers != SHOW_DIVIDER_NONE) && (mDivider != null); 296 } 297 298 /** 299 * Set how dividers should be shown between items in this layout 300 * 301 * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING}, 302 * {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END} 303 * to show dividers, or {@link #SHOW_DIVIDER_NONE} to show no dividers. 304 */ 305 public void setShowDividers(@DividerMode int showDividers) { 306 if (showDividers == mShowDividers) { 307 return; 308 } 309 mShowDividers = showDividers; 310 311 setWillNotDraw(!isShowingDividers()); 312 requestLayout(); 313 } 314 315 @Override 316 public boolean shouldDelayChildPressedState() { 317 return false; 318 } 319 320 /** 321 * @return A flag set indicating how dividers should be shown around items. 322 * @see #setShowDividers(int) 323 */ 324 @DividerMode 325 public int getShowDividers() { 326 return mShowDividers; 327 } 328 329 /** 330 * @return the divider Drawable that will divide each item. 331 * 332 * @see #setDividerDrawable(Drawable) 333 * 334 * @attr ref android.R.styleable#LinearLayout_divider 335 */ 336 public Drawable getDividerDrawable() { 337 return mDivider; 338 } 339 340 /** 341 * Set a drawable to be used as a divider between items. 342 * 343 * @param divider Drawable that will divide each item. 344 * 345 * @see #setShowDividers(int) 346 * 347 * @attr ref android.R.styleable#LinearLayout_divider 348 */ 349 public void setDividerDrawable(Drawable divider) { 350 if (divider == mDivider) { 351 return; 352 } 353 mDivider = divider; 354 if (divider != null) { 355 mDividerWidth = divider.getIntrinsicWidth(); 356 mDividerHeight = divider.getIntrinsicHeight(); 357 } else { 358 mDividerWidth = 0; 359 mDividerHeight = 0; 360 } 361 362 setWillNotDraw(!isShowingDividers()); 363 requestLayout(); 364 } 365 366 /** 367 * Set padding displayed on both ends of dividers. For a vertical layout, the padding is applied 368 * to left and right end of dividers. For a horizontal layout, the padding is applied to top and 369 * bottom end of dividers. 370 * 371 * @param padding Padding value in pixels that will be applied to each end 372 * 373 * @see #setShowDividers(int) 374 * @see #setDividerDrawable(Drawable) 375 * @see #getDividerPadding() 376 */ 377 public void setDividerPadding(int padding) { 378 if (padding == mDividerPadding) { 379 return; 380 } 381 mDividerPadding = padding; 382 383 if (isShowingDividers()) { 384 requestLayout(); 385 invalidate(); 386 } 387 } 388 389 /** 390 * Get the padding size used to inset dividers in pixels 391 * 392 * @see #setShowDividers(int) 393 * @see #setDividerDrawable(Drawable) 394 * @see #setDividerPadding(int) 395 */ 396 public int getDividerPadding() { 397 return mDividerPadding; 398 } 399 400 /** 401 * Get the width of the current divider drawable. 402 * 403 * @hide Used internally by framework. 404 */ 405 public int getDividerWidth() { 406 return mDividerWidth; 407 } 408 409 @Override 410 protected void onDraw(Canvas canvas) { 411 if (mDivider == null) { 412 return; 413 } 414 415 if (mOrientation == VERTICAL) { 416 drawDividersVertical(canvas); 417 } else { 418 drawDividersHorizontal(canvas); 419 } 420 } 421 422 void drawDividersVertical(Canvas canvas) { 423 final int count = getVirtualChildCount(); 424 for (int i = 0; i < count; i++) { 425 final View child = getVirtualChildAt(i); 426 if (child != null && child.getVisibility() != GONE) { 427 if (hasDividerBeforeChildAt(i)) { 428 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 429 final int top = child.getTop() - lp.topMargin - mDividerHeight; 430 drawHorizontalDivider(canvas, top); 431 } 432 } 433 } 434 435 if (hasDividerBeforeChildAt(count)) { 436 final View child = getLastNonGoneChild(); 437 int bottom = 0; 438 if (child == null) { 439 bottom = getHeight() - getPaddingBottom() - mDividerHeight; 440 } else { 441 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 442 bottom = child.getBottom() + lp.bottomMargin; 443 } 444 drawHorizontalDivider(canvas, bottom); 445 } 446 } 447 448 /** 449 * Finds the last child that is not gone. The last child will be used as the reference for 450 * where the end divider should be drawn. 451 */ 452 private View getLastNonGoneChild() { 453 for (int i = getVirtualChildCount() - 1; i >= 0; i--) { 454 final View child = getVirtualChildAt(i); 455 if (child != null && child.getVisibility() != GONE) { 456 return child; 457 } 458 } 459 return null; 460 } 461 462 void drawDividersHorizontal(Canvas canvas) { 463 final int count = getVirtualChildCount(); 464 final boolean isLayoutRtl = isLayoutRtl(); 465 for (int i = 0; i < count; i++) { 466 final View child = getVirtualChildAt(i); 467 if (child != null && child.getVisibility() != GONE) { 468 if (hasDividerBeforeChildAt(i)) { 469 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 470 final int position; 471 if (isLayoutRtl) { 472 position = child.getRight() + lp.rightMargin; 473 } else { 474 position = child.getLeft() - lp.leftMargin - mDividerWidth; 475 } 476 drawVerticalDivider(canvas, position); 477 } 478 } 479 } 480 481 if (hasDividerBeforeChildAt(count)) { 482 final View child = getLastNonGoneChild(); 483 int position; 484 if (child == null) { 485 if (isLayoutRtl) { 486 position = getPaddingLeft(); 487 } else { 488 position = getWidth() - getPaddingRight() - mDividerWidth; 489 } 490 } else { 491 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 492 if (isLayoutRtl) { 493 position = child.getLeft() - lp.leftMargin - mDividerWidth; 494 } else { 495 position = child.getRight() + lp.rightMargin; 496 } 497 } 498 drawVerticalDivider(canvas, position); 499 } 500 } 501 502 void drawHorizontalDivider(Canvas canvas, int top) { 503 mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, 504 getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); 505 mDivider.draw(canvas); 506 } 507 508 void drawVerticalDivider(Canvas canvas, int left) { 509 mDivider.setBounds(left, getPaddingTop() + mDividerPadding, 510 left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding); 511 mDivider.draw(canvas); 512 } 513 514 /** 515 * <p>Indicates whether widgets contained within this layout are aligned 516 * on their baseline or not.</p> 517 * 518 * @return true when widgets are baseline-aligned, false otherwise 519 */ 520 public boolean isBaselineAligned() { 521 return mBaselineAligned; 522 } 523 524 /** 525 * <p>Defines whether widgets contained in this layout are 526 * baseline-aligned or not.</p> 527 * 528 * @param baselineAligned true to align widgets on their baseline, 529 * false otherwise 530 * 531 * @attr ref android.R.styleable#LinearLayout_baselineAligned 532 */ 533 @android.view.RemotableViewMethod 534 public void setBaselineAligned(boolean baselineAligned) { 535 mBaselineAligned = baselineAligned; 536 } 537 538 /** 539 * When true, all children with a weight will be considered having 540 * the minimum size of the largest child. If false, all children are 541 * measured normally. 542 * 543 * @return True to measure children with a weight using the minimum 544 * size of the largest child, false otherwise. 545 * 546 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 547 */ 548 public boolean isMeasureWithLargestChildEnabled() { 549 return mUseLargestChild; 550 } 551 552 /** 553 * When set to true, all children with a weight will be considered having 554 * the minimum size of the largest child. If false, all children are 555 * measured normally. 556 * 557 * Disabled by default. 558 * 559 * @param enabled True to measure children with a weight using the 560 * minimum size of the largest child, false otherwise. 561 * 562 * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild 563 */ 564 @android.view.RemotableViewMethod 565 public void setMeasureWithLargestChildEnabled(boolean enabled) { 566 mUseLargestChild = enabled; 567 } 568 569 @Override 570 public int getBaseline() { 571 if (mBaselineAlignedChildIndex < 0) { 572 return super.getBaseline(); 573 } 574 575 if (getChildCount() <= mBaselineAlignedChildIndex) { 576 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout " 577 + "set to an index that is out of bounds."); 578 } 579 580 final View child = getChildAt(mBaselineAlignedChildIndex); 581 final int childBaseline = child.getBaseline(); 582 583 if (childBaseline == -1) { 584 if (mBaselineAlignedChildIndex == 0) { 585 // this is just the default case, safe to return -1 586 return -1; 587 } 588 // the user picked an index that points to something that doesn't 589 // know how to calculate its baseline. 590 throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout " 591 + "points to a View that doesn't know how to get its baseline."); 592 } 593 594 // TODO: This should try to take into account the virtual offsets 595 // (See getNextLocationOffset and getLocationOffset) 596 // We should add to childTop: 597 // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex]) 598 // and also add: 599 // getLocationOffset(child) 600 int childTop = mBaselineChildTop; 601 602 if (mOrientation == VERTICAL) { 603 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 604 if (majorGravity != Gravity.TOP) { 605 switch (majorGravity) { 606 case Gravity.BOTTOM: 607 childTop = mBottom - mTop - mPaddingBottom - mTotalLength; 608 break; 609 610 case Gravity.CENTER_VERTICAL: 611 childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) - 612 mTotalLength) / 2; 613 break; 614 } 615 } 616 } 617 618 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 619 return childTop + lp.topMargin + childBaseline; 620 } 621 622 /** 623 * @return The index of the child that will be used if this layout is 624 * part of a larger layout that is baseline aligned, or -1 if none has 625 * been set. 626 */ 627 public int getBaselineAlignedChildIndex() { 628 return mBaselineAlignedChildIndex; 629 } 630 631 /** 632 * @param i The index of the child that will be used if this layout is 633 * part of a larger layout that is baseline aligned. 634 * 635 * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex 636 */ 637 @android.view.RemotableViewMethod 638 public void setBaselineAlignedChildIndex(int i) { 639 if ((i < 0) || (i >= getChildCount())) { 640 throw new IllegalArgumentException("base aligned child index out " 641 + "of range (0, " + getChildCount() + ")"); 642 } 643 mBaselineAlignedChildIndex = i; 644 } 645 646 /** 647 * <p>Returns the view at the specified index. This method can be overriden 648 * to take into account virtual children. Refer to 649 * {@link android.widget.TableLayout} and {@link android.widget.TableRow} 650 * for an example.</p> 651 * 652 * @param index the child's index 653 * @return the child at the specified index, may be {@code null} 654 */ 655 @Nullable 656 View getVirtualChildAt(int index) { 657 return getChildAt(index); 658 } 659 660 /** 661 * <p>Returns the virtual number of children. This number might be different 662 * than the actual number of children if the layout can hold virtual 663 * children. Refer to 664 * {@link android.widget.TableLayout} and {@link android.widget.TableRow} 665 * for an example.</p> 666 * 667 * @return the virtual number of children 668 */ 669 int getVirtualChildCount() { 670 return getChildCount(); 671 } 672 673 /** 674 * Returns the desired weights sum. 675 * 676 * @return A number greater than 0.0f if the weight sum is defined, or 677 * a number lower than or equals to 0.0f if not weight sum is 678 * to be used. 679 */ 680 public float getWeightSum() { 681 return mWeightSum; 682 } 683 684 /** 685 * Defines the desired weights sum. If unspecified the weights sum is computed 686 * at layout time by adding the layout_weight of each child. 687 * 688 * This can be used for instance to give a single child 50% of the total 689 * available space by giving it a layout_weight of 0.5 and setting the 690 * weightSum to 1.0. 691 * 692 * @param weightSum a number greater than 0.0f, or a number lower than or equals 693 * to 0.0f if the weight sum should be computed from the children's 694 * layout_weight 695 */ 696 @android.view.RemotableViewMethod 697 public void setWeightSum(float weightSum) { 698 mWeightSum = Math.max(0.0f, weightSum); 699 } 700 701 @Override 702 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 703 if (mOrientation == VERTICAL) { 704 measureVertical(widthMeasureSpec, heightMeasureSpec); 705 } else { 706 measureHorizontal(widthMeasureSpec, heightMeasureSpec); 707 } 708 } 709 710 /** 711 * Determines where to position dividers between children. 712 * 713 * @param childIndex Index of child to check for preceding divider 714 * @return true if there should be a divider before the child at childIndex 715 * @hide Pending API consideration. Currently only used internally by the system. 716 */ 717 protected boolean hasDividerBeforeChildAt(int childIndex) { 718 if (childIndex == getVirtualChildCount()) { 719 // Check whether the end divider should draw. 720 return (mShowDividers & SHOW_DIVIDER_END) != 0; 721 } 722 boolean allViewsAreGoneBefore = allViewsAreGoneBefore(childIndex); 723 if (allViewsAreGoneBefore) { 724 // This is the first view that's not gone, check if beginning divider is enabled. 725 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0; 726 } else { 727 return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0; 728 } 729 } 730 731 /** 732 * Checks whether all (virtual) child views before the given index are gone. 733 */ 734 private boolean allViewsAreGoneBefore(int childIndex) { 735 for (int i = childIndex - 1; i >= 0; i--) { 736 final View child = getVirtualChildAt(i); 737 if (child != null && child.getVisibility() != GONE) { 738 return false; 739 } 740 } 741 return true; 742 } 743 744 /** 745 * Measures the children when the orientation of this LinearLayout is set 746 * to {@link #VERTICAL}. 747 * 748 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 749 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 750 * 751 * @see #getOrientation() 752 * @see #setOrientation(int) 753 * @see #onMeasure(int, int) 754 */ 755 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { 756 mTotalLength = 0; 757 int maxWidth = 0; 758 int childState = 0; 759 int alternativeMaxWidth = 0; 760 int weightedMaxWidth = 0; 761 boolean allFillParent = true; 762 float totalWeight = 0; 763 764 final int count = getVirtualChildCount(); 765 766 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 767 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 768 769 boolean matchWidth = false; 770 boolean skippedMeasure = false; 771 772 final int baselineChildIndex = mBaselineAlignedChildIndex; 773 final boolean useLargestChild = mUseLargestChild; 774 775 int largestChildHeight = Integer.MIN_VALUE; 776 int consumedExcessSpace = 0; 777 778 int nonSkippedChildCount = 0; 779 780 // See how tall everyone is. Also remember max width. 781 for (int i = 0; i < count; ++i) { 782 final View child = getVirtualChildAt(i); 783 if (child == null) { 784 mTotalLength += measureNullChild(i); 785 continue; 786 } 787 788 if (child.getVisibility() == View.GONE) { 789 i += getChildrenSkipCount(child, i); 790 continue; 791 } 792 793 nonSkippedChildCount++; 794 if (hasDividerBeforeChildAt(i)) { 795 mTotalLength += mDividerHeight; 796 } 797 798 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 799 800 totalWeight += lp.weight; 801 802 final boolean useExcessSpace = lp.height == 0 && lp.weight > 0; 803 if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) { 804 // Optimization: don't bother measuring children who are only 805 // laid out using excess space. These views will get measured 806 // later if we have space to distribute. 807 final int totalLength = mTotalLength; 808 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin); 809 skippedMeasure = true; 810 } else { 811 if (useExcessSpace) { 812 // The heightMode is either UNSPECIFIED or AT_MOST, and 813 // this child is only laid out using excess space. Measure 814 // using WRAP_CONTENT so that we can find out the view's 815 // optimal height. We'll restore the original height of 0 816 // after measurement. 817 lp.height = LayoutParams.WRAP_CONTENT; 818 } 819 820 // Determine how big this child would like to be. If this or 821 // previous children have given a weight, then we allow it to 822 // use all available space (and we will shrink things later 823 // if needed). 824 final int usedHeight = totalWeight == 0 ? mTotalLength : 0; 825 measureChildBeforeLayout(child, i, widthMeasureSpec, 0, 826 heightMeasureSpec, usedHeight); 827 828 final int childHeight = child.getMeasuredHeight(); 829 if (useExcessSpace) { 830 // Restore the original height and record how much space 831 // we've allocated to excess-only children so that we can 832 // match the behavior of EXACTLY measurement. 833 lp.height = 0; 834 consumedExcessSpace += childHeight; 835 } 836 837 final int totalLength = mTotalLength; 838 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + 839 lp.bottomMargin + getNextLocationOffset(child)); 840 841 if (useLargestChild) { 842 largestChildHeight = Math.max(childHeight, largestChildHeight); 843 } 844 } 845 846 /** 847 * If applicable, compute the additional offset to the child's baseline 848 * we'll need later when asked {@link #getBaseline}. 849 */ 850 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) { 851 mBaselineChildTop = mTotalLength; 852 } 853 854 // if we are trying to use a child index for our baseline, the above 855 // book keeping only works if there are no children above it with 856 // weight. fail fast to aid the developer. 857 if (i < baselineChildIndex && lp.weight > 0) { 858 throw new RuntimeException("A child of LinearLayout with index " 859 + "less than mBaselineAlignedChildIndex has weight > 0, which " 860 + "won't work. Either remove the weight, or don't set " 861 + "mBaselineAlignedChildIndex."); 862 } 863 864 boolean matchWidthLocally = false; 865 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) { 866 // The width of the linear layout will scale, and at least one 867 // child said it wanted to match our width. Set a flag 868 // indicating that we need to remeasure at least that view when 869 // we know our width. 870 matchWidth = true; 871 matchWidthLocally = true; 872 } 873 874 final int margin = lp.leftMargin + lp.rightMargin; 875 final int measuredWidth = child.getMeasuredWidth() + margin; 876 maxWidth = Math.max(maxWidth, measuredWidth); 877 childState = combineMeasuredStates(childState, child.getMeasuredState()); 878 879 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 880 if (lp.weight > 0) { 881 /* 882 * Widths of weighted Views are bogus if we end up 883 * remeasuring, so keep them separate. 884 */ 885 weightedMaxWidth = Math.max(weightedMaxWidth, 886 matchWidthLocally ? margin : measuredWidth); 887 } else { 888 alternativeMaxWidth = Math.max(alternativeMaxWidth, 889 matchWidthLocally ? margin : measuredWidth); 890 } 891 892 i += getChildrenSkipCount(child, i); 893 } 894 895 if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) { 896 mTotalLength += mDividerHeight; 897 } 898 899 if (useLargestChild && 900 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) { 901 mTotalLength = 0; 902 903 for (int i = 0; i < count; ++i) { 904 final View child = getVirtualChildAt(i); 905 if (child == null) { 906 mTotalLength += measureNullChild(i); 907 continue; 908 } 909 910 if (child.getVisibility() == GONE) { 911 i += getChildrenSkipCount(child, i); 912 continue; 913 } 914 915 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 916 child.getLayoutParams(); 917 // Account for negative margins 918 final int totalLength = mTotalLength; 919 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight + 920 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 921 } 922 } 923 924 // Add in our padding 925 mTotalLength += mPaddingTop + mPaddingBottom; 926 927 int heightSize = mTotalLength; 928 929 // Check against our minimum height 930 heightSize = Math.max(heightSize, getSuggestedMinimumHeight()); 931 932 // Reconcile our calculated size with the heightMeasureSpec 933 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0); 934 heightSize = heightSizeAndState & MEASURED_SIZE_MASK; 935 // Either expand children with weight to take up available space or 936 // shrink them if they extend beyond our current bounds. If we skipped 937 // measurement on any children, we need to measure them now. 938 int remainingExcess = heightSize - mTotalLength 939 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace); 940 if (skippedMeasure 941 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) { 942 float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 943 944 mTotalLength = 0; 945 946 for (int i = 0; i < count; ++i) { 947 final View child = getVirtualChildAt(i); 948 if (child == null || child.getVisibility() == View.GONE) { 949 continue; 950 } 951 952 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 953 final float childWeight = lp.weight; 954 if (childWeight > 0) { 955 final int share = (int) (childWeight * remainingExcess / remainingWeightSum); 956 remainingExcess -= share; 957 remainingWeightSum -= childWeight; 958 959 final int childHeight; 960 if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) { 961 childHeight = largestChildHeight; 962 } else if (lp.height == 0 && (!mAllowInconsistentMeasurement 963 || heightMode == MeasureSpec.EXACTLY)) { 964 // This child needs to be laid out from scratch using 965 // only its share of excess space. 966 childHeight = share; 967 } else { 968 // This child had some intrinsic height to which we 969 // need to add its share of excess space. 970 childHeight = child.getMeasuredHeight() + share; 971 } 972 973 final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 974 Math.max(0, childHeight), MeasureSpec.EXACTLY); 975 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 976 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin, 977 lp.width); 978 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 979 980 // Child may now not fit in vertical dimension. 981 childState = combineMeasuredStates(childState, child.getMeasuredState() 982 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); 983 } 984 985 final int margin = lp.leftMargin + lp.rightMargin; 986 final int measuredWidth = child.getMeasuredWidth() + margin; 987 maxWidth = Math.max(maxWidth, measuredWidth); 988 989 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY && 990 lp.width == LayoutParams.MATCH_PARENT; 991 992 alternativeMaxWidth = Math.max(alternativeMaxWidth, 993 matchWidthLocally ? margin : measuredWidth); 994 995 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 996 997 final int totalLength = mTotalLength; 998 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() + 999 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 1000 } 1001 1002 // Add in our padding 1003 mTotalLength += mPaddingTop + mPaddingBottom; 1004 // TODO: Should we recompute the heightSpec based on the new total length? 1005 } else { 1006 alternativeMaxWidth = Math.max(alternativeMaxWidth, 1007 weightedMaxWidth); 1008 1009 1010 // We have no limit, so make all weighted views as tall as the largest child. 1011 // Children will have already been measured once. 1012 if (useLargestChild && heightMode != MeasureSpec.EXACTLY) { 1013 for (int i = 0; i < count; i++) { 1014 final View child = getVirtualChildAt(i); 1015 if (child == null || child.getVisibility() == View.GONE) { 1016 continue; 1017 } 1018 1019 final LinearLayout.LayoutParams lp = 1020 (LinearLayout.LayoutParams) child.getLayoutParams(); 1021 1022 float childExtra = lp.weight; 1023 if (childExtra > 0) { 1024 child.measure( 1025 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), 1026 MeasureSpec.EXACTLY), 1027 MeasureSpec.makeMeasureSpec(largestChildHeight, 1028 MeasureSpec.EXACTLY)); 1029 } 1030 } 1031 } 1032 } 1033 1034 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) { 1035 maxWidth = alternativeMaxWidth; 1036 } 1037 1038 maxWidth += mPaddingLeft + mPaddingRight; 1039 1040 // Check against our minimum width 1041 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 1042 1043 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 1044 heightSizeAndState); 1045 1046 if (matchWidth) { 1047 forceUniformWidth(count, heightMeasureSpec); 1048 } 1049 } 1050 1051 private void forceUniformWidth(int count, int heightMeasureSpec) { 1052 // Pretend that the linear layout has an exact size. 1053 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), 1054 MeasureSpec.EXACTLY); 1055 for (int i = 0; i< count; ++i) { 1056 final View child = getVirtualChildAt(i); 1057 if (child != null && child.getVisibility() != GONE) { 1058 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams()); 1059 1060 if (lp.width == LayoutParams.MATCH_PARENT) { 1061 // Temporarily force children to reuse their old measured height 1062 // FIXME: this may not be right for something like wrapping text? 1063 int oldHeight = lp.height; 1064 lp.height = child.getMeasuredHeight(); 1065 1066 // Remeasue with new dimensions 1067 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0); 1068 lp.height = oldHeight; 1069 } 1070 } 1071 } 1072 } 1073 1074 /** 1075 * Measures the children when the orientation of this LinearLayout is set 1076 * to {@link #HORIZONTAL}. 1077 * 1078 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 1079 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 1080 * 1081 * @see #getOrientation() 1082 * @see #setOrientation(int) 1083 * @see #onMeasure(int, int) 1084 */ 1085 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { 1086 mTotalLength = 0; 1087 int maxHeight = 0; 1088 int childState = 0; 1089 int alternativeMaxHeight = 0; 1090 int weightedMaxHeight = 0; 1091 boolean allFillParent = true; 1092 float totalWeight = 0; 1093 1094 final int count = getVirtualChildCount(); 1095 1096 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 1097 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 1098 1099 boolean matchHeight = false; 1100 boolean skippedMeasure = false; 1101 1102 if (mMaxAscent == null || mMaxDescent == null) { 1103 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT]; 1104 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT]; 1105 } 1106 1107 final int[] maxAscent = mMaxAscent; 1108 final int[] maxDescent = mMaxDescent; 1109 1110 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 1111 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 1112 1113 final boolean baselineAligned = mBaselineAligned; 1114 final boolean useLargestChild = mUseLargestChild; 1115 1116 final boolean isExactly = widthMode == MeasureSpec.EXACTLY; 1117 1118 int largestChildWidth = Integer.MIN_VALUE; 1119 int usedExcessSpace = 0; 1120 1121 int nonSkippedChildCount = 0; 1122 1123 // See how wide everyone is. Also remember max height. 1124 for (int i = 0; i < count; ++i) { 1125 final View child = getVirtualChildAt(i); 1126 if (child == null) { 1127 mTotalLength += measureNullChild(i); 1128 continue; 1129 } 1130 1131 if (child.getVisibility() == GONE) { 1132 i += getChildrenSkipCount(child, i); 1133 continue; 1134 } 1135 1136 nonSkippedChildCount++; 1137 if (hasDividerBeforeChildAt(i)) { 1138 mTotalLength += mDividerWidth; 1139 } 1140 1141 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1142 1143 totalWeight += lp.weight; 1144 1145 final boolean useExcessSpace = lp.width == 0 && lp.weight > 0; 1146 if (widthMode == MeasureSpec.EXACTLY && useExcessSpace) { 1147 // Optimization: don't bother measuring children who are only 1148 // laid out using excess space. These views will get measured 1149 // later if we have space to distribute. 1150 if (isExactly) { 1151 mTotalLength += lp.leftMargin + lp.rightMargin; 1152 } else { 1153 final int totalLength = mTotalLength; 1154 mTotalLength = Math.max(totalLength, totalLength + 1155 lp.leftMargin + lp.rightMargin); 1156 } 1157 1158 // Baseline alignment requires to measure widgets to obtain the 1159 // baseline offset (in particular for TextViews). The following 1160 // defeats the optimization mentioned above. Allow the child to 1161 // use as much space as it wants because we can shrink things 1162 // later (and re-measure). 1163 if (baselineAligned) { 1164 final int freeWidthSpec = MeasureSpec.makeSafeMeasureSpec( 1165 MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED); 1166 final int freeHeightSpec = MeasureSpec.makeSafeMeasureSpec( 1167 MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED); 1168 child.measure(freeWidthSpec, freeHeightSpec); 1169 } else { 1170 skippedMeasure = true; 1171 } 1172 } else { 1173 if (useExcessSpace) { 1174 // The widthMode is either UNSPECIFIED or AT_MOST, and 1175 // this child is only laid out using excess space. Measure 1176 // using WRAP_CONTENT so that we can find out the view's 1177 // optimal width. We'll restore the original width of 0 1178 // after measurement. 1179 lp.width = LayoutParams.WRAP_CONTENT; 1180 } 1181 1182 // Determine how big this child would like to be. If this or 1183 // previous children have given a weight, then we allow it to 1184 // use all available space (and we will shrink things later 1185 // if needed). 1186 final int usedWidth = totalWeight == 0 ? mTotalLength : 0; 1187 measureChildBeforeLayout(child, i, widthMeasureSpec, usedWidth, 1188 heightMeasureSpec, 0); 1189 1190 final int childWidth = child.getMeasuredWidth(); 1191 if (useExcessSpace) { 1192 // Restore the original width and record how much space 1193 // we've allocated to excess-only children so that we can 1194 // match the behavior of EXACTLY measurement. 1195 lp.width = 0; 1196 usedExcessSpace += childWidth; 1197 } 1198 1199 if (isExactly) { 1200 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin 1201 + getNextLocationOffset(child); 1202 } else { 1203 final int totalLength = mTotalLength; 1204 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin 1205 + lp.rightMargin + getNextLocationOffset(child)); 1206 } 1207 1208 if (useLargestChild) { 1209 largestChildWidth = Math.max(childWidth, largestChildWidth); 1210 } 1211 } 1212 1213 boolean matchHeightLocally = false; 1214 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) { 1215 // The height of the linear layout will scale, and at least one 1216 // child said it wanted to match our height. Set a flag indicating that 1217 // we need to remeasure at least that view when we know our height. 1218 matchHeight = true; 1219 matchHeightLocally = true; 1220 } 1221 1222 final int margin = lp.topMargin + lp.bottomMargin; 1223 final int childHeight = child.getMeasuredHeight() + margin; 1224 childState = combineMeasuredStates(childState, child.getMeasuredState()); 1225 1226 if (baselineAligned) { 1227 final int childBaseline = child.getBaseline(); 1228 if (childBaseline != -1) { 1229 // Translates the child's vertical gravity into an index 1230 // in the range 0..VERTICAL_GRAVITY_COUNT 1231 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1232 & Gravity.VERTICAL_GRAVITY_MASK; 1233 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1234 & ~Gravity.AXIS_SPECIFIED) >> 1; 1235 1236 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1237 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline); 1238 } 1239 } 1240 1241 maxHeight = Math.max(maxHeight, childHeight); 1242 1243 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1244 if (lp.weight > 0) { 1245 /* 1246 * Heights of weighted Views are bogus if we end up 1247 * remeasuring, so keep them separate. 1248 */ 1249 weightedMaxHeight = Math.max(weightedMaxHeight, 1250 matchHeightLocally ? margin : childHeight); 1251 } else { 1252 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1253 matchHeightLocally ? margin : childHeight); 1254 } 1255 1256 i += getChildrenSkipCount(child, i); 1257 } 1258 1259 if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) { 1260 mTotalLength += mDividerWidth; 1261 } 1262 1263 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1264 // the most common case 1265 if (maxAscent[INDEX_TOP] != -1 || 1266 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1267 maxAscent[INDEX_BOTTOM] != -1 || 1268 maxAscent[INDEX_FILL] != -1) { 1269 final int ascent = Math.max(maxAscent[INDEX_FILL], 1270 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1271 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1272 final int descent = Math.max(maxDescent[INDEX_FILL], 1273 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1274 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1275 maxHeight = Math.max(maxHeight, ascent + descent); 1276 } 1277 1278 if (useLargestChild && 1279 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { 1280 mTotalLength = 0; 1281 1282 for (int i = 0; i < count; ++i) { 1283 final View child = getVirtualChildAt(i); 1284 if (child == null) { 1285 mTotalLength += measureNullChild(i); 1286 continue; 1287 } 1288 1289 if (child.getVisibility() == GONE) { 1290 i += getChildrenSkipCount(child, i); 1291 continue; 1292 } 1293 1294 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 1295 child.getLayoutParams(); 1296 if (isExactly) { 1297 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin + 1298 getNextLocationOffset(child); 1299 } else { 1300 final int totalLength = mTotalLength; 1301 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth + 1302 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1303 } 1304 } 1305 } 1306 1307 // Add in our padding 1308 mTotalLength += mPaddingLeft + mPaddingRight; 1309 1310 int widthSize = mTotalLength; 1311 1312 // Check against our minimum width 1313 widthSize = Math.max(widthSize, getSuggestedMinimumWidth()); 1314 1315 // Reconcile our calculated size with the widthMeasureSpec 1316 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0); 1317 widthSize = widthSizeAndState & MEASURED_SIZE_MASK; 1318 1319 // Either expand children with weight to take up available space or 1320 // shrink them if they extend beyond our current bounds. If we skipped 1321 // measurement on any children, we need to measure them now. 1322 int remainingExcess = widthSize - mTotalLength 1323 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace); 1324 if (skippedMeasure 1325 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) { 1326 float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 1327 1328 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 1329 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 1330 maxHeight = -1; 1331 1332 mTotalLength = 0; 1333 1334 for (int i = 0; i < count; ++i) { 1335 final View child = getVirtualChildAt(i); 1336 if (child == null || child.getVisibility() == View.GONE) { 1337 continue; 1338 } 1339 1340 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1341 final float childWeight = lp.weight; 1342 if (childWeight > 0) { 1343 final int share = (int) (childWeight * remainingExcess / remainingWeightSum); 1344 remainingExcess -= share; 1345 remainingWeightSum -= childWeight; 1346 1347 final int childWidth; 1348 if (mUseLargestChild && widthMode != MeasureSpec.EXACTLY) { 1349 childWidth = largestChildWidth; 1350 } else if (lp.width == 0 && (!mAllowInconsistentMeasurement 1351 || widthMode == MeasureSpec.EXACTLY)) { 1352 // This child needs to be laid out from scratch using 1353 // only its share of excess space. 1354 childWidth = share; 1355 } else { 1356 // This child had some intrinsic width to which we 1357 // need to add its share of excess space. 1358 childWidth = child.getMeasuredWidth() + share; 1359 } 1360 1361 final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( 1362 Math.max(0, childWidth), MeasureSpec.EXACTLY); 1363 final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 1364 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin, 1365 lp.height); 1366 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1367 1368 // Child may now not fit in horizontal dimension. 1369 childState = combineMeasuredStates(childState, 1370 child.getMeasuredState() & MEASURED_STATE_MASK); 1371 } 1372 1373 if (isExactly) { 1374 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin + 1375 getNextLocationOffset(child); 1376 } else { 1377 final int totalLength = mTotalLength; 1378 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() + 1379 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1380 } 1381 1382 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY && 1383 lp.height == LayoutParams.MATCH_PARENT; 1384 1385 final int margin = lp.topMargin + lp .bottomMargin; 1386 int childHeight = child.getMeasuredHeight() + margin; 1387 maxHeight = Math.max(maxHeight, childHeight); 1388 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1389 matchHeightLocally ? margin : childHeight); 1390 1391 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1392 1393 if (baselineAligned) { 1394 final int childBaseline = child.getBaseline(); 1395 if (childBaseline != -1) { 1396 // Translates the child's vertical gravity into an index in the range 0..2 1397 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1398 & Gravity.VERTICAL_GRAVITY_MASK; 1399 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1400 & ~Gravity.AXIS_SPECIFIED) >> 1; 1401 1402 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1403 maxDescent[index] = Math.max(maxDescent[index], 1404 childHeight - childBaseline); 1405 } 1406 } 1407 } 1408 1409 // Add in our padding 1410 mTotalLength += mPaddingLeft + mPaddingRight; 1411 // TODO: Should we update widthSize with the new total length? 1412 1413 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1414 // the most common case 1415 if (maxAscent[INDEX_TOP] != -1 || 1416 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1417 maxAscent[INDEX_BOTTOM] != -1 || 1418 maxAscent[INDEX_FILL] != -1) { 1419 final int ascent = Math.max(maxAscent[INDEX_FILL], 1420 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1421 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1422 final int descent = Math.max(maxDescent[INDEX_FILL], 1423 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1424 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1425 maxHeight = Math.max(maxHeight, ascent + descent); 1426 } 1427 } else { 1428 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); 1429 1430 // We have no limit, so make all weighted views as wide as the largest child. 1431 // Children will have already been measured once. 1432 if (useLargestChild && widthMode != MeasureSpec.EXACTLY) { 1433 for (int i = 0; i < count; i++) { 1434 final View child = getVirtualChildAt(i); 1435 if (child == null || child.getVisibility() == View.GONE) { 1436 continue; 1437 } 1438 1439 final LinearLayout.LayoutParams lp = 1440 (LinearLayout.LayoutParams) child.getLayoutParams(); 1441 1442 float childExtra = lp.weight; 1443 if (childExtra > 0) { 1444 child.measure( 1445 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY), 1446 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), 1447 MeasureSpec.EXACTLY)); 1448 } 1449 } 1450 } 1451 } 1452 1453 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { 1454 maxHeight = alternativeMaxHeight; 1455 } 1456 1457 maxHeight += mPaddingTop + mPaddingBottom; 1458 1459 // Check against our minimum height 1460 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 1461 1462 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK), 1463 resolveSizeAndState(maxHeight, heightMeasureSpec, 1464 (childState<<MEASURED_HEIGHT_STATE_SHIFT))); 1465 1466 if (matchHeight) { 1467 forceUniformHeight(count, widthMeasureSpec); 1468 } 1469 } 1470 1471 private void forceUniformHeight(int count, int widthMeasureSpec) { 1472 // Pretend that the linear layout has an exact size. This is the measured height of 1473 // ourselves. The measured height should be the max height of the children, changed 1474 // to accommodate the heightMeasureSpec from the parent 1475 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), 1476 MeasureSpec.EXACTLY); 1477 for (int i = 0; i < count; ++i) { 1478 final View child = getVirtualChildAt(i); 1479 if (child != null && child.getVisibility() != GONE) { 1480 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 1481 1482 if (lp.height == LayoutParams.MATCH_PARENT) { 1483 // Temporarily force children to reuse their old measured width 1484 // FIXME: this may not be right for something like wrapping text? 1485 int oldWidth = lp.width; 1486 lp.width = child.getMeasuredWidth(); 1487 1488 // Remeasure with new dimensions 1489 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0); 1490 lp.width = oldWidth; 1491 } 1492 } 1493 } 1494 } 1495 1496 /** 1497 * <p>Returns the number of children to skip after measuring/laying out 1498 * the specified child.</p> 1499 * 1500 * @param child the child after which we want to skip children 1501 * @param index the index of the child after which we want to skip children 1502 * @return the number of children to skip, 0 by default 1503 */ 1504 int getChildrenSkipCount(View child, int index) { 1505 return 0; 1506 } 1507 1508 /** 1509 * <p>Returns the size (width or height) that should be occupied by a null 1510 * child.</p> 1511 * 1512 * @param childIndex the index of the null child 1513 * @return the width or height of the child depending on the orientation 1514 */ 1515 int measureNullChild(int childIndex) { 1516 return 0; 1517 } 1518 1519 /** 1520 * <p>Measure the child according to the parent's measure specs. This 1521 * method should be overriden by subclasses to force the sizing of 1522 * children. This method is called by {@link #measureVertical(int, int)} and 1523 * {@link #measureHorizontal(int, int)}.</p> 1524 * 1525 * @param child the child to measure 1526 * @param childIndex the index of the child in this view 1527 * @param widthMeasureSpec horizontal space requirements as imposed by the parent 1528 * @param totalWidth extra space that has been used up by the parent horizontally 1529 * @param heightMeasureSpec vertical space requirements as imposed by the parent 1530 * @param totalHeight extra space that has been used up by the parent vertically 1531 */ 1532 void measureChildBeforeLayout(View child, int childIndex, 1533 int widthMeasureSpec, int totalWidth, int heightMeasureSpec, 1534 int totalHeight) { 1535 measureChildWithMargins(child, widthMeasureSpec, totalWidth, 1536 heightMeasureSpec, totalHeight); 1537 } 1538 1539 /** 1540 * <p>Return the location offset of the specified child. This can be used 1541 * by subclasses to change the location of a given widget.</p> 1542 * 1543 * @param child the child for which to obtain the location offset 1544 * @return the location offset in pixels 1545 */ 1546 int getLocationOffset(View child) { 1547 return 0; 1548 } 1549 1550 /** 1551 * <p>Return the size offset of the next sibling of the specified child. 1552 * This can be used by subclasses to change the location of the widget 1553 * following <code>child</code>.</p> 1554 * 1555 * @param child the child whose next sibling will be moved 1556 * @return the location offset of the next child in pixels 1557 */ 1558 int getNextLocationOffset(View child) { 1559 return 0; 1560 } 1561 1562 @Override 1563 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1564 if (mOrientation == VERTICAL) { 1565 layoutVertical(l, t, r, b); 1566 } else { 1567 layoutHorizontal(l, t, r, b); 1568 } 1569 } 1570 1571 /** 1572 * Position the children during a layout pass if the orientation of this 1573 * LinearLayout is set to {@link #VERTICAL}. 1574 * 1575 * @see #getOrientation() 1576 * @see #setOrientation(int) 1577 * @see #onLayout(boolean, int, int, int, int) 1578 * @param left 1579 * @param top 1580 * @param right 1581 * @param bottom 1582 */ 1583 void layoutVertical(int left, int top, int right, int bottom) { 1584 final int paddingLeft = mPaddingLeft; 1585 1586 int childTop; 1587 int childLeft; 1588 1589 // Where right end of child should go 1590 final int width = right - left; 1591 int childRight = width - mPaddingRight; 1592 1593 // Space available for child 1594 int childSpace = width - paddingLeft - mPaddingRight; 1595 1596 final int count = getVirtualChildCount(); 1597 1598 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1599 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1600 1601 switch (majorGravity) { 1602 case Gravity.BOTTOM: 1603 // mTotalLength contains the padding already 1604 childTop = mPaddingTop + bottom - top - mTotalLength; 1605 break; 1606 1607 // mTotalLength contains the padding already 1608 case Gravity.CENTER_VERTICAL: 1609 childTop = mPaddingTop + (bottom - top - mTotalLength) / 2; 1610 break; 1611 1612 case Gravity.TOP: 1613 default: 1614 childTop = mPaddingTop; 1615 break; 1616 } 1617 1618 for (int i = 0; i < count; i++) { 1619 final View child = getVirtualChildAt(i); 1620 if (child == null) { 1621 childTop += measureNullChild(i); 1622 } else if (child.getVisibility() != GONE) { 1623 final int childWidth = child.getMeasuredWidth(); 1624 final int childHeight = child.getMeasuredHeight(); 1625 1626 final LinearLayout.LayoutParams lp = 1627 (LinearLayout.LayoutParams) child.getLayoutParams(); 1628 1629 int gravity = lp.gravity; 1630 if (gravity < 0) { 1631 gravity = minorGravity; 1632 } 1633 final int layoutDirection = getLayoutDirection(); 1634 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 1635 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 1636 case Gravity.CENTER_HORIZONTAL: 1637 childLeft = paddingLeft + ((childSpace - childWidth) / 2) 1638 + lp.leftMargin - lp.rightMargin; 1639 break; 1640 1641 case Gravity.RIGHT: 1642 childLeft = childRight - childWidth - lp.rightMargin; 1643 break; 1644 1645 case Gravity.LEFT: 1646 default: 1647 childLeft = paddingLeft + lp.leftMargin; 1648 break; 1649 } 1650 1651 if (hasDividerBeforeChildAt(i)) { 1652 childTop += mDividerHeight; 1653 } 1654 1655 childTop += lp.topMargin; 1656 setChildFrame(child, childLeft, childTop + getLocationOffset(child), 1657 childWidth, childHeight); 1658 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); 1659 1660 i += getChildrenSkipCount(child, i); 1661 } 1662 } 1663 } 1664 1665 @Override 1666 public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) { 1667 super.onRtlPropertiesChanged(layoutDirection); 1668 if (layoutDirection != mLayoutDirection) { 1669 mLayoutDirection = layoutDirection; 1670 if (mOrientation == HORIZONTAL) { 1671 requestLayout(); 1672 } 1673 } 1674 } 1675 1676 /** 1677 * Position the children during a layout pass if the orientation of this 1678 * LinearLayout is set to {@link #HORIZONTAL}. 1679 * 1680 * @see #getOrientation() 1681 * @see #setOrientation(int) 1682 * @see #onLayout(boolean, int, int, int, int) 1683 * @param left 1684 * @param top 1685 * @param right 1686 * @param bottom 1687 */ 1688 void layoutHorizontal(int left, int top, int right, int bottom) { 1689 final boolean isLayoutRtl = isLayoutRtl(); 1690 final int paddingTop = mPaddingTop; 1691 1692 int childTop; 1693 int childLeft; 1694 1695 // Where bottom of child should go 1696 final int height = bottom - top; 1697 int childBottom = height - mPaddingBottom; 1698 1699 // Space available for child 1700 int childSpace = height - paddingTop - mPaddingBottom; 1701 1702 final int count = getVirtualChildCount(); 1703 1704 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1705 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1706 1707 final boolean baselineAligned = mBaselineAligned; 1708 1709 final int[] maxAscent = mMaxAscent; 1710 final int[] maxDescent = mMaxDescent; 1711 1712 final int layoutDirection = getLayoutDirection(); 1713 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) { 1714 case Gravity.RIGHT: 1715 // mTotalLength contains the padding already 1716 childLeft = mPaddingLeft + right - left - mTotalLength; 1717 break; 1718 1719 case Gravity.CENTER_HORIZONTAL: 1720 // mTotalLength contains the padding already 1721 childLeft = mPaddingLeft + (right - left - mTotalLength) / 2; 1722 break; 1723 1724 case Gravity.LEFT: 1725 default: 1726 childLeft = mPaddingLeft; 1727 break; 1728 } 1729 1730 int start = 0; 1731 int dir = 1; 1732 //In case of RTL, start drawing from the last child. 1733 if (isLayoutRtl) { 1734 start = count - 1; 1735 dir = -1; 1736 } 1737 1738 for (int i = 0; i < count; i++) { 1739 final int childIndex = start + dir * i; 1740 final View child = getVirtualChildAt(childIndex); 1741 if (child == null) { 1742 childLeft += measureNullChild(childIndex); 1743 } else if (child.getVisibility() != GONE) { 1744 final int childWidth = child.getMeasuredWidth(); 1745 final int childHeight = child.getMeasuredHeight(); 1746 int childBaseline = -1; 1747 1748 final LinearLayout.LayoutParams lp = 1749 (LinearLayout.LayoutParams) child.getLayoutParams(); 1750 1751 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) { 1752 childBaseline = child.getBaseline(); 1753 } 1754 1755 int gravity = lp.gravity; 1756 if (gravity < 0) { 1757 gravity = minorGravity; 1758 } 1759 1760 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 1761 case Gravity.TOP: 1762 childTop = paddingTop + lp.topMargin; 1763 if (childBaseline != -1) { 1764 childTop += maxAscent[INDEX_TOP] - childBaseline; 1765 } 1766 break; 1767 1768 case Gravity.CENTER_VERTICAL: 1769 // Removed support for baseline alignment when layout_gravity or 1770 // gravity == center_vertical. See bug #1038483. 1771 // Keep the code around if we need to re-enable this feature 1772 // if (childBaseline != -1) { 1773 // // Align baselines vertically only if the child is smaller than us 1774 // if (childSpace - childHeight > 0) { 1775 // childTop = paddingTop + (childSpace / 2) - childBaseline; 1776 // } else { 1777 // childTop = paddingTop + (childSpace - childHeight) / 2; 1778 // } 1779 // } else { 1780 childTop = paddingTop + ((childSpace - childHeight) / 2) 1781 + lp.topMargin - lp.bottomMargin; 1782 break; 1783 1784 case Gravity.BOTTOM: 1785 childTop = childBottom - childHeight - lp.bottomMargin; 1786 if (childBaseline != -1) { 1787 int descent = child.getMeasuredHeight() - childBaseline; 1788 childTop -= (maxDescent[INDEX_BOTTOM] - descent); 1789 } 1790 break; 1791 default: 1792 childTop = paddingTop; 1793 break; 1794 } 1795 1796 if (hasDividerBeforeChildAt(childIndex)) { 1797 childLeft += mDividerWidth; 1798 } 1799 1800 childLeft += lp.leftMargin; 1801 setChildFrame(child, childLeft + getLocationOffset(child), childTop, 1802 childWidth, childHeight); 1803 childLeft += childWidth + lp.rightMargin + 1804 getNextLocationOffset(child); 1805 1806 i += getChildrenSkipCount(child, childIndex); 1807 } 1808 } 1809 } 1810 1811 private void setChildFrame(View child, int left, int top, int width, int height) { 1812 child.layout(left, top, left + width, top + height); 1813 } 1814 1815 /** 1816 * Should the layout be a column or a row. 1817 * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default 1818 * value is {@link #HORIZONTAL}. 1819 * 1820 * @attr ref android.R.styleable#LinearLayout_orientation 1821 */ 1822 public void setOrientation(@OrientationMode int orientation) { 1823 if (mOrientation != orientation) { 1824 mOrientation = orientation; 1825 requestLayout(); 1826 } 1827 } 1828 1829 /** 1830 * Returns the current orientation. 1831 * 1832 * @return either {@link #HORIZONTAL} or {@link #VERTICAL} 1833 */ 1834 @OrientationMode 1835 public int getOrientation() { 1836 return mOrientation; 1837 } 1838 1839 /** 1840 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If 1841 * this layout has a VERTICAL orientation, this controls where all the child 1842 * views are placed if there is extra vertical space. If this layout has a 1843 * HORIZONTAL orientation, this controls the alignment of the children. 1844 * 1845 * @param gravity See {@link android.view.Gravity} 1846 * 1847 * @attr ref android.R.styleable#LinearLayout_gravity 1848 */ 1849 @android.view.RemotableViewMethod 1850 public void setGravity(int gravity) { 1851 if (mGravity != gravity) { 1852 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { 1853 gravity |= Gravity.START; 1854 } 1855 1856 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 1857 gravity |= Gravity.TOP; 1858 } 1859 1860 mGravity = gravity; 1861 requestLayout(); 1862 } 1863 } 1864 1865 /** 1866 * Returns the current gravity. See {@link android.view.Gravity} 1867 * 1868 * @return the current gravity. 1869 * @see #setGravity 1870 */ 1871 public int getGravity() { 1872 return mGravity; 1873 } 1874 1875 @android.view.RemotableViewMethod 1876 public void setHorizontalGravity(int horizontalGravity) { 1877 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1878 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) { 1879 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity; 1880 requestLayout(); 1881 } 1882 } 1883 1884 @android.view.RemotableViewMethod 1885 public void setVerticalGravity(int verticalGravity) { 1886 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK; 1887 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) { 1888 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity; 1889 requestLayout(); 1890 } 1891 } 1892 1893 @Override 1894 public LayoutParams generateLayoutParams(AttributeSet attrs) { 1895 return new LinearLayout.LayoutParams(getContext(), attrs); 1896 } 1897 1898 /** 1899 * Returns a set of layout parameters with a width of 1900 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} 1901 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} 1902 * when the layout's orientation is {@link #VERTICAL}. When the orientation is 1903 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT} 1904 * and the height to {@link LayoutParams#WRAP_CONTENT}. 1905 */ 1906 @Override 1907 protected LayoutParams generateDefaultLayoutParams() { 1908 if (mOrientation == HORIZONTAL) { 1909 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 1910 } else if (mOrientation == VERTICAL) { 1911 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 1912 } 1913 return null; 1914 } 1915 1916 @Override 1917 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 1918 if (sPreserveMarginParamsInLayoutParamConversion) { 1919 if (lp instanceof LayoutParams) { 1920 return new LayoutParams((LayoutParams) lp); 1921 } else if (lp instanceof MarginLayoutParams) { 1922 return new LayoutParams((MarginLayoutParams) lp); 1923 } 1924 } 1925 return new LayoutParams(lp); 1926 } 1927 1928 1929 // Override to allow type-checking of LayoutParams. 1930 @Override 1931 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1932 return p instanceof LinearLayout.LayoutParams; 1933 } 1934 1935 @Override 1936 public CharSequence getAccessibilityClassName() { 1937 return LinearLayout.class.getName(); 1938 } 1939 1940 /** @hide */ 1941 @Override 1942 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 1943 super.encodeProperties(encoder); 1944 encoder.addProperty("layout:baselineAligned", mBaselineAligned); 1945 encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex); 1946 encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop); 1947 encoder.addProperty("measurement:orientation", mOrientation); 1948 encoder.addProperty("measurement:gravity", mGravity); 1949 encoder.addProperty("measurement:totalLength", mTotalLength); 1950 encoder.addProperty("layout:totalLength", mTotalLength); 1951 encoder.addProperty("layout:useLargestChild", mUseLargestChild); 1952 } 1953 1954 /** 1955 * Per-child layout information associated with ViewLinearLayout. 1956 * 1957 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight 1958 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity 1959 */ 1960 public static class LayoutParams extends ViewGroup.MarginLayoutParams { 1961 /** 1962 * Indicates how much of the extra space in the LinearLayout will be 1963 * allocated to the view associated with these LayoutParams. Specify 1964 * 0 if the view should not be stretched. Otherwise the extra pixels 1965 * will be pro-rated among all views whose weight is greater than 0. 1966 */ 1967 @ViewDebug.ExportedProperty(category = "layout") 1968 public float weight; 1969 1970 /** 1971 * Gravity for the view associated with these LayoutParams. 1972 * 1973 * @see android.view.Gravity 1974 */ 1975 @ViewDebug.ExportedProperty(category = "layout", mapping = { 1976 @ViewDebug.IntToString(from = -1, to = "NONE"), 1977 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), 1978 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), 1979 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), 1980 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), 1981 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), 1982 @ViewDebug.IntToString(from = Gravity.START, to = "START"), 1983 @ViewDebug.IntToString(from = Gravity.END, to = "END"), 1984 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), 1985 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), 1986 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), 1987 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), 1988 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), 1989 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") 1990 }) 1991 public int gravity = -1; 1992 1993 /** 1994 * {@inheritDoc} 1995 */ 1996 public LayoutParams(Context c, AttributeSet attrs) { 1997 super(c, attrs); 1998 TypedArray a = 1999 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout); 2000 2001 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0); 2002 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1); 2003 2004 a.recycle(); 2005 } 2006 2007 /** 2008 * {@inheritDoc} 2009 */ 2010 public LayoutParams(int width, int height) { 2011 super(width, height); 2012 weight = 0; 2013 } 2014 2015 /** 2016 * Creates a new set of layout parameters with the specified width, height 2017 * and weight. 2018 * 2019 * @param width the width, either {@link #MATCH_PARENT}, 2020 * {@link #WRAP_CONTENT} or a fixed size in pixels 2021 * @param height the height, either {@link #MATCH_PARENT}, 2022 * {@link #WRAP_CONTENT} or a fixed size in pixels 2023 * @param weight the weight 2024 */ 2025 public LayoutParams(int width, int height, float weight) { 2026 super(width, height); 2027 this.weight = weight; 2028 } 2029 2030 /** 2031 * {@inheritDoc} 2032 */ 2033 public LayoutParams(ViewGroup.LayoutParams p) { 2034 super(p); 2035 } 2036 2037 /** 2038 * {@inheritDoc} 2039 */ 2040 public LayoutParams(ViewGroup.MarginLayoutParams source) { 2041 super(source); 2042 } 2043 2044 /** 2045 * Copy constructor. Clones the width, height, margin values, weight, 2046 * and gravity of the source. 2047 * 2048 * @param source The layout params to copy from. 2049 */ 2050 public LayoutParams(LayoutParams source) { 2051 super(source); 2052 2053 this.weight = source.weight; 2054 this.gravity = source.gravity; 2055 } 2056 2057 @Override 2058 public String debug(String output) { 2059 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) + 2060 ", height=" + sizeToString(height) + " weight=" + weight + "}"; 2061 } 2062 2063 /** @hide */ 2064 @Override 2065 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 2066 super.encodeProperties(encoder); 2067 2068 encoder.addProperty("layout:weight", weight); 2069 encoder.addProperty("layout:gravity", gravity); 2070 } 2071 } 2072 } 2073