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