public class

Flow

extends VirtualLayout

Gradle dependencies

compile group: 'androidx.constraintlayout', name: 'constraintlayout-core', version: '1.1.0-beta01'

  • groupId: androidx.constraintlayout
  • artifactId: constraintlayout-core
  • version: 1.1.0-beta01

Artifact androidx.constraintlayout:constraintlayout-core:1.1.0-beta01 it located at Google repository (https://maven.google.com/)

Overview

Implements the Flow virtual layout.

Summary

Fields
public static final intHORIZONTAL_ALIGN_CENTER

public static final intHORIZONTAL_ALIGN_END

public static final intHORIZONTAL_ALIGN_START

public static final intVERTICAL_ALIGN_BASELINE

public static final intVERTICAL_ALIGN_BOTTOM

public static final intVERTICAL_ALIGN_CENTER

public static final intVERTICAL_ALIGN_TOP

public static final intWRAP_ALIGNED

public static final intWRAP_CHAIN

public static final intWRAP_CHAIN_NEW

public static final intWRAP_NONE

from VirtualLayoutmMeasure
from HelperWidgetmWidgets[], mWidgetsCount
from ConstraintWidgetANCHOR_BASELINE, ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_RIGHT, ANCHOR_TOP, BOTH, CHAIN_PACKED, CHAIN_SPREAD, CHAIN_SPREAD_INSIDE, DEFAULT_BIAS, DIRECT, frame, GONE, HORIZONTAL, horizontalChainRun, horizontalGroup, INVISIBLE, isTerminalWidget[], mAnchors, MATCH_CONSTRAINT_PERCENT, MATCH_CONSTRAINT_RATIO, MATCH_CONSTRAINT_RATIO_RESOLVED, MATCH_CONSTRAINT_SPREAD, MATCH_CONSTRAINT_WRAP, mBaseline, mBottom, mCenter, mCircleConstraintAngle, mDimensionRatio, mDimensionRatioSide, measured, mHorizontalResolution, mHorizontalRun, mIsHeightWrapContent, mIsWidthWrapContent, mLeft, mListAnchors[], mListDimensionBehaviors[], mListNextMatchConstraintsWidget[], mMatchConstraintDefaultHeight, mMatchConstraintDefaultWidth, mMatchConstraintMaxHeight, mMatchConstraintMaxWidth, mMatchConstraintMinHeight, mMatchConstraintMinWidth, mMatchConstraintPercentHeight, mMatchConstraintPercentWidth, mMinHeight, mMinWidth, mNextChainWidget[], mOffsetX, mOffsetY, mParent, mResolvedMatchConstraintDefault[], mRight, mTop, mVerticalResolution, mVerticalRun, mWeight[], mX, mY, run[], SOLVER, stringId, UNKNOWN, VERTICAL, verticalChainRun, verticalGroup, VISIBLE, WRAP_BEHAVIOR_HORIZONTAL_ONLY, WRAP_BEHAVIOR_INCLUDED, WRAP_BEHAVIOR_SKIPPED, WRAP_BEHAVIOR_VERTICAL_ONLY
Constructors
publicFlow()

Methods
public voidaddToSolver(LinearSystem system, boolean optimize)

Add this widget to the solver

public voidcopy(ConstraintWidget src, java.util.HashMap<ConstraintWidget, ConstraintWidget> map)

public floatgetMaxElementsWrap()

public voidmeasure(int widthMode, int widthSize, int heightMode, int heightSize)

public voidsetFirstHorizontalBias(float value)

public voidsetFirstHorizontalStyle(int value)

public voidsetFirstVerticalBias(float value)

public voidsetFirstVerticalStyle(int value)

public voidsetHorizontalAlign(int value)

public voidsetHorizontalBias(float value)

public voidsetHorizontalGap(int value)

public voidsetHorizontalStyle(int value)

public voidsetLastHorizontalBias(float value)

public voidsetLastHorizontalStyle(int value)

public voidsetLastVerticalBias(float value)

public voidsetLastVerticalStyle(int value)

public voidsetMaxElementsWrap(int value)

public voidsetOrientation(int value)

public voidsetVerticalAlign(int value)

public voidsetVerticalBias(float value)

public voidsetVerticalGap(int value)

public voidsetVerticalStyle(int value)

public voidsetWrapMode(int value)

from VirtualLayoutapplyRtl, captureWidgets, contains, getMeasuredHeight, getMeasuredWidth, getPaddingBottom, getPaddingLeft, getPaddingRight, getPaddingTop, measure, measureChildren, needsCallbackFromSolver, needSolverPass, setMeasure, setPadding, setPaddingBottom, setPaddingEnd, setPaddingLeft, setPaddingRight, setPaddingStart, setPaddingTop, updateConstraints
from HelperWidgetadd, addDependents, findGroupInDependents, removeAllIds
from ConstraintWidgetaddChildrenToSolverByDependency, allowedInBarrier, connect, connect, connect, connectCircularConstraint, createObjectVariables, ensureMeasureRequested, ensureWidgetRuns, getAnchor, getAnchors, getBaselineDistance, getBiasPercent, getBottom, getCompanionWidget, getContainerItemSkip, getDebugName, getDimensionBehaviour, getDimensionRatio, getDimensionRatioSide, getHasBaseline, getHeight, getHorizontalBiasPercent, getHorizontalChainControlWidget, getHorizontalChainStyle, getHorizontalDimensionBehaviour, getHorizontalMargin, getLastHorizontalMeasureSpec, getLastVerticalMeasureSpec, getLeft, getLength, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getNextChainMember, getOptimizerWrapHeight, getOptimizerWrapWidth, getParent, getPreviousChainMember, getRight, getRootX, getRootY, getRun, getSceneString, getTop, getType, getVerticalBiasPercent, getVerticalChainControlWidget, getVerticalChainStyle, getVerticalDimensionBehaviour, getVerticalMargin, getVisibility, getWidth, getWrapBehaviorInParent, getX, getY, hasBaseline, hasDanglingDimension, hasDependencies, hasDimensionOverride, hasResolvedTargets, immediateConnect, isAnimated, isHeightWrapContent, isHorizontalSolvingPassDone, isInBarrier, isInHorizontalChain, isInPlaceholder, isInVerticalChain, isInVirtualLayout, isMeasureRequested, isResolvedHorizontally, isResolvedVertically, isRoot, isSpreadHeight, isSpreadWidth, isVerticalSolvingPassDone, isWidthWrapContent, markHorizontalSolvingPassDone, markVerticalSolvingPassDone, oppositeDimensionDependsOn, oppositeDimensionsTied, reset, resetAllConstraints, resetAnchor, resetAnchors, resetFinalResolution, resetSolverVariables, resetSolvingPassFlag, serialize, setAnimated, setBaselineDistance, setCompanionWidget, setContainerItemSkip, setDebugName, setDebugSolverName, setDimension, setDimensionRatio, setDimensionRatio, setFinalBaseline, setFinalFrame, setFinalHorizontal, setFinalLeft, setFinalTop, setFinalVertical, setFrame, setFrame, setGoneMargin, setHasBaseline, setHeight, setHeightWrapContent, setHorizontalBiasPercent, setHorizontalChainStyle, setHorizontalDimension, setHorizontalDimensionBehaviour, setHorizontalMatchStyle, setHorizontalWeight, setInBarrier, setInPlaceholder, setInVirtualLayout, setLastMeasureSpec, setLength, setMaxHeight, setMaxWidth, setMeasureRequested, setMinHeight, setMinWidth, setOffset, setOrigin, setParent, setType, setupDimensionRatio, setVerticalBiasPercent, setVerticalChainStyle, setVerticalDimension, setVerticalDimensionBehaviour, setVerticalMatchStyle, setVerticalWeight, setVisibility, setWidth, setWidthWrapContent, setWrapBehaviorInParent, setX, setY, toString, updateFromRuns, updateFromSolver
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Fields

public static final int HORIZONTAL_ALIGN_START

public static final int HORIZONTAL_ALIGN_END

public static final int HORIZONTAL_ALIGN_CENTER

public static final int VERTICAL_ALIGN_TOP

public static final int VERTICAL_ALIGN_BOTTOM

public static final int VERTICAL_ALIGN_CENTER

public static final int VERTICAL_ALIGN_BASELINE

public static final int WRAP_NONE

public static final int WRAP_CHAIN

public static final int WRAP_ALIGNED

public static final int WRAP_CHAIN_NEW

Constructors

public Flow()

Methods

public void copy(ConstraintWidget src, java.util.HashMap<ConstraintWidget, ConstraintWidget> map)

public void setOrientation(int value)

public void setFirstHorizontalStyle(int value)

public void setFirstVerticalStyle(int value)

public void setLastHorizontalStyle(int value)

public void setLastVerticalStyle(int value)

public void setHorizontalStyle(int value)

public void setVerticalStyle(int value)

public void setHorizontalBias(float value)

public void setVerticalBias(float value)

public void setFirstHorizontalBias(float value)

public void setFirstVerticalBias(float value)

public void setLastHorizontalBias(float value)

public void setLastVerticalBias(float value)

public void setHorizontalAlign(int value)

public void setVerticalAlign(int value)

public void setWrapMode(int value)

public void setHorizontalGap(int value)

public void setVerticalGap(int value)

public void setMaxElementsWrap(int value)

public float getMaxElementsWrap()

public void measure(int widthMode, int widthSize, int heightMode, int heightSize)

public void addToSolver(LinearSystem system, boolean optimize)

Add this widget to the solver

Parameters:

system: the solver we want to add the widget to
optimize: true if Optimizer.OPTIMIZATION_GRAPH is on

Source

/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.constraintlayout.core.widgets;

import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.AT_MOST;
import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.EXACTLY;
import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.UNSPECIFIED;

import androidx.constraintlayout.core.LinearSystem;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

/**
 * Implements the Flow virtual layout.
 */
public class Flow extends VirtualLayout {

    public static final int HORIZONTAL_ALIGN_START = 0;
    public static final int HORIZONTAL_ALIGN_END = 1;
    public static final int HORIZONTAL_ALIGN_CENTER = 2;

    public static final int VERTICAL_ALIGN_TOP = 0;
    public static final int VERTICAL_ALIGN_BOTTOM = 1;
    public static final int VERTICAL_ALIGN_CENTER = 2;
    public static final int VERTICAL_ALIGN_BASELINE = 3;

    public static final int WRAP_NONE = 0;
    public static final int WRAP_CHAIN = 1;
    public static final int WRAP_ALIGNED = 2;
    public static final int WRAP_CHAIN_NEW = 3;

    private int mHorizontalStyle = UNKNOWN;
    private int mVerticalStyle = UNKNOWN;
    private int mFirstHorizontalStyle = UNKNOWN;
    private int mFirstVerticalStyle = UNKNOWN;
    private int mLastHorizontalStyle = UNKNOWN;
    private int mLastVerticalStyle = UNKNOWN;

    private float mHorizontalBias = 0.5f;
    private float mVerticalBias = 0.5f;
    private float mFirstHorizontalBias = 0.5f;
    private float mFirstVerticalBias = 0.5f;
    private float mLastHorizontalBias = 0.5f;
    private float mLastVerticalBias = 0.5f;

    private int mHorizontalGap = 0;
    private int mVerticalGap = 0;

    private int mHorizontalAlign = HORIZONTAL_ALIGN_CENTER;
    private int mVerticalAlign = VERTICAL_ALIGN_CENTER;
    private int mWrapMode = WRAP_NONE;

    private int mMaxElementsWrap = UNKNOWN;

    private int mOrientation = HORIZONTAL;

    private ArrayList<WidgetsList> mChainList = new ArrayList<>();

    // Aligned management

    private ConstraintWidget[] mAlignedBiggestElementsInRows = null;
    private ConstraintWidget[] mAlignedBiggestElementsInCols = null;
    private int[] mAlignedDimensions = null;
    private ConstraintWidget[] mDisplayedWidgets;
    private int mDisplayedWidgetsCount = 0;


    @Override
    public void copy(ConstraintWidget src, HashMap<ConstraintWidget, ConstraintWidget> map) {
        super.copy(src, map);
        Flow srcFLow = (Flow) src;

        mHorizontalStyle = srcFLow.mHorizontalStyle;
        mVerticalStyle = srcFLow.mVerticalStyle;
        mFirstHorizontalStyle = srcFLow.mFirstHorizontalStyle;
        mFirstVerticalStyle = srcFLow.mFirstVerticalStyle;
        mLastHorizontalStyle = srcFLow.mLastHorizontalStyle;
        mLastVerticalStyle = srcFLow.mLastVerticalStyle;

        mHorizontalBias = srcFLow.mHorizontalBias;
        mVerticalBias = srcFLow.mVerticalBias;
        mFirstHorizontalBias = srcFLow.mFirstHorizontalBias;
        mFirstVerticalBias = srcFLow.mFirstVerticalBias;
        mLastHorizontalBias = srcFLow.mLastHorizontalBias;
        mLastVerticalBias = srcFLow.mLastVerticalBias;

        mHorizontalGap = srcFLow.mHorizontalGap;
        mVerticalGap = srcFLow.mVerticalGap;

        mHorizontalAlign = srcFLow.mHorizontalAlign;
        mVerticalAlign = srcFLow.mVerticalAlign;
        mWrapMode = srcFLow.mWrapMode;

        mMaxElementsWrap = srcFLow.mMaxElementsWrap;

        mOrientation = srcFLow.mOrientation;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Accessors
    /////////////////////////////////////////////////////////////////////////////////////////////

    public void setOrientation(int value) {
        mOrientation = value;
    }

    public void setFirstHorizontalStyle(int value) {
        mFirstHorizontalStyle = value;
    }

    public void setFirstVerticalStyle(int value) {
        mFirstVerticalStyle = value;
    }

    public void setLastHorizontalStyle(int value) {
        mLastHorizontalStyle = value;
    }

    public void setLastVerticalStyle(int value) {
        mLastVerticalStyle = value;
    }

    public void setHorizontalStyle(int value) {
        mHorizontalStyle = value;
    }

    public void setVerticalStyle(int value) {
        mVerticalStyle = value;
    }

    public void setHorizontalBias(float value) {
        mHorizontalBias = value;
    }

    public void setVerticalBias(float value) {
        mVerticalBias = value;
    }

    public void setFirstHorizontalBias(float value) {
        mFirstHorizontalBias = value;
    }

    public void setFirstVerticalBias(float value) {
        mFirstVerticalBias = value;
    }

    public void setLastHorizontalBias(float value) {
        mLastHorizontalBias = value;
    }

    public void setLastVerticalBias(float value) {
        mLastVerticalBias = value;
    }

    public void setHorizontalAlign(int value) {
        mHorizontalAlign = value;
    }

    public void setVerticalAlign(int value) {
        mVerticalAlign = value;
    }

    public void setWrapMode(int value) {
        mWrapMode = value;
    }

    public void setHorizontalGap(int value) {
        mHorizontalGap = value;
    }

    public void setVerticalGap(int value) {
        mVerticalGap = value;
    }

    public void setMaxElementsWrap(int value) {
        mMaxElementsWrap = value;
    }

    public float getMaxElementsWrap() {
        return mMaxElementsWrap;
    }
    /////////////////////////////////////////////////////////////////////////////////////////////
    // Utility methods
    /////////////////////////////////////////////////////////////////////////////////////////////

    private int getWidgetWidth(ConstraintWidget widget, int max) {
        if (widget == null) {
            return 0;
        }
        if (widget.getHorizontalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) {
            if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD) {
                return 0;
            } else if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_PERCENT) {
                int value = (int) (widget.mMatchConstraintPercentWidth * max);
                if (value != widget.getWidth()) {
                    widget.setMeasureRequested(true);
                    measure(widget, DimensionBehaviour.FIXED, value,
                            widget.getVerticalDimensionBehaviour(), widget.getHeight());
                }
                return value;
            } else if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_WRAP) {
                return widget.getWidth();
            } else if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_RATIO) {
                return (int) (widget.getHeight() * widget.mDimensionRatio + 0.5f);
            }
        }
        return widget.getWidth();
    }

    private int getWidgetHeight(ConstraintWidget widget, int max) {
        if (widget == null) {
            return 0;
        }
        if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) {
            if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD) {
                return 0;
            } else if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_PERCENT) {
                int value = (int) (widget.mMatchConstraintPercentHeight * max);
                if (value != widget.getHeight()) {
                    widget.setMeasureRequested(true);
                    measure(widget, widget.getHorizontalDimensionBehaviour(),
                            widget.getWidth(), DimensionBehaviour.FIXED, value);
                }
                return value;
            } else if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_WRAP) {
                return widget.getHeight();
            } else if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_RATIO) {
                return (int) (widget.getWidth() * widget.mDimensionRatio + 0.5f);
            }
        }
        return widget.getHeight();
    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Measure
    /////////////////////////////////////////////////////////////////////////////////////////////

    // @TODO: add description
    @Override
    public void measure(int widthMode, int widthSize, int heightMode, int heightSize) {
        if (mWidgetsCount > 0 && !measureChildren()) {
            setMeasure(0, 0);
            needsCallbackFromSolver(false);
            return;
        }

        @SuppressWarnings("unused") int width = 0;
        @SuppressWarnings("unused") int height = 0;
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        int[] measured = new int[2];
        int max = widthSize - paddingLeft - paddingRight;
        if (mOrientation == VERTICAL) {
            max = heightSize - paddingTop - paddingBottom;
        }

        if (mOrientation == HORIZONTAL) {
            if (mHorizontalStyle == UNKNOWN) {
                mHorizontalStyle = CHAIN_SPREAD;
            }
            if (mVerticalStyle == UNKNOWN) {
                mVerticalStyle = CHAIN_SPREAD;
            }
        } else {
            if (mHorizontalStyle == UNKNOWN) {
                mHorizontalStyle = CHAIN_SPREAD;
            }
            if (mVerticalStyle == UNKNOWN) {
                mVerticalStyle = CHAIN_SPREAD;
            }
        }

        ConstraintWidget[] widgets = mWidgets;

        int gone = 0;
        for (int i = 0; i < mWidgetsCount; i++) {
            ConstraintWidget widget = mWidgets[i];
            if (widget.getVisibility() == GONE) {
                gone++;
            }
        }
        int count = mWidgetsCount;
        if (gone > 0) {
            widgets = new ConstraintWidget[mWidgetsCount - gone];
            int j = 0;
            for (int i = 0; i < mWidgetsCount; i++) {
                ConstraintWidget widget = mWidgets[i];
                if (widget.getVisibility() != GONE) {
                    widgets[j] = widget;
                    j++;
                }
            }
            count = j;
        }
        mDisplayedWidgets = widgets;
        mDisplayedWidgetsCount = count;
        switch (mWrapMode) {
            case WRAP_ALIGNED: {
                measureAligned(widgets, count, mOrientation, max, measured);
            }
            break;
            case WRAP_CHAIN: {
                measureChainWrap(widgets, count, mOrientation, max, measured);
            }
            break;
            case WRAP_NONE: {
                measureNoWrap(widgets, count, mOrientation, max, measured);
            }
            break;
            case WRAP_CHAIN_NEW: {
                measureChainWrap_new(widgets, count, mOrientation, max, measured);
            }
            break;

        }

        width = measured[HORIZONTAL] + paddingLeft + paddingRight;
        height = measured[VERTICAL] + paddingTop + paddingBottom;

        int measuredWidth = 0;
        int measuredHeight = 0;

        if (widthMode == EXACTLY) {
            measuredWidth = widthSize;
        } else if (widthMode == AT_MOST) {
            measuredWidth = Math.min(width, widthSize);
        } else if (widthMode == UNSPECIFIED) {
            measuredWidth = width;
        }

        if (heightMode == EXACTLY) {
            measuredHeight = heightSize;
        } else if (heightMode == AT_MOST) {
            measuredHeight = Math.min(height, heightSize);
        } else if (heightMode == UNSPECIFIED) {
            measuredHeight = height;
        }

        setMeasure(measuredWidth, measuredHeight);
        setWidth(measuredWidth);
        setHeight(measuredHeight);
        needsCallbackFromSolver(mWidgetsCount > 0);
    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Utility class representing a single chain
    /////////////////////////////////////////////////////////////////////////////////////////////

    private class WidgetsList {
        private int mOrientation = HORIZONTAL;
        private ConstraintWidget mBiggest = null;
        int mBiggestDimension = 0;
        private ConstraintAnchor mLeft;
        private ConstraintAnchor mTop;
        private ConstraintAnchor mRight;
        private ConstraintAnchor mBottom;
        private int mPaddingLeft = 0;
        private int mPaddingTop = 0;
        private int mPaddingRight = 0;
        private int mPaddingBottom = 0;
        private int mWidth = 0;
        private int mHeight = 0;
        private int mStartIndex = 0;
        private int mCount = 0;
        private int mNbMatchConstraintsWidgets = 0;
        private int mMax = 0;

        WidgetsList(int orientation,
                ConstraintAnchor left, ConstraintAnchor top,
                ConstraintAnchor right, ConstraintAnchor bottom,
                int max) {
            mOrientation = orientation;
            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;
            mPaddingLeft = getPaddingLeft();
            mPaddingTop = getPaddingTop();
            mPaddingRight = getPaddingRight();
            mPaddingBottom = getPaddingBottom();
            mMax = max;
        }

        public void setup(int orientation, ConstraintAnchor left, ConstraintAnchor top,
                ConstraintAnchor right, ConstraintAnchor bottom,
                int paddingLeft, int paddingTop, int paddingRight, int paddingBottom,
                int max) {
            mOrientation = orientation;
            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;
            mPaddingLeft = paddingLeft;
            mPaddingTop = paddingTop;
            mPaddingRight = paddingRight;
            mPaddingBottom = paddingBottom;
            mMax = max;
        }

        public void clear() {
            mBiggestDimension = 0;
            mBiggest = null;
            mWidth = 0;
            mHeight = 0;
            mStartIndex = 0;
            mCount = 0;
            mNbMatchConstraintsWidgets = 0;
        }

        public void setStartIndex(int value) {
            mStartIndex = value;
        }

        public int getWidth() {
            if (mOrientation == HORIZONTAL) {
                return mWidth - mHorizontalGap;
            }
            return mWidth;
        }

        public int getHeight() {
            if (mOrientation == VERTICAL) {
                return mHeight - mVerticalGap;
            }
            return mHeight;
        }

        public void add(ConstraintWidget widget) {
            if (mOrientation == HORIZONTAL) {
                int width = getWidgetWidth(widget, mMax);
                if (widget.getHorizontalDimensionBehaviour()
                        == DimensionBehaviour.MATCH_CONSTRAINT) {
                    mNbMatchConstraintsWidgets++;
                    width = 0;
                }
                int gap = mHorizontalGap;
                if (widget.getVisibility() == GONE) {
                    gap = 0;
                }
                mWidth += width + gap;
                int height = getWidgetHeight(widget, mMax);
                if (mBiggest == null || mBiggestDimension < height) {
                    mBiggest = widget;
                    mBiggestDimension = height;
                    mHeight = height;
                }
            } else {
                int width = getWidgetWidth(widget, mMax);
                int height = getWidgetHeight(widget, mMax);
                if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) {
                    mNbMatchConstraintsWidgets++;
                    height = 0;
                }
                int gap = mVerticalGap;
                if (widget.getVisibility() == GONE) {
                    gap = 0;
                }
                mHeight += height + gap;
                if (mBiggest == null || mBiggestDimension < width) {
                    mBiggest = widget;
                    mBiggestDimension = width;
                    mWidth = width;
                }
            }
            mCount++;
        }

        public void createConstraints(boolean isInRtl, int chainIndex, boolean isLastChain) {
            final int count = mCount;
            for (int i = 0; i < count; i++) {
                if (mStartIndex + i >= mDisplayedWidgetsCount) {
                    break;
                }
                ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i];
                if (widget != null) {
                    widget.resetAnchors();
                }
            }
            if (count == 0 || mBiggest == null) {
                return;
            }

            boolean singleChain = isLastChain && chainIndex == 0;
            int firstVisible = -1;
            int lastVisible = -1;
            for (int i = 0; i < count; i++) {
                int index = i;
                if (isInRtl) {
                    index = count - 1 - i;
                }
                if (mStartIndex + index >= mDisplayedWidgetsCount) {
                    break;
                }
                ConstraintWidget widget = mDisplayedWidgets[mStartIndex + index];
                if (widget != null && widget.getVisibility() == VISIBLE) {
                    if (firstVisible == -1) {
                        firstVisible = i;
                    }
                    lastVisible = i;
                }
            }

            ConstraintWidget previous = null;
            if (mOrientation == HORIZONTAL) {
                ConstraintWidget verticalWidget = mBiggest;
                verticalWidget.setVerticalChainStyle(mVerticalStyle);
                int padding = mPaddingTop;
                if (chainIndex > 0) {
                    padding += mVerticalGap;
                }
                verticalWidget.mTop.connect(mTop, padding);
                if (isLastChain) {
                    verticalWidget.mBottom.connect(mBottom, mPaddingBottom);
                }
                if (chainIndex > 0) {
                    ConstraintAnchor bottom = mTop.mOwner.mBottom;
                    bottom.connect(verticalWidget.mTop, 0);
                }

                ConstraintWidget baselineVerticalWidget = verticalWidget;
                if (mVerticalAlign == VERTICAL_ALIGN_BASELINE && !verticalWidget.hasBaseline()) {
                    for (int i = 0; i < count; i++) {
                        int index = i;
                        if (isInRtl) {
                            index = count - 1 - i;
                        }
                        if (mStartIndex + index >= mDisplayedWidgetsCount) {
                            break;
                        }
                        ConstraintWidget widget = mDisplayedWidgets[mStartIndex + index];
                        if (widget.hasBaseline()) {
                            baselineVerticalWidget = widget;
                            break;
                        }
                    }
                }

                for (int i = 0; i < count; i++) {
                    int index = i;
                    if (isInRtl) {
                        index = count - 1 - i;
                    }
                    if (mStartIndex + index >= mDisplayedWidgetsCount) {
                        break;
                    }
                    ConstraintWidget widget = mDisplayedWidgets[mStartIndex + index];
                    if (widget == null) {
                        continue;
                    }
                    if (i == 0) {
                        widget.connect(widget.mLeft, mLeft, mPaddingLeft);
                    }

                    // ChainHead is always based on index, not i.
                    // E.g. RTL would have head at the right most widget.
                    if (index == 0) {
                        int style = mHorizontalStyle;
                        float bias = isInRtl ? (1 - mHorizontalBias) : mHorizontalBias;
                        if (mStartIndex == 0 && mFirstHorizontalStyle != UNKNOWN) {
                            style = mFirstHorizontalStyle;
                            bias = isInRtl ? (1 - mFirstHorizontalBias) : mFirstHorizontalBias;
                        } else if (isLastChain && mLastHorizontalStyle != UNKNOWN) {
                            style = mLastHorizontalStyle;
                            bias = isInRtl ? (1 - mLastHorizontalBias) : mLastHorizontalBias;
                        }
                        widget.setHorizontalChainStyle(style);
                        widget.setHorizontalBiasPercent(bias);
                    }
                    if (i == count - 1) {
                        widget.connect(widget.mRight, mRight, mPaddingRight);
                    }
                    if (previous != null) {
                        widget.mLeft.connect(previous.mRight, mHorizontalGap);
                        if (i == firstVisible) {
                            widget.mLeft.setGoneMargin(mPaddingLeft);
                        }
                        previous.mRight.connect(widget.mLeft, 0);
                        if (i == lastVisible + 1) {
                            previous.mRight.setGoneMargin(mPaddingRight);
                        }
                    }
                    if (widget != verticalWidget) {
                        if (mVerticalAlign == VERTICAL_ALIGN_BASELINE
                                && baselineVerticalWidget.hasBaseline()
                                && widget != baselineVerticalWidget
                                && widget.hasBaseline()) {
                            widget.mBaseline.connect(baselineVerticalWidget.mBaseline, 0);
                        } else {
                            switch (mVerticalAlign) {
                                case VERTICAL_ALIGN_TOP: {
                                    widget.mTop.connect(verticalWidget.mTop, 0);
                                }
                                break;
                                case VERTICAL_ALIGN_BOTTOM: {
                                    widget.mBottom.connect(verticalWidget.mBottom, 0);
                                }
                                break;
                                case VERTICAL_ALIGN_CENTER:
                                default: {
                                    if (singleChain) {
                                        widget.mTop.connect(mTop, mPaddingTop);
                                        widget.mBottom.connect(mBottom, mPaddingBottom);
                                    } else {
                                        widget.mTop.connect(verticalWidget.mTop, 0);
                                        widget.mBottom.connect(verticalWidget.mBottom, 0);
                                    }
                                }
                            }
                        }
                    }
                    previous = widget;
                }
            } else {
                ConstraintWidget horizontalWidget = mBiggest;
                horizontalWidget.setHorizontalChainStyle(mHorizontalStyle);
                int padding = mPaddingLeft;
                if (chainIndex > 0) {
                    padding += mHorizontalGap;
                }
                if (isInRtl) {
                    horizontalWidget.mRight.connect(mRight, padding);
                    if (isLastChain) {
                        horizontalWidget.mLeft.connect(mLeft, mPaddingRight);
                    }
                    if (chainIndex > 0) {
                        ConstraintAnchor left = mRight.mOwner.mLeft;
                        left.connect(horizontalWidget.mRight, 0);
                    }
                } else {
                    horizontalWidget.mLeft.connect(mLeft, padding);
                    if (isLastChain) {
                        horizontalWidget.mRight.connect(mRight, mPaddingRight);
                    }
                    if (chainIndex > 0) {
                        ConstraintAnchor right = mLeft.mOwner.mRight;
                        right.connect(horizontalWidget.mLeft, 0);
                    }
                }
                for (int i = 0; i < count; i++) {
                    if (mStartIndex + i >= mDisplayedWidgetsCount) {
                        break;
                    }
                    ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i];
                    if (widget == null) {
                        continue;
                    }
                    if (i == 0) {
                        widget.connect(widget.mTop, mTop, mPaddingTop);
                        int style = mVerticalStyle;
                        float bias = mVerticalBias;
                        if (mStartIndex == 0 && mFirstVerticalStyle != UNKNOWN) {
                            style = mFirstVerticalStyle;
                            bias = mFirstVerticalBias;
                        } else if (isLastChain && mLastVerticalStyle != UNKNOWN) {
                            style = mLastVerticalStyle;
                            bias = mLastVerticalBias;
                        }
                        widget.setVerticalChainStyle(style);
                        widget.setVerticalBiasPercent(bias);
                    }
                    if (i == count - 1) {
                        widget.connect(widget.mBottom, mBottom, mPaddingBottom);
                    }
                    if (previous != null) {
                        widget.mTop.connect(previous.mBottom, mVerticalGap);
                        if (i == firstVisible) {
                            widget.mTop.setGoneMargin(mPaddingTop);
                        }
                        previous.mBottom.connect(widget.mTop, 0);
                        if (i == lastVisible + 1) {
                            previous.mBottom.setGoneMargin(mPaddingBottom);
                        }
                    }
                    if (widget != horizontalWidget) {
                        if (isInRtl) {
                            switch (mHorizontalAlign) {
                                case HORIZONTAL_ALIGN_START: {
                                    widget.mRight.connect(horizontalWidget.mRight, 0);
                                }
                                break;
                                case HORIZONTAL_ALIGN_CENTER: {
                                    widget.mLeft.connect(horizontalWidget.mLeft, 0);
                                    widget.mRight.connect(horizontalWidget.mRight, 0);
                                }
                                break;
                                case HORIZONTAL_ALIGN_END: {
                                    widget.mLeft.connect(horizontalWidget.mLeft, 0);
                                }
                                break;
                            }
                        } else {
                            switch (mHorizontalAlign) {
                                case HORIZONTAL_ALIGN_START: {
                                    widget.mLeft.connect(horizontalWidget.mLeft, 0);
                                }
                                break;
                                case HORIZONTAL_ALIGN_CENTER: {
                                    if (singleChain) {
                                        widget.mLeft.connect(mLeft, mPaddingLeft);
                                        widget.mRight.connect(mRight, mPaddingRight);
                                    } else {
                                        widget.mLeft.connect(horizontalWidget.mLeft, 0);
                                        widget.mRight.connect(horizontalWidget.mRight, 0);
                                    }
                                }
                                break;
                                case HORIZONTAL_ALIGN_END: {
                                    widget.mRight.connect(horizontalWidget.mRight, 0);
                                }
                                break;
                            }
                        }
                    }
                    previous = widget;
                }
            }
        }

        public void measureMatchConstraints(int availableSpace) {
            if (mNbMatchConstraintsWidgets == 0) {
                return;
            }
            final int count = mCount;

            // that's completely incorrect and only works for spread with no weights?
            int widgetSize = availableSpace / mNbMatchConstraintsWidgets;
            for (int i = 0; i < count; i++) {
                if (mStartIndex + i >= mDisplayedWidgetsCount) {
                    break;
                }
                ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i];
                if (mOrientation == HORIZONTAL) {
                    if (widget != null && widget.getHorizontalDimensionBehaviour()
                            == DimensionBehaviour.MATCH_CONSTRAINT) {
                        if (widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD) {
                            measure(widget, DimensionBehaviour.FIXED, widgetSize,
                                    widget.getVerticalDimensionBehaviour(), widget.getHeight());
                        }
                    }
                } else {
                    if (widget != null && widget.getVerticalDimensionBehaviour()
                            == DimensionBehaviour.MATCH_CONSTRAINT) {
                        if (widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD) {
                            measure(widget, widget.getHorizontalDimensionBehaviour(),
                                    widget.getWidth(), DimensionBehaviour.FIXED, widgetSize);
                        }
                    }
                }
            }
            recomputeDimensions();
        }

        private void recomputeDimensions() {
            mWidth = 0;
            mHeight = 0;
            mBiggest = null;
            mBiggestDimension = 0;
            final int count = mCount;
            for (int i = 0; i < count; i++) {
                if (mStartIndex + i >= mDisplayedWidgetsCount) {
                    break;
                }
                ConstraintWidget widget = mDisplayedWidgets[mStartIndex + i];
                if (mOrientation == HORIZONTAL) {
                    int width = widget.getWidth();
                    int gap = mHorizontalGap;
                    if (widget.getVisibility() == GONE) {
                        gap = 0;
                    }
                    mWidth += width + gap;
                    int height = getWidgetHeight(widget, mMax);
                    if (mBiggest == null || mBiggestDimension < height) {
                        mBiggest = widget;
                        mBiggestDimension = height;
                        mHeight = height;
                    }
                } else {
                    int width = getWidgetWidth(widget, mMax);
                    int height = getWidgetHeight(widget, mMax);
                    int gap = mVerticalGap;
                    if (widget.getVisibility() == GONE) {
                        gap = 0;
                    }
                    mHeight += height + gap;
                    if (mBiggest == null || mBiggestDimension < width) {
                        mBiggest = widget;
                        mBiggestDimension = width;
                        mWidth = width;
                    }
                }
            }
        }

    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Measure Chain Wrap
    /////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Measure the virtual layout using a list of chains for the children
     *
     * @param widgets     list of widgets
     * @param orientation the layout orientation (horizontal or vertical)
     * @param max         the maximum available space
     * @param measured    output parameters -- will contain the resulting measure
     */
    private void measureChainWrap(ConstraintWidget[] widgets,
            int count,
            int orientation,
            int max,
            int[] measured) {
        if (count == 0) {
            return;
        }

        mChainList.clear();
        WidgetsList list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max);
        mChainList.add(list);

        int nbMatchConstraintsWidgets = 0;

        if (orientation == HORIZONTAL) {
            int width = 0;
            for (int i = 0; i < count; i++) {
                ConstraintWidget widget = widgets[i];
                int w = getWidgetWidth(widget, max);
                if (widget.getHorizontalDimensionBehaviour()
                        == DimensionBehaviour.MATCH_CONSTRAINT) {
                    nbMatchConstraintsWidgets++;
                }
                boolean doWrap = (width == max || (width + mHorizontalGap + w) > max)
                        && list.mBiggest != null;
                if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (i % mMaxElementsWrap == 0)) {
                    doWrap = true;
                }
                if (doWrap) {
                    width = w;
                    list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max);
                    list.setStartIndex(i);
                    mChainList.add(list);
                } else {
                    if (i > 0) {
                        width += mHorizontalGap + w;
                    } else {
                        width = w;
                    }
                }
                list.add(widget);
            }
        } else {
            int height = 0;
            for (int i = 0; i < count; i++) {
                ConstraintWidget widget = widgets[i];
                int h = getWidgetHeight(widget, max);
                if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) {
                    nbMatchConstraintsWidgets++;
                }
                boolean doWrap = (height == max || (height + mVerticalGap + h) > max)
                        && list.mBiggest != null;
                if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (i % mMaxElementsWrap == 0)) {
                    doWrap = true;
                }
                if (doWrap) {
                    height = h;
                    list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max);
                    list.setStartIndex(i);
                    mChainList.add(list);
                } else {
                    if (i > 0) {
                        height += mVerticalGap + h;
                    } else {
                        height = h;
                    }
                }
                list.add(widget);
            }
        }
        final int listCount = mChainList.size();

        ConstraintAnchor left = mLeft;
        ConstraintAnchor top = mTop;
        ConstraintAnchor right = mRight;
        ConstraintAnchor bottom = mBottom;

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int maxWidth = 0;
        int maxHeight = 0;

        boolean needInternalMeasure =
                getHorizontalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT
                        || getVerticalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT;

        if (nbMatchConstraintsWidgets > 0 && needInternalMeasure) {
            // we have to remeasure them.
            for (int i = 0; i < listCount; i++) {
                WidgetsList current = mChainList.get(i);
                if (orientation == HORIZONTAL) {
                    current.measureMatchConstraints(max - current.getWidth());
                } else {
                    current.measureMatchConstraints(max - current.getHeight());
                }
            }
        }

        for (int i = 0; i < listCount; i++) {
            WidgetsList current = mChainList.get(i);
            if (orientation == HORIZONTAL) {
                if (i < listCount - 1) {
                    WidgetsList next = mChainList.get(i + 1);
                    bottom = next.mBiggest.mTop;
                    paddingBottom = 0;
                } else {
                    bottom = mBottom;
                    paddingBottom = getPaddingBottom();
                }
                ConstraintAnchor currentBottom = current.mBiggest.mBottom;
                current.setup(orientation, left, top, right, bottom,
                        paddingLeft, paddingTop, paddingRight, paddingBottom, max);
                top = currentBottom;
                paddingTop = 0;
                maxWidth = Math.max(maxWidth, current.getWidth());
                maxHeight += current.getHeight();
                if (i > 0) {
                    maxHeight += mVerticalGap;
                }
            } else {
                if (i < listCount - 1) {
                    WidgetsList next = mChainList.get(i + 1);
                    right = next.mBiggest.mLeft;
                    paddingRight = 0;
                } else {
                    right = mRight;
                    paddingRight = getPaddingRight();
                }
                ConstraintAnchor currentRight = current.mBiggest.mRight;
                current.setup(orientation, left, top, right, bottom,
                        paddingLeft, paddingTop, paddingRight, paddingBottom, max);
                left = currentRight;
                paddingLeft = 0;
                maxWidth += current.getWidth();
                maxHeight = Math.max(maxHeight, current.getHeight());
                if (i > 0) {
                    maxWidth += mHorizontalGap;
                }
            }
        }
        measured[HORIZONTAL] = maxWidth;
        measured[VERTICAL] = maxHeight;
    }
    /////////////////////////////////////////////////////////////////////////////////////////////
    // Measure Chain Wrap new
    /////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Measure the virtual layout using a list of chains for the children in new "fixed way"
     *
     * @param widgets     list of widgets
     * @param orientation the layout orientation (horizontal or vertical)
     * @param max         the maximum available space
     * @param measured    output parameters -- will contain the resulting measure
     */
    private void measureChainWrap_new(ConstraintWidget[] widgets,
            int count,
            int orientation,
            int max,
            int[] measured) {
        if (count == 0) {
            return;
        }

        mChainList.clear();
        WidgetsList list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max);
        mChainList.add(list);

        int nbMatchConstraintsWidgets = 0;

        if (orientation == HORIZONTAL) {
            int width = 0;
            int col = 0;
            for (int i = 0; i < count; i++) {
                col++;
                ConstraintWidget widget = widgets[i];
                int w = getWidgetWidth(widget, max);
                if (widget.getHorizontalDimensionBehaviour()
                        == DimensionBehaviour.MATCH_CONSTRAINT) {
                    nbMatchConstraintsWidgets++;
                }
                boolean doWrap = (width == max || (width + mHorizontalGap + w) > max)
                        && list.mBiggest != null;
                if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (col > mMaxElementsWrap)) {
                    doWrap = true;
                }
                if (doWrap) {
                    col = 1;
                    width = w;
                    list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max);
                    list.setStartIndex(i);
                    mChainList.add(list);
                } else {
                    if (i > 0) {
                        width += mHorizontalGap + w;
                    } else {
                        width = w;
                    }
                }
                list.add(widget);
            }
        } else {
            int height = 0;
            int row = 0;
            for (int i = 0; i < count; i++) {
                row++;
                ConstraintWidget widget = widgets[i];
                int h = getWidgetHeight(widget, max);
                if (widget.getVerticalDimensionBehaviour() == DimensionBehaviour.MATCH_CONSTRAINT) {
                    nbMatchConstraintsWidgets++;
                }
                boolean doWrap = (height == max || (height + mVerticalGap + h) > max)
                        && list.mBiggest != null;
                if (!doWrap && i > 0 && mMaxElementsWrap > 0 && (row > mMaxElementsWrap)) {
                    doWrap = true;
                }
                if (doWrap) {
                    row = 1;
                    height = h;
                    list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max);
                    list.setStartIndex(i);
                    mChainList.add(list);
                } else {
                    if (i > 0) {
                        height += mVerticalGap + h;
                    } else {
                        height = h;
                    }
                }
                list.add(widget);
            }
        }
        final int listCount = mChainList.size();

        ConstraintAnchor left = mLeft;
        ConstraintAnchor top = mTop;
        ConstraintAnchor right = mRight;
        ConstraintAnchor bottom = mBottom;

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int maxWidth = 0;
        int maxHeight = 0;

        boolean needInternalMeasure =
                getHorizontalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT
                        || getVerticalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT;

        if (nbMatchConstraintsWidgets > 0 && needInternalMeasure) {
            // we have to remeasure them.
            for (int i = 0; i < listCount; i++) {
                WidgetsList current = mChainList.get(i);
                if (orientation == HORIZONTAL) {
                    current.measureMatchConstraints(max - current.getWidth());
                } else {
                    current.measureMatchConstraints(max - current.getHeight());
                }
            }
        }

        for (int i = 0; i < listCount; i++) {
            WidgetsList current = mChainList.get(i);
            if (orientation == HORIZONTAL) {
                if (i < listCount - 1) {
                    WidgetsList next = mChainList.get(i + 1);
                    bottom = next.mBiggest.mTop;
                    paddingBottom = 0;
                } else {
                    bottom = mBottom;
                    paddingBottom = getPaddingBottom();
                }
                ConstraintAnchor currentBottom = current.mBiggest.mBottom;
                current.setup(orientation, left, top, right, bottom,
                        paddingLeft, paddingTop, paddingRight, paddingBottom, max);
                top = currentBottom;
                paddingTop = 0;
                maxWidth = Math.max(maxWidth, current.getWidth());
                maxHeight += current.getHeight();
                if (i > 0) {
                    maxHeight += mVerticalGap;
                }
            } else {
                if (i < listCount - 1) {
                    WidgetsList next = mChainList.get(i + 1);
                    right = next.mBiggest.mLeft;
                    paddingRight = 0;
                } else {
                    right = mRight;
                    paddingRight = getPaddingRight();
                }
                ConstraintAnchor currentRight = current.mBiggest.mRight;
                current.setup(orientation, left, top, right, bottom,
                        paddingLeft, paddingTop, paddingRight, paddingBottom, max);
                left = currentRight;
                paddingLeft = 0;
                maxWidth += current.getWidth();
                maxHeight = Math.max(maxHeight, current.getHeight());
                if (i > 0) {
                    maxWidth += mHorizontalGap;
                }
            }
        }
        measured[HORIZONTAL] = maxWidth;
        measured[VERTICAL] = maxHeight;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Measure No Wrap
    /////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Measure the virtual layout using a single chain for the children
     *
     * @param widgets     list of widgets
     * @param orientation the layout orientation (horizontal or vertical)
     * @param max         the maximum available space
     * @param measured    output parameters -- will contain the resulting measure
     */
    private void measureNoWrap(ConstraintWidget[] widgets,
            int count,
            int orientation,
            int max,
            int[] measured) {
        if (count == 0) {
            return;
        }
        WidgetsList list = null;
        if (mChainList.size() == 0) {
            list = new WidgetsList(orientation, mLeft, mTop, mRight, mBottom, max);
            mChainList.add(list);
        } else {
            list = mChainList.get(0);
            list.clear();
            list.setup(orientation, mLeft, mTop, mRight, mBottom,
                    getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom(), max);
        }

        for (int i = 0; i < count; i++) {
            ConstraintWidget widget = widgets[i];
            list.add(widget);
        }

        measured[HORIZONTAL] = list.getWidth();
        measured[VERTICAL] = list.getHeight();
    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Measure Aligned
    /////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Measure the virtual layout arranging the children in a regular grid
     *
     * @param widgets     list of widgets
     * @param orientation the layout orientation (horizontal or vertical)
     * @param max         the maximum available space
     * @param measured    output parameters -- will contain the resulting measure
     */
    private void measureAligned(ConstraintWidget[] widgets,
            int count,
            int orientation,
            int max,
            int[] measured) {
        boolean done = false;
        int rows = 0;
        int cols = 0;

        if (orientation == HORIZONTAL) {
            cols = mMaxElementsWrap;
            if (cols <= 0) {
                // let's initialize cols with an acceptable value
                int w = 0;
                cols = 0;
                for (int i = 0; i < count; i++) {
                    if (i > 0) {
                        w += mHorizontalGap;
                    }
                    ConstraintWidget widget = widgets[i];
                    if (widget == null) {
                        continue;
                    }
                    w += getWidgetWidth(widget, max);
                    if (w > max) {
                        break;
                    }
                    cols++;
                }
            }
        } else {
            rows = mMaxElementsWrap;
            if (rows <= 0) {
                // let's initialize rows with an acceptable value
                int h = 0;
                rows = 0;
                for (int i = 0; i < count; i++) {
                    if (i > 0) {
                        h += mVerticalGap;
                    }
                    ConstraintWidget widget = widgets[i];
                    if (widget == null) {
                        continue;
                    }
                    h += getWidgetHeight(widget, max);
                    if (h > max) {
                        break;
                    }
                    rows++;
                }
            }
        }

        if (mAlignedDimensions == null) {
            mAlignedDimensions = new int[2];
        }

        if ((rows == 0 && orientation == VERTICAL)
                || (cols == 0 && orientation == HORIZONTAL)) {
            done = true;
        }

        while (!done) {
            // get a num of rows (or cols)
            // get for each row and cols the chain of biggest elements

            if (orientation == HORIZONTAL) {
                rows = (int) Math.ceil(count / (float) cols);
            } else {
                cols = (int) Math.ceil(count / (float) rows);
            }

            if (mAlignedBiggestElementsInCols == null
                    || mAlignedBiggestElementsInCols.length < cols) {
                mAlignedBiggestElementsInCols = new ConstraintWidget[cols];
            } else {
                Arrays.fill(mAlignedBiggestElementsInCols, null);
            }
            if (mAlignedBiggestElementsInRows == null
                    || mAlignedBiggestElementsInRows.length < rows) {
                mAlignedBiggestElementsInRows = new ConstraintWidget[rows];
            } else {
                Arrays.fill(mAlignedBiggestElementsInRows, null);
            }

            for (int i = 0; i < cols; i++) {
                for (int j = 0; j < rows; j++) {
                    int index = j * cols + i;
                    if (orientation == VERTICAL) {
                        index = i * rows + j;
                    }
                    if (index >= widgets.length) {
                        continue;
                    }
                    ConstraintWidget widget = widgets[index];
                    if (widget == null) {
                        continue;
                    }
                    int w = getWidgetWidth(widget, max);
                    if (mAlignedBiggestElementsInCols[i] == null
                            || mAlignedBiggestElementsInCols[i].getWidth() < w) {
                        mAlignedBiggestElementsInCols[i] = widget;
                    }
                    int h = getWidgetHeight(widget, max);
                    if (mAlignedBiggestElementsInRows[j] == null
                            || mAlignedBiggestElementsInRows[j].getHeight() < h) {
                        mAlignedBiggestElementsInRows[j] = widget;
                    }
                }
            }

            int w = 0;
            for (int i = 0; i < cols; i++) {
                ConstraintWidget widget = mAlignedBiggestElementsInCols[i];
                if (widget != null) {
                    if (i > 0) {
                        w += mHorizontalGap;
                    }
                    w += getWidgetWidth(widget, max);
                }
            }
            int h = 0;
            for (int j = 0; j < rows; j++) {
                ConstraintWidget widget = mAlignedBiggestElementsInRows[j];
                if (widget != null) {
                    if (j > 0) {
                        h += mVerticalGap;
                    }
                    h += getWidgetHeight(widget, max);
                }
            }
            measured[HORIZONTAL] = w;
            measured[VERTICAL] = h;

            if (orientation == HORIZONTAL) {
                if (w > max) {
                    if (cols > 1) {
                        cols--;
                    } else {
                        done = true;
                    }
                } else {
                    done = true;
                }
            } else { // VERTICAL
                if (h > max) {
                    if (rows > 1) {
                        rows--;
                    } else {
                        done = true;
                    }
                } else {
                    done = true;
                }
            }
        }
        mAlignedDimensions[HORIZONTAL] = cols;
        mAlignedDimensions[VERTICAL] = rows;
    }

    private void createAlignedConstraints(boolean isInRtl) {
        if (mAlignedDimensions == null
                || mAlignedBiggestElementsInCols == null
                || mAlignedBiggestElementsInRows == null) {
            return;
        }

        for (int i = 0; i < mDisplayedWidgetsCount; i++) {
            ConstraintWidget widget = mDisplayedWidgets[i];
            widget.resetAnchors();
        }

        int cols = mAlignedDimensions[HORIZONTAL];
        int rows = mAlignedDimensions[VERTICAL];

        ConstraintWidget previous = null;
        float horizontalBias = mHorizontalBias;
        for (int i = 0; i < cols; i++) {
            int index = i;
            if (isInRtl) {
                index = cols - i - 1;
                horizontalBias = 1 - mHorizontalBias;
            }
            ConstraintWidget widget = mAlignedBiggestElementsInCols[index];
            if (widget == null || widget.getVisibility() == GONE) {
                continue;
            }
            if (i == 0) {
                widget.connect(widget.mLeft, mLeft, getPaddingLeft());
                widget.setHorizontalChainStyle(mHorizontalStyle);
                widget.setHorizontalBiasPercent(horizontalBias);
            }
            if (i == cols - 1) {
                widget.connect(widget.mRight, mRight, getPaddingRight());
            }
            if (i > 0 && previous != null) {
                widget.connect(widget.mLeft, previous.mRight, mHorizontalGap);
                previous.connect(previous.mRight, widget.mLeft, 0);
            }
            previous = widget;
        }
        for (int j = 0; j < rows; j++) {
            ConstraintWidget widget = mAlignedBiggestElementsInRows[j];
            if (widget == null || widget.getVisibility() == GONE) {
                continue;
            }
            if (j == 0) {
                widget.connect(widget.mTop, mTop, getPaddingTop());
                widget.setVerticalChainStyle(mVerticalStyle);
                widget.setVerticalBiasPercent(mVerticalBias);
            }
            if (j == rows - 1) {
                widget.connect(widget.mBottom, mBottom, getPaddingBottom());
            }
            if (j > 0 && previous != null) {
                widget.connect(widget.mTop, previous.mBottom, mVerticalGap);
                previous.connect(previous.mBottom, widget.mTop, 0);
            }
            previous = widget;
        }

        for (int i = 0; i < cols; i++) {
            for (int j = 0; j < rows; j++) {
                int index = j * cols + i;
                if (mOrientation == VERTICAL) {
                    index = i * rows + j;
                }
                if (index >= mDisplayedWidgets.length) {
                    continue;
                }
                ConstraintWidget widget = mDisplayedWidgets[index];
                if (widget == null || widget.getVisibility() == GONE) {
                    continue;
                }
                ConstraintWidget biggestInCol = mAlignedBiggestElementsInCols[i];
                ConstraintWidget biggestInRow = mAlignedBiggestElementsInRows[j];
                if (widget != biggestInCol) {
                    widget.connect(widget.mLeft, biggestInCol.mLeft, 0);
                    widget.connect(widget.mRight, biggestInCol.mRight, 0);
                }
                if (widget != biggestInRow) {
                    widget.connect(widget.mTop, biggestInRow.mTop, 0);
                    widget.connect(widget.mBottom, biggestInRow.mBottom, 0);
                }
            }
        }
    }

    /////////////////////////////////////////////////////////////////////////////////////////////
    // Add constraints to solver
    /////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Add this widget to the solver
     *
     * @param system   the solver we want to add the widget to
     * @param optimize true if {@link Optimizer#OPTIMIZATION_GRAPH} is on
     */
    @Override
    public void addToSolver(LinearSystem system, boolean optimize) {
        super.addToSolver(system, optimize);

        boolean isInRtl = getParent() != null && ((ConstraintWidgetContainer) getParent()).isRtl();
        switch (mWrapMode) {
            case WRAP_CHAIN: {
                final int count = mChainList.size();
                for (int i = 0; i < count; i++) {
                    WidgetsList list = mChainList.get(i);
                    list.createConstraints(isInRtl, i, i == count - 1);
                }
            }
            break;
            case WRAP_NONE: {
                if (mChainList.size() > 0) {
                    WidgetsList list = mChainList.get(0);
                    list.createConstraints(isInRtl, 0, true);
                }
            }
            break;
            case WRAP_ALIGNED: {
                createAlignedConstraints(isInRtl);
            }
            break;
            case WRAP_CHAIN_NEW: {
                final int count = mChainList.size();
                for (int i = 0; i < count; i++) {
                    WidgetsList list = mChainList.get(i);
                    list.createConstraints(isInRtl, i, i == count - 1);
                }
            }
            break;
        }
        needsCallbackFromSolver(false);
    }
}