1 /* 2 * Copyright (C) 2019 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 androidx.constraintlayout.core.widgets; 18 19 import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.AT_MOST; 20 import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.EXACTLY; 21 import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.UNSPECIFIED; 22 23 import androidx.constraintlayout.core.LinearSystem; 24 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.HashMap; 28 29 /** 30 * Implements the Flow virtual layout. 31 */ 32 public class Flow extends VirtualLayout { 33 34 public static final int HORIZONTAL_ALIGN_START = 0; 35 public static final int HORIZONTAL_ALIGN_END = 1; 36 public static final int HORIZONTAL_ALIGN_CENTER = 2; 37 38 public static final int VERTICAL_ALIGN_TOP = 0; 39 public static final int VERTICAL_ALIGN_BOTTOM = 1; 40 public static final int VERTICAL_ALIGN_CENTER = 2; 41 public static final int VERTICAL_ALIGN_BASELINE = 3; 42 43 public static final int WRAP_NONE = 0; 44 public static final int WRAP_CHAIN = 1; 45 public static final int WRAP_ALIGNED = 2; 46 public static final int WRAP_CHAIN_NEW = 3; 47 48 private int mHorizontalStyle = UNKNOWN; 49 private int mVerticalStyle = UNKNOWN; 50 private int mFirstHorizontalStyle = UNKNOWN; 51 private int mFirstVerticalStyle = UNKNOWN; 52 private int mLastHorizontalStyle = UNKNOWN; 53 private int mLastVerticalStyle = UNKNOWN; 54 55 private float mHorizontalBias = 0.5f; 56 private float mVerticalBias = 0.5f; 57 private float mFirstHorizontalBias = 0.5f; 58 private float mFirstVerticalBias = 0.5f; 59 private float mLastHorizontalBias = 0.5f; 60 private float mLastVerticalBias = 0.5f; 61 62 private int mHorizontalGap = 0; 63 private int mVerticalGap = 0; 64 65 private int mHorizontalAlign = HORIZONTAL_ALIGN_CENTER; 66 private int mVerticalAlign = VERTICAL_ALIGN_CENTER; 67 private int mWrapMode = WRAP_NONE; 68 69 private int mMaxElementsWrap = UNKNOWN; 70 71 private int mOrientation = HORIZONTAL; 72 73 private ArrayList<WidgetsList> mChainList = new ArrayList<>(); 74 75 // Aligned management 76 77 private ConstraintWidget[] mAlignedBiggestElementsInRows = null; 78 private ConstraintWidget[] mAlignedBiggestElementsInCols = null; 79 private int[] mAlignedDimensions = null; 80 private ConstraintWidget[] mDisplayedWidgets; 81 private int mDisplayedWidgetsCount = 0; 82 83 84 @Override copy(ConstraintWidget src, HashMap<ConstraintWidget, ConstraintWidget> map)85 public void copy(ConstraintWidget src, HashMap<ConstraintWidget, ConstraintWidget> map) { 86 super.copy(src, map); 87 Flow srcFLow = (Flow) src; 88 89 mHorizontalStyle = srcFLow.mHorizontalStyle; 90 mVerticalStyle = srcFLow.mVerticalStyle; 91 mFirstHorizontalStyle = srcFLow.mFirstHorizontalStyle; 92 mFirstVerticalStyle = srcFLow.mFirstVerticalStyle; 93 mLastHorizontalStyle = srcFLow.mLastHorizontalStyle; 94 mLastVerticalStyle = srcFLow.mLastVerticalStyle; 95 96 mHorizontalBias = srcFLow.mHorizontalBias; 97 mVerticalBias = srcFLow.mVerticalBias; 98 mFirstHorizontalBias = srcFLow.mFirstHorizontalBias; 99 mFirstVerticalBias = srcFLow.mFirstVerticalBias; 100 mLastHorizontalBias = srcFLow.mLastHorizontalBias; 101 mLastVerticalBias = srcFLow.mLastVerticalBias; 102 103 mHorizontalGap = srcFLow.mHorizontalGap; 104 mVerticalGap = srcFLow.mVerticalGap; 105 106 mHorizontalAlign = srcFLow.mHorizontalAlign; 107 mVerticalAlign = srcFLow.mVerticalAlign; 108 mWrapMode = srcFLow.mWrapMode; 109 110 mMaxElementsWrap = srcFLow.mMaxElementsWrap; 111 112 mOrientation = srcFLow.mOrientation; 113 } 114 115 ///////////////////////////////////////////////////////////////////////////////////////////// 116 // Accessors 117 ///////////////////////////////////////////////////////////////////////////////////////////// 118 setOrientation(int value)119 public void setOrientation(int value) { 120 mOrientation = value; 121 } 122 setFirstHorizontalStyle(int value)123 public void setFirstHorizontalStyle(int value) { 124 mFirstHorizontalStyle = value; 125 } 126 setFirstVerticalStyle(int value)127 public void setFirstVerticalStyle(int value) { 128 mFirstVerticalStyle = value; 129 } 130 setLastHorizontalStyle(int value)131 public void setLastHorizontalStyle(int value) { 132 mLastHorizontalStyle = value; 133 } 134 setLastVerticalStyle(int value)135 public void setLastVerticalStyle(int value) { 136 mLastVerticalStyle = value; 137 } 138 setHorizontalStyle(int value)139 public void setHorizontalStyle(int value) { 140 mHorizontalStyle = value; 141 } 142 setVerticalStyle(int value)143 public void setVerticalStyle(int value) { 144 mVerticalStyle = value; 145 } 146 setHorizontalBias(float value)147 public void setHorizontalBias(float value) { 148 mHorizontalBias = value; 149 } 150 setVerticalBias(float value)151 public void setVerticalBias(float value) { 152 mVerticalBias = value; 153 } 154 setFirstHorizontalBias(float value)155 public void setFirstHorizontalBias(float value) { 156 mFirstHorizontalBias = value; 157 } 158 setFirstVerticalBias(float value)159 public void setFirstVerticalBias(float value) { 160 mFirstVerticalBias = value; 161 } 162 setLastHorizontalBias(float value)163 public void setLastHorizontalBias(float value) { 164 mLastHorizontalBias = value; 165 } 166 setLastVerticalBias(float value)167 public void setLastVerticalBias(float value) { 168 mLastVerticalBias = value; 169 } 170 setHorizontalAlign(int value)171 public void setHorizontalAlign(int value) { 172 mHorizontalAlign = value; 173 } 174 setVerticalAlign(int value)175 public void setVerticalAlign(int value) { 176 mVerticalAlign = value; 177 } 178 setWrapMode(int value)179 public void setWrapMode(int value) { 180 mWrapMode = value; 181 } 182 setHorizontalGap(int value)183 public void setHorizontalGap(int value) { 184 mHorizontalGap = value; 185 } 186 setVerticalGap(int value)187 public void setVerticalGap(int value) { 188 mVerticalGap = value; 189 } 190 setMaxElementsWrap(int value)191 public void setMaxElementsWrap(int value) { 192 mMaxElementsWrap = value; 193 } 194 getMaxElementsWrap()195 public float getMaxElementsWrap() { 196 return mMaxElementsWrap; 197 } 198 ///////////////////////////////////////////////////////////////////////////////////////////// 199 // Utility methods 200 ///////////////////////////////////////////////////////////////////////////////////////////// 201 getWidgetWidth(ConstraintWidget widget, int max)202 private int getWidgetWidth(ConstraintWidget widget, int max) { 203 if (widget == null) { 204 return 0; 205 } 206 if (widget.getHorizontalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) { 207 if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD) { 208 return 0; 209 } else if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_PERCENT) { 210 int value = (int) (widget.mMatchConstraintPercentWidth * max); 211 if (value != widget.getWidth()) { 212 widget.setMeasureRequested(true); 213 measure(widget, DimensionBehaviour.FIXED, value, 214 widget.getVerticalDimensionBehaviour(), widget.getHeight()); 215 } 216 return value; 217 } else if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_WRAP) { 218 return widget.getWidth(); 219 } else if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_RATIO) { 220 return (int) (widget.getHeight() * widget.mDimensionRatio + 0.5f); 221 } 222 } 223 return widget.getWidth(); 224 } 225 getWidgetHeight(ConstraintWidget widget, int max)226 private int getWidgetHeight(ConstraintWidget widget, int max) { 227 if (widget == null) { 228 return 0; 229 } 230 if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) { 231 if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD) { 232 return 0; 233 } else if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_PERCENT) { 234 int value = (int) (widget.mMatchConstraintPercentHeight * max); 235 if (value != widget.getHeight()) { 236 widget.setMeasureRequested(true); 237 measure(widget, widget.getHorizontalDimensionBehaviour(), 238 widget.getWidth(), DimensionBehaviour.FIXED, value); 239 } 240 return value; 241 } else if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_WRAP) { 242 return widget.getHeight(); 243 } else if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_RATIO) { 244 return (int) (widget.getWidth() * widget.mDimensionRatio + 0.5f); 245 } 246 } 247 return widget.getHeight(); 248 } 249 250 ///////////////////////////////////////////////////////////////////////////////////////////// 251 // Measure 252 ///////////////////////////////////////////////////////////////////////////////////////////// 253 254 // @TODO: add description 255 @Override measure(int widthMode, int widthSize, int heightMode, int heightSize)256 public void measure(int widthMode, int widthSize, int heightMode, int heightSize) { 257 if (mWidgetsCount > 0 && !measureChildren()) { 258 setMeasure(0, 0); 259 needsCallbackFromSolver(false); 260 return; 261 } 262 263 @SuppressWarnings("unused") int width = 0; 264 @SuppressWarnings("unused") int height = 0; 265 int paddingLeft = getPaddingLeft(); 266 int paddingRight = getPaddingRight(); 267 int paddingTop = getPaddingTop(); 268 int paddingBottom = getPaddingBottom(); 269 270 int[] measured = new int[2]; 271 int max = widthSize - paddingLeft - paddingRight; 272 if (mOrientation == VERTICAL) { 273 max = heightSize - paddingTop - paddingBottom; 274 } 275 276 if (mOrientation == HORIZONTAL) { 277 if (mHorizontalStyle == UNKNOWN) { 278 mHorizontalStyle = CHAIN_SPREAD; 279 } 280 if (mVerticalStyle == UNKNOWN) { 281 mVerticalStyle = CHAIN_SPREAD; 282 } 283 } else { 284 if (mHorizontalStyle == UNKNOWN) { 285 mHorizontalStyle = CHAIN_SPREAD; 286 } 287 if (mVerticalStyle == UNKNOWN) { 288 mVerticalStyle = CHAIN_SPREAD; 289 } 290 } 291 292 ConstraintWidget[] widgets = mWidgets; 293 294 int gone = 0; 295 for (int i = 0; i < mWidgetsCount; i++) { 296 ConstraintWidget widget = mWidgets[i]; 297 if (widget.getVisibility() == GONE) { 298 gone++; 299 } 300 } 301 int count = mWidgetsCount; 302 if (gone > 0) { 303 widgets = new ConstraintWidget[mWidgetsCount - gone]; 304 int j = 0; 305 for (int i = 0; i < mWidgetsCount; i++) { 306 ConstraintWidget widget = mWidgets[i]; 307 if (widget.getVisibility() != GONE) { 308 widgets[j] = widget; 309 j++; 310 } 311 } 312 count = j; 313 } 314 mDisplayedWidgets = widgets; 315 mDisplayedWidgetsCount = count; 316 switch (mWrapMode) { 317 case WRAP_ALIGNED: { 318 measureAligned(widgets, count, mOrientation, max, measured); 319 } 320 break; 321 case WRAP_CHAIN: { 322 measureChainWrap(widgets, count, mOrientation, max, measured); 323 } 324 break; 325 case WRAP_NONE: { 326 measureNoWrap(widgets, count, mOrientation, max, measured); 327 } 328 break; 329 case WRAP_CHAIN_NEW: { 330 measureChainWrap_new(widgets, count, mOrientation, max, measured); 331 } 332 break; 333 334 } 335 336 width = measured[HORIZONTAL] + paddingLeft + paddingRight; 337 height = measured[VERTICAL] + paddingTop + paddingBottom; 338 339 int measuredWidth = 0; 340 int measuredHeight = 0; 341 342 if (widthMode == EXACTLY) { 343 measuredWidth = widthSize; 344 } else if (widthMode == AT_MOST) { 345 measuredWidth = Math.min(width, widthSize); 346 } else if (widthMode == UNSPECIFIED) { 347 measuredWidth = width; 348 } 349 350 if (heightMode == EXACTLY) { 351 measuredHeight = heightSize; 352 } else if (heightMode == AT_MOST) { 353 measuredHeight = Math.min(height, heightSize); 354 } else if (heightMode == UNSPECIFIED) { 355 measuredHeight = height; 356 } 357 358 setMeasure(measuredWidth, measuredHeight); 359 setWidth(measuredWidth); 360 setHeight(measuredHeight); 361 needsCallbackFromSolver(mWidgetsCount > 0); 362 } 363 364 ///////////////////////////////////////////////////////////////////////////////////////////// 365 // Utility class representing a single chain 366 ///////////////////////////////////////////////////////////////////////////////////////////// 367 368 private class WidgetsList { 369 private int mOrientation = HORIZONTAL; 370 private ConstraintWidget mBiggest = null; 371 int mBiggestDimension = 0; 372 private ConstraintAnchor mLeft; 373 private ConstraintAnchor mTop; 374 private ConstraintAnchor mRight; 375 private ConstraintAnchor mBottom; 376 private int mPaddingLeft = 0; 377 private int mPaddingTop = 0; 378 private int mPaddingRight = 0; 379 private int mPaddingBottom = 0; 380 private int mWidth = 0; 381 private int mHeight = 0; 382 private int mStartIndex = 0; 383 private int mCount = 0; 384 private int mNbMatchConstraintsWidgets = 0; 385 private int mMax = 0; 386 WidgetsList(int orientation, ConstraintAnchor left, ConstraintAnchor top, ConstraintAnchor right, ConstraintAnchor bottom, int max)387 WidgetsList(int orientation, 388 ConstraintAnchor left, ConstraintAnchor top, 389 ConstraintAnchor right, ConstraintAnchor bottom, 390 int max) { 391 mOrientation = orientation; 392 mLeft = left; 393 mTop = top; 394 mRight = right; 395 mBottom = bottom; 396 mPaddingLeft = getPaddingLeft(); 397 mPaddingTop = getPaddingTop(); 398 mPaddingRight = getPaddingRight(); 399 mPaddingBottom = getPaddingBottom(); 400 mMax = max; 401 } 402 setup(int orientation, ConstraintAnchor left, ConstraintAnchor top, ConstraintAnchor right, ConstraintAnchor bottom, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, int max)403 public void setup(int orientation, ConstraintAnchor left, ConstraintAnchor top, 404 ConstraintAnchor right, ConstraintAnchor bottom, 405 int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, 406 int max) { 407 mOrientation = orientation; 408 mLeft = left; 409 mTop = top; 410 mRight = right; 411 mBottom = bottom; 412 mPaddingLeft = paddingLeft; 413 mPaddingTop = paddingTop; 414 mPaddingRight = paddingRight; 415 mPaddingBottom = paddingBottom; 416 mMax = max; 417 } 418 clear()419 public void clear() { 420 mBiggestDimension = 0; 421 mBiggest = null; 422 mWidth = 0; 423 mHeight = 0; 424 mStartIndex = 0; 425 mCount = 0; 426 mNbMatchConstraintsWidgets = 0; 427 } 428 setStartIndex(int value)429 public void setStartIndex(int value) { 430 mStartIndex = value; 431 } 432 getWidth()433 public int getWidth() { 434 if (mOrientation == HORIZONTAL) { 435 return mWidth - mHorizontalGap; 436 } 437 return mWidth; 438 } 439 getHeight()440 public int getHeight() { 441 if (mOrientation == VERTICAL) { 442 return mHeight - mVerticalGap; 443 } 444 return mHeight; 445 } 446 add(ConstraintWidget widget)447 public void add(ConstraintWidget widget) { 448 if (mOrientation == HORIZONTAL) { 449 int width = getWidgetWidth(widget, mMax); 450 if (widget.getHorizontalDimensionBehaviour() 451 == DimensionBehaviour.MATCH_CONSTRAINT) { 452 mNbMatchConstraintsWidgets++; 453 width = 0; 454 } 455 int gap = mHorizontalGap; 456 if (widget.getVisibility() == GONE) { 457 gap = 0; 458 } 459 mWidth += width + gap; 460 int height = getWidgetHeight(widget, mMax); 461 if (mBiggest == null || mBiggestDimension < height) { 462 mBiggest = widget; 463 mBiggestDimension = height; 464 mHeight = height; 465 } 466 } else { 467 int width = getWidgetWidth(widget, mMax); 468 int height = getWidgetHeight(widget, mMax); 469 if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) { 470 mNbMatchConstraintsWidgets++; 471 height = 0; 472 } 473 int gap = mVerticalGap; 474 if (widget.getVisibility() == GONE) { 475 gap = 0; 476 } 477 mHeight += height + gap; 478 if (mBiggest == null || mBiggestDimension < width) { 479 mBiggest = widget; 480 mBiggestDimension = width; 481 mWidth = width; 482 } 483 } 484 mCount++; 485 } 486 createConstraints(boolean isInRtl, int chainIndex, boolean isLastChain)487 public void createConstraints(boolean isInRtl, int chainIndex, boolean isLastChain) { 488 final int count = mCount; 489 for (int i = 0; i < count; i++) { 490 if (mStartIndex + i >= mDisplayedWidgetsCount) { 491 break; 492 } 493 ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i]; 494 if (widget != null) { 495 widget.resetAnchors(); 496 } 497 } 498 if (count == 0 || mBiggest == null) { 499 return; 500 } 501 502 boolean singleChain = isLastChain && chainIndex == 0; 503 int firstVisible = -1; 504 int lastVisible = -1; 505 for (int i = 0; i < count; i++) { 506 int index = i; 507 if (isInRtl) { 508 index = count - 1 - i; 509 } 510 if (mStartIndex + index >= mDisplayedWidgetsCount) { 511 break; 512 } 513 ConstraintWidget widget = mDisplayedWidgets[mStartIndex + index]; 514 if (widget != null && widget.getVisibility() == VISIBLE) { 515 if (firstVisible == -1) { 516 firstVisible = i; 517 } 518 lastVisible = i; 519 } 520 } 521 522 ConstraintWidget previous = null; 523 if (mOrientation == HORIZONTAL) { 524 ConstraintWidget verticalWidget = mBiggest; 525 verticalWidget.setVerticalChainStyle(mVerticalStyle); 526 int padding = mPaddingTop; 527 if (chainIndex > 0) { 528 padding += mVerticalGap; 529 } 530 verticalWidget.mTop.connect(mTop, padding); 531 if (isLastChain) { 532 verticalWidget.mBottom.connect(mBottom, mPaddingBottom); 533 } 534 if (chainIndex > 0) { 535 ConstraintAnchor bottom = mTop.mOwner.mBottom; 536 bottom.connect(verticalWidget.mTop, 0); 537 } 538 539 ConstraintWidget baselineVerticalWidget = verticalWidget; 540 if (mVerticalAlign == VERTICAL_ALIGN_BASELINE && !verticalWidget.hasBaseline()) { 541 for (int i = 0; i < count; i++) { 542 int index = i; 543 if (isInRtl) { 544 index = count - 1 - i; 545 } 546 if (mStartIndex + index >= mDisplayedWidgetsCount) { 547 break; 548 } 549 ConstraintWidget widget = mDisplayedWidgets[mStartIndex + index]; 550 if (widget.hasBaseline()) { 551 baselineVerticalWidget = widget; 552 break; 553 } 554 } 555 } 556 557 for (int i = 0; i < count; i++) { 558 int index = i; 559 if (isInRtl) { 560 index = count - 1 - i; 561 } 562 if (mStartIndex + index >= mDisplayedWidgetsCount) { 563 break; 564 } 565 ConstraintWidget widget = mDisplayedWidgets[mStartIndex + index]; 566 if (widget == null) { 567 continue; 568 } 569 if (i == 0) { 570 widget.connect(widget.mLeft, mLeft, mPaddingLeft); 571 } 572 573 // ChainHead is always based on index, not i. 574 // E.g. RTL would have head at the right most widget. 575 if (index == 0) { 576 int style = mHorizontalStyle; 577 float bias = isInRtl ? (1 - mHorizontalBias) : mHorizontalBias; 578 if (mStartIndex == 0 && mFirstHorizontalStyle != UNKNOWN) { 579 style = mFirstHorizontalStyle; 580 bias = isInRtl ? (1 - mFirstHorizontalBias) : mFirstHorizontalBias; 581 } else if (isLastChain && mLastHorizontalStyle != UNKNOWN) { 582 style = mLastHorizontalStyle; 583 bias = isInRtl ? (1 - mLastHorizontalBias) : mLastHorizontalBias; 584 } 585 widget.setHorizontalChainStyle(style); 586 widget.setHorizontalBiasPercent(bias); 587 } 588 if (i == count - 1) { 589 widget.connect(widget.mRight, mRight, mPaddingRight); 590 } 591 if (previous != null) { 592 widget.mLeft.connect(previous.mRight, mHorizontalGap); 593 if (i == firstVisible) { 594 widget.mLeft.setGoneMargin(mPaddingLeft); 595 } 596 previous.mRight.connect(widget.mLeft, 0); 597 if (i == lastVisible + 1) { 598 previous.mRight.setGoneMargin(mPaddingRight); 599 } 600 } 601 if (widget != verticalWidget) { 602 if (mVerticalAlign == VERTICAL_ALIGN_BASELINE 603 && baselineVerticalWidget.hasBaseline() 604 && widget != baselineVerticalWidget 605 && widget.hasBaseline()) { 606 widget.mBaseline.connect(baselineVerticalWidget.mBaseline, 0); 607 } else { 608 switch (mVerticalAlign) { 609 case VERTICAL_ALIGN_TOP: { 610 widget.mTop.connect(verticalWidget.mTop, 0); 611 } 612 break; 613 case VERTICAL_ALIGN_BOTTOM: { 614 widget.mBottom.connect(verticalWidget.mBottom, 0); 615 } 616 break; 617 case VERTICAL_ALIGN_CENTER: 618 default: { 619 if (singleChain) { 620 widget.mTop.connect(mTop, mPaddingTop); 621 widget.mBottom.connect(mBottom, mPaddingBottom); 622 } else { 623 widget.mTop.connect(verticalWidget.mTop, 0); 624 widget.mBottom.connect(verticalWidget.mBottom, 0); 625 } 626 } 627 } 628 } 629 } 630 previous = widget; 631 } 632 } else { 633 ConstraintWidget horizontalWidget = mBiggest; 634 horizontalWidget.setHorizontalChainStyle(mHorizontalStyle); 635 int padding = mPaddingLeft; 636 if (chainIndex > 0) { 637 padding += mHorizontalGap; 638 } 639 if (isInRtl) { 640 horizontalWidget.mRight.connect(mRight, padding); 641 if (isLastChain) { 642 horizontalWidget.mLeft.connect(mLeft, mPaddingRight); 643 } 644 if (chainIndex > 0) { 645 ConstraintAnchor left = mRight.mOwner.mLeft; 646 left.connect(horizontalWidget.mRight, 0); 647 } 648 } else { 649 horizontalWidget.mLeft.connect(mLeft, padding); 650 if (isLastChain) { 651 horizontalWidget.mRight.connect(mRight, mPaddingRight); 652 } 653 if (chainIndex > 0) { 654 ConstraintAnchor right = mLeft.mOwner.mRight; 655 right.connect(horizontalWidget.mLeft, 0); 656 } 657 } 658 for (int i = 0; i < count; i++) { 659 if (mStartIndex + i >= mDisplayedWidgetsCount) { 660 break; 661 } 662 ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i]; 663 if (widget == null) { 664 continue; 665 } 666 if (i == 0) { 667 widget.connect(widget.mTop, mTop, mPaddingTop); 668 int style = mVerticalStyle; 669 float bias = mVerticalBias; 670 if (mStartIndex == 0 && mFirstVerticalStyle != UNKNOWN) { 671 style = mFirstVerticalStyle; 672 bias = mFirstVerticalBias; 673 } else if (isLastChain && mLastVerticalStyle != UNKNOWN) { 674 style = mLastVerticalStyle; 675 bias = mLastVerticalBias; 676 } 677 widget.setVerticalChainStyle(style); 678 widget.setVerticalBiasPercent(bias); 679 } 680 if (i == count - 1) { 681 widget.connect(widget.mBottom, mBottom, mPaddingBottom); 682 } 683 if (previous != null) { 684 widget.mTop.connect(previous.mBottom, mVerticalGap); 685 if (i == firstVisible) { 686 widget.mTop.setGoneMargin(mPaddingTop); 687 } 688 previous.mBottom.connect(widget.mTop, 0); 689 if (i == lastVisible + 1) { 690 previous.mBottom.setGoneMargin(mPaddingBottom); 691 } 692 } 693 if (widget != horizontalWidget) { 694 if (isInRtl) { 695 switch (mHorizontalAlign) { 696 case HORIZONTAL_ALIGN_START: { 697 widget.mRight.connect(horizontalWidget.mRight, 0); 698 } 699 break; 700 case HORIZONTAL_ALIGN_CENTER: { 701 widget.mLeft.connect(horizontalWidget.mLeft, 0); 702 widget.mRight.connect(horizontalWidget.mRight, 0); 703 } 704 break; 705 case HORIZONTAL_ALIGN_END: { 706 widget.mLeft.connect(horizontalWidget.mLeft, 0); 707 } 708 break; 709 } 710 } else { 711 switch (mHorizontalAlign) { 712 case HORIZONTAL_ALIGN_START: { 713 widget.mLeft.connect(horizontalWidget.mLeft, 0); 714 } 715 break; 716 case HORIZONTAL_ALIGN_CENTER: { 717 if (singleChain) { 718 widget.mLeft.connect(mLeft, mPaddingLeft); 719 widget.mRight.connect(mRight, mPaddingRight); 720 } else { 721 widget.mLeft.connect(horizontalWidget.mLeft, 0); 722 widget.mRight.connect(horizontalWidget.mRight, 0); 723 } 724 } 725 break; 726 case HORIZONTAL_ALIGN_END: { 727 widget.mRight.connect(horizontalWidget.mRight, 0); 728 } 729 break; 730 } 731 } 732 } 733 previous = widget; 734 } 735 } 736 } 737 measureMatchConstraints(int availableSpace)738 public void measureMatchConstraints(int availableSpace) { 739 if (mNbMatchConstraintsWidgets == 0) { 740 return; 741 } 742 final int count = mCount; 743 744 // that's completely incorrect and only works for spread with no weights? 745 int widgetSize = availableSpace / mNbMatchConstraintsWidgets; 746 for (int i = 0; i < count; i++) { 747 if (mStartIndex + i >= mDisplayedWidgetsCount) { 748 break; 749 } 750 ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i]; 751 if (mOrientation == HORIZONTAL) { 752 if (widget != null && widget.getHorizontalDimensionBehaviour() 753 == DimensionBehaviour.MATCH_CONSTRAINT) { 754 if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD) { 755 measure(widget, DimensionBehaviour.FIXED, widgetSize, 756 widget.getVerticalDimensionBehaviour(), widget.getHeight()); 757 } 758 } 759 } else { 760 if (widget != null && widget.getVerticalDimensionBehaviour() 761 == DimensionBehaviour.MATCH_CONSTRAINT) { 762 if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD) { 763 measure(widget, widget.getHorizontalDimensionBehaviour(), 764 widget.getWidth(), DimensionBehaviour.FIXED, widgetSize); 765 } 766 } 767 } 768 } 769 recomputeDimensions(); 770 } 771 recomputeDimensions()772 private void recomputeDimensions() { 773 mWidth = 0; 774 mHeight = 0; 775 mBiggest = null; 776 mBiggestDimension = 0; 777 final int count = mCount; 778 for (int i = 0; i < count; i++) { 779 if (mStartIndex + i >= mDisplayedWidgetsCount) { 780 break; 781 } 782 ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i]; 783 if (mOrientation == HORIZONTAL) { 784 int width = widget.getWidth(); 785 int gap = mHorizontalGap; 786 if (widget.getVisibility() == GONE) { 787 gap = 0; 788 } 789 mWidth += width + gap; 790 int height = getWidgetHeight(widget, mMax); 791 if (mBiggest == null || mBiggestDimension < height) { 792 mBiggest = widget; 793 mBiggestDimension = height; 794 mHeight = height; 795 } 796 } else { 797 int width = getWidgetWidth(widget, mMax); 798 int height = getWidgetHeight(widget, mMax); 799 int gap = mVerticalGap; 800 if (widget.getVisibility() == GONE) { 801 gap = 0; 802 } 803 mHeight += height + gap; 804 if (mBiggest == null || mBiggestDimension < width) { 805 mBiggest = widget; 806 mBiggestDimension = width; 807 mWidth = width; 808 } 809 } 810 } 811 } 812 813 } 814 815 ///////////////////////////////////////////////////////////////////////////////////////////// 816 // Measure Chain Wrap 817 ///////////////////////////////////////////////////////////////////////////////////////////// 818 819 /** 820 * Measure the virtual layout using a list of chains for the children 821 * 822 * @param widgets list of widgets 823 * @param orientation the layout orientation (horizontal or vertical) 824 * @param max the maximum available space 825 * @param measured output parameters -- will contain the resulting measure 826 */ measureChainWrap(ConstraintWidget[] widgets, int count, int orientation, int max, int[] measured)827 private void measureChainWrap(ConstraintWidget[] widgets, 828 int count, 829 int orientation, 830 int max, 831 int[] measured) { 832 if (count == 0) { 833 return; 834 } 835 836 mChainList.clear(); 837 WidgetsList list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max); 838 mChainList.add(list); 839 840 int nbMatchConstraintsWidgets = 0; 841 842 if (orientation == HORIZONTAL) { 843 int width = 0; 844 for (int i = 0; i < count; i++) { 845 ConstraintWidget widget = widgets[i]; 846 int w = getWidgetWidth(widget, max); 847 if (widget.getHorizontalDimensionBehaviour() 848 == DimensionBehaviour.MATCH_CONSTRAINT) { 849 nbMatchConstraintsWidgets++; 850 } 851 boolean doWrap = (width == max || (width + mHorizontalGap + w) > max) 852 && list.mBiggest != null; 853 if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (i % mMaxElementsWrap == 0)) { 854 doWrap = true; 855 } 856 if (doWrap) { 857 width = w; 858 list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max); 859 list.setStartIndex(i); 860 mChainList.add(list); 861 } else { 862 if (i > 0) { 863 width += mHorizontalGap + w; 864 } else { 865 width = w; 866 } 867 } 868 list.add(widget); 869 } 870 } else { 871 int height = 0; 872 for (int i = 0; i < count; i++) { 873 ConstraintWidget widget = widgets[i]; 874 int h = getWidgetHeight(widget, max); 875 if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) { 876 nbMatchConstraintsWidgets++; 877 } 878 boolean doWrap = (height == max || (height + mVerticalGap + h) > max) 879 && list.mBiggest != null; 880 if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (i % mMaxElementsWrap == 0)) { 881 doWrap = true; 882 } 883 if (doWrap) { 884 height = h; 885 list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max); 886 list.setStartIndex(i); 887 mChainList.add(list); 888 } else { 889 if (i > 0) { 890 height += mVerticalGap + h; 891 } else { 892 height = h; 893 } 894 } 895 list.add(widget); 896 } 897 } 898 final int listCount = mChainList.size(); 899 900 ConstraintAnchor left = mLeft; 901 ConstraintAnchor top = mTop; 902 ConstraintAnchor right = mRight; 903 ConstraintAnchor bottom = mBottom; 904 905 int paddingLeft = getPaddingLeft(); 906 int paddingTop = getPaddingTop(); 907 int paddingRight = getPaddingRight(); 908 int paddingBottom = getPaddingBottom(); 909 910 int maxWidth = 0; 911 int maxHeight = 0; 912 913 boolean needInternalMeasure = 914 getHorizontalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT 915 || getVerticalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT; 916 917 if (nbMatchConstraintsWidgets > 0 && needInternalMeasure) { 918 // we have to remeasure them. 919 for (int i = 0; i < listCount; i++) { 920 WidgetsList current = mChainList.get(i); 921 if (orientation == HORIZONTAL) { 922 current.measureMatchConstraints(max - current.getWidth()); 923 } else { 924 current.measureMatchConstraints(max - current.getHeight()); 925 } 926 } 927 } 928 929 for (int i = 0; i < listCount; i++) { 930 WidgetsList current = mChainList.get(i); 931 if (orientation == HORIZONTAL) { 932 if (i < listCount - 1) { 933 WidgetsList next = mChainList.get(i + 1); 934 bottom = next.mBiggest.mTop; 935 paddingBottom = 0; 936 } else { 937 bottom = mBottom; 938 paddingBottom = getPaddingBottom(); 939 } 940 ConstraintAnchor currentBottom = current.mBiggest.mBottom; 941 current.setup(orientation, left, top, right, bottom, 942 paddingLeft, paddingTop, paddingRight, paddingBottom, max); 943 top = currentBottom; 944 paddingTop = 0; 945 maxWidth = Math.max(maxWidth, current.getWidth()); 946 maxHeight += current.getHeight(); 947 if (i > 0) { 948 maxHeight += mVerticalGap; 949 } 950 } else { 951 if (i < listCount - 1) { 952 WidgetsList next = mChainList.get(i + 1); 953 right = next.mBiggest.mLeft; 954 paddingRight = 0; 955 } else { 956 right = mRight; 957 paddingRight = getPaddingRight(); 958 } 959 ConstraintAnchor currentRight = current.mBiggest.mRight; 960 current.setup(orientation, left, top, right, bottom, 961 paddingLeft, paddingTop, paddingRight, paddingBottom, max); 962 left = currentRight; 963 paddingLeft = 0; 964 maxWidth += current.getWidth(); 965 maxHeight = Math.max(maxHeight, current.getHeight()); 966 if (i > 0) { 967 maxWidth += mHorizontalGap; 968 } 969 } 970 } 971 measured[HORIZONTAL] = maxWidth; 972 measured[VERTICAL] = maxHeight; 973 } 974 ///////////////////////////////////////////////////////////////////////////////////////////// 975 // Measure Chain Wrap new 976 ///////////////////////////////////////////////////////////////////////////////////////////// 977 978 /** 979 * Measure the virtual layout using a list of chains for the children in new "fixed way" 980 * 981 * @param widgets list of widgets 982 * @param orientation the layout orientation (horizontal or vertical) 983 * @param max the maximum available space 984 * @param measured output parameters -- will contain the resulting measure 985 */ measureChainWrap_new(ConstraintWidget[] widgets, int count, int orientation, int max, int[] measured)986 private void measureChainWrap_new(ConstraintWidget[] widgets, 987 int count, 988 int orientation, 989 int max, 990 int[] measured) { 991 if (count == 0) { 992 return; 993 } 994 995 mChainList.clear(); 996 WidgetsList list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max); 997 mChainList.add(list); 998 999 int nbMatchConstraintsWidgets = 0; 1000 1001 if (orientation == HORIZONTAL) { 1002 int width = 0; 1003 int col = 0; 1004 for (int i = 0; i < count; i++) { 1005 col++; 1006 ConstraintWidget widget = widgets[i]; 1007 int w = getWidgetWidth(widget, max); 1008 if (widget.getHorizontalDimensionBehaviour() 1009 == DimensionBehaviour.MATCH_CONSTRAINT) { 1010 nbMatchConstraintsWidgets++; 1011 } 1012 boolean doWrap = (width == max || (width + mHorizontalGap + w) > max) 1013 && list.mBiggest != null; 1014 if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (col > mMaxElementsWrap)) { 1015 doWrap = true; 1016 } 1017 if (doWrap) { 1018 col = 1; 1019 width = w; 1020 list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max); 1021 list.setStartIndex(i); 1022 mChainList.add(list); 1023 } else { 1024 if (i > 0) { 1025 width += mHorizontalGap + w; 1026 } else { 1027 width = w; 1028 } 1029 } 1030 list.add(widget); 1031 } 1032 } else { 1033 int height = 0; 1034 int row = 0; 1035 for (int i = 0; i < count; i++) { 1036 row++; 1037 ConstraintWidget widget = widgets[i]; 1038 int h = getWidgetHeight(widget, max); 1039 if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) { 1040 nbMatchConstraintsWidgets++; 1041 } 1042 boolean doWrap = (height == max || (height + mVerticalGap + h) > max) 1043 && list.mBiggest != null; 1044 if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (row > mMaxElementsWrap)) { 1045 doWrap = true; 1046 } 1047 if (doWrap) { 1048 row = 1; 1049 height = h; 1050 list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max); 1051 list.setStartIndex(i); 1052 mChainList.add(list); 1053 } else { 1054 if (i > 0) { 1055 height += mVerticalGap + h; 1056 } else { 1057 height = h; 1058 } 1059 } 1060 list.add(widget); 1061 } 1062 } 1063 final int listCount = mChainList.size(); 1064 1065 ConstraintAnchor left = mLeft; 1066 ConstraintAnchor top = mTop; 1067 ConstraintAnchor right = mRight; 1068 ConstraintAnchor bottom = mBottom; 1069 1070 int paddingLeft = getPaddingLeft(); 1071 int paddingTop = getPaddingTop(); 1072 int paddingRight = getPaddingRight(); 1073 int paddingBottom = getPaddingBottom(); 1074 1075 int maxWidth = 0; 1076 int maxHeight = 0; 1077 1078 boolean needInternalMeasure = 1079 getHorizontalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT 1080 || getVerticalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT; 1081 1082 if (nbMatchConstraintsWidgets > 0 && needInternalMeasure) { 1083 // we have to remeasure them. 1084 for (int i = 0; i < listCount; i++) { 1085 WidgetsList current = mChainList.get(i); 1086 if (orientation == HORIZONTAL) { 1087 current.measureMatchConstraints(max - current.getWidth()); 1088 } else { 1089 current.measureMatchConstraints(max - current.getHeight()); 1090 } 1091 } 1092 } 1093 1094 for (int i = 0; i < listCount; i++) { 1095 WidgetsList current = mChainList.get(i); 1096 if (orientation == HORIZONTAL) { 1097 if (i < listCount - 1) { 1098 WidgetsList next = mChainList.get(i + 1); 1099 bottom = next.mBiggest.mTop; 1100 paddingBottom = 0; 1101 } else { 1102 bottom = mBottom; 1103 paddingBottom = getPaddingBottom(); 1104 } 1105 ConstraintAnchor currentBottom = current.mBiggest.mBottom; 1106 current.setup(orientation, left, top, right, bottom, 1107 paddingLeft, paddingTop, paddingRight, paddingBottom, max); 1108 top = currentBottom; 1109 paddingTop = 0; 1110 maxWidth = Math.max(maxWidth, current.getWidth()); 1111 maxHeight += current.getHeight(); 1112 if (i > 0) { 1113 maxHeight += mVerticalGap; 1114 } 1115 } else { 1116 if (i < listCount - 1) { 1117 WidgetsList next = mChainList.get(i + 1); 1118 right = next.mBiggest.mLeft; 1119 paddingRight = 0; 1120 } else { 1121 right = mRight; 1122 paddingRight = getPaddingRight(); 1123 } 1124 ConstraintAnchor currentRight = current.mBiggest.mRight; 1125 current.setup(orientation, left, top, right, bottom, 1126 paddingLeft, paddingTop, paddingRight, paddingBottom, max); 1127 left = currentRight; 1128 paddingLeft = 0; 1129 maxWidth += current.getWidth(); 1130 maxHeight = Math.max(maxHeight, current.getHeight()); 1131 if (i > 0) { 1132 maxWidth += mHorizontalGap; 1133 } 1134 } 1135 } 1136 measured[HORIZONTAL] = maxWidth; 1137 measured[VERTICAL] = maxHeight; 1138 } 1139 1140 ///////////////////////////////////////////////////////////////////////////////////////////// 1141 // Measure No Wrap 1142 ///////////////////////////////////////////////////////////////////////////////////////////// 1143 1144 /** 1145 * Measure the virtual layout using a single chain for the children 1146 * 1147 * @param widgets list of widgets 1148 * @param orientation the layout orientation (horizontal or vertical) 1149 * @param max the maximum available space 1150 * @param measured output parameters -- will contain the resulting measure 1151 */ measureNoWrap(ConstraintWidget[] widgets, int count, int orientation, int max, int[] measured)1152 private void measureNoWrap(ConstraintWidget[] widgets, 1153 int count, 1154 int orientation, 1155 int max, 1156 int[] measured) { 1157 if (count == 0) { 1158 return; 1159 } 1160 WidgetsList list = null; 1161 if (mChainList.size() == 0) { 1162 list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max); 1163 mChainList.add(list); 1164 } else { 1165 list = mChainList.get(0); 1166 list.clear(); 1167 list.setup(orientation, mLeft, mTop, mRight, mBottom, 1168 getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom(), max); 1169 } 1170 1171 for (int i = 0; i < count; i++) { 1172 ConstraintWidget widget = widgets[i]; 1173 list.add(widget); 1174 } 1175 1176 measured[HORIZONTAL] = list.getWidth(); 1177 measured[VERTICAL] = list.getHeight(); 1178 } 1179 1180 ///////////////////////////////////////////////////////////////////////////////////////////// 1181 // Measure Aligned 1182 ///////////////////////////////////////////////////////////////////////////////////////////// 1183 1184 /** 1185 * Measure the virtual layout arranging the children in a regular grid 1186 * 1187 * @param widgets list of widgets 1188 * @param orientation the layout orientation (horizontal or vertical) 1189 * @param max the maximum available space 1190 * @param measured output parameters -- will contain the resulting measure 1191 */ measureAligned(ConstraintWidget[] widgets, int count, int orientation, int max, int[] measured)1192 private void measureAligned(ConstraintWidget[] widgets, 1193 int count, 1194 int orientation, 1195 int max, 1196 int[] measured) { 1197 boolean done = false; 1198 int rows = 0; 1199 int cols = 0; 1200 1201 if (orientation == HORIZONTAL) { 1202 cols = mMaxElementsWrap; 1203 if (cols <= 0) { 1204 // let's initialize cols with an acceptable value 1205 int w = 0; 1206 cols = 0; 1207 for (int i = 0; i < count; i++) { 1208 if (i > 0) { 1209 w += mHorizontalGap; 1210 } 1211 ConstraintWidget widget = widgets[i]; 1212 if (widget == null) { 1213 continue; 1214 } 1215 w += getWidgetWidth(widget, max); 1216 if (w > max) { 1217 break; 1218 } 1219 cols++; 1220 } 1221 } 1222 } else { 1223 rows = mMaxElementsWrap; 1224 if (rows <= 0) { 1225 // let's initialize rows with an acceptable value 1226 int h = 0; 1227 rows = 0; 1228 for (int i = 0; i < count; i++) { 1229 if (i > 0) { 1230 h += mVerticalGap; 1231 } 1232 ConstraintWidget widget = widgets[i]; 1233 if (widget == null) { 1234 continue; 1235 } 1236 h += getWidgetHeight(widget, max); 1237 if (h > max) { 1238 break; 1239 } 1240 rows++; 1241 } 1242 } 1243 } 1244 1245 if (mAlignedDimensions == null) { 1246 mAlignedDimensions = new int[2]; 1247 } 1248 1249 if ((rows == 0 && orientation == VERTICAL) 1250 || (cols == 0 && orientation == HORIZONTAL)) { 1251 done = true; 1252 } 1253 1254 while (!done) { 1255 // get a num of rows (or cols) 1256 // get for each row and cols the chain of biggest elements 1257 1258 if (orientation == HORIZONTAL) { 1259 rows = (int) Math.ceil(count / (float) cols); 1260 } else { 1261 cols = (int) Math.ceil(count / (float) rows); 1262 } 1263 1264 if (mAlignedBiggestElementsInCols == null 1265 || mAlignedBiggestElementsInCols.length < cols) { 1266 mAlignedBiggestElementsInCols = new ConstraintWidget[cols]; 1267 } else { 1268 Arrays.fill(mAlignedBiggestElementsInCols, null); 1269 } 1270 if (mAlignedBiggestElementsInRows == null 1271 || mAlignedBiggestElementsInRows.length < rows) { 1272 mAlignedBiggestElementsInRows = new ConstraintWidget[rows]; 1273 } else { 1274 Arrays.fill(mAlignedBiggestElementsInRows, null); 1275 } 1276 1277 for (int i = 0; i < cols; i++) { 1278 for (int j = 0; j < rows; j++) { 1279 int index = j * cols + i; 1280 if (orientation == VERTICAL) { 1281 index = i * rows + j; 1282 } 1283 if (index >= widgets.length) { 1284 continue; 1285 } 1286 ConstraintWidget widget = widgets[index]; 1287 if (widget == null) { 1288 continue; 1289 } 1290 int w = getWidgetWidth(widget, max); 1291 if (mAlignedBiggestElementsInCols[i] == null 1292 || mAlignedBiggestElementsInCols[i].getWidth() < w) { 1293 mAlignedBiggestElementsInCols[i] = widget; 1294 } 1295 int h = getWidgetHeight(widget, max); 1296 if (mAlignedBiggestElementsInRows[j] == null 1297 || mAlignedBiggestElementsInRows[j].getHeight() < h) { 1298 mAlignedBiggestElementsInRows[j] = widget; 1299 } 1300 } 1301 } 1302 1303 int w = 0; 1304 for (int i = 0; i < cols; i++) { 1305 ConstraintWidget widget = mAlignedBiggestElementsInCols[i]; 1306 if (widget != null) { 1307 if (i > 0) { 1308 w += mHorizontalGap; 1309 } 1310 w += getWidgetWidth(widget, max); 1311 } 1312 } 1313 int h = 0; 1314 for (int j = 0; j < rows; j++) { 1315 ConstraintWidget widget = mAlignedBiggestElementsInRows[j]; 1316 if (widget != null) { 1317 if (j > 0) { 1318 h += mVerticalGap; 1319 } 1320 h += getWidgetHeight(widget, max); 1321 } 1322 } 1323 measured[HORIZONTAL] = w; 1324 measured[VERTICAL] = h; 1325 1326 if (orientation == HORIZONTAL) { 1327 if (w > max) { 1328 if (cols > 1) { 1329 cols--; 1330 } else { 1331 done = true; 1332 } 1333 } else { 1334 done = true; 1335 } 1336 } else { // VERTICAL 1337 if (h > max) { 1338 if (rows > 1) { 1339 rows--; 1340 } else { 1341 done = true; 1342 } 1343 } else { 1344 done = true; 1345 } 1346 } 1347 } 1348 mAlignedDimensions[HORIZONTAL] = cols; 1349 mAlignedDimensions[VERTICAL] = rows; 1350 } 1351 createAlignedConstraints(boolean isInRtl)1352 private void createAlignedConstraints(boolean isInRtl) { 1353 if (mAlignedDimensions == null 1354 || mAlignedBiggestElementsInCols == null 1355 || mAlignedBiggestElementsInRows == null) { 1356 return; 1357 } 1358 1359 for (int i = 0; i < mDisplayedWidgetsCount; i++) { 1360 ConstraintWidget widget = mDisplayedWidgets[i]; 1361 widget.resetAnchors(); 1362 } 1363 1364 int cols = mAlignedDimensions[HORIZONTAL]; 1365 int rows = mAlignedDimensions[VERTICAL]; 1366 1367 ConstraintWidget previous = null; 1368 float horizontalBias = mHorizontalBias; 1369 for (int i = 0; i < cols; i++) { 1370 int index = i; 1371 if (isInRtl) { 1372 index = cols - i - 1; 1373 horizontalBias = 1 - mHorizontalBias; 1374 } 1375 ConstraintWidget widget = mAlignedBiggestElementsInCols[index]; 1376 if (widget == null || widget.getVisibility() == GONE) { 1377 continue; 1378 } 1379 if (i == 0) { 1380 widget.connect(widget.mLeft, mLeft, getPaddingLeft()); 1381 widget.setHorizontalChainStyle(mHorizontalStyle); 1382 widget.setHorizontalBiasPercent(horizontalBias); 1383 } 1384 if (i == cols - 1) { 1385 widget.connect(widget.mRight, mRight, getPaddingRight()); 1386 } 1387 if (i > 0 && previous != null) { 1388 widget.connect(widget.mLeft, previous.mRight, mHorizontalGap); 1389 previous.connect(previous.mRight, widget.mLeft, 0); 1390 } 1391 previous = widget; 1392 } 1393 for (int j = 0; j < rows; j++) { 1394 ConstraintWidget widget = mAlignedBiggestElementsInRows[j]; 1395 if (widget == null || widget.getVisibility() == GONE) { 1396 continue; 1397 } 1398 if (j == 0) { 1399 widget.connect(widget.mTop, mTop, getPaddingTop()); 1400 widget.setVerticalChainStyle(mVerticalStyle); 1401 widget.setVerticalBiasPercent(mVerticalBias); 1402 } 1403 if (j == rows - 1) { 1404 widget.connect(widget.mBottom, mBottom, getPaddingBottom()); 1405 } 1406 if (j > 0 && previous != null) { 1407 widget.connect(widget.mTop, previous.mBottom, mVerticalGap); 1408 previous.connect(previous.mBottom, widget.mTop, 0); 1409 } 1410 previous = widget; 1411 } 1412 1413 for (int i = 0; i < cols; i++) { 1414 for (int j = 0; j < rows; j++) { 1415 int index = j * cols + i; 1416 if (mOrientation == VERTICAL) { 1417 index = i * rows + j; 1418 } 1419 if (index >= mDisplayedWidgets.length) { 1420 continue; 1421 } 1422 ConstraintWidget widget = mDisplayedWidgets[index]; 1423 if (widget == null || widget.getVisibility() == GONE) { 1424 continue; 1425 } 1426 ConstraintWidget biggestInCol = mAlignedBiggestElementsInCols[i]; 1427 ConstraintWidget biggestInRow = mAlignedBiggestElementsInRows[j]; 1428 if (widget != biggestInCol) { 1429 widget.connect(widget.mLeft, biggestInCol.mLeft, 0); 1430 widget.connect(widget.mRight, biggestInCol.mRight, 0); 1431 } 1432 if (widget != biggestInRow) { 1433 widget.connect(widget.mTop, biggestInRow.mTop, 0); 1434 widget.connect(widget.mBottom, biggestInRow.mBottom, 0); 1435 } 1436 } 1437 } 1438 } 1439 1440 ///////////////////////////////////////////////////////////////////////////////////////////// 1441 // Add constraints to solver 1442 ///////////////////////////////////////////////////////////////////////////////////////////// 1443 1444 /** 1445 * Add this widget to the solver 1446 * 1447 * @param system the solver we want to add the widget to 1448 * @param optimize true if {@link Optimizer#OPTIMIZATION_GRAPH} is on 1449 */ 1450 @Override addToSolver(LinearSystem system, boolean optimize)1451 public void addToSolver(LinearSystem system, boolean optimize) { 1452 super.addToSolver(system, optimize); 1453 1454 boolean isInRtl = getParent() != null && ((ConstraintWidgetContainer) getParent()).isRtl(); 1455 switch (mWrapMode) { 1456 case WRAP_CHAIN: { 1457 final int count = mChainList.size(); 1458 for (int i = 0; i < count; i++) { 1459 WidgetsList list = mChainList.get(i); 1460 list.createConstraints(isInRtl, i, i == count - 1); 1461 } 1462 } 1463 break; 1464 case WRAP_NONE: { 1465 if (mChainList.size() > 0) { 1466 WidgetsList list = mChainList.get(0); 1467 list.createConstraints(isInRtl, 0, true); 1468 } 1469 } 1470 break; 1471 case WRAP_ALIGNED: { 1472 createAlignedConstraints(isInRtl); 1473 } 1474 break; 1475 case WRAP_CHAIN_NEW: { 1476 final int count = mChainList.size(); 1477 for (int i = 0; i < count; i++) { 1478 WidgetsList list = mChainList.get(i); 1479 list.createConstraints(isInRtl, i, i == count - 1); 1480 } 1481 } 1482 break; 1483 } 1484 needsCallbackFromSolver(false); 1485 } 1486 } 1487