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 216 private static final int INDEX_TOP = 1; 217 @UnsupportedAppUsage 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 (childIndex == getVirtualChildCount()) { 736 // Check whether the end divider should draw. 737 return (mShowDividers & SHOW_DIVIDER_END) != 0; 738 } 739 boolean allViewsAreGoneBefore = allViewsAreGoneBefore(childIndex); 740 if (allViewsAreGoneBefore) { 741 // This is the first view that's not gone, check if beginning divider is enabled. 742 return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0; 743 } else { 744 return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0; 745 } 746 } 747 748 /** 749 * Checks whether all (virtual) child views before the given index are gone. 750 */ allViewsAreGoneBefore(int childIndex)751 private boolean allViewsAreGoneBefore(int childIndex) { 752 for (int i = childIndex - 1; i >= 0; i--) { 753 final View child = getVirtualChildAt(i); 754 if (child != null && child.getVisibility() != GONE) { 755 return false; 756 } 757 } 758 return true; 759 } 760 761 /** 762 * Measures the children when the orientation of this LinearLayout is set 763 * to {@link #VERTICAL}. 764 * 765 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 766 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 767 * 768 * @see #getOrientation() 769 * @see #setOrientation(int) 770 * @see #onMeasure(int, int) 771 */ measureVertical(int widthMeasureSpec, int heightMeasureSpec)772 void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { 773 mTotalLength = 0; 774 int maxWidth = 0; 775 int childState = 0; 776 int alternativeMaxWidth = 0; 777 int weightedMaxWidth = 0; 778 boolean allFillParent = true; 779 float totalWeight = 0; 780 781 final int count = getVirtualChildCount(); 782 783 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 784 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 785 786 boolean matchWidth = false; 787 boolean skippedMeasure = false; 788 789 final int baselineChildIndex = mBaselineAlignedChildIndex; 790 final boolean useLargestChild = mUseLargestChild; 791 792 int largestChildHeight = Integer.MIN_VALUE; 793 int consumedExcessSpace = 0; 794 795 int nonSkippedChildCount = 0; 796 797 // See how tall everyone is. Also remember max width. 798 for (int i = 0; i < count; ++i) { 799 final View child = getVirtualChildAt(i); 800 if (child == null) { 801 mTotalLength += measureNullChild(i); 802 continue; 803 } 804 805 if (child.getVisibility() == View.GONE) { 806 i += getChildrenSkipCount(child, i); 807 continue; 808 } 809 810 nonSkippedChildCount++; 811 if (hasDividerBeforeChildAt(i)) { 812 mTotalLength += mDividerHeight; 813 } 814 815 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 816 817 totalWeight += lp.weight; 818 819 final boolean useExcessSpace = lp.height == 0 && lp.weight > 0; 820 if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) { 821 // Optimization: don't bother measuring children who are only 822 // laid out using excess space. These views will get measured 823 // later if we have space to distribute. 824 final int totalLength = mTotalLength; 825 mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin); 826 skippedMeasure = true; 827 } else { 828 if (useExcessSpace) { 829 // The heightMode is either UNSPECIFIED or AT_MOST, and 830 // this child is only laid out using excess space. Measure 831 // using WRAP_CONTENT so that we can find out the view's 832 // optimal height. We'll restore the original height of 0 833 // after measurement. 834 lp.height = LayoutParams.WRAP_CONTENT; 835 } 836 837 // Determine how big this child would like to be. If this or 838 // previous children have given a weight, then we allow it to 839 // use all available space (and we will shrink things later 840 // if needed). 841 final int usedHeight = totalWeight == 0 ? mTotalLength : 0; 842 measureChildBeforeLayout(child, i, widthMeasureSpec, 0, 843 heightMeasureSpec, usedHeight); 844 845 final int childHeight = child.getMeasuredHeight(); 846 if (useExcessSpace) { 847 // Restore the original height and record how much space 848 // we've allocated to excess-only children so that we can 849 // match the behavior of EXACTLY measurement. 850 lp.height = 0; 851 consumedExcessSpace += childHeight; 852 } 853 854 final int totalLength = mTotalLength; 855 mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + 856 lp.bottomMargin + getNextLocationOffset(child)); 857 858 if (useLargestChild) { 859 largestChildHeight = Math.max(childHeight, largestChildHeight); 860 } 861 } 862 863 /** 864 * If applicable, compute the additional offset to the child's baseline 865 * we'll need later when asked {@link #getBaseline}. 866 */ 867 if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) { 868 mBaselineChildTop = mTotalLength; 869 } 870 871 // if we are trying to use a child index for our baseline, the above 872 // book keeping only works if there are no children above it with 873 // weight. fail fast to aid the developer. 874 if (i < baselineChildIndex && lp.weight > 0) { 875 throw new RuntimeException("A child of LinearLayout with index " 876 + "less than mBaselineAlignedChildIndex has weight > 0, which " 877 + "won't work. Either remove the weight, or don't set " 878 + "mBaselineAlignedChildIndex."); 879 } 880 881 boolean matchWidthLocally = false; 882 if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) { 883 // The width of the linear layout will scale, and at least one 884 // child said it wanted to match our width. Set a flag 885 // indicating that we need to remeasure at least that view when 886 // we know our width. 887 matchWidth = true; 888 matchWidthLocally = true; 889 } 890 891 final int margin = lp.leftMargin + lp.rightMargin; 892 final int measuredWidth = child.getMeasuredWidth() + margin; 893 maxWidth = Math.max(maxWidth, measuredWidth); 894 childState = combineMeasuredStates(childState, child.getMeasuredState()); 895 896 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 897 if (lp.weight > 0) { 898 /* 899 * Widths of weighted Views are bogus if we end up 900 * remeasuring, so keep them separate. 901 */ 902 weightedMaxWidth = Math.max(weightedMaxWidth, 903 matchWidthLocally ? margin : measuredWidth); 904 } else { 905 alternativeMaxWidth = Math.max(alternativeMaxWidth, 906 matchWidthLocally ? margin : measuredWidth); 907 } 908 909 i += getChildrenSkipCount(child, i); 910 } 911 912 if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) { 913 mTotalLength += mDividerHeight; 914 } 915 916 if (useLargestChild && 917 (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) { 918 mTotalLength = 0; 919 920 for (int i = 0; i < count; ++i) { 921 final View child = getVirtualChildAt(i); 922 if (child == null) { 923 mTotalLength += measureNullChild(i); 924 continue; 925 } 926 927 if (child.getVisibility() == GONE) { 928 i += getChildrenSkipCount(child, i); 929 continue; 930 } 931 932 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 933 child.getLayoutParams(); 934 // Account for negative margins 935 final int totalLength = mTotalLength; 936 mTotalLength = Math.max(totalLength, totalLength + largestChildHeight + 937 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 938 } 939 } 940 941 // Add in our padding 942 mTotalLength += mPaddingTop + mPaddingBottom; 943 944 int heightSize = mTotalLength; 945 946 // Check against our minimum height 947 heightSize = Math.max(heightSize, getSuggestedMinimumHeight()); 948 949 // Reconcile our calculated size with the heightMeasureSpec 950 int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0); 951 heightSize = heightSizeAndState & MEASURED_SIZE_MASK; 952 // Either expand children with weight to take up available space or 953 // shrink them if they extend beyond our current bounds. If we skipped 954 // measurement on any children, we need to measure them now. 955 int remainingExcess = heightSize - mTotalLength 956 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace); 957 if (skippedMeasure 958 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) { 959 float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 960 961 mTotalLength = 0; 962 963 for (int i = 0; i < count; ++i) { 964 final View child = getVirtualChildAt(i); 965 if (child == null || child.getVisibility() == View.GONE) { 966 continue; 967 } 968 969 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 970 final float childWeight = lp.weight; 971 if (childWeight > 0) { 972 final int share = (int) (childWeight * remainingExcess / remainingWeightSum); 973 remainingExcess -= share; 974 remainingWeightSum -= childWeight; 975 976 final int childHeight; 977 if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) { 978 childHeight = largestChildHeight; 979 } else if (lp.height == 0 && (!mAllowInconsistentMeasurement 980 || heightMode == MeasureSpec.EXACTLY)) { 981 // This child needs to be laid out from scratch using 982 // only its share of excess space. 983 childHeight = share; 984 } else { 985 // This child had some intrinsic height to which we 986 // need to add its share of excess space. 987 childHeight = child.getMeasuredHeight() + share; 988 } 989 990 final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 991 Math.max(0, childHeight), MeasureSpec.EXACTLY); 992 final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 993 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin, 994 lp.width); 995 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 996 997 // Child may now not fit in vertical dimension. 998 childState = combineMeasuredStates(childState, child.getMeasuredState() 999 & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT)); 1000 } 1001 1002 final int margin = lp.leftMargin + lp.rightMargin; 1003 final int measuredWidth = child.getMeasuredWidth() + margin; 1004 maxWidth = Math.max(maxWidth, measuredWidth); 1005 1006 boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY && 1007 lp.width == LayoutParams.MATCH_PARENT; 1008 1009 alternativeMaxWidth = Math.max(alternativeMaxWidth, 1010 matchWidthLocally ? margin : measuredWidth); 1011 1012 allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; 1013 1014 final int totalLength = mTotalLength; 1015 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() + 1016 lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); 1017 } 1018 1019 // Add in our padding 1020 mTotalLength += mPaddingTop + mPaddingBottom; 1021 // TODO: Should we recompute the heightSpec based on the new total length? 1022 } else { 1023 alternativeMaxWidth = Math.max(alternativeMaxWidth, 1024 weightedMaxWidth); 1025 1026 1027 // We have no limit, so make all weighted views as tall as the largest child. 1028 // Children will have already been measured once. 1029 if (useLargestChild && heightMode != MeasureSpec.EXACTLY) { 1030 for (int i = 0; i < count; i++) { 1031 final View child = getVirtualChildAt(i); 1032 if (child == null || child.getVisibility() == View.GONE) { 1033 continue; 1034 } 1035 1036 final LinearLayout.LayoutParams lp = 1037 (LinearLayout.LayoutParams) child.getLayoutParams(); 1038 1039 float childExtra = lp.weight; 1040 if (childExtra > 0) { 1041 child.measure( 1042 MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), 1043 MeasureSpec.EXACTLY), 1044 MeasureSpec.makeMeasureSpec(largestChildHeight, 1045 MeasureSpec.EXACTLY)); 1046 } 1047 } 1048 } 1049 } 1050 1051 if (!allFillParent && widthMode != MeasureSpec.EXACTLY) { 1052 maxWidth = alternativeMaxWidth; 1053 } 1054 1055 maxWidth += mPaddingLeft + mPaddingRight; 1056 1057 // Check against our minimum width 1058 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 1059 1060 setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 1061 heightSizeAndState); 1062 1063 if (matchWidth) { 1064 forceUniformWidth(count, heightMeasureSpec); 1065 } 1066 } 1067 forceUniformWidth(int count, int heightMeasureSpec)1068 private void forceUniformWidth(int count, int heightMeasureSpec) { 1069 // Pretend that the linear layout has an exact size. 1070 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), 1071 MeasureSpec.EXACTLY); 1072 for (int i = 0; i< count; ++i) { 1073 final View child = getVirtualChildAt(i); 1074 if (child != null && child.getVisibility() != GONE) { 1075 LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams()); 1076 1077 if (lp.width == LayoutParams.MATCH_PARENT) { 1078 // Temporarily force children to reuse their old measured height 1079 // FIXME: this may not be right for something like wrapping text? 1080 int oldHeight = lp.height; 1081 lp.height = child.getMeasuredHeight(); 1082 1083 // Remeasue with new dimensions 1084 measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0); 1085 lp.height = oldHeight; 1086 } 1087 } 1088 } 1089 } 1090 1091 /** 1092 * Measures the children when the orientation of this LinearLayout is set 1093 * to {@link #HORIZONTAL}. 1094 * 1095 * @param widthMeasureSpec Horizontal space requirements as imposed by the parent. 1096 * @param heightMeasureSpec Vertical space requirements as imposed by the parent. 1097 * 1098 * @see #getOrientation() 1099 * @see #setOrientation(int) 1100 * @see #onMeasure(int, int) 1101 */ measureHorizontal(int widthMeasureSpec, int heightMeasureSpec)1102 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { 1103 mTotalLength = 0; 1104 int maxHeight = 0; 1105 int childState = 0; 1106 int alternativeMaxHeight = 0; 1107 int weightedMaxHeight = 0; 1108 boolean allFillParent = true; 1109 float totalWeight = 0; 1110 1111 final int count = getVirtualChildCount(); 1112 1113 final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 1114 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 1115 1116 boolean matchHeight = false; 1117 boolean skippedMeasure = false; 1118 1119 if (mMaxAscent == null || mMaxDescent == null) { 1120 mMaxAscent = new int[VERTICAL_GRAVITY_COUNT]; 1121 mMaxDescent = new int[VERTICAL_GRAVITY_COUNT]; 1122 } 1123 1124 final int[] maxAscent = mMaxAscent; 1125 final int[] maxDescent = mMaxDescent; 1126 1127 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 1128 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 1129 1130 final boolean baselineAligned = mBaselineAligned; 1131 final boolean useLargestChild = mUseLargestChild; 1132 1133 final boolean isExactly = widthMode == MeasureSpec.EXACTLY; 1134 1135 int largestChildWidth = Integer.MIN_VALUE; 1136 int usedExcessSpace = 0; 1137 1138 int nonSkippedChildCount = 0; 1139 1140 // See how wide everyone is. Also remember max height. 1141 for (int i = 0; i < count; ++i) { 1142 final View child = getVirtualChildAt(i); 1143 if (child == null) { 1144 mTotalLength += measureNullChild(i); 1145 continue; 1146 } 1147 1148 if (child.getVisibility() == GONE) { 1149 i += getChildrenSkipCount(child, i); 1150 continue; 1151 } 1152 1153 nonSkippedChildCount++; 1154 if (hasDividerBeforeChildAt(i)) { 1155 mTotalLength += mDividerWidth; 1156 } 1157 1158 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1159 1160 totalWeight += lp.weight; 1161 1162 final boolean useExcessSpace = lp.width == 0 && lp.weight > 0; 1163 if (widthMode == MeasureSpec.EXACTLY && useExcessSpace) { 1164 // Optimization: don't bother measuring children who are only 1165 // laid out using excess space. These views will get measured 1166 // later if we have space to distribute. 1167 if (isExactly) { 1168 mTotalLength += lp.leftMargin + lp.rightMargin; 1169 } else { 1170 final int totalLength = mTotalLength; 1171 mTotalLength = Math.max(totalLength, totalLength + 1172 lp.leftMargin + lp.rightMargin); 1173 } 1174 1175 // Baseline alignment requires to measure widgets to obtain the 1176 // baseline offset (in particular for TextViews). The following 1177 // defeats the optimization mentioned above. Allow the child to 1178 // use as much space as it wants because we can shrink things 1179 // later (and re-measure). 1180 if (baselineAligned) { 1181 final int freeWidthSpec = MeasureSpec.makeSafeMeasureSpec( 1182 MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.UNSPECIFIED); 1183 final int freeHeightSpec = MeasureSpec.makeSafeMeasureSpec( 1184 MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED); 1185 child.measure(freeWidthSpec, freeHeightSpec); 1186 } else { 1187 skippedMeasure = true; 1188 } 1189 } else { 1190 if (useExcessSpace) { 1191 // The widthMode is either UNSPECIFIED or AT_MOST, and 1192 // this child is only laid out using excess space. Measure 1193 // using WRAP_CONTENT so that we can find out the view's 1194 // optimal width. We'll restore the original width of 0 1195 // after measurement. 1196 lp.width = LayoutParams.WRAP_CONTENT; 1197 } 1198 1199 // Determine how big this child would like to be. If this or 1200 // previous children have given a weight, then we allow it to 1201 // use all available space (and we will shrink things later 1202 // if needed). 1203 final int usedWidth = totalWeight == 0 ? mTotalLength : 0; 1204 measureChildBeforeLayout(child, i, widthMeasureSpec, usedWidth, 1205 heightMeasureSpec, 0); 1206 1207 final int childWidth = child.getMeasuredWidth(); 1208 if (useExcessSpace) { 1209 // Restore the original width and record how much space 1210 // we've allocated to excess-only children so that we can 1211 // match the behavior of EXACTLY measurement. 1212 lp.width = 0; 1213 usedExcessSpace += childWidth; 1214 } 1215 1216 if (isExactly) { 1217 mTotalLength += childWidth + lp.leftMargin + lp.rightMargin 1218 + getNextLocationOffset(child); 1219 } else { 1220 final int totalLength = mTotalLength; 1221 mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin 1222 + lp.rightMargin + getNextLocationOffset(child)); 1223 } 1224 1225 if (useLargestChild) { 1226 largestChildWidth = Math.max(childWidth, largestChildWidth); 1227 } 1228 } 1229 1230 boolean matchHeightLocally = false; 1231 if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) { 1232 // The height of the linear layout will scale, and at least one 1233 // child said it wanted to match our height. Set a flag indicating that 1234 // we need to remeasure at least that view when we know our height. 1235 matchHeight = true; 1236 matchHeightLocally = true; 1237 } 1238 1239 final int margin = lp.topMargin + lp.bottomMargin; 1240 final int childHeight = child.getMeasuredHeight() + margin; 1241 childState = combineMeasuredStates(childState, child.getMeasuredState()); 1242 1243 if (baselineAligned) { 1244 final int childBaseline = child.getBaseline(); 1245 if (childBaseline != -1) { 1246 // Translates the child's vertical gravity into an index 1247 // in the range 0..VERTICAL_GRAVITY_COUNT 1248 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1249 & Gravity.VERTICAL_GRAVITY_MASK; 1250 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1251 & ~Gravity.AXIS_SPECIFIED) >> 1; 1252 1253 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1254 maxDescent[index] = Math.max(maxDescent[index], childHeight - childBaseline); 1255 } 1256 } 1257 1258 maxHeight = Math.max(maxHeight, childHeight); 1259 1260 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1261 if (lp.weight > 0) { 1262 /* 1263 * Heights of weighted Views are bogus if we end up 1264 * remeasuring, so keep them separate. 1265 */ 1266 weightedMaxHeight = Math.max(weightedMaxHeight, 1267 matchHeightLocally ? margin : childHeight); 1268 } else { 1269 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1270 matchHeightLocally ? margin : childHeight); 1271 } 1272 1273 i += getChildrenSkipCount(child, i); 1274 } 1275 1276 if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) { 1277 mTotalLength += mDividerWidth; 1278 } 1279 1280 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1281 // the most common case 1282 if (maxAscent[INDEX_TOP] != -1 || 1283 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1284 maxAscent[INDEX_BOTTOM] != -1 || 1285 maxAscent[INDEX_FILL] != -1) { 1286 final int ascent = Math.max(maxAscent[INDEX_FILL], 1287 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1288 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1289 final int descent = Math.max(maxDescent[INDEX_FILL], 1290 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1291 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1292 maxHeight = Math.max(maxHeight, ascent + descent); 1293 } 1294 1295 if (useLargestChild && 1296 (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { 1297 mTotalLength = 0; 1298 1299 for (int i = 0; i < count; ++i) { 1300 final View child = getVirtualChildAt(i); 1301 if (child == null) { 1302 mTotalLength += measureNullChild(i); 1303 continue; 1304 } 1305 1306 if (child.getVisibility() == GONE) { 1307 i += getChildrenSkipCount(child, i); 1308 continue; 1309 } 1310 1311 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) 1312 child.getLayoutParams(); 1313 if (isExactly) { 1314 mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin + 1315 getNextLocationOffset(child); 1316 } else { 1317 final int totalLength = mTotalLength; 1318 mTotalLength = Math.max(totalLength, totalLength + largestChildWidth + 1319 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1320 } 1321 } 1322 } 1323 1324 // Add in our padding 1325 mTotalLength += mPaddingLeft + mPaddingRight; 1326 1327 int widthSize = mTotalLength; 1328 1329 // Check against our minimum width 1330 widthSize = Math.max(widthSize, getSuggestedMinimumWidth()); 1331 1332 // Reconcile our calculated size with the widthMeasureSpec 1333 int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0); 1334 widthSize = widthSizeAndState & MEASURED_SIZE_MASK; 1335 1336 // Either expand children with weight to take up available space or 1337 // shrink them if they extend beyond our current bounds. If we skipped 1338 // measurement on any children, we need to measure them now. 1339 int remainingExcess = widthSize - mTotalLength 1340 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace); 1341 if (skippedMeasure 1342 || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) { 1343 float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; 1344 1345 maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; 1346 maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; 1347 maxHeight = -1; 1348 1349 mTotalLength = 0; 1350 1351 for (int i = 0; i < count; ++i) { 1352 final View child = getVirtualChildAt(i); 1353 if (child == null || child.getVisibility() == View.GONE) { 1354 continue; 1355 } 1356 1357 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1358 final float childWeight = lp.weight; 1359 if (childWeight > 0) { 1360 final int share = (int) (childWeight * remainingExcess / remainingWeightSum); 1361 remainingExcess -= share; 1362 remainingWeightSum -= childWeight; 1363 1364 final int childWidth; 1365 if (mUseLargestChild && widthMode != MeasureSpec.EXACTLY) { 1366 childWidth = largestChildWidth; 1367 } else if (lp.width == 0 && (!mAllowInconsistentMeasurement 1368 || widthMode == MeasureSpec.EXACTLY)) { 1369 // This child needs to be laid out from scratch using 1370 // only its share of excess space. 1371 childWidth = share; 1372 } else { 1373 // This child had some intrinsic width to which we 1374 // need to add its share of excess space. 1375 childWidth = child.getMeasuredWidth() + share; 1376 } 1377 1378 final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( 1379 Math.max(0, childWidth), MeasureSpec.EXACTLY); 1380 final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 1381 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin, 1382 lp.height); 1383 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1384 1385 // Child may now not fit in horizontal dimension. 1386 childState = combineMeasuredStates(childState, 1387 child.getMeasuredState() & MEASURED_STATE_MASK); 1388 } 1389 1390 if (isExactly) { 1391 mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin + 1392 getNextLocationOffset(child); 1393 } else { 1394 final int totalLength = mTotalLength; 1395 mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() + 1396 lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); 1397 } 1398 1399 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY && 1400 lp.height == LayoutParams.MATCH_PARENT; 1401 1402 final int margin = lp.topMargin + lp .bottomMargin; 1403 int childHeight = child.getMeasuredHeight() + margin; 1404 maxHeight = Math.max(maxHeight, childHeight); 1405 alternativeMaxHeight = Math.max(alternativeMaxHeight, 1406 matchHeightLocally ? margin : childHeight); 1407 1408 allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; 1409 1410 if (baselineAligned) { 1411 final int childBaseline = child.getBaseline(); 1412 if (childBaseline != -1) { 1413 // Translates the child's vertical gravity into an index in the range 0..2 1414 final int gravity = (lp.gravity < 0 ? mGravity : lp.gravity) 1415 & Gravity.VERTICAL_GRAVITY_MASK; 1416 final int index = ((gravity >> Gravity.AXIS_Y_SHIFT) 1417 & ~Gravity.AXIS_SPECIFIED) >> 1; 1418 1419 maxAscent[index] = Math.max(maxAscent[index], childBaseline); 1420 maxDescent[index] = Math.max(maxDescent[index], 1421 childHeight - childBaseline); 1422 } 1423 } 1424 } 1425 1426 // Add in our padding 1427 mTotalLength += mPaddingLeft + mPaddingRight; 1428 // TODO: Should we update widthSize with the new total length? 1429 1430 // Check mMaxAscent[INDEX_TOP] first because it maps to Gravity.TOP, 1431 // the most common case 1432 if (maxAscent[INDEX_TOP] != -1 || 1433 maxAscent[INDEX_CENTER_VERTICAL] != -1 || 1434 maxAscent[INDEX_BOTTOM] != -1 || 1435 maxAscent[INDEX_FILL] != -1) { 1436 final int ascent = Math.max(maxAscent[INDEX_FILL], 1437 Math.max(maxAscent[INDEX_CENTER_VERTICAL], 1438 Math.max(maxAscent[INDEX_TOP], maxAscent[INDEX_BOTTOM]))); 1439 final int descent = Math.max(maxDescent[INDEX_FILL], 1440 Math.max(maxDescent[INDEX_CENTER_VERTICAL], 1441 Math.max(maxDescent[INDEX_TOP], maxDescent[INDEX_BOTTOM]))); 1442 maxHeight = Math.max(maxHeight, ascent + descent); 1443 } 1444 } else { 1445 alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); 1446 1447 // We have no limit, so make all weighted views as wide as the largest child. 1448 // Children will have already been measured once. 1449 if (useLargestChild && widthMode != MeasureSpec.EXACTLY) { 1450 for (int i = 0; i < count; i++) { 1451 final View child = getVirtualChildAt(i); 1452 if (child == null || child.getVisibility() == View.GONE) { 1453 continue; 1454 } 1455 1456 final LinearLayout.LayoutParams lp = 1457 (LinearLayout.LayoutParams) child.getLayoutParams(); 1458 1459 float childExtra = lp.weight; 1460 if (childExtra > 0) { 1461 child.measure( 1462 MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY), 1463 MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), 1464 MeasureSpec.EXACTLY)); 1465 } 1466 } 1467 } 1468 } 1469 1470 if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { 1471 maxHeight = alternativeMaxHeight; 1472 } 1473 1474 maxHeight += mPaddingTop + mPaddingBottom; 1475 1476 // Check against our minimum height 1477 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 1478 1479 setMeasuredDimension(widthSizeAndState | (childState&MEASURED_STATE_MASK), 1480 resolveSizeAndState(maxHeight, heightMeasureSpec, 1481 (childState<<MEASURED_HEIGHT_STATE_SHIFT))); 1482 1483 if (matchHeight) { 1484 forceUniformHeight(count, widthMeasureSpec); 1485 } 1486 } 1487 forceUniformHeight(int count, int widthMeasureSpec)1488 private void forceUniformHeight(int count, int widthMeasureSpec) { 1489 // Pretend that the linear layout has an exact size. This is the measured height of 1490 // ourselves. The measured height should be the max height of the children, changed 1491 // to accommodate the heightMeasureSpec from the parent 1492 int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), 1493 MeasureSpec.EXACTLY); 1494 for (int i = 0; i < count; ++i) { 1495 final View child = getVirtualChildAt(i); 1496 if (child != null && child.getVisibility() != GONE) { 1497 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); 1498 1499 if (lp.height == LayoutParams.MATCH_PARENT) { 1500 // Temporarily force children to reuse their old measured width 1501 // FIXME: this may not be right for something like wrapping text? 1502 int oldWidth = lp.width; 1503 lp.width = child.getMeasuredWidth(); 1504 1505 // Remeasure with new dimensions 1506 measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0); 1507 lp.width = oldWidth; 1508 } 1509 } 1510 } 1511 } 1512 1513 /** 1514 * <p>Returns the number of children to skip after measuring/laying out 1515 * the specified child.</p> 1516 * 1517 * @param child the child after which we want to skip children 1518 * @param index the index of the child after which we want to skip children 1519 * @return the number of children to skip, 0 by default 1520 */ getChildrenSkipCount(View child, int index)1521 int getChildrenSkipCount(View child, int index) { 1522 return 0; 1523 } 1524 1525 /** 1526 * <p>Returns the size (width or height) that should be occupied by a null 1527 * child.</p> 1528 * 1529 * @param childIndex the index of the null child 1530 * @return the width or height of the child depending on the orientation 1531 */ measureNullChild(int childIndex)1532 int measureNullChild(int childIndex) { 1533 return 0; 1534 } 1535 1536 /** 1537 * <p>Measure the child according to the parent's measure specs. This 1538 * method should be overridden by subclasses to force the sizing of 1539 * children. This method is called by {@link #measureVertical(int, int)} and 1540 * {@link #measureHorizontal(int, int)}.</p> 1541 * 1542 * @param child the child to measure 1543 * @param childIndex the index of the child in this view 1544 * @param widthMeasureSpec horizontal space requirements as imposed by the parent 1545 * @param totalWidth extra space that has been used up by the parent horizontally 1546 * @param heightMeasureSpec vertical space requirements as imposed by the parent 1547 * @param totalHeight extra space that has been used up by the parent vertically 1548 */ measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight)1549 void measureChildBeforeLayout(View child, int childIndex, 1550 int widthMeasureSpec, int totalWidth, int heightMeasureSpec, 1551 int totalHeight) { 1552 measureChildWithMargins(child, widthMeasureSpec, totalWidth, 1553 heightMeasureSpec, totalHeight); 1554 } 1555 1556 /** 1557 * <p>Return the location offset of the specified child. This can be used 1558 * by subclasses to change the location of a given widget.</p> 1559 * 1560 * @param child the child for which to obtain the location offset 1561 * @return the location offset in pixels 1562 */ getLocationOffset(View child)1563 int getLocationOffset(View child) { 1564 return 0; 1565 } 1566 1567 /** 1568 * <p>Return the size offset of the next sibling of the specified child. 1569 * This can be used by subclasses to change the location of the widget 1570 * following <code>child</code>.</p> 1571 * 1572 * @param child the child whose next sibling will be moved 1573 * @return the location offset of the next child in pixels 1574 */ getNextLocationOffset(View child)1575 int getNextLocationOffset(View child) { 1576 return 0; 1577 } 1578 1579 @Override onLayout(boolean changed, int l, int t, int r, int b)1580 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1581 if (mOrientation == VERTICAL) { 1582 layoutVertical(l, t, r, b); 1583 } else { 1584 layoutHorizontal(l, t, r, b); 1585 } 1586 } 1587 1588 /** 1589 * Position the children during a layout pass if the orientation of this 1590 * LinearLayout is set to {@link #VERTICAL}. 1591 * 1592 * @see #getOrientation() 1593 * @see #setOrientation(int) 1594 * @see #onLayout(boolean, int, int, int, int) 1595 * @param left 1596 * @param top 1597 * @param right 1598 * @param bottom 1599 */ layoutVertical(int left, int top, int right, int bottom)1600 void layoutVertical(int left, int top, int right, int bottom) { 1601 final int paddingLeft = mPaddingLeft; 1602 1603 int childTop; 1604 int childLeft; 1605 1606 // Where right end of child should go 1607 final int width = right - left; 1608 int childRight = width - mPaddingRight; 1609 1610 // Space available for child 1611 int childSpace = width - paddingLeft - mPaddingRight; 1612 1613 final int count = getVirtualChildCount(); 1614 1615 final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1616 final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1617 1618 switch (majorGravity) { 1619 case Gravity.BOTTOM: 1620 // mTotalLength contains the padding already 1621 childTop = mPaddingTop + bottom - top - mTotalLength; 1622 break; 1623 1624 // mTotalLength contains the padding already 1625 case Gravity.CENTER_VERTICAL: 1626 childTop = mPaddingTop + (bottom - top - mTotalLength) / 2; 1627 break; 1628 1629 case Gravity.TOP: 1630 default: 1631 childTop = mPaddingTop; 1632 break; 1633 } 1634 1635 for (int i = 0; i < count; i++) { 1636 final View child = getVirtualChildAt(i); 1637 if (child == null) { 1638 childTop += measureNullChild(i); 1639 } else if (child.getVisibility() != GONE) { 1640 final int childWidth = child.getMeasuredWidth(); 1641 final int childHeight = child.getMeasuredHeight(); 1642 1643 final LinearLayout.LayoutParams lp = 1644 (LinearLayout.LayoutParams) child.getLayoutParams(); 1645 1646 int gravity = lp.gravity; 1647 if (gravity < 0) { 1648 gravity = minorGravity; 1649 } 1650 final int layoutDirection = getLayoutDirection(); 1651 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 1652 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 1653 case Gravity.CENTER_HORIZONTAL: 1654 childLeft = paddingLeft + ((childSpace - childWidth) / 2) 1655 + lp.leftMargin - lp.rightMargin; 1656 break; 1657 1658 case Gravity.RIGHT: 1659 childLeft = childRight - childWidth - lp.rightMargin; 1660 break; 1661 1662 case Gravity.LEFT: 1663 default: 1664 childLeft = paddingLeft + lp.leftMargin; 1665 break; 1666 } 1667 1668 if (hasDividerBeforeChildAt(i)) { 1669 childTop += mDividerHeight; 1670 } 1671 1672 childTop += lp.topMargin; 1673 setChildFrame(child, childLeft, childTop + getLocationOffset(child), 1674 childWidth, childHeight); 1675 childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); 1676 1677 i += getChildrenSkipCount(child, i); 1678 } 1679 } 1680 } 1681 1682 @Override onRtlPropertiesChanged(@esolvedLayoutDir int layoutDirection)1683 public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) { 1684 super.onRtlPropertiesChanged(layoutDirection); 1685 if (layoutDirection != mLayoutDirection) { 1686 mLayoutDirection = layoutDirection; 1687 if (mOrientation == HORIZONTAL) { 1688 requestLayout(); 1689 } 1690 } 1691 } 1692 1693 /** 1694 * Position the children during a layout pass if the orientation of this 1695 * LinearLayout is set to {@link #HORIZONTAL}. 1696 * 1697 * @see #getOrientation() 1698 * @see #setOrientation(int) 1699 * @see #onLayout(boolean, int, int, int, int) 1700 * @param left 1701 * @param top 1702 * @param right 1703 * @param bottom 1704 */ layoutHorizontal(int left, int top, int right, int bottom)1705 void layoutHorizontal(int left, int top, int right, int bottom) { 1706 final boolean isLayoutRtl = isLayoutRtl(); 1707 final int paddingTop = mPaddingTop; 1708 1709 int childTop; 1710 int childLeft; 1711 1712 // Where bottom of child should go 1713 final int height = bottom - top; 1714 int childBottom = height - mPaddingBottom; 1715 1716 // Space available for child 1717 int childSpace = height - paddingTop - mPaddingBottom; 1718 1719 final int count = getVirtualChildCount(); 1720 1721 final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1722 final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1723 1724 final boolean baselineAligned = mBaselineAligned; 1725 1726 final int[] maxAscent = mMaxAscent; 1727 final int[] maxDescent = mMaxDescent; 1728 1729 final int layoutDirection = getLayoutDirection(); 1730 switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) { 1731 case Gravity.RIGHT: 1732 // mTotalLength contains the padding already 1733 childLeft = mPaddingLeft + right - left - mTotalLength; 1734 break; 1735 1736 case Gravity.CENTER_HORIZONTAL: 1737 // mTotalLength contains the padding already 1738 childLeft = mPaddingLeft + (right - left - mTotalLength) / 2; 1739 break; 1740 1741 case Gravity.LEFT: 1742 default: 1743 childLeft = mPaddingLeft; 1744 break; 1745 } 1746 1747 int start = 0; 1748 int dir = 1; 1749 //In case of RTL, start drawing from the last child. 1750 if (isLayoutRtl) { 1751 start = count - 1; 1752 dir = -1; 1753 } 1754 1755 for (int i = 0; i < count; i++) { 1756 final int childIndex = start + dir * i; 1757 final View child = getVirtualChildAt(childIndex); 1758 if (child == null) { 1759 childLeft += measureNullChild(childIndex); 1760 } else if (child.getVisibility() != GONE) { 1761 final int childWidth = child.getMeasuredWidth(); 1762 final int childHeight = child.getMeasuredHeight(); 1763 int childBaseline = -1; 1764 1765 final LinearLayout.LayoutParams lp = 1766 (LinearLayout.LayoutParams) child.getLayoutParams(); 1767 1768 if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) { 1769 childBaseline = child.getBaseline(); 1770 } 1771 1772 int gravity = lp.gravity; 1773 if (gravity < 0) { 1774 gravity = minorGravity; 1775 } 1776 1777 switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { 1778 case Gravity.TOP: 1779 childTop = paddingTop + lp.topMargin; 1780 if (childBaseline != -1) { 1781 childTop += maxAscent[INDEX_TOP] - childBaseline; 1782 } 1783 break; 1784 1785 case Gravity.CENTER_VERTICAL: 1786 // Removed support for baseline alignment when layout_gravity or 1787 // gravity == center_vertical. See bug #1038483. 1788 // Keep the code around if we need to re-enable this feature 1789 // if (childBaseline != -1) { 1790 // // Align baselines vertically only if the child is smaller than us 1791 // if (childSpace - childHeight > 0) { 1792 // childTop = paddingTop + (childSpace / 2) - childBaseline; 1793 // } else { 1794 // childTop = paddingTop + (childSpace - childHeight) / 2; 1795 // } 1796 // } else { 1797 childTop = paddingTop + ((childSpace - childHeight) / 2) 1798 + lp.topMargin - lp.bottomMargin; 1799 break; 1800 1801 case Gravity.BOTTOM: 1802 childTop = childBottom - childHeight - lp.bottomMargin; 1803 if (childBaseline != -1) { 1804 int descent = child.getMeasuredHeight() - childBaseline; 1805 childTop -= (maxDescent[INDEX_BOTTOM] - descent); 1806 } 1807 break; 1808 default: 1809 childTop = paddingTop; 1810 break; 1811 } 1812 1813 if (hasDividerBeforeChildAt(childIndex)) { 1814 childLeft += mDividerWidth; 1815 } 1816 1817 childLeft += lp.leftMargin; 1818 setChildFrame(child, childLeft + getLocationOffset(child), childTop, 1819 childWidth, childHeight); 1820 childLeft += childWidth + lp.rightMargin + 1821 getNextLocationOffset(child); 1822 1823 i += getChildrenSkipCount(child, childIndex); 1824 } 1825 } 1826 } 1827 setChildFrame(View child, int left, int top, int width, int height)1828 private void setChildFrame(View child, int left, int top, int width, int height) { 1829 child.layout(left, top, left + width, top + height); 1830 } 1831 1832 /** 1833 * Should the layout be a column or a row. 1834 * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default 1835 * value is {@link #HORIZONTAL}. 1836 * 1837 * @attr ref android.R.styleable#LinearLayout_orientation 1838 */ setOrientation(@rientationMode int orientation)1839 public void setOrientation(@OrientationMode int orientation) { 1840 if (mOrientation != orientation) { 1841 mOrientation = orientation; 1842 requestLayout(); 1843 } 1844 } 1845 1846 /** 1847 * Returns the current orientation. 1848 * 1849 * @return either {@link #HORIZONTAL} or {@link #VERTICAL} 1850 */ 1851 @OrientationMode 1852 @InspectableProperty(enumMapping = { 1853 @InspectableProperty.EnumEntry(value = HORIZONTAL, name = "horizontal"), 1854 @InspectableProperty.EnumEntry(value = VERTICAL, name = "vertical") 1855 }) getOrientation()1856 public int getOrientation() { 1857 return mOrientation; 1858 } 1859 1860 /** 1861 * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If 1862 * this layout has a VERTICAL orientation, this controls where all the child 1863 * views are placed if there is extra vertical space. If this layout has a 1864 * HORIZONTAL orientation, this controls the alignment of the children. 1865 * 1866 * @param gravity See {@link android.view.Gravity} 1867 * 1868 * @attr ref android.R.styleable#LinearLayout_gravity 1869 */ 1870 @android.view.RemotableViewMethod setGravity(int gravity)1871 public void setGravity(int gravity) { 1872 if (mGravity != gravity) { 1873 if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { 1874 gravity |= Gravity.START; 1875 } 1876 1877 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 1878 gravity |= Gravity.TOP; 1879 } 1880 1881 mGravity = gravity; 1882 requestLayout(); 1883 } 1884 } 1885 1886 /** 1887 * Returns the current gravity. See {@link android.view.Gravity} 1888 * 1889 * @return the current gravity. 1890 * @see #setGravity 1891 */ 1892 @InspectableProperty(valueType = InspectableProperty.ValueType.GRAVITY) getGravity()1893 public int getGravity() { 1894 return mGravity; 1895 } 1896 1897 @android.view.RemotableViewMethod setHorizontalGravity(int horizontalGravity)1898 public void setHorizontalGravity(int horizontalGravity) { 1899 final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; 1900 if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) { 1901 mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity; 1902 requestLayout(); 1903 } 1904 } 1905 1906 @android.view.RemotableViewMethod setVerticalGravity(int verticalGravity)1907 public void setVerticalGravity(int verticalGravity) { 1908 final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK; 1909 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) { 1910 mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity; 1911 requestLayout(); 1912 } 1913 } 1914 1915 @Override generateLayoutParams(AttributeSet attrs)1916 public LayoutParams generateLayoutParams(AttributeSet attrs) { 1917 return new LinearLayout.LayoutParams(getContext(), attrs); 1918 } 1919 1920 /** 1921 * Returns a set of layout parameters with a width of 1922 * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} 1923 * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} 1924 * when the layout's orientation is {@link #VERTICAL}. When the orientation is 1925 * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT} 1926 * and the height to {@link LayoutParams#WRAP_CONTENT}. 1927 */ 1928 @Override generateDefaultLayoutParams()1929 protected LayoutParams generateDefaultLayoutParams() { 1930 if (mOrientation == HORIZONTAL) { 1931 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 1932 } else if (mOrientation == VERTICAL) { 1933 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 1934 } 1935 return null; 1936 } 1937 1938 @Override generateLayoutParams(ViewGroup.LayoutParams lp)1939 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 1940 if (sPreserveMarginParamsInLayoutParamConversion) { 1941 if (lp instanceof LayoutParams) { 1942 return new LayoutParams((LayoutParams) lp); 1943 } else if (lp instanceof MarginLayoutParams) { 1944 return new LayoutParams((MarginLayoutParams) lp); 1945 } 1946 } 1947 return new LayoutParams(lp); 1948 } 1949 1950 1951 // Override to allow type-checking of LayoutParams. 1952 @Override checkLayoutParams(ViewGroup.LayoutParams p)1953 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1954 return p instanceof LinearLayout.LayoutParams; 1955 } 1956 1957 @Override getAccessibilityClassName()1958 public CharSequence getAccessibilityClassName() { 1959 return LinearLayout.class.getName(); 1960 } 1961 1962 /** @hide */ 1963 @Override encodeProperties(@onNull ViewHierarchyEncoder encoder)1964 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 1965 super.encodeProperties(encoder); 1966 encoder.addProperty("layout:baselineAligned", mBaselineAligned); 1967 encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex); 1968 encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop); 1969 encoder.addProperty("measurement:orientation", mOrientation); 1970 encoder.addProperty("measurement:gravity", mGravity); 1971 encoder.addProperty("measurement:totalLength", mTotalLength); 1972 encoder.addProperty("layout:totalLength", mTotalLength); 1973 encoder.addProperty("layout:useLargestChild", mUseLargestChild); 1974 } 1975 1976 /** 1977 * Per-child layout information associated with ViewLinearLayout. 1978 * 1979 * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight 1980 * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity 1981 */ 1982 public static class LayoutParams extends ViewGroup.MarginLayoutParams { 1983 /** 1984 * Indicates how much of the extra space in the LinearLayout will be 1985 * allocated to the view associated with these LayoutParams. Specify 1986 * 0 if the view should not be stretched. Otherwise the extra pixels 1987 * will be pro-rated among all views whose weight is greater than 0. 1988 */ 1989 @ViewDebug.ExportedProperty(category = "layout") 1990 @InspectableProperty(name = "layout_weight") 1991 public float weight; 1992 1993 /** 1994 * Gravity for the view associated with these LayoutParams. 1995 * 1996 * @see android.view.Gravity 1997 */ 1998 @ViewDebug.ExportedProperty(category = "layout", mapping = { 1999 @ViewDebug.IntToString(from = -1, to = "NONE"), 2000 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), 2001 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), 2002 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), 2003 @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), 2004 @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), 2005 @ViewDebug.IntToString(from = Gravity.START, to = "START"), 2006 @ViewDebug.IntToString(from = Gravity.END, to = "END"), 2007 @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), 2008 @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), 2009 @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), 2010 @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), 2011 @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), 2012 @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") 2013 }) 2014 @InspectableProperty( 2015 name = "layout_gravity", 2016 valueType = InspectableProperty.ValueType.GRAVITY) 2017 public int gravity = -1; 2018 2019 /** 2020 * {@inheritDoc} 2021 */ LayoutParams(Context c, AttributeSet attrs)2022 public LayoutParams(Context c, AttributeSet attrs) { 2023 super(c, attrs); 2024 TypedArray a = 2025 c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout); 2026 2027 weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0); 2028 gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1); 2029 2030 a.recycle(); 2031 } 2032 2033 /** 2034 * {@inheritDoc} 2035 */ LayoutParams(int width, int height)2036 public LayoutParams(int width, int height) { 2037 super(width, height); 2038 weight = 0; 2039 } 2040 2041 /** 2042 * Creates a new set of layout parameters with the specified width, height 2043 * and weight. 2044 * 2045 * @param width the width, either {@link #MATCH_PARENT}, 2046 * {@link #WRAP_CONTENT} or a fixed size in pixels 2047 * @param height the height, either {@link #MATCH_PARENT}, 2048 * {@link #WRAP_CONTENT} or a fixed size in pixels 2049 * @param weight the weight 2050 */ LayoutParams(int width, int height, float weight)2051 public LayoutParams(int width, int height, float weight) { 2052 super(width, height); 2053 this.weight = weight; 2054 } 2055 2056 /** 2057 * {@inheritDoc} 2058 */ LayoutParams(ViewGroup.LayoutParams p)2059 public LayoutParams(ViewGroup.LayoutParams p) { 2060 super(p); 2061 } 2062 2063 /** 2064 * {@inheritDoc} 2065 */ LayoutParams(ViewGroup.MarginLayoutParams source)2066 public LayoutParams(ViewGroup.MarginLayoutParams source) { 2067 super(source); 2068 } 2069 2070 /** 2071 * Copy constructor. Clones the width, height, margin values, weight, 2072 * and gravity of the source. 2073 * 2074 * @param source The layout params to copy from. 2075 */ LayoutParams(LayoutParams source)2076 public LayoutParams(LayoutParams source) { 2077 super(source); 2078 2079 this.weight = source.weight; 2080 this.gravity = source.gravity; 2081 } 2082 2083 @Override debug(String output)2084 public String debug(String output) { 2085 return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) + 2086 ", height=" + sizeToString(height) + " weight=" + weight + "}"; 2087 } 2088 2089 /** @hide */ 2090 @Override 2091 @UnsupportedAppUsage encodeProperties(@onNull ViewHierarchyEncoder encoder)2092 protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { 2093 super.encodeProperties(encoder); 2094 2095 encoder.addProperty("layout:weight", weight); 2096 encoder.addProperty("layout:gravity", gravity); 2097 } 2098 } 2099 } 2100