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