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
A container of ConstraintWidget that can layout its children
Summary
Fields |
---|
public DependencyGraph | mDependencyGraph |
public boolean | mGroupsWrapOptimized |
public int | mHorizontalChainsSize |
public boolean | mHorizontalWrapOptimized |
public BasicMeasure.Measure | mMeasure |
protected BasicMeasure.Measurer | mMeasurer |
public Metrics | mMetrics |
public boolean | mSkipSolver |
protected LinearSystem | mSystem |
public int | mVerticalChainsSize |
public boolean | mVerticalWrapOptimized |
public int | mWrapFixedHeight |
public int | mWrapFixedWidth |
from WidgetContainer | mChildren |
from ConstraintWidget | ANCHOR_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 |
Methods |
---|
public boolean | addChildrenToSolver(LinearSystem system)
Add this widget to the solver |
public void | addHorizontalWrapMaxVariable(ConstraintAnchor right)
|
public void | addHorizontalWrapMinVariable(ConstraintAnchor left)
|
public void | defineTerminalWidgets()
|
public boolean | directMeasure(boolean optimizeWrap)
|
public boolean | directMeasureSetup(boolean optimizeWrap)
|
public boolean | directMeasureWithOrientation(boolean optimizeWrap, int orientation)
|
public void | fillMetrics(Metrics metrics)
|
public java.util.ArrayList<Guideline> | getHorizontalGuidelines()
Accessor to the horizontal guidelines contained in the table. |
public BasicMeasure.Measurer | getMeasurer()
|
public int | getOptimizationLevel()
Returns the current optimization level |
public void | getSceneString(java.lang.StringBuilder ret)
|
public LinearSystem | getSystem()
|
public java.lang.String | getType()
Specify the xml type for the container |
public java.util.ArrayList<Guideline> | getVerticalGuidelines()
Accessor to the vertical guidelines contained in the table. |
public boolean | handlesInternalConstraints()
Indicates if the container knows how to layout its content on its own |
public void | invalidateGraph()
Invalidate the graph of constraints |
public void | invalidateMeasures()
Invalidate the widgets measures |
public boolean | isHeightMeasuredTooSmall()
Return true if the height given is too small for the content laid out |
public boolean | isRtl()
Returns the rtl status. |
public boolean | isWidthMeasuredTooSmall()
Return true if the width given is too small for the content laid out |
public void | layout()
Layout the tree of widgets |
public static boolean | measure(int level, ConstraintWidget widget, BasicMeasure.Measurer measurer, BasicMeasure.Measure measure, int measureStrategy)
|
public long | measure(int optimizationLevel, int widthMode, int widthSize, int heightMode, int heightSize, int lastMeasureWidth, int lastMeasureHeight, int paddingX, int paddingY)
Measure the layout |
public boolean | optimizeFor(int feature)
Returns true if the given feature should be optimized |
public void | reset()
|
public void | setMeasurer(BasicMeasure.Measurer measurer)
|
public void | setOptimizationLevel(int value)
Resolves the system directly when possible |
public void | setPadding(int left, int top, int right, int bottom)
Set the padding on this container. |
public void | setPass(int pass)
Keep track of the # of passes |
public void | setRtl(boolean isRtl)
Set the rtl status. |
public boolean | updateChildrenFromSolver(LinearSystem system, boolean[] flags[])
Update the frame of the layout and its children from the solver |
public void | updateFromRuns(boolean updateHorizontal, boolean updateVertical)
|
public void | updateHierarchy()
|
from WidgetContainer | add, add, getChildren, getRootConstraintContainer, remove, removeAllChildren, resetSolverVariables, setOffset |
from ConstraintWidget | addChildrenToSolverByDependency, addToSolver, allowedInBarrier, connect, connect, connect, connectCircularConstraint, copy, 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, getTop, 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, resetAllConstraints, resetAnchor, resetAnchors, resetFinalResolution, 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, setOrigin, setParent, setType, setupDimensionRatio, setVerticalBiasPercent, setVerticalChainStyle, setVerticalDimension, setVerticalDimensionBehaviour, setVerticalMatchStyle, setVerticalWeight, setVisibility, setWidth, setWidthWrapContent, setWrapBehaviorInParent, setX, setY, toString, updateFromSolver |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
Fields
public int
mHorizontalChainsSizepublic int
mVerticalChainsSizepublic boolean
mGroupsWrapOptimizedpublic boolean
mHorizontalWrapOptimizedpublic boolean
mVerticalWrapOptimizedpublic int
mWrapFixedWidthpublic int
mWrapFixedHeightpublic boolean
mSkipSolverConstructors
public
ConstraintWidgetContainer()
Default constructor
public
ConstraintWidgetContainer(int x, int y, int width, int height)
Constructor
Parameters:
x: x position
y: y position
width: width of the layout
height: height of the layout
public
ConstraintWidgetContainer(int width, int height)
Constructor
Parameters:
width: width of the layout
height: height of the layout
public
ConstraintWidgetContainer(java.lang.String debugName, int width, int height)
Methods
public void
invalidateGraph()
Invalidate the graph of constraints
public void
invalidateMeasures()
Invalidate the widgets measures
public boolean
directMeasure(boolean optimizeWrap)
public boolean
directMeasureSetup(boolean optimizeWrap)
public boolean
directMeasureWithOrientation(boolean optimizeWrap, int orientation)
public void
defineTerminalWidgets()
public long
measure(int optimizationLevel, int widthMode, int widthSize, int heightMode, int heightSize, int lastMeasureWidth, int lastMeasureHeight, int paddingX, int paddingY)
Measure the layout
public void
updateHierarchy()
public void
fillMetrics(
Metrics metrics)
public void
setOptimizationLevel(int value)
Resolves the system directly when possible
Parameters:
value: optimization level
public int
getOptimizationLevel()
Returns the current optimization level
public boolean
optimizeFor(int feature)
Returns true if the given feature should be optimized
public java.lang.String
getType()
Specify the xml type for the container
public boolean
isWidthMeasuredTooSmall()
Return true if the width given is too small for the content laid out
public boolean
isHeightMeasuredTooSmall()
Return true if the height given is too small for the content laid out
Add this widget to the solver
Parameters:
system: the solver we want to add the widget to
public boolean
updateChildrenFromSolver(
LinearSystem system, boolean[] flags[])
Update the frame of the layout and its children from the solver
Parameters:
system: the solver we get the values from.
public void
updateFromRuns(boolean updateHorizontal, boolean updateVertical)
public void
setPadding(int left, int top, int right, int bottom)
Set the padding on this container. It will apply to the position of the children.
Parameters:
left: left padding
top: top padding
right: right padding
bottom: bottom padding
public void
setRtl(boolean isRtl)
Set the rtl status. This has implications for Chains.
Parameters:
isRtl: true if we are in RTL.
Returns the rtl status.
Returns:
true if in RTL, false otherwise.
Layout the tree of widgets
public boolean
handlesInternalConstraints()
Indicates if the container knows how to layout its content on its own
Returns:
true if the container does the layout, false otherwise
public java.util.ArrayList<Guideline>
getVerticalGuidelines()
Accessor to the vertical guidelines contained in the table.
Returns:
array of guidelines
public java.util.ArrayList<Guideline>
getHorizontalGuidelines()
Accessor to the horizontal guidelines contained in the table.
Returns:
array of guidelines
public void
setPass(int pass)
Keep track of the # of passes
public void
getSceneString(java.lang.StringBuilder ret)
Source
/*
* Copyright (C) 2015 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.LinearSystem.FULL_DEBUG;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.FIXED;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
import androidx.constraintlayout.core.LinearSystem;
import androidx.constraintlayout.core.Metrics;
import androidx.constraintlayout.core.SolverVariable;
import androidx.constraintlayout.core.widgets.analyzer.BasicMeasure;
import androidx.constraintlayout.core.widgets.analyzer.DependencyGraph;
import androidx.constraintlayout.core.widgets.analyzer.Direct;
import androidx.constraintlayout.core.widgets.analyzer.Grouping;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/**
* A container of ConstraintWidget that can layout its children
*/
public class ConstraintWidgetContainer extends WidgetContainer {
private static final int MAX_ITERATIONS = 8;
private static final boolean DEBUG = FULL_DEBUG;
private static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_GRAPH = false;
BasicMeasure mBasicMeasureSolver = new BasicMeasure(this);
////////////////////////////////////////////////////////////////////////////////////////////////
// Graph measures
////////////////////////////////////////////////////////////////////////////////////////////////
public DependencyGraph mDependencyGraph = new DependencyGraph(this);
private int mPass; // number of layout passes
/**
* Invalidate the graph of constraints
*/
public void invalidateGraph() {
mDependencyGraph.invalidateGraph();
}
/**
* Invalidate the widgets measures
*/
public void invalidateMeasures() {
mDependencyGraph.invalidateMeasures();
}
// @TODO: add description
public boolean directMeasure(boolean optimizeWrap) {
return mDependencyGraph.directMeasure(optimizeWrap);
// int paddingLeft = getX();
// int paddingTop = getY();
// if (mDependencyGraph.directMeasureSetup(optimizeWrap)) {
// mDependencyGraph.measureWidgets();
// boolean allResolved =
// mDependencyGraph.directMeasureWithOrientation(optimizeWrap, HORIZONTAL);
// allResolved &= mDependencyGraph.directMeasureWithOrientation(optimizeWrap, VERTICAL);
// for (ConstraintWidget child : mChildren) {
// child.setDrawX(child.getDrawX() + paddingLeft);
// child.setDrawY(child.getDrawY() + paddingTop);
// }
// setX(paddingLeft);
// setY(paddingTop);
// return allResolved;
// }
// return false;
}
// @TODO: add description
public boolean directMeasureSetup(boolean optimizeWrap) {
return mDependencyGraph.directMeasureSetup(optimizeWrap);
}
// @TODO: add description
public boolean directMeasureWithOrientation(boolean optimizeWrap, int orientation) {
return mDependencyGraph.directMeasureWithOrientation(optimizeWrap, orientation);
}
// @TODO: add description
public void defineTerminalWidgets() {
mDependencyGraph.defineTerminalWidgets(getHorizontalDimensionBehaviour(),
getVerticalDimensionBehaviour());
}
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Measure the layout
*/
public long measure(int optimizationLevel, int widthMode, int widthSize,
int heightMode, int heightSize, int lastMeasureWidth,
int lastMeasureHeight, int paddingX, int paddingY) {
mPaddingLeft = paddingX;
mPaddingTop = paddingY;
return mBasicMeasureSolver.solverMeasure(this, optimizationLevel, paddingX, paddingY,
widthMode, widthSize, heightMode, heightSize,
lastMeasureWidth, lastMeasureHeight);
}
// @TODO: add description
public void updateHierarchy() {
mBasicMeasureSolver.updateHierarchy(this);
}
protected BasicMeasure.Measurer mMeasurer = null;
// @TODO: add description
public void setMeasurer(BasicMeasure.Measurer measurer) {
mMeasurer = measurer;
mDependencyGraph.setMeasurer(measurer);
}
public BasicMeasure.Measurer getMeasurer() {
return mMeasurer;
}
private boolean mIsRtl = false;
public Metrics mMetrics;
// @TODO: add description
public void fillMetrics(Metrics metrics) {
mMetrics = metrics;
mSystem.fillMetrics(metrics);
}
protected LinearSystem mSystem = new LinearSystem();
int mPaddingLeft;
int mPaddingTop;
int mPaddingRight;
int mPaddingBottom;
public int mHorizontalChainsSize = 0;
public int mVerticalChainsSize = 0;
ChainHead[] mVerticalChainsArray = new ChainHead[4];
ChainHead[] mHorizontalChainsArray = new ChainHead[4];
public boolean mGroupsWrapOptimized = false;
public boolean mHorizontalWrapOptimized = false;
public boolean mVerticalWrapOptimized = false;
public int mWrapFixedWidth = 0;
public int mWrapFixedHeight = 0;
private int mOptimizationLevel = Optimizer.OPTIMIZATION_STANDARD;
public boolean mSkipSolver = false;
private boolean mWidthMeasuredTooSmall = false;
private boolean mHeightMeasuredTooSmall = false;
/*-----------------------------------------------------------------------*/
// Construction
/*-----------------------------------------------------------------------*/
/**
* Default constructor
*/
public ConstraintWidgetContainer() {
}
/**
* Constructor
*
* @param x x position
* @param y y position
* @param width width of the layout
* @param height height of the layout
*/
public ConstraintWidgetContainer(int x, int y, int width, int height) {
super(x, y, width, height);
}
/**
* Constructor
*
* @param width width of the layout
* @param height height of the layout
*/
public ConstraintWidgetContainer(int width, int height) {
super(width, height);
}
public ConstraintWidgetContainer(String debugName, int width, int height) {
super(width, height);
setDebugName(debugName);
}
/**
* Resolves the system directly when possible
*
* @param value optimization level
*/
public void setOptimizationLevel(int value) {
mOptimizationLevel = value;
mSystem.USE_DEPENDENCY_ORDERING = optimizeFor(Optimizer.OPTIMIZATION_DEPENDENCY_ORDERING);
}
/**
* Returns the current optimization level
*/
public int getOptimizationLevel() {
return mOptimizationLevel;
}
/**
* Returns true if the given feature should be optimized
*/
public boolean optimizeFor(int feature) {
return (mOptimizationLevel & feature) == feature;
}
/**
* Specify the xml type for the container
*/
@Override
public String getType() {
return "ConstraintLayout";
}
@Override
public void reset() {
mSystem.reset();
mPaddingLeft = 0;
mPaddingRight = 0;
mPaddingTop = 0;
mPaddingBottom = 0;
mSkipSolver = false;
super.reset();
}
/**
* Return true if the width given is too small for the content laid out
*/
public boolean isWidthMeasuredTooSmall() {
return mWidthMeasuredTooSmall;
}
/**
* Return true if the height given is too small for the content laid out
*/
public boolean isHeightMeasuredTooSmall() {
return mHeightMeasuredTooSmall;
}
int mDebugSolverPassCount = 0;
private WeakReference<ConstraintAnchor> mVerticalWrapMin = null;
private WeakReference<ConstraintAnchor> mHorizontalWrapMin = null;
private WeakReference<ConstraintAnchor> mVerticalWrapMax = null;
private WeakReference<ConstraintAnchor> mHorizontalWrapMax = null;
void addVerticalWrapMinVariable(ConstraintAnchor top) {
if (mVerticalWrapMin == null || mVerticalWrapMin.get() == null
|| top.getFinalValue() > mVerticalWrapMin.get().getFinalValue()) {
mVerticalWrapMin = new WeakReference<>(top);
}
}
// @TODO: add description
public void addHorizontalWrapMinVariable(ConstraintAnchor left) {
if (mHorizontalWrapMin == null || mHorizontalWrapMin.get() == null
|| left.getFinalValue() > mHorizontalWrapMin.get().getFinalValue()) {
mHorizontalWrapMin = new WeakReference<>(left);
}
}
void addVerticalWrapMaxVariable(ConstraintAnchor bottom) {
if (mVerticalWrapMax == null || mVerticalWrapMax.get() == null
|| bottom.getFinalValue() > mVerticalWrapMax.get().getFinalValue()) {
mVerticalWrapMax = new WeakReference<>(bottom);
}
}
// @TODO: add description
public void addHorizontalWrapMaxVariable(ConstraintAnchor right) {
if (mHorizontalWrapMax == null || mHorizontalWrapMax.get() == null
|| right.getFinalValue() > mHorizontalWrapMax.get().getFinalValue()) {
mHorizontalWrapMax = new WeakReference<>(right);
}
}
private void addMinWrap(ConstraintAnchor constraintAnchor, SolverVariable parentMin) {
SolverVariable variable = mSystem.createObjectVariable(constraintAnchor);
int wrapStrength = SolverVariable.STRENGTH_EQUALITY;
mSystem.addGreaterThan(variable, parentMin, 0, wrapStrength);
}
private void addMaxWrap(ConstraintAnchor constraintAnchor, SolverVariable parentMax) {
SolverVariable variable = mSystem.createObjectVariable(constraintAnchor);
int wrapStrength = SolverVariable.STRENGTH_EQUALITY;
mSystem.addGreaterThan(parentMax, variable, 0, wrapStrength);
}
HashSet<ConstraintWidget> mWidgetsToAdd = new HashSet<>();
/**
* Add this widget to the solver
*
* @param system the solver we want to add the widget to
*/
public boolean addChildrenToSolver(LinearSystem system) {
if (DEBUG) {
System.out.println("\n#######################################");
System.out.println("## ADD CHILDREN TO SOLVER (" + mDebugSolverPassCount + ") ##");
System.out.println("#######################################\n");
mDebugSolverPassCount++;
}
boolean optimize = optimizeFor(Optimizer.OPTIMIZATION_GRAPH);
addToSolver(system, optimize);
final int count = mChildren.size();
boolean hasBarriers = false;
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
widget.setInBarrier(HORIZONTAL, false);
widget.setInBarrier(VERTICAL, false);
if (widget instanceof Barrier) {
hasBarriers = true;
}
}
if (hasBarriers) {
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
if (widget instanceof Barrier) {
((Barrier) widget).markWidgets();
}
}
}
mWidgetsToAdd.clear();
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
if (widget.addFirst()) {
if (widget instanceof VirtualLayout) {
mWidgetsToAdd.add(widget);
} else {
widget.addToSolver(system, optimize);
}
}
}
// If we have virtual layouts, we need to add them to the solver in the correct
// order (in case they reference one another)
while (mWidgetsToAdd.size() > 0) {
int numLayouts = mWidgetsToAdd.size();
VirtualLayout layout = null;
for (ConstraintWidget widget : mWidgetsToAdd) {
layout = (VirtualLayout) widget;
// we'll go through the virtual layouts that references others first, to give
// them a shot at setting their constraints.
if (layout.contains(mWidgetsToAdd)) {
layout.addToSolver(system, optimize);
mWidgetsToAdd.remove(layout);
break;
}
}
if (numLayouts == mWidgetsToAdd.size()) {
// looks we didn't find anymore dependency, let's add everything.
for (ConstraintWidget widget : mWidgetsToAdd) {
widget.addToSolver(system, optimize);
}
mWidgetsToAdd.clear();
}
}
if (LinearSystem.USE_DEPENDENCY_ORDERING) {
HashSet<ConstraintWidget> widgetsToAdd = new HashSet<>();
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
if (!widget.addFirst()) {
widgetsToAdd.add(widget);
}
}
int orientation = VERTICAL;
if (getHorizontalDimensionBehaviour() == WRAP_CONTENT) {
orientation = HORIZONTAL;
}
addChildrenToSolverByDependency(this, system, widgetsToAdd, orientation, false);
for (ConstraintWidget widget : widgetsToAdd) {
Optimizer.checkMatchParent(this, system, widget);
widget.addToSolver(system, optimize);
}
} else {
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
if (widget instanceof ConstraintWidgetContainer) {
DimensionBehaviour horizontalBehaviour =
widget.mListDimensionBehaviors[DIMENSION_HORIZONTAL];
DimensionBehaviour verticalBehaviour =
widget.mListDimensionBehaviors[DIMENSION_VERTICAL];
if (horizontalBehaviour == WRAP_CONTENT) {
widget.setHorizontalDimensionBehaviour(FIXED);
}
if (verticalBehaviour == WRAP_CONTENT) {
widget.setVerticalDimensionBehaviour(FIXED);
}
widget.addToSolver(system, optimize);
if (horizontalBehaviour == WRAP_CONTENT) {
widget.setHorizontalDimensionBehaviour(horizontalBehaviour);
}
if (verticalBehaviour == WRAP_CONTENT) {
widget.setVerticalDimensionBehaviour(verticalBehaviour);
}
} else {
Optimizer.checkMatchParent(this, system, widget);
if (!widget.addFirst()) {
widget.addToSolver(system, optimize);
}
}
}
}
if (mHorizontalChainsSize > 0) {
Chain.applyChainConstraints(this, system, null, HORIZONTAL);
}
if (mVerticalChainsSize > 0) {
Chain.applyChainConstraints(this, system, null, VERTICAL);
}
return true;
}
/**
* Update the frame of the layout and its children from the solver
*
* @param system the solver we get the values from.
*/
public boolean updateChildrenFromSolver(LinearSystem system, boolean[] flags) {
flags[Optimizer.FLAG_RECOMPUTE_BOUNDS] = false;
boolean optimize = optimizeFor(Optimizer.OPTIMIZATION_GRAPH);
updateFromSolver(system, optimize);
final int count = mChildren.size();
boolean hasOverride = false;
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
widget.updateFromSolver(system, optimize);
if (widget.hasDimensionOverride()) {
hasOverride = true;
}
}
return hasOverride;
}
@Override
public void updateFromRuns(boolean updateHorizontal, boolean updateVertical) {
super.updateFromRuns(updateHorizontal, updateVertical);
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
widget.updateFromRuns(updateHorizontal, updateVertical);
}
}
/**
* Set the padding on this container. It will apply to the position of the children.
*
* @param left left padding
* @param top top padding
* @param right right padding
* @param bottom bottom padding
*/
public void setPadding(int left, int top, int right, int bottom) {
mPaddingLeft = left;
mPaddingTop = top;
mPaddingRight = right;
mPaddingBottom = bottom;
}
/**
* Set the rtl status. This has implications for Chains.
*
* @param isRtl true if we are in RTL.
*/
public void setRtl(boolean isRtl) {
mIsRtl = isRtl;
}
/**
* Returns the rtl status.
*
* @return true if in RTL, false otherwise.
*/
public boolean isRtl() {
return mIsRtl;
}
/*-----------------------------------------------------------------------*/
// Overloaded methods from ConstraintWidget
/*-----------------------------------------------------------------------*/
public BasicMeasure.Measure mMeasure = new BasicMeasure.Measure();
// @TODO: add description
public static boolean measure(int level,
ConstraintWidget widget,
BasicMeasure.Measurer measurer,
BasicMeasure.Measure measure,
int measureStrategy) {
if (DEBUG) {
System.out.println(Direct.ls(level) + "(M) call to measure " + widget.getDebugName());
}
if (measurer == null) {
return false;
}
if (widget.getVisibility() == GONE
|| widget instanceof Guideline
|| widget instanceof Barrier) {
if (DEBUG) {
System.out.println(Direct.ls(level)
+ "(M) no measure needed for " + widget.getDebugName());
}
measure.measuredWidth = 0;
measure.measuredHeight = 0;
return false;
}
measure.horizontalBehavior = widget.getHorizontalDimensionBehaviour();
measure.verticalBehavior = widget.getVerticalDimensionBehaviour();
measure.horizontalDimension = widget.getWidth();
measure.verticalDimension = widget.getHeight();
measure.measuredNeedsSolverPass = false;
measure.measureStrategy = measureStrategy;
boolean horizontalMatchConstraints =
(measure.horizontalBehavior == DimensionBehaviour.MATCH_CONSTRAINT);
boolean verticalMatchConstraints =
(measure.verticalBehavior == DimensionBehaviour.MATCH_CONSTRAINT);
boolean horizontalUseRatio = horizontalMatchConstraints && widget.mDimensionRatio > 0;
boolean verticalUseRatio = verticalMatchConstraints && widget.mDimensionRatio > 0;
if (horizontalMatchConstraints && widget.hasDanglingDimension(HORIZONTAL)
&& widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD
&& !horizontalUseRatio) {
horizontalMatchConstraints = false;
measure.horizontalBehavior = WRAP_CONTENT;
if (verticalMatchConstraints
&& widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD) {
// if match x match, size would be zero.
measure.horizontalBehavior = FIXED;
}
}
if (verticalMatchConstraints && widget.hasDanglingDimension(VERTICAL)
&& widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD
&& !verticalUseRatio) {
verticalMatchConstraints = false;
measure.verticalBehavior = WRAP_CONTENT;
if (horizontalMatchConstraints
&& widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD) {
// if match x match, size would be zero.
measure.verticalBehavior = FIXED;
}
}
if (widget.isResolvedHorizontally()) {
horizontalMatchConstraints = false;
measure.horizontalBehavior = FIXED;
}
if (widget.isResolvedVertically()) {
verticalMatchConstraints = false;
measure.verticalBehavior = FIXED;
}
if (horizontalUseRatio) {
if (widget.mResolvedMatchConstraintDefault[HORIZONTAL]
== ConstraintWidget.MATCH_CONSTRAINT_RATIO_RESOLVED) {
measure.horizontalBehavior = FIXED;
} else if (!verticalMatchConstraints) {
// let's measure here
int measuredHeight;
if (measure.verticalBehavior == FIXED) {
measuredHeight = measure.verticalDimension;
} else {
measure.horizontalBehavior = WRAP_CONTENT;
measurer.measure(widget, measure);
measuredHeight = measure.measuredHeight;
}
measure.horizontalBehavior = FIXED;
// regardless of which side we are using for the ratio, getDimensionRatio() already
// made sure that it's expressed in WxH format, so we can simply go and multiply
measure.horizontalDimension = (int) (widget.getDimensionRatio() * measuredHeight);
if (DEBUG) {
System.out.println("(M) Measured once for ratio on horizontal side...");
}
}
}
if (verticalUseRatio) {
if (widget.mResolvedMatchConstraintDefault[VERTICAL]
== ConstraintWidget.MATCH_CONSTRAINT_RATIO_RESOLVED) {
measure.verticalBehavior = FIXED;
} else if (!horizontalMatchConstraints) {
// let's measure here
int measuredWidth;
if (measure.horizontalBehavior == FIXED) {
measuredWidth = measure.horizontalDimension;
} else {
measure.verticalBehavior = WRAP_CONTENT;
measurer.measure(widget, measure);
measuredWidth = measure.measuredWidth;
}
measure.verticalBehavior = FIXED;
if (widget.getDimensionRatioSide() == -1) {
// regardless of which side we are using for the ratio,
// getDimensionRatio() already
// made sure that it's expressed in WxH format,
// so we can simply go and divide
measure.verticalDimension = (int) (measuredWidth / widget.getDimensionRatio());
} else {
// getDimensionRatio() already got reverted, so we can simply multiply
measure.verticalDimension = (int) (widget.getDimensionRatio() * measuredWidth);
}
if (DEBUG) {
System.out.println("(M) Measured once for ratio on vertical side...");
}
}
}
measurer.measure(widget, measure);
widget.setWidth(measure.measuredWidth);
widget.setHeight(measure.measuredHeight);
widget.setHasBaseline(measure.measuredHasBaseline);
widget.setBaselineDistance(measure.measuredBaseline);
measure.measureStrategy = BasicMeasure.Measure.SELF_DIMENSIONS;
if (DEBUG) {
System.out.println("(M) Measured " + widget.getDebugName() + " with : "
+ widget.getHorizontalDimensionBehaviour() + " x "
+ widget.getVerticalDimensionBehaviour() + " => "
+ widget.getWidth() + " x " + widget.getHeight());
}
return measure.measuredNeedsSolverPass;
}
static int sMyCounter = 0;
/**
* Layout the tree of widgets
*/
@Override
public void layout() {
if (DEBUG) {
System.out.println("\n#####################################");
System.out.println("## CL LAYOUT PASS ##");
System.out.println("#####################################\n");
mDebugSolverPassCount = 0;
}
mX = 0;
mY = 0;
mWidthMeasuredTooSmall = false;
mHeightMeasuredTooSmall = false;
final int count = mChildren.size();
int preW = Math.max(0, getWidth());
int preH = Math.max(0, getHeight());
DimensionBehaviour originalVerticalDimensionBehaviour =
mListDimensionBehaviors[DIMENSION_VERTICAL];
DimensionBehaviour originalHorizontalDimensionBehaviour =
mListDimensionBehaviors[DIMENSION_HORIZONTAL];
if (DEBUG_LAYOUT) {
System.out.println("layout with preW: " + preW + " ("
+ mListDimensionBehaviors[DIMENSION_HORIZONTAL] + ") preH: " + preH
+ " (" + mListDimensionBehaviors[DIMENSION_VERTICAL] + ")");
}
if (mMetrics != null) {
mMetrics.layouts++;
}
boolean wrap_override = false;
if (FULL_DEBUG) {
System.out.println("OPTIMIZATION LEVEL " + mOptimizationLevel);
}
// Only try the direct optimization in the first layout pass
if (mPass == 0 && Optimizer.enabled(mOptimizationLevel, Optimizer.OPTIMIZATION_DIRECT)) {
if (FULL_DEBUG) {
System.out.println("Direct pass " + sMyCounter++);
}
Direct.solvingPass(this, getMeasurer());
if (FULL_DEBUG) {
System.out.println("Direct pass done.");
}
for (int i = 0; i < count; i++) {
ConstraintWidget child = mChildren.get(i);
if (FULL_DEBUG) {
if (child.isInHorizontalChain()) {
System.out.print("H");
} else {
System.out.print(" ");
}
if (child.isInVerticalChain()) {
System.out.print("V");
} else {
System.out.print(" ");
}
if (child.isResolvedHorizontally() && child.isResolvedVertically()) {
System.out.print("*");
} else {
System.out.print(" ");
}
System.out.println("[" + i + "] child " + child.getDebugName()
+ " H: " + child.isResolvedHorizontally()
+ " V: " + child.isResolvedVertically());
}
if (child.isMeasureRequested()
&& !(child instanceof Guideline)
&& !(child instanceof Barrier)
&& !(child instanceof VirtualLayout)
&& !child.isInVirtualLayout()) {
DimensionBehaviour widthBehavior = child.getDimensionBehaviour(HORIZONTAL);
DimensionBehaviour heightBehavior = child.getDimensionBehaviour(VERTICAL);
boolean skip = widthBehavior == DimensionBehaviour.MATCH_CONSTRAINT
&& child.mMatchConstraintDefaultWidth != MATCH_CONSTRAINT_WRAP
&& heightBehavior == DimensionBehaviour.MATCH_CONSTRAINT
&& child.mMatchConstraintDefaultHeight != MATCH_CONSTRAINT_WRAP;
if (!skip) {
BasicMeasure.Measure measure = new BasicMeasure.Measure();
ConstraintWidgetContainer.measure(0, child, mMeasurer,
measure, BasicMeasure.Measure.SELF_DIMENSIONS);
}
}
}
// let's measure children
if (FULL_DEBUG) {
System.out.println("Direct pass all done.");
}
} else {
if (FULL_DEBUG) {
System.out.println("No DIRECT PASS");
}
}
if (count > 2 && (originalHorizontalDimensionBehaviour == WRAP_CONTENT
|| originalVerticalDimensionBehaviour == WRAP_CONTENT)
&& Optimizer.enabled(mOptimizationLevel, Optimizer.OPTIMIZATION_GROUPING)) {
if (Grouping.simpleSolvingPass(this, getMeasurer())) {
if (originalHorizontalDimensionBehaviour == WRAP_CONTENT) {
if (preW < getWidth() && preW > 0) {
if (DEBUG_LAYOUT) {
System.out.println("Override width " + getWidth() + " to " + preH);
}
setWidth(preW);
mWidthMeasuredTooSmall = true;
} else {
preW = getWidth();
}
}
if (originalVerticalDimensionBehaviour == WRAP_CONTENT) {
if (preH < getHeight() && preH > 0) {
if (DEBUG_LAYOUT) {
System.out.println("Override height " + getHeight() + " to " + preH);
}
setHeight(preH);
mHeightMeasuredTooSmall = true;
} else {
preH = getHeight();
}
}
wrap_override = true;
if (DEBUG_LAYOUT) {
System.out.println("layout post opt, preW: " + preW
+ " (" + mListDimensionBehaviors[DIMENSION_HORIZONTAL]
+ ") preH: " + preH + " (" + mListDimensionBehaviors[DIMENSION_VERTICAL]
+ "), new size " + getWidth() + " x " + getHeight());
}
}
}
boolean useGraphOptimizer = optimizeFor(Optimizer.OPTIMIZATION_GRAPH)
|| optimizeFor(Optimizer.OPTIMIZATION_GRAPH_WRAP);
mSystem.graphOptimizer = false;
mSystem.newgraphOptimizer = false;
if (mOptimizationLevel != Optimizer.OPTIMIZATION_NONE
&& useGraphOptimizer) {
mSystem.newgraphOptimizer = true;
}
@SuppressWarnings("unused") int countSolve = 0;
final List<ConstraintWidget> allChildren = mChildren;
boolean hasWrapContent = getHorizontalDimensionBehaviour() == WRAP_CONTENT
|| getVerticalDimensionBehaviour() == WRAP_CONTENT;
// Reset the chains before iterating on our children
resetChains();
countSolve = 0;
// Before we solve our system, we should call layout() on any
// of our children that is a container.
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
if (widget instanceof WidgetContainer) {
((WidgetContainer) widget).layout();
}
}
boolean optimize = optimizeFor(Optimizer.OPTIMIZATION_GRAPH);
// Now let's solve our system as usual
boolean needsSolving = true;
while (needsSolving) {
countSolve++;
try {
mSystem.reset();
resetChains();
if (DEBUG) {
String debugName = getDebugName();
if (debugName == null) {
debugName = "root";
}
setDebugSolverName(mSystem, debugName);
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
if (widget.getDebugName() != null) {
widget.setDebugSolverName(mSystem, widget.getDebugName());
}
}
} else {
createObjectVariables(mSystem);
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
widget.createObjectVariables(mSystem);
}
}
needsSolving = addChildrenToSolver(mSystem);
if (mVerticalWrapMin != null && mVerticalWrapMin.get() != null) {
addMinWrap(mVerticalWrapMin.get(), mSystem.createObjectVariable(mTop));
mVerticalWrapMin = null;
}
if (mVerticalWrapMax != null && mVerticalWrapMax.get() != null) {
addMaxWrap(mVerticalWrapMax.get(), mSystem.createObjectVariable(mBottom));
mVerticalWrapMax = null;
}
if (mHorizontalWrapMin != null && mHorizontalWrapMin.get() != null) {
addMinWrap(mHorizontalWrapMin.get(), mSystem.createObjectVariable(mLeft));
mHorizontalWrapMin = null;
}
if (mHorizontalWrapMax != null && mHorizontalWrapMax.get() != null) {
addMaxWrap(mHorizontalWrapMax.get(), mSystem.createObjectVariable(mRight));
mHorizontalWrapMax = null;
}
if (needsSolving) {
mSystem.minimize();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("EXCEPTION : " + e);
}
if (needsSolving) {
needsSolving = updateChildrenFromSolver(mSystem, Optimizer.sFlags);
} else {
updateFromSolver(mSystem, optimize);
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
widget.updateFromSolver(mSystem, optimize);
}
needsSolving = false;
}
if (hasWrapContent && countSolve < MAX_ITERATIONS
&& Optimizer.sFlags[Optimizer.FLAG_RECOMPUTE_BOUNDS]) {
// let's get the new bounds
int maxX = 0;
int maxY = 0;
for (int i = 0; i < count; i++) {
ConstraintWidget widget = mChildren.get(i);
maxX = Math.max(maxX, widget.mX + widget.getWidth());
maxY = Math.max(maxY, widget.mY + widget.getHeight());
}
maxX = Math.max(mMinWidth, maxX);
maxY = Math.max(mMinHeight, maxY);
if (originalHorizontalDimensionBehaviour == WRAP_CONTENT) {
if (getWidth() < maxX) {
if (DEBUG_LAYOUT) {
System.out.println( countSolve +
"layout override width from " + getWidth() + " vs " + maxX);
}
setWidth(maxX);
// force using the solver
mListDimensionBehaviors[DIMENSION_HORIZONTAL] = WRAP_CONTENT;
wrap_override = true;
needsSolving = true;
}
}
if (originalVerticalDimensionBehaviour == WRAP_CONTENT) {
if (getHeight() < maxY) {
if (DEBUG_LAYOUT) {
System.out.println(
"layout override height from " + getHeight() + " vs " + maxY);
}
setHeight(maxY);
// force using the solver
mListDimensionBehaviors[DIMENSION_VERTICAL] = WRAP_CONTENT;
wrap_override = true;
needsSolving = true;
}
}
}
if (true) {
int width = Math.max(mMinWidth, getWidth());
if (width > getWidth()) {
if (DEBUG_LAYOUT) {
System.out.println(
"layout override 2, width from " + getWidth() + " vs " + width);
}
setWidth(width);
mListDimensionBehaviors[DIMENSION_HORIZONTAL] = FIXED;
wrap_override = true;
needsSolving = true;
}
int height = Math.max(mMinHeight, getHeight());
if (height > getHeight()) {
if (DEBUG_LAYOUT) {
System.out.println(
"layout override 2, height from " + getHeight() + " vs " + height);
}
setHeight(height);
mListDimensionBehaviors[DIMENSION_VERTICAL] = FIXED;
wrap_override = true;
needsSolving = true;
}
if (!wrap_override) {
if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == WRAP_CONTENT
&& preW > 0) {
if (getWidth() > preW) {
if (DEBUG_LAYOUT) {
System.out.println(
"layout override 3, width from " + getWidth() + " vs "
+ preW);
}
mWidthMeasuredTooSmall = true;
wrap_override = true;
mListDimensionBehaviors[DIMENSION_HORIZONTAL] = FIXED;
setWidth(preW);
needsSolving = true;
}
}
if (mListDimensionBehaviors[DIMENSION_VERTICAL] == WRAP_CONTENT
&& preH > 0) {
if (getHeight() > preH) {
if (DEBUG_LAYOUT) {
System.out.println(
"layout override 3, height from " + getHeight() + " vs "
+ preH);
}
mHeightMeasuredTooSmall = true;
wrap_override = true;
mListDimensionBehaviors[DIMENSION_VERTICAL] = FIXED;
setHeight(preH);
needsSolving = true;
}
}
}
if (countSolve > MAX_ITERATIONS) {
needsSolving = false;
}
}
}
if (DEBUG_LAYOUT) {
System.out.println(
"Solved system in " + countSolve + " iterations (" + getWidth() + " x "
+ getHeight() + ")");
}
mChildren = (ArrayList<ConstraintWidget>) allChildren;
if (wrap_override) {
mListDimensionBehaviors[DIMENSION_HORIZONTAL] = originalHorizontalDimensionBehaviour;
mListDimensionBehaviors[DIMENSION_VERTICAL] = originalVerticalDimensionBehaviour;
}
resetSolverVariables(mSystem.getCache());
}
/**
* Indicates if the container knows how to layout its content on its own
*
* @return true if the container does the layout, false otherwise
*/
public boolean handlesInternalConstraints() {
return false;
}
/*-----------------------------------------------------------------------*/
// Guidelines
/*-----------------------------------------------------------------------*/
/**
* Accessor to the vertical guidelines contained in the table.
*
* @return array of guidelines
*/
public ArrayList<Guideline> getVerticalGuidelines() {
ArrayList<Guideline> guidelines = new ArrayList<>();
for (int i = 0, mChildrenSize = mChildren.size(); i < mChildrenSize; i++) {
final ConstraintWidget widget = mChildren.get(i);
if (widget instanceof Guideline) {
Guideline guideline = (Guideline) widget;
if (guideline.getOrientation() == Guideline.VERTICAL) {
guidelines.add(guideline);
}
}
}
return guidelines;
}
/**
* Accessor to the horizontal guidelines contained in the table.
*
* @return array of guidelines
*/
public ArrayList<Guideline> getHorizontalGuidelines() {
ArrayList<Guideline> guidelines = new ArrayList<>();
for (int i = 0, mChildrenSize = mChildren.size(); i < mChildrenSize; i++) {
final ConstraintWidget widget = mChildren.get(i);
if (widget instanceof Guideline) {
Guideline guideline = (Guideline) widget;
if (guideline.getOrientation() == Guideline.HORIZONTAL) {
guidelines.add(guideline);
}
}
}
return guidelines;
}
public LinearSystem getSystem() {
return mSystem;
}
/*-----------------------------------------------------------------------*/
// Chains
/*-----------------------------------------------------------------------*/
/**
* Reset the chains array. Need to be called before layout.
*/
private void resetChains() {
mHorizontalChainsSize = 0;
mVerticalChainsSize = 0;
}
/**
* Add the chain which constraintWidget is part of. Called by ConstraintWidget::addToSolver()
*
* @param type HORIZONTAL or VERTICAL chain
*/
void addChain(ConstraintWidget constraintWidget, int type) {
ConstraintWidget widget = constraintWidget;
if (type == HORIZONTAL) {
addHorizontalChain(widget);
} else if (type == VERTICAL) {
addVerticalChain(widget);
}
}
/**
* Add a widget to the list of horizontal chains. The widget is the left-most widget
* of the chain which doesn't have a left dual connection.
*
* @param widget widget starting the chain
*/
private void addHorizontalChain(ConstraintWidget widget) {
if (mHorizontalChainsSize + 1 >= mHorizontalChainsArray.length) {
mHorizontalChainsArray = Arrays
.copyOf(mHorizontalChainsArray, mHorizontalChainsArray.length * 2);
}
mHorizontalChainsArray[mHorizontalChainsSize] = new ChainHead(widget, HORIZONTAL, isRtl());
mHorizontalChainsSize++;
}
/**
* Add a widget to the list of vertical chains. The widget is the top-most widget
* of the chain which doesn't have a top dual connection.
*
* @param widget widget starting the chain
*/
private void addVerticalChain(ConstraintWidget widget) {
if (mVerticalChainsSize + 1 >= mVerticalChainsArray.length) {
mVerticalChainsArray = Arrays
.copyOf(mVerticalChainsArray, mVerticalChainsArray.length * 2);
}
mVerticalChainsArray[mVerticalChainsSize] = new ChainHead(widget, VERTICAL, isRtl());
mVerticalChainsSize++;
}
/**
* Keep track of the # of passes
*/
public void setPass(int pass) {
this.mPass = pass;
}
// @TODO: add description
@Override
public void getSceneString(StringBuilder ret) {
ret.append(stringId + ":{\n");
ret.append(" actualWidth:" + mWidth);
ret.append("\n");
ret.append(" actualHeight:" + mHeight);
ret.append("\n");
ArrayList<ConstraintWidget> children = getChildren();
for (ConstraintWidget child : children) {
child.getSceneString(ret);
ret.append(",\n");
}
ret.append("}");
}
}