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