summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Roard <nicolasroard@google.com>2018-04-05 18:14:03 -0700
committerNicolas Roard <nicolasroard@google.com>2018-04-10 23:30:38 -0700
commitfc2b3a677298c422b210dd23892d0d16e7a9de2a (patch)
tree84d31df9d8af6fdfc44b37560d18387186a8e276
parent8b826acf465e2688a3a61c3d6f34632b25de7545 (diff)
downloadsherpa-fc2b3a677298c422b210dd23892d0d16e7a9de2a.tar.gz
Add dimensions optimization.
Limit the number of measures on children when possible. Test: sherpa tests passing. Fixes: 77503701 Change-Id: Ic154a725ff9c1722baec8037b94b5e1a7f9b858e
-rw-r--r--constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java292
-rw-r--r--constraintlayout/src/main/res/values/attrs.xml1
-rw-r--r--solver/src/main/java/android/support/constraint/solver/Metrics.java9
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/Barrier.java20
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ConstraintAnchor.java9
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java69
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java156
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/Guideline.java35
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/Optimizer.java224
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ResolutionAnchor.java314
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ResolutionDimension.java43
-rw-r--r--solver/src/main/java/android/support/constraint/solver/widgets/ResolutionNode.java267
12 files changed, 1021 insertions, 418 deletions
diff --git a/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java b/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java
index e17f689..24c44b4 100644
--- a/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java
+++ b/constraintlayout/src/main/java/android/support/constraint/ConstraintLayout.java
@@ -465,6 +465,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
* <li><b>direct</b> : optimize direct constraints</li>
* <li><b>barrier</b> : optimize barrier constraints</li>
* <li><b>chain</b> : optimize chain constraints (experimental)</li>
+ * <li><b>dimensions</b> : optimize dimensions measures (experimental), reducing the number of measures of match constraints elements</li>
* </ul>
* </p>
* <p>This attribute is a mask, so you can decide to turn on or off specific optimizations by listing the ones you want.
@@ -479,7 +480,7 @@ public class ConstraintLayout extends ViewGroup {
static final boolean ALLOWS_EMBEDDED = false;
/** @hide */
- public static final String VERSION="ConstraintLayout-1.1.0";
+ public static final String VERSION = "ConstraintLayout-1.1.0";
private static final String TAG = "ConstraintLayout";
private static final boolean USE_CONSTRAINTS_HELPER = true;
@@ -522,6 +523,7 @@ public class ConstraintLayout extends ViewGroup {
* @hide
*/
public final static int DESIGN_INFO_ID = 0;
+ private Metrics mMetrics;
/**
* @hide
@@ -712,7 +714,6 @@ public class ConstraintLayout extends ViewGroup {
* The minimum width of this view.
*
* @return The minimum width of this view
- *
* @see #setMinWidth(int)
*/
public int getMinWidth() {
@@ -723,7 +724,6 @@ public class ConstraintLayout extends ViewGroup {
* The minimum height of this view.
*
* @return The minimum height of this view
- *
* @see #setMinHeight(int)
*/
public int getMinHeight() {
@@ -771,7 +771,6 @@ public class ConstraintLayout extends ViewGroup {
* The maximum height of this view.
*
* @return The maximum height of this view
- *
* @see #setMaxHeight(int)
*/
public int getMaxHeight() {
@@ -1130,9 +1129,9 @@ public class ConstraintLayout extends ViewGroup {
}
/**
- * @hide
* @param view
* @return
+ * @hide
*/
public final ConstraintWidget getViewWidget(View view) {
if (view == this) {
@@ -1165,13 +1164,13 @@ public class ConstraintLayout extends ViewGroup {
// unless they are marked as MATCH_CONSTRAINT_WRAP
boolean doMeasure =
(params.horizontalDimensionFixed
- || params.verticalDimensionFixed)
- || (!params.horizontalDimensionFixed
+ || params.verticalDimensionFixed)
+ || (!params.horizontalDimensionFixed
&& (params.matchConstraintDefaultWidth == MATCH_CONSTRAINT_WRAP)
- || params.width == MATCH_PARENT)
- || (!params.verticalDimensionFixed
+ || params.width == MATCH_PARENT)
+ || (!params.verticalDimensionFixed
&& (params.matchConstraintDefaultHeight == MATCH_CONSTRAINT_WRAP
- || params.height == MATCH_PARENT));
+ || params.height == MATCH_PARENT));
boolean didWrapMeasureWidth = false;
boolean didWrapMeasureHeight = false;
@@ -1209,6 +1208,9 @@ public class ConstraintLayout extends ViewGroup {
heightPadding, height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ if (mMetrics != null) {
+ mMetrics.measures++;
+ }
widget.setWidthWrapContent(width == WRAP_CONTENT);
widget.setHeightWrapContent(height == WRAP_CONTENT);
@@ -1233,6 +1235,10 @@ public class ConstraintLayout extends ViewGroup {
}
}
}
+ }
+
+ private void updatePostMeasures() {
+ final int widgetsCount = getChildCount();
for (int i = 0; i < widgetsCount; i++) {
final View child = getChildAt(i);
if (child instanceof Placeholder) {
@@ -1251,12 +1257,226 @@ public class ConstraintLayout extends ViewGroup {
/**
* @hide
+ * Measures widgets in two steps, trying to solve the constraints partially.
+ *
+ * @param parentWidthSpec
+ * @param parentHeightSpec
+ */
+ private void internalMeasureDimensions(int parentWidthSpec, int parentHeightSpec) {
+ int heightPadding = getPaddingTop() + getPaddingBottom();
+ int widthPadding = getPaddingLeft() + getPaddingRight();
+
+ final int widgetsCount = getChildCount();
+ for (int i = 0; i < widgetsCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ ConstraintWidget widget = params.widget;
+ if (params.isGuideline || params.isHelper) {
+ continue;
+ }
+ widget.setVisibility(child.getVisibility());
+
+ int width = params.width;
+ int height = params.height;
+
+ if (width == MATCH_CONSTRAINT || height == MATCH_CONSTRAINT) {
+ widget.getResolutionWidth().invalidate();
+ widget.getResolutionHeight().invalidate();
+ continue;
+ }
+
+ boolean didWrapMeasureWidth = false;
+ boolean didWrapMeasureHeight = false;
+
+ final int childWidthMeasureSpec;
+ final int childHeightMeasureSpec;
+ if (width == WRAP_CONTENT) {
+ didWrapMeasureWidth = true;
+ }
+ childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
+ widthPadding, width);
+ if (height == WRAP_CONTENT) {
+ didWrapMeasureHeight = true;
+ }
+ childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
+ heightPadding, height);
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ if (mMetrics != null) {
+ mMetrics.measures++;
+ }
+
+ widget.setWidthWrapContent(width == WRAP_CONTENT);
+ widget.setHeightWrapContent(height == WRAP_CONTENT);
+ width = child.getMeasuredWidth();
+ height = child.getMeasuredHeight();
+
+ widget.setWidth(width);
+ widget.setHeight(height);
+
+ if (didWrapMeasureWidth) {
+ widget.setWrapWidth(width);
+ }
+ if (didWrapMeasureHeight) {
+ widget.setWrapHeight(height);
+ }
+
+ if (params.needsBaseline) {
+ int baseline = child.getBaseline();
+ if (baseline != -1) {
+ widget.setBaselineDistance(baseline);
+ }
+ }
+
+ if (params.horizontalDimensionFixed && params.verticalDimensionFixed) {
+ widget.getResolutionWidth().resolve(width);
+ widget.getResolutionHeight().resolve(height);
+ }
+ }
+
+ // ok now let's try to analyse the graph, see if that solves the flexible dimensions
+ mLayoutWidget.solveGraph();
+
+ for (int i = 0; i < widgetsCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ ConstraintWidget widget = params.widget;
+ if (params.isGuideline || params.isHelper) {
+ continue;
+ }
+ widget.setVisibility(child.getVisibility());
+
+ int width = params.width;
+ int height = params.height;
+
+ if (!(width == MATCH_CONSTRAINT || height == MATCH_CONSTRAINT)) {
+ continue;
+ }
+
+ ResolutionAnchor left = widget.getAnchor(ConstraintAnchor.Type.LEFT).getResolutionNode();
+ ResolutionAnchor right = widget.getAnchor(ConstraintAnchor.Type.RIGHT).getResolutionNode();
+ boolean bothHorizontal = widget.getAnchor(ConstraintAnchor.Type.LEFT).getTarget() != null
+ && widget.getAnchor(ConstraintAnchor.Type.RIGHT).getTarget() != null;
+ ResolutionAnchor top = widget.getAnchor(ConstraintAnchor.Type.TOP).getResolutionNode();
+ ResolutionAnchor bottom = widget.getAnchor(ConstraintAnchor.Type.BOTTOM).getResolutionNode();
+ boolean bothVertical = widget.getAnchor(ConstraintAnchor.Type.TOP).getTarget() != null
+ && widget.getAnchor(ConstraintAnchor.Type.BOTTOM).getTarget() != null;
+
+ if (width == MATCH_CONSTRAINT && height == MATCH_CONSTRAINT && bothHorizontal && bothVertical) {
+ continue;
+ }
+
+ boolean didWrapMeasureWidth = false;
+ boolean didWrapMeasureHeight = false;
+ boolean resolveWidth = mLayoutWidget.getHorizontalDimensionBehaviour() != ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
+ boolean resolveHeight = mLayoutWidget.getVerticalDimensionBehaviour() != ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
+
+ final int childWidthMeasureSpec;
+ final int childHeightMeasureSpec;
+
+ if (!resolveWidth) {
+ widget.getResolutionWidth().invalidate();
+ }
+ if (!resolveHeight) {
+ widget.getResolutionHeight().invalidate();
+ }
+ if (width == MATCH_CONSTRAINT) {
+ if (resolveWidth && widget.isSpreadWidth() && bothHorizontal && left.isResolved() && right.isResolved()) {
+ width = (int) (right.getResolvedValue() - left.getResolvedValue());
+ widget.getResolutionWidth().resolve(width);
+ childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
+ widthPadding, width);
+ } else {
+ childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
+ widthPadding, LayoutParams.WRAP_CONTENT);
+ didWrapMeasureWidth = true;
+ resolveWidth = false;
+ }
+ } else if (width == MATCH_PARENT) {
+ childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
+ widthPadding, LayoutParams.MATCH_PARENT);
+ } else {
+ if (width == WRAP_CONTENT) {
+ didWrapMeasureWidth = true;
+ }
+ childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
+ widthPadding, width);
+ }
+ if (height == MATCH_CONSTRAINT) {
+ if (resolveHeight && widget.isSpreadHeight() && bothVertical && top.isResolved() && bottom.isResolved()) {
+ height = (int) (bottom.getResolvedValue() - top.getResolvedValue());
+ widget.getResolutionHeight().resolve(height);
+ childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
+ heightPadding, height);
+ } else {
+ childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
+ heightPadding, LayoutParams.WRAP_CONTENT);
+ didWrapMeasureHeight = true;
+ resolveHeight = false;
+ }
+ } else if (height == MATCH_PARENT) {
+ childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
+ heightPadding, LayoutParams.MATCH_PARENT);
+ } else {
+ if (height == WRAP_CONTENT) {
+ didWrapMeasureHeight = true;
+ }
+ childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
+ heightPadding, height);
+ }
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ if (mMetrics != null) {
+ mMetrics.measures++;
+ }
+
+ widget.setWidthWrapContent(width == WRAP_CONTENT);
+ widget.setHeightWrapContent(height == WRAP_CONTENT);
+ width = child.getMeasuredWidth();
+ height = child.getMeasuredHeight();
+
+ widget.setWidth(width);
+ widget.setHeight(height);
+
+ if (didWrapMeasureWidth) {
+ widget.setWrapWidth(width);
+ }
+ if (didWrapMeasureHeight) {
+ widget.setWrapHeight(height);
+ }
+ if (resolveWidth) {
+ widget.getResolutionWidth().resolve(width);
+ } else {
+ widget.getResolutionWidth().remove();
+ }
+ if (resolveHeight) {
+ widget.getResolutionHeight().resolve(height);
+ } else {
+ widget.getResolutionHeight().remove();
+ }
+
+ if (params.needsBaseline) {
+ int baseline = child.getBaseline();
+ if (baseline != -1) {
+ widget.setBaselineDistance(baseline);
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
*
* Fills metrics object
*
* @param metrics
*/
public void fillMetrics(Metrics metrics) {
+ mMetrics = metrics;
mLayoutWidget.fillMetrics(metrics);
}
@@ -1265,6 +1485,10 @@ public class ConstraintLayout extends ViewGroup {
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ long time = System.currentTimeMillis();
+ int REMEASURES_A = 0;
+ int REMEASURES_B = 0;
+
if (DEBUG) {
System.out.println("onMeasure width: " + MeasureSpec.toString(widthMeasureSpec)
+ " height: " + MeasureSpec.toString(heightMeasureSpec));
@@ -1315,7 +1539,17 @@ public class ConstraintLayout extends ViewGroup {
mDirtyHierarchy = false;
updateHierarchy();
}
- internalMeasureChildren(widthMeasureSpec, heightMeasureSpec);
+
+ final boolean optimiseDimensions = (mOptimizationLevel & Optimizer.OPTIMIZATION_DIMENSIONS)
+ == Optimizer.OPTIMIZATION_DIMENSIONS;
+ if (optimiseDimensions) {
+ mLayoutWidget.preOptimize();
+ mLayoutWidget.optimizeForDimensions(startingWidth, startingHeight);
+ internalMeasureDimensions(widthMeasureSpec, heightMeasureSpec);
+ } else {
+ internalMeasureChildren(widthMeasureSpec, heightMeasureSpec);
+ }
+ updatePostMeasures();
//noinspection PointlessBooleanExpression
if (ALLOWS_EMBEDDED && mLayoutWidget.getParent() != null) {
@@ -1361,6 +1595,10 @@ public class ConstraintLayout extends ViewGroup {
continue;
}
+ if (optimiseDimensions && widget.getResolutionWidth().isResolved()
+ && widget.getResolutionHeight().isResolved()) {
+ continue;
+ }
int widthSpec = 0;
int heightSpec = 0;
@@ -1377,12 +1615,20 @@ public class ConstraintLayout extends ViewGroup {
// we need to re-measure the child...
child.measure(widthSpec, heightSpec);
+ if (mMetrics != null) {
+ mMetrics.additionalMeasures++;
+ }
+
+ REMEASURES_A++;
int measuredWidth = child.getMeasuredWidth();
int measuredHeight = child.getMeasuredHeight();
if (measuredWidth != widget.getWidth()) {
widget.setWidth(measuredWidth);
+ if (optimiseDimensions) {
+ widget.getResolutionWidth().resolve(measuredWidth);
+ }
if (containerWrapWidth && widget.getRight() > minWidth) {
int w = widget.getRight()
+ widget.getAnchor(ConstraintAnchor.Type.RIGHT).getMargin();
@@ -1392,6 +1638,9 @@ public class ConstraintLayout extends ViewGroup {
}
if (measuredHeight != widget.getHeight()) {
widget.setHeight(measuredHeight);
+ if (optimiseDimensions) {
+ widget.getResolutionHeight().resolve(measuredHeight);
+ }
if (containerWrapHeight && widget.getBottom() > minHeight) {
int h = widget.getBottom()
+ widget.getAnchor(ConstraintAnchor.Type.BOTTOM).getMargin();
@@ -1414,6 +1663,9 @@ public class ConstraintLayout extends ViewGroup {
if (needSolverPass) {
mLayoutWidget.setWidth(startingWidth);
mLayoutWidget.setHeight(startingHeight);
+ if (optimiseDimensions) {
+ mLayoutWidget.solveGraph();
+ }
solveLinearSystem("2nd pass");
needSolverPass = false;
if (mLayoutWidget.getWidth() < minWidth) {
@@ -1434,10 +1686,15 @@ public class ConstraintLayout extends ViewGroup {
if (child == null) {
continue;
}
- if (child.getWidth() != widget.getWidth() || child.getHeight() != widget.getHeight()) {
+ if (child.getMeasuredWidth() != widget.getWidth() || child.getMeasuredHeight() != widget.getHeight()) {
int widthSpec = MeasureSpec.makeMeasureSpec(widget.getWidth(), MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(widget.getHeight(), MeasureSpec.EXACTLY);
child.measure(widthSpec, heightSpec);
+ if (mMetrics != null) {
+ mMetrics.additionalMeasures++;
+ }
+
+ REMEASURES_B++;
}
}
}
@@ -1467,6 +1724,14 @@ public class ConstraintLayout extends ViewGroup {
mLastMeasureWidth = androidLayoutWidth;
mLastMeasureHeight = androidLayoutHeight;
}
+ if (DEBUG) {
+ time = System.currentTimeMillis() - time;
+ System.out.println("" + this + " (" + getChildCount() + ") DONE onMeasure width: " + MeasureSpec.toString(widthMeasureSpec)
+ + " height: " + MeasureSpec.toString(heightMeasureSpec)
+ + "lasted " + time
+ + "remeasures (" + REMEASURES_A + "/" + REMEASURES_B + ") "
+ );
+ }
}
private void setSelfDimensionBehaviour(int widthMeasureSpec, int heightMeasureSpec) {
@@ -1533,6 +1798,9 @@ public class ConstraintLayout extends ViewGroup {
System.out.println("solve <" + reason + ">");
}
mLayoutWidget.layout();
+ if (mMetrics != null) {
+ mMetrics.resolutions++;
+ }
}
/**
diff --git a/constraintlayout/src/main/res/values/attrs.xml b/constraintlayout/src/main/res/values/attrs.xml
index f278657..d9d25dc 100644
--- a/constraintlayout/src/main/res/values/attrs.xml
+++ b/constraintlayout/src/main/res/values/attrs.xml
@@ -145,6 +145,7 @@
<flag name="direct" value="1"/>
<flag name="barrier" value="2"/>
<flag name="chains" value="4"/>
+ <flag name="dimensions" value="8"/>
</attr>
<!-- Specify the style of match constraint -->
diff --git a/solver/src/main/java/android/support/constraint/solver/Metrics.java b/solver/src/main/java/android/support/constraint/solver/Metrics.java
index 002f54a..570ca44 100644
--- a/solver/src/main/java/android/support/constraint/solver/Metrics.java
+++ b/solver/src/main/java/android/support/constraint/solver/Metrics.java
@@ -23,6 +23,9 @@ import java.util.ArrayList;
* Utility class to track metrics during the system resolution
*/
public class Metrics {
+ public long measures;
+ public long additionalMeasures;
+ public long resolutions;
public long tableSizeIncrease;
public long minimize;
public long constraints;
@@ -53,6 +56,9 @@ public class Metrics {
public String toString() {
return "\n*** Metrics ***\n"
+ + "measures: " + measures + "\n"
+ + "additionalMeasures: " + additionalMeasures + "\n"
+ + "resolutions passes: " + resolutions + "\n"
+ "table increases: " + tableSizeIncrease + "\n"
+ "maxTableSize: " + maxTableSize + "\n"
+ "maxVariables: " + maxVariables + "\n"
@@ -82,6 +88,9 @@ public class Metrics {
;
}
public void reset() {
+ measures = 0;
+ additionalMeasures = 0;
+ resolutions = 0;
tableSizeIncrease = 0;
maxTableSize = 0;
lastTableSize = 0;
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/Barrier.java b/solver/src/main/java/android/support/constraint/solver/widgets/Barrier.java
index 6e42f14..f6a09c2 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/Barrier.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/Barrier.java
@@ -20,7 +20,6 @@ import android.support.constraint.solver.LinearSystem;
import android.support.constraint.solver.SolverVariable;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* A Barrier takes multiple widgets
@@ -33,7 +32,7 @@ public class Barrier extends Helper {
public static final int BOTTOM = 3;
private int mBarrierType = LEFT;
- private ArrayList<ResolutionNode> mNodes = new ArrayList<>(4);
+ private ArrayList<ResolutionAnchor> mNodes = new ArrayList<>(4);
private boolean mAllowsGoneWidget = true;
@@ -56,9 +55,10 @@ public class Barrier extends Helper {
/**
* Graph analysis
+ * @param optimizationLevel
*/
@Override
- public void analyze() {
+ public void analyze(int optimizationLevel) {
if (mParent == null) {
return;
}
@@ -66,7 +66,7 @@ public class Barrier extends Helper {
return;
}
- ResolutionNode node;
+ ResolutionAnchor node;
switch (mBarrierType) {
case LEFT:
node = mLeft.getResolutionNode();
@@ -83,7 +83,7 @@ public class Barrier extends Helper {
default:
return;
}
- node.setType(ResolutionNode.BARRIER_CONNECTION);
+ node.setType(ResolutionAnchor.BARRIER_CONNECTION);
if (mBarrierType == LEFT || mBarrierType == RIGHT) {
mTop.getResolutionNode().resolve(null, 0);
@@ -99,7 +99,7 @@ public class Barrier extends Helper {
if (!mAllowsGoneWidget && !widget.allowedInBarrier()) {
continue;
}
- ResolutionNode depends = null;
+ ResolutionAnchor depends = null;
switch (mBarrierType) {
case LEFT:
depends = widget.mLeft.getResolutionNode();
@@ -126,7 +126,7 @@ public class Barrier extends Helper {
*/
@Override
public void resolve() {
- ResolutionNode node = null;
+ ResolutionAnchor node = null;
float value = 0;
switch (mBarrierType) {
case LEFT: {
@@ -148,10 +148,10 @@ public class Barrier extends Helper {
}
final int count = mNodes.size();
- ResolutionNode resolvedTarget = null;
+ ResolutionAnchor resolvedTarget = null;
for (int i = 0; i < count; i++) {
- ResolutionNode n = mNodes.get(i);
- if (n.state != ResolutionNode.RESOLVED) {
+ ResolutionAnchor n = mNodes.get(i);
+ if (n.state != ResolutionAnchor.RESOLVED) {
return;
}
if (mBarrierType == LEFT || mBarrierType == TOP) {
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintAnchor.java b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintAnchor.java
index 27f0f18..a8157bf 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintAnchor.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintAnchor.java
@@ -16,7 +16,6 @@
package android.support.constraint.solver.widgets;
import android.support.constraint.solver.Cache;
-import android.support.constraint.solver.LinearSystem;
import android.support.constraint.solver.SolverVariable;
import java.util.ArrayList;
@@ -49,14 +48,14 @@ public class ConstraintAnchor {
/**
* Resolution node, used by graph resolution
*/
- private ResolutionNode mResolutionNode = new ResolutionNode(this);
+ private ResolutionAnchor mResolutionAnchor = new ResolutionAnchor(this);
/**
* Resolution node accessor
* @return the Resolution node for this ConstraintAnchor
*/
- public ResolutionNode getResolutionNode() {
- return mResolutionNode;
+ public ResolutionAnchor getResolutionNode() {
+ return mResolutionAnchor;
}
/**
@@ -179,7 +178,7 @@ public class ConstraintAnchor {
mStrength = Strength.STRONG;
mConnectionCreator = USER_CREATOR;
mConnectionType = ConnectionType.RELAXED;
- mResolutionNode.reset();
+ mResolutionAnchor.reset();
}
/**
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java
index 12b1bee..dd6d92b 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidget.java
@@ -19,6 +19,7 @@ import android.support.constraint.solver.*;
import java.util.ArrayList;
+import static android.support.constraint.solver.widgets.ConstraintWidget.DimensionBehaviour.MATCH_CONSTRAINT;
import static android.support.constraint.solver.widgets.ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
/**
@@ -68,6 +69,9 @@ public class ConstraintWidget {
private static final int WRAP = -2;
+ ResolutionDimension mResolutionWidth;
+ ResolutionDimension mResolutionHeight;
+
int mMatchConstraintDefaultWidth = MATCH_CONSTRAINT_SPREAD;
int mMatchConstraintDefaultHeight = MATCH_CONSTRAINT_SPREAD;
int mMatchConstraintMinWidth = 0;
@@ -101,6 +105,22 @@ public class ConstraintWidget {
mMaxDimension[VERTICAL] = maxWidth;
}
+ public boolean isSpreadWidth() {
+ return mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD
+ && mDimensionRatio == 0
+ && mMatchConstraintMinWidth == 0
+ && mMatchConstraintMaxWidth == 0
+ && mListDimensionBehaviors[HORIZONTAL] == MATCH_CONSTRAINT;
+ }
+
+ public boolean isSpreadHeight() {
+ return mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD
+ && mDimensionRatio == 0
+ && mMatchConstraintMinHeight == 0
+ && mMatchConstraintMaxHeight == 0
+ && mListDimensionBehaviors[VERTICAL] == MATCH_CONSTRAINT;
+ }
+
/**
* Define how the content of a widget should align, if the widget has children
*/
@@ -278,6 +298,12 @@ public class ConstraintWidget {
mMatchConstraintMinHeight = 0;
mResolvedDimensionRatioSide = UNKNOWN;
mResolvedDimensionRatio = 1f;
+ if (mResolutionWidth != null) {
+ mResolutionWidth.reset();
+ }
+ if (mResolutionHeight != null) {
+ mResolutionHeight.reset();
+ }
}
/*-----------------------------------------------------------------------*/
@@ -304,16 +330,17 @@ public class ConstraintWidget {
/**
* Graph analysis
+ * @param optimizationLevel the current optimisation level
*/
- public void analyze() {
- Optimizer.analyze(this);
+ public void analyze(int optimizationLevel) {
+ Optimizer.analyze(optimizationLevel,this);
}
/**
* Try resolving the graph analysis
*/
public void resolve() {
- // basic constraints resolution is done in ResolutionNode
+ // basic constraints resolution is done in ResolutionAnchor
}
/**
@@ -322,15 +349,37 @@ public class ConstraintWidget {
* @return true if the widget is fully resolved
*/
public boolean isFullyResolved() {
- if (mLeft.getResolutionNode().state == ResolutionNode.RESOLVED
- && mRight.getResolutionNode().state == ResolutionNode.RESOLVED
- && mTop.getResolutionNode().state == ResolutionNode.RESOLVED
- && mBottom.getResolutionNode().state == ResolutionNode.RESOLVED) {
+ if (mLeft.getResolutionNode().state == ResolutionAnchor.RESOLVED
+ && mRight.getResolutionNode().state == ResolutionAnchor.RESOLVED
+ && mTop.getResolutionNode().state == ResolutionAnchor.RESOLVED
+ && mBottom.getResolutionNode().state == ResolutionAnchor.RESOLVED) {
return true;
}
return false;
}
+ /**
+ * Return a ResolutionDimension for the width
+ * @return
+ */
+ public ResolutionDimension getResolutionWidth() {
+ if (mResolutionWidth == null) {
+ mResolutionWidth = new ResolutionDimension();
+ }
+ return mResolutionWidth;
+ }
+
+ /**
+ * Return a ResolutionDimension for the height
+ * @return
+ */
+ public ResolutionDimension getResolutionHeight() {
+ if (mResolutionHeight == null) {
+ mResolutionHeight = new ResolutionDimension();
+ }
+ return mResolutionHeight;
+ }
+
/*-----------------------------------------------------------------------*/
// Creation
/*-----------------------------------------------------------------------*/
@@ -2244,7 +2293,7 @@ public class ConstraintWidget {
|| mResolvedDimensionRatioSide == UNKNOWN);
if (mBaselineDistance > 0) {
- if (mBaseline.getResolutionNode().state == ResolutionNode.RESOLVED) {
+ if (mBaseline.getResolutionNode().state == ResolutionAnchor.RESOLVED) {
mBaseline.getResolutionNode().addResolvedValue(system);
} else {
system.addEquality(baseline, top, getBaselineDistance(), SolverVariable.STRENGTH_FIXED);
@@ -2382,8 +2431,8 @@ public class ConstraintWidget {
SolverVariable endTarget = system.createObjectVariable(endAnchor.getTarget());
if (system.graphOptimizer) {
- if (beginAnchor.getResolutionNode().state == ResolutionNode.RESOLVED
- && endAnchor.getResolutionNode().state == ResolutionNode.RESOLVED) {
+ if (beginAnchor.getResolutionNode().state == ResolutionAnchor.RESOLVED
+ && endAnchor.getResolutionNode().state == ResolutionAnchor.RESOLVED) {
if (system.getMetrics() != null) {
system.getMetrics().resolvedWidgets++;
}
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java
index 2d962f6..15a2ce2 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ConstraintWidgetContainer.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import static android.support.constraint.solver.LinearSystem.FULL_DEBUG;
+import static android.support.constraint.solver.widgets.ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
/**
* A container of ConstraintWidget that can layout its children
@@ -173,17 +174,17 @@ public class ConstraintWidgetContainer extends WidgetContainer {
if (widget instanceof ConstraintWidgetContainer) {
DimensionBehaviour horizontalBehaviour = widget.mListDimensionBehaviors[DIMENSION_HORIZONTAL];
DimensionBehaviour verticalBehaviour = widget.mListDimensionBehaviors[DIMENSION_VERTICAL];
- if (horizontalBehaviour == DimensionBehaviour.WRAP_CONTENT) {
+ if (horizontalBehaviour == WRAP_CONTENT) {
widget.setHorizontalDimensionBehaviour(DimensionBehaviour.FIXED);
}
- if (verticalBehaviour == DimensionBehaviour.WRAP_CONTENT) {
+ if (verticalBehaviour == WRAP_CONTENT) {
widget.setVerticalDimensionBehaviour(DimensionBehaviour.FIXED);
}
widget.addToSolver(system);
- if (horizontalBehaviour == DimensionBehaviour.WRAP_CONTENT) {
+ if (horizontalBehaviour == WRAP_CONTENT) {
widget.setHorizontalDimensionBehaviour(horizontalBehaviour);
}
- if (verticalBehaviour == DimensionBehaviour.WRAP_CONTENT) {
+ if (verticalBehaviour == WRAP_CONTENT) {
widget.setVerticalDimensionBehaviour(verticalBehaviour);
}
} else {
@@ -261,13 +262,14 @@ public class ConstraintWidgetContainer extends WidgetContainer {
/**
* Graph analysis
+ * @param optimizationLevel the current optimisation level
*/
@Override
- public void analyze() {
- super.analyze();
+ public void analyze(int optimizationLevel) {
+ super.analyze(optimizationLevel);
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
- mChildren.get(i).analyze();
+ mChildren.get(i).analyze(optimizationLevel);
}
}
@@ -308,49 +310,10 @@ public class ConstraintWidgetContainer extends WidgetContainer {
}
if (mOptimizationLevel != Optimizer.OPTIMIZATION_NONE) {
- if (DEBUG_GRAPH) {
- System.out.println("### Graph resolution... " + mWidth + " x " + mHeight + " ###");
- }
- final int count = mChildren.size();
- resetResolutionNodes();
- for (int i = 0; i < count; i++) {
- mChildren.get(i).resetResolutionNodes();
- }
- if (DEBUG_GRAPH) {
- System.out.println("### Update Constraints Graph ###");
- setDebugName("Root");
- }
-
- analyze();
-
- if (DEBUG_GRAPH) {
- for (int i = 0; i < mChildren.size(); i++) {
- ConstraintWidget widget = mChildren.get(i);
- System.out.println("(pre) child [" + i + "/" + mChildren.size() + "] - " + widget.mLeft.getResolutionNode()
- + ", " + widget.mTop.getResolutionNode()
- + ", " + widget.mRight.getResolutionNode()
- + ", " + widget.mBottom.getResolutionNode());
- }
- }
- ResolutionNode leftNode = getAnchor(ConstraintAnchor.Type.LEFT).getResolutionNode();
- ResolutionNode topNode = getAnchor(ConstraintAnchor.Type.TOP).getResolutionNode();
-
- if (DEBUG_GRAPH) {
- System.out.println("### RESOLUTION ###");
- }
-
- leftNode.resolve(null, 0);
- topNode.resolve(null, 0);
-
- if (DEBUG_GRAPH) {
- for (int i = 0; i < mChildren.size(); i++) {
- ConstraintWidget widget = mChildren.get(i);
- System.out.println("child [" + i + "/" + mChildren.size() + "] - " + widget.mLeft.getResolutionNode()
- + ", " + widget.mTop.getResolutionNode()
- + ", " + widget.mRight.getResolutionNode()
- + ", " + widget.mBottom.getResolutionNode());
- }
+ if (!optimizeFor(Optimizer.OPTIMIZATION_DIMENSIONS)) {
+ optimizeReset();
}
+ optimize();
mSystem.graphOptimizer = true;
} else {
mSystem.graphOptimizer = false;
@@ -433,24 +396,24 @@ public class ConstraintWidgetContainer extends WidgetContainer {
}
maxX = Math.max(mMinWidth, maxX);
maxY = Math.max(mMinHeight, maxY);
- if (originalHorizontalDimensionBehaviour == DimensionBehaviour.WRAP_CONTENT) {
+ if (originalHorizontalDimensionBehaviour == WRAP_CONTENT) {
if (getWidth() < maxX) {
if (DEBUG_LAYOUT) {
System.out.println("layout override width from " + getWidth() + " vs " + maxX);
}
setWidth(maxX);
- mListDimensionBehaviors[DIMENSION_HORIZONTAL] = DimensionBehaviour.WRAP_CONTENT; // force using the solver
+ mListDimensionBehaviors[DIMENSION_HORIZONTAL] = WRAP_CONTENT; // force using the solver
wrap_override = true;
needsSolving = true;
}
}
- if (originalVerticalDimensionBehaviour == DimensionBehaviour.WRAP_CONTENT) {
+ if (originalVerticalDimensionBehaviour == WRAP_CONTENT) {
if (getHeight() < maxY) {
if (DEBUG_LAYOUT) {
System.out.println("layout override height from " + getHeight() + " vs " + maxY);
}
setHeight(maxY);
- mListDimensionBehaviors[DIMENSION_VERTICAL] = DimensionBehaviour.WRAP_CONTENT; // force using the solver
+ mListDimensionBehaviors[DIMENSION_VERTICAL] = WRAP_CONTENT; // force using the solver
wrap_override = true;
needsSolving = true;
}
@@ -480,7 +443,7 @@ public class ConstraintWidgetContainer extends WidgetContainer {
}
if (!wrap_override) {
- if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.WRAP_CONTENT && prew > 0) {
+ 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);
@@ -492,7 +455,7 @@ public class ConstraintWidgetContainer extends WidgetContainer {
needsSolving = true;
}
}
- if (mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.WRAP_CONTENT && preh > 0) {
+ 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);
@@ -542,6 +505,89 @@ public class ConstraintWidgetContainer extends WidgetContainer {
}
}
+ public void preOptimize() {
+ optimizeReset();
+ analyze(mOptimizationLevel);
+ }
+
+ public void solveGraph() {
+ ResolutionAnchor leftNode = getAnchor(ConstraintAnchor.Type.LEFT).getResolutionNode();
+ ResolutionAnchor topNode = getAnchor(ConstraintAnchor.Type.TOP).getResolutionNode();
+
+ if (DEBUG_GRAPH) {
+ System.out.println("### RESOLUTION ###");
+ }
+
+ leftNode.resolve(null, 0);
+ topNode.resolve(null, 0);
+ }
+
+ public void resetGraph() {
+ ResolutionAnchor leftNode = getAnchor(ConstraintAnchor.Type.LEFT).getResolutionNode();
+ ResolutionAnchor topNode = getAnchor(ConstraintAnchor.Type.TOP).getResolutionNode();
+
+ if (DEBUG_GRAPH) {
+ System.out.println("### RESET ###");
+ }
+
+ leftNode.invalidateAnchors();
+ topNode.invalidateAnchors();
+ leftNode.resolve(null, 0);
+ topNode.resolve(null, 0);
+ }
+
+ public void optimizeForDimensions(int width, int height) {
+ if (mListDimensionBehaviors[HORIZONTAL] != WRAP_CONTENT && mResolutionWidth != null) {
+ mResolutionWidth.resolve(width);
+ }
+ if (mListDimensionBehaviors[VERTICAL] != WRAP_CONTENT && mResolutionHeight != null) {
+ mResolutionHeight.resolve(height);
+ }
+ }
+
+ public void optimizeReset() {
+ final int count = mChildren.size();
+ resetResolutionNodes();
+ for (int i = 0; i < count; i++) {
+ mChildren.get(i).resetResolutionNodes();
+ }
+ }
+
+ public void optimize() {
+ if (DEBUG_GRAPH) {
+ System.out.println("### Graph resolution... " + mWidth + " x " + mHeight + " ###");
+ }
+
+ if (DEBUG_GRAPH) {
+ System.out.println("### Update Constraints Graph ###");
+ setDebugName("Root");
+ }
+
+ if (!optimizeFor(Optimizer.OPTIMIZATION_DIMENSIONS)) {
+ analyze(mOptimizationLevel);
+ }
+
+ if (DEBUG_GRAPH) {
+ for (int i = 0; i < mChildren.size(); i++) {
+ ConstraintWidget widget = mChildren.get(i);
+ System.out.println("(pre) child [" + i + "/" + mChildren.size() + "] - " + widget.mLeft.getResolutionNode()
+ + ", " + widget.mTop.getResolutionNode()
+ + ", " + widget.mRight.getResolutionNode()
+ + ", " + widget.mBottom.getResolutionNode());
+ }
+ }
+ solveGraph();
+ if (DEBUG_GRAPH) {
+ for (int i = 0; i < mChildren.size(); i++) {
+ ConstraintWidget widget = mChildren.get(i);
+ System.out.println("child [" + i + "/" + mChildren.size() + "] - " + widget.mLeft.getResolutionNode()
+ + ", " + widget.mTop.getResolutionNode()
+ + ", " + widget.mRight.getResolutionNode()
+ + ", " + widget.mBottom.getResolutionNode());
+ }
+ }
+ }
+
/**
* Indicates if the container knows how to layout its content on its own
*
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/Guideline.java b/solver/src/main/java/android/support/constraint/solver/widgets/Guideline.java
index d51c299..66354e4 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/Guideline.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/Guideline.java
@@ -206,40 +206,41 @@ public class Guideline extends ConstraintWidget {
/**
* Graph analysis
+ * @param optimizationLevel
*/
@Override
- public void analyze() {
+ public void analyze(int optimizationLevel) {
ConstraintWidget constraintWidgetContainer = getParent();
if (constraintWidgetContainer == null) {
return;
}
if (getOrientation() == Guideline.VERTICAL) {
- mTop.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION,constraintWidgetContainer.mTop.getResolutionNode(), 0);
- mBottom.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), 0);
+ mTop.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION,constraintWidgetContainer.mTop.getResolutionNode(), 0);
+ mBottom.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), 0);
if (mRelativeBegin != -1) {
- mLeft.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), mRelativeBegin);
- mRight.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), mRelativeBegin);
+ mLeft.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), mRelativeBegin);
+ mRight.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), mRelativeBegin);
} else if (mRelativeEnd != -1) {
- mLeft.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mRight.getResolutionNode(), -mRelativeEnd);
- mRight.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mRight.getResolutionNode(), -mRelativeEnd);
+ mLeft.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mRight.getResolutionNode(), -mRelativeEnd);
+ mRight.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mRight.getResolutionNode(), -mRelativeEnd);
} else if (mRelativePercent != -1 && constraintWidgetContainer.getHorizontalDimensionBehaviour() == FIXED) {
int position = (int) (constraintWidgetContainer.mWidth * mRelativePercent);
- mLeft.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), position);
- mRight.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), position);
+ mLeft.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), position);
+ mRight.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), position);
}
} else {
- mLeft.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), 0);
- mRight.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), 0);
+ mLeft.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), 0);
+ mRight.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mLeft.getResolutionNode(), 0);
if (mRelativeBegin != -1) {
- mTop.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), mRelativeBegin);
- mBottom.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), mRelativeBegin);
+ mTop.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), mRelativeBegin);
+ mBottom.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), mRelativeBegin);
} else if (mRelativeEnd != -1) {
- mTop.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mBottom.getResolutionNode(), -mRelativeEnd);
- mBottom.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mBottom.getResolutionNode(), -mRelativeEnd);
+ mTop.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mBottom.getResolutionNode(), -mRelativeEnd);
+ mBottom.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mBottom.getResolutionNode(), -mRelativeEnd);
} else if (mRelativePercent != -1 && constraintWidgetContainer.getVerticalDimensionBehaviour() == FIXED) {
int position = (int) (constraintWidgetContainer.mHeight * mRelativePercent);
- mTop.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), position);
- mBottom.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), position);
+ mTop.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), position);
+ mBottom.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, constraintWidgetContainer.mTop.getResolutionNode(), position);
}
}
}
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/Optimizer.java b/solver/src/main/java/android/support/constraint/solver/widgets/Optimizer.java
index b2af858..c80ca07 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/Optimizer.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/Optimizer.java
@@ -31,8 +31,13 @@ public class Optimizer {
public static final int OPTIMIZATION_DIRECT = 1;
public static final int OPTIMIZATION_BARRIER = 1 << 1;
public static final int OPTIMIZATION_CHAIN = 1 << 2;
- public static final int OPTIMIZATION_RATIO = 1 << 3;
- public static final int OPTIMIZATION_STANDARD = OPTIMIZATION_DIRECT | OPTIMIZATION_BARRIER /* | OPTIMIZATION_CHAIN */;
+ public static final int OPTIMIZATION_DIMENSIONS = 1 << 3;
+ public static final int OPTIMIZATION_RATIO = 1 << 4;
+ public static final int OPTIMIZATION_STANDARD = OPTIMIZATION_DIRECT
+ | OPTIMIZATION_BARRIER
+ /* | OPTIMIZATION_CHAIN */
+ /* | OPTIMIZATION_DIMENSIONS */
+ ;
// Internal use.
static boolean[] flags = new boolean[3];
@@ -136,7 +141,7 @@ public class Optimizer {
*
* @param widget
*/
- static void analyze(ConstraintWidget widget) {
+ static void analyze(int optimisationLevel, ConstraintWidget widget) {
// Let's update the graph from the nodes!
// This will only apply if the nodes are not part of a chain.
@@ -145,57 +150,98 @@ public class Optimizer {
widget.updateResolutionNodes();
- ResolutionNode leftNode = widget.mLeft.getResolutionNode();
- ResolutionNode topNode = widget.mTop.getResolutionNode();
- ResolutionNode rightNode = widget.mRight.getResolutionNode();
- ResolutionNode bottomNode = widget.mBottom.getResolutionNode();
+ ResolutionAnchor leftNode = widget.mLeft.getResolutionNode();
+ ResolutionAnchor topNode = widget.mTop.getResolutionNode();
+ ResolutionAnchor rightNode = widget.mRight.getResolutionNode();
+ ResolutionAnchor bottomNode = widget.mBottom.getResolutionNode();
+
+ boolean optimiseDimensions = (optimisationLevel & OPTIMIZATION_DIMENSIONS) == OPTIMIZATION_DIMENSIONS;
// First the horizontal nodes...
- if (leftNode.type != ResolutionNode.CHAIN_CONNECTION
- && rightNode.type != ResolutionNode.CHAIN_CONNECTION) {
+ if (leftNode.type != ResolutionAnchor.CHAIN_CONNECTION
+ && rightNode.type != ResolutionAnchor.CHAIN_CONNECTION) {
if (widget.mListDimensionBehaviors[HORIZONTAL] == FIXED) {
if (widget.mLeft.mTarget == null && widget.mRight.mTarget == null) {
- leftNode.setType(ResolutionNode.DIRECT_CONNECTION);
- rightNode.setType(ResolutionNode.DIRECT_CONNECTION);
- rightNode.dependsOn(leftNode, widget.getWidth());
+ leftNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ rightNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ if (optimiseDimensions) {
+ rightNode.dependsOn(leftNode, 1, widget.getResolutionWidth());
+ } else {
+ rightNode.dependsOn(leftNode, widget.getWidth());
+ }
} else if (widget.mLeft.mTarget != null && widget.mRight.mTarget == null) {
- leftNode.setType(ResolutionNode.DIRECT_CONNECTION);
- rightNode.setType(ResolutionNode.DIRECT_CONNECTION);
- rightNode.dependsOn(leftNode, widget.getWidth());
+ leftNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ rightNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ if (optimiseDimensions) {
+ rightNode.dependsOn(leftNode, 1, widget.getResolutionWidth());
+ } else {
+ rightNode.dependsOn(leftNode, widget.getWidth());
+ }
} else if (widget.mLeft.mTarget == null && widget.mRight.mTarget != null) {
- leftNode.setType(ResolutionNode.DIRECT_CONNECTION);
- rightNode.setType(ResolutionNode.DIRECT_CONNECTION);
+ leftNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ rightNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
leftNode.dependsOn(rightNode, -widget.getWidth());
+ if (optimiseDimensions) {
+ leftNode.dependsOn(rightNode, -1, widget.getResolutionWidth());
+ } else {
+ leftNode.dependsOn(rightNode, -widget.getWidth());
+ }
} else if (widget.mLeft.mTarget != null && widget.mRight.mTarget != null) {
- leftNode.setType(ResolutionNode.CENTER_CONNECTION);
- rightNode.setType(ResolutionNode.CENTER_CONNECTION);
- leftNode.setOpposite(rightNode, -widget.getWidth());
- rightNode.setOpposite(leftNode, widget.getWidth());
+ leftNode.setType(ResolutionAnchor.CENTER_CONNECTION);
+ rightNode.setType(ResolutionAnchor.CENTER_CONNECTION);
+ if (optimiseDimensions) {
+ widget.getResolutionWidth().addDependent(leftNode);
+ widget.getResolutionWidth().addDependent(rightNode);
+ leftNode.setOpposite(rightNode, -1, widget.getResolutionWidth());
+ rightNode.setOpposite(leftNode, 1, widget.getResolutionWidth());
+ } else {
+ leftNode.setOpposite(rightNode, -widget.getWidth());
+ rightNode.setOpposite(leftNode, widget.getWidth());
+ }
}
} else if (widget.mListDimensionBehaviors[HORIZONTAL] == MATCH_CONSTRAINT
&& optimizableMatchConstraint(widget, HORIZONTAL)) {
int width = widget.getWidth();
- if (widget.mDimensionRatio != 0) {
- width = (int) (widget.getHeight() * widget.mDimensionRatio);
- }
- leftNode.setType(ResolutionNode.DIRECT_CONNECTION);
- rightNode.setType(ResolutionNode.DIRECT_CONNECTION);
+ // TODO: ratio won't work with optimiseDimensions as it is
+ // ...but ratio won't work period for now as optimizableMatchConstraint will return false
+ // if (widget.mDimensionRatio != 0) {
+ // width = (int) (widget.getHeight() * widget.mDimensionRatio);
+ // }
+ leftNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ rightNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
if (widget.mLeft.mTarget == null && widget.mRight.mTarget == null) {
- rightNode.dependsOn(leftNode, width);
+ if (optimiseDimensions) {
+ rightNode.dependsOn(leftNode, 1, widget.getResolutionWidth());
+ } else {
+ rightNode.dependsOn(leftNode, width);
+ }
} else if (widget.mLeft.mTarget != null && widget.mRight.mTarget == null) {
- rightNode.dependsOn(leftNode, width);
+ if (optimiseDimensions) {
+ rightNode.dependsOn(leftNode, 1, widget.getResolutionWidth());
+ } else {
+ rightNode.dependsOn(leftNode, width);
+ }
} else if (widget.mLeft.mTarget == null && widget.mRight.mTarget != null) {
- leftNode.dependsOn(rightNode, -width);
+ if (optimiseDimensions) {
+ leftNode.dependsOn(rightNode, -1, widget.getResolutionWidth());
+ } else {
+ leftNode.dependsOn(rightNode, -width);
+ }
} else if (widget.mLeft.mTarget != null && widget.mRight.mTarget != null) {
+ if (optimiseDimensions) {
+ widget.getResolutionWidth().addDependent(leftNode);
+ widget.getResolutionWidth().addDependent(rightNode);
+ }
if (widget.mDimensionRatio == 0) {
- leftNode.setType(ResolutionNode.MATCH_CONNECTION);
- rightNode.setType(ResolutionNode.MATCH_CONNECTION);
+ leftNode.setType(ResolutionAnchor.MATCH_CONNECTION);
+ rightNode.setType(ResolutionAnchor.MATCH_CONNECTION);
leftNode.setOpposite(rightNode, 0);
rightNode.setOpposite(leftNode, 0);
} else {
- leftNode.setType(ResolutionNode.CENTER_CONNECTION);
- rightNode.setType(ResolutionNode.CENTER_CONNECTION);
+ // TODO -- fix ratio. For now this won't work.
+ leftNode.setType(ResolutionAnchor.CENTER_CONNECTION);
+ rightNode.setType(ResolutionAnchor.CENTER_CONNECTION);
leftNode.setOpposite(rightNode, -width);
rightNode.setOpposite(leftNode, width);
widget.setWidth(width);
@@ -206,70 +252,106 @@ public class Optimizer {
// ...then the vertical ones
- if (topNode.type != ResolutionNode.CHAIN_CONNECTION
- && bottomNode.type != ResolutionNode.CHAIN_CONNECTION
- /* && mBaseline.getResolutionNode().type == ResolutionNode.UNCONNECTED */) {
+ if (topNode.type != ResolutionAnchor.CHAIN_CONNECTION
+ && bottomNode.type != ResolutionAnchor.CHAIN_CONNECTION
+ /* && mBaseline.getResolutionNode().type == ResolutionAnchor.UNCONNECTED */) {
if (widget.mListDimensionBehaviors[VERTICAL] == FIXED) {
if (widget.mTop.mTarget == null && widget.mBottom.mTarget == null) {
- topNode.setType(ResolutionNode.DIRECT_CONNECTION);
- bottomNode.setType(ResolutionNode.DIRECT_CONNECTION);
- bottomNode.dependsOn(topNode, widget.getHeight());
+ topNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ bottomNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ if (optimiseDimensions) {
+ bottomNode.dependsOn(topNode, 1, widget.getResolutionHeight());
+ } else {
+ bottomNode.dependsOn(topNode, widget.getHeight());
+ }
if (widget.mBaseline.mTarget != null) {
- widget.mBaseline.getResolutionNode().setType(ResolutionNode.DIRECT_CONNECTION);
- topNode.dependsOn(ResolutionNode.DIRECT_CONNECTION,
+ widget.mBaseline.getResolutionNode().setType(ResolutionAnchor.DIRECT_CONNECTION);
+ topNode.dependsOn(ResolutionAnchor.DIRECT_CONNECTION,
widget.mBaseline.getResolutionNode(), -widget.mBaselineDistance);
}
} else if (widget.mTop.mTarget != null && widget.mBottom.mTarget == null) {
- topNode.setType(ResolutionNode.DIRECT_CONNECTION);
- bottomNode.setType(ResolutionNode.DIRECT_CONNECTION);
- bottomNode.dependsOn(topNode, widget.getHeight());
+ topNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ bottomNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ if (optimiseDimensions) {
+ bottomNode.dependsOn(topNode, 1, widget.getResolutionHeight());
+ } else {
+ bottomNode.dependsOn(topNode, widget.getHeight());
+ }
if (widget.mBaselineDistance > 0) {
- widget.mBaseline.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
+ widget.mBaseline.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
}
} else if (widget.mTop.mTarget == null && widget.mBottom.mTarget != null) {
- topNode.setType(ResolutionNode.DIRECT_CONNECTION);
- bottomNode.setType(ResolutionNode.DIRECT_CONNECTION);
- topNode.dependsOn(bottomNode, -widget.getHeight());
+ topNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ bottomNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ if (optimiseDimensions) {
+ topNode.dependsOn(bottomNode, -1, widget.getResolutionHeight());
+ } else {
+ topNode.dependsOn(bottomNode, -widget.getHeight());
+ }
if (widget.mBaselineDistance > 0) {
- widget.mBaseline.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
+ widget.mBaseline.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
}
} else if (widget.mTop.mTarget != null && widget.mBottom.mTarget != null) {
- topNode.setType(ResolutionNode.CENTER_CONNECTION);
- bottomNode.setType(ResolutionNode.CENTER_CONNECTION);
- topNode.setOpposite(bottomNode, -widget.getHeight());
- bottomNode.setOpposite(topNode, widget.getHeight());
+ topNode.setType(ResolutionAnchor.CENTER_CONNECTION);
+ bottomNode.setType(ResolutionAnchor.CENTER_CONNECTION);
+ if (optimiseDimensions) {
+ topNode.setOpposite(bottomNode, -1, widget.getResolutionHeight());
+ bottomNode.setOpposite(topNode, 1, widget.getResolutionHeight());
+ widget.getResolutionHeight().addDependent(topNode);
+ widget.getResolutionWidth().addDependent(bottomNode);
+ } else {
+ topNode.setOpposite(bottomNode, -widget.getHeight());
+ bottomNode.setOpposite(topNode, widget.getHeight());
+ }
if (widget.mBaselineDistance > 0) {
- widget.mBaseline.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
+ widget.mBaseline.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
}
}
} else if (widget.mListDimensionBehaviors[VERTICAL] == MATCH_CONSTRAINT
&& optimizableMatchConstraint(widget, VERTICAL)) {
int height = widget.getHeight();
- if (widget.mDimensionRatio != 0) {
- height = (int) (widget.getWidth() * widget.mDimensionRatio);
- }
- topNode.setType(ResolutionNode.DIRECT_CONNECTION);
- bottomNode.setType(ResolutionNode.DIRECT_CONNECTION);
+ // TODO: fix ratio (right it won't work, optimizableMatchConstraint will return false
+ // if (widget.mDimensionRatio != 0) {
+ // height = (int) (widget.getWidth() * widget.mDimensionRatio);
+ // }
+ topNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
+ bottomNode.setType(ResolutionAnchor.DIRECT_CONNECTION);
if (widget.mTop.mTarget == null && widget.mBottom.mTarget == null) {
- bottomNode.dependsOn(topNode, height);
+ if (optimiseDimensions) {
+ bottomNode.dependsOn(topNode, 1, widget.getResolutionHeight());
+ } else {
+ bottomNode.dependsOn(topNode, height);
+ }
} else if (widget.mTop.mTarget != null && widget.mBottom.mTarget == null) {
- bottomNode.dependsOn(topNode, height);
+ if (optimiseDimensions) {
+ bottomNode.dependsOn(topNode, 1, widget.getResolutionHeight());
+ } else {
+ bottomNode.dependsOn(topNode, height);
+ }
} else if (widget.mTop.mTarget == null && widget.mBottom.mTarget != null) {
- topNode.dependsOn(bottomNode, -height);
+ if (optimiseDimensions) {
+ topNode.dependsOn(bottomNode, -1, widget.getResolutionHeight());
+ } else {
+ topNode.dependsOn(bottomNode, -height);
+ }
} else if (widget.mTop.mTarget != null && widget.mBottom.mTarget != null) {
+ if (optimiseDimensions) {
+ widget.getResolutionHeight().addDependent(topNode);
+ widget.getResolutionWidth().addDependent(bottomNode);
+ }
if (widget.mDimensionRatio == 0) {
- topNode.setType(ResolutionNode.MATCH_CONNECTION);
- bottomNode.setType(ResolutionNode.MATCH_CONNECTION);
+ topNode.setType(ResolutionAnchor.MATCH_CONNECTION);
+ bottomNode.setType(ResolutionAnchor.MATCH_CONNECTION);
topNode.setOpposite(bottomNode, 0);
bottomNode.setOpposite(topNode, 0);
} else {
- topNode.setType(ResolutionNode.CENTER_CONNECTION);
- bottomNode.setType(ResolutionNode.CENTER_CONNECTION);
+ topNode.setType(ResolutionAnchor.CENTER_CONNECTION);
+ bottomNode.setType(ResolutionAnchor.CENTER_CONNECTION);
topNode.setOpposite(bottomNode, -height);
bottomNode.setOpposite(topNode, height);
widget.setHeight(height);
if (widget.mBaselineDistance > 0) {
- widget.mBaseline.getResolutionNode().dependsOn(ResolutionNode.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
+ widget.mBaseline.getResolutionNode().dependsOn(ResolutionAnchor.DIRECT_CONNECTION, topNode, widget.mBaselineDistance);
}
}
}
@@ -428,8 +510,8 @@ public class Optimizer {
}
ConstraintWidget last = widget;
- ResolutionNode firstNode = first.mListAnchors[offset].getResolutionNode();
- ResolutionNode lastNode = last.mListAnchors[offset + 1].getResolutionNode();
+ ResolutionAnchor firstNode = first.mListAnchors[offset].getResolutionNode();
+ ResolutionAnchor lastNode = last.mListAnchors[offset + 1].getResolutionNode();
if (firstNode.target == null || lastNode.target == null) {
// dangling chain, let's bail for now
@@ -437,8 +519,8 @@ public class Optimizer {
}
// let's look at the endpoints
- if (firstNode.target.state != ResolutionNode.RESOLVED
- && lastNode.target.state != ResolutionNode.RESOLVED) {
+ if (firstNode.target.state != ResolutionAnchor.RESOLVED
+ && lastNode.target.state != ResolutionAnchor.RESOLVED) {
// No resolved endpoints, let's exit
return false;
}
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionAnchor.java b/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionAnchor.java
new file mode 100644
index 0000000..32e2b6e
--- /dev/null
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionAnchor.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2018 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 android.support.constraint.solver.widgets;
+
+import android.support.constraint.solver.LinearSystem;
+import android.support.constraint.solver.SolverVariable;
+
+/**
+ * Implements a mechanism to resolve nodes via a dependency graph
+ */
+public class ResolutionAnchor extends ResolutionNode {
+ ConstraintAnchor myAnchor;
+ float computedValue;
+ ResolutionAnchor target;
+ float offset;
+
+ ResolutionAnchor resolvedTarget;
+ float resolvedOffset;
+
+ int type = UNCONNECTED;
+
+ public static final int UNCONNECTED = 0;
+ public static final int DIRECT_CONNECTION = 1;
+ public static final int CENTER_CONNECTION = 2;
+ public static final int MATCH_CONNECTION = 3;
+ public static final int CHAIN_CONNECTION = 4;
+ public static final int BARRIER_CONNECTION = 5;
+
+ private ResolutionAnchor opposite;
+ private float oppositeOffset;
+
+ private ResolutionDimension dimension = null;
+ private int dimensionMultiplier = 1;
+ private ResolutionDimension oppositeDimension = null;
+ private int oppositeDimensionMultiplier = 1;
+
+ public ResolutionAnchor(ConstraintAnchor anchor) {
+ myAnchor = anchor;
+ }
+
+ public void remove(ResolutionDimension resolutionDimension) {
+ if (dimension == resolutionDimension) {
+ dimension = null;
+ offset = dimensionMultiplier;
+ } else if (dimension == oppositeDimension) {
+ oppositeDimension = null;
+ oppositeOffset = oppositeDimensionMultiplier;
+ }
+ resolve();
+ }
+
+ @Override
+ public String toString() {
+ if (state == RESOLVED) {
+ if (resolvedTarget == this) {
+ return "[" + myAnchor + ", RESOLVED: " + resolvedOffset + "] " + " type: " + sType(type);
+ }
+ return "[" + myAnchor + ", RESOLVED: " + resolvedTarget + ":" + resolvedOffset + "]"
+ + " type: " + sType(type);
+ }
+ return "{ " + myAnchor + " UNRESOLVED} type: " + sType(type);
+ }
+
+ public void resolve(ResolutionAnchor target, float offset) {
+ if (state == UNRESOLVED || (resolvedTarget != target && resolvedOffset != offset)) {
+ resolvedTarget = target;
+ resolvedOffset = offset;
+ if (state == RESOLVED) {
+ invalidate();
+ }
+ didResolve();
+ }
+ }
+
+ String sType(int type) {
+ if (type == 1) {
+ return "DIRECT";
+ } else if (type == 2) {
+ return "CENTER";
+ } else if (type == 3) {
+ return "MATCH";
+ } else if (type == 4) {
+ return "CHAIN";
+ } else if (type == 5) {
+ return "BARRIER";
+ }
+ return "UNCONNECTED";
+ }
+
+ @Override
+ public void resolve() {
+ if (ConstraintWidgetContainer.DEBUG_GRAPH) {
+ System.out.println("resolve " + this + " type: " + sType(type)
+ + " current target: " + target + " with offset " + offset);
+ }
+ if (state == RESOLVED) {
+ return;
+ }
+ if (type == CHAIN_CONNECTION) {
+ return;
+ }
+ if (dimension != null) {
+ if (dimension.state != RESOLVED) {
+ return;
+ }
+ offset = dimensionMultiplier * dimension.value;
+ }
+ if (oppositeDimension != null) {
+ if (oppositeDimension.state != RESOLVED) {
+ return;
+ }
+ oppositeOffset = oppositeDimensionMultiplier * oppositeDimension.value;
+ }
+ if (type == DIRECT_CONNECTION
+ && ((target == null) || (target.state == RESOLVED))) {
+
+ // Let's solve direct connections...
+
+ if (target == null) {
+ resolvedTarget = this;
+ resolvedOffset = offset;
+ } else {
+ resolvedTarget = target.resolvedTarget;
+ resolvedOffset = target.resolvedOffset + offset;
+ }
+ didResolve();
+ } else if (type == CENTER_CONNECTION
+ && target != null
+ && target.state == RESOLVED
+ && opposite != null && opposite.target != null
+ && opposite.target.state == RESOLVED) {
+
+ // Let's solve center connections...
+
+ if (LinearSystem.getMetrics() != null) {
+ LinearSystem.getMetrics().centerConnectionResolved++;
+ }
+ resolvedTarget = target.resolvedTarget;
+ opposite.resolvedTarget = opposite.target.resolvedTarget;
+
+ float distance = 0;
+ float percent = 0.5f;
+
+ if (oppositeOffset > 0) {
+ // we are right or bottom
+ distance = target.resolvedOffset - opposite.target.resolvedOffset;
+ } else {
+ distance = opposite.target.resolvedOffset - target.resolvedOffset;
+ }
+
+ if (myAnchor.mType == ConstraintAnchor.Type.LEFT
+ || myAnchor.mType == ConstraintAnchor.Type.RIGHT) {
+ distance -= myAnchor.mOwner.getWidth();
+ percent = myAnchor.mOwner.mHorizontalBiasPercent;
+ } else {
+ distance -= myAnchor.mOwner.getHeight();
+ percent = myAnchor.mOwner.mVerticalBiasPercent;
+ }
+ int margin = myAnchor.getMargin();
+ int oppositeMargin = opposite.myAnchor.getMargin();
+ if (myAnchor.getTarget() == opposite.myAnchor.getTarget()) {
+ percent = 0.5f;
+ margin = 0;
+ oppositeMargin = 0;
+ }
+
+ distance -= margin;
+ distance -= oppositeMargin;
+
+ if (oppositeOffset > 0) {
+ // we are right or bottom
+ opposite.resolvedOffset = opposite.target.resolvedOffset
+ + oppositeMargin + distance * percent;
+ resolvedOffset = target.resolvedOffset - margin - (distance * (1 - percent));
+ } else {
+ resolvedOffset = target.resolvedOffset + margin + distance * percent;
+ opposite.resolvedOffset = opposite.target.resolvedOffset
+ - oppositeMargin - (distance * (1 - percent));
+ }
+
+ didResolve();
+ opposite.didResolve();
+ } else if (type == MATCH_CONNECTION
+ && target != null
+ && target.state == RESOLVED
+ && opposite != null && opposite.target != null
+ && opposite.target.state == RESOLVED) {
+
+ // Let's solve match connections...
+
+ if (LinearSystem.getMetrics() != null) {
+ LinearSystem.getMetrics().matchConnectionResolved++;
+ }
+ resolvedTarget = target.resolvedTarget;
+ opposite.resolvedTarget = opposite.target.resolvedTarget;
+
+ resolvedOffset = target.resolvedOffset + offset;
+ opposite.resolvedOffset = opposite.target.resolvedOffset + opposite.offset;
+
+ didResolve();
+ opposite.didResolve();
+ } else if (type == BARRIER_CONNECTION) {
+ myAnchor.mOwner.resolve();
+ }
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ target = null;
+ offset = 0;
+ dimension = null;
+ dimensionMultiplier = 1;
+ oppositeDimension = null;
+ oppositeDimensionMultiplier = 1;
+ resolvedTarget = null;
+ resolvedOffset = 0;
+ computedValue = 0;
+ opposite = null;
+ oppositeOffset = 0;
+ type = UNCONNECTED;
+ }
+
+ public void update() {
+ ConstraintAnchor targetAnchor = myAnchor.getTarget();
+ if (targetAnchor == null) {
+ return;
+ }
+ if (targetAnchor.getTarget() == myAnchor) {
+ type = CHAIN_CONNECTION;
+ targetAnchor.getResolutionNode().type = CHAIN_CONNECTION;
+ }
+ int margin = myAnchor.getMargin();
+ if (myAnchor.mType == ConstraintAnchor.Type.RIGHT
+ || myAnchor.mType == ConstraintAnchor.Type.BOTTOM) {
+ margin = -margin;
+ }
+ dependsOn(targetAnchor.getResolutionNode(), margin);
+ }
+
+ public void dependsOn(int type, ResolutionAnchor node, int offset) {
+ this.type = type;
+ target = node;
+ this.offset = offset;
+ target.addDependent(this);
+ if (ConstraintWidgetContainer.DEBUG_GRAPH) {
+ System.out.println("a- " + this + " DEPENDS [" + sType(type) + "] ON " + node + " WITH OFFSET " + offset);
+ }
+ }
+
+ public void dependsOn(ResolutionAnchor node, int offset) {
+ target = node;
+ this.offset = offset;
+ target.addDependent(this);
+ if (ConstraintWidgetContainer.DEBUG_GRAPH) {
+ System.out.println("b- " + this + " DEPENDS [" + sType(type) + "] ON " + node + " WITH OFFSET " + offset);
+ }
+ }
+
+ public void dependsOn(ResolutionAnchor node, int multiplier, ResolutionDimension dimension) {
+ target = node;
+ target.addDependent(this);
+ this.dimension = dimension;
+ this.dimensionMultiplier = multiplier;
+ this.dimension.addDependent(this);
+
+ if (ConstraintWidgetContainer.DEBUG_GRAPH) {
+ System.out.println("c- " + this + " DEPENDS [" + sType(type) + "] ON " + node + " WITH DIMENSION " + dimension);
+ }
+ }
+
+ public void setOpposite(ResolutionAnchor opposite, float oppositeOffset) {
+ this.opposite = opposite;
+ this.oppositeOffset = oppositeOffset;
+ }
+
+ public void setOpposite(ResolutionAnchor opposite, int multiplier, ResolutionDimension dimension) {
+ this.opposite = opposite;
+ this.oppositeDimension = dimension;
+ this.oppositeDimensionMultiplier = multiplier;
+ }
+
+ void addResolvedValue(LinearSystem system) {
+ SolverVariable sv = myAnchor.getSolverVariable();
+
+ if (resolvedTarget == null) {
+ system.addEquality(sv, (int) resolvedOffset);
+ } else {
+ SolverVariable v = system.createObjectVariable(resolvedTarget.myAnchor);
+ system.addEquality(sv, v, (int) resolvedOffset, SolverVariable.STRENGTH_FIXED);
+ }
+ }
+
+ public float getResolvedValue() {
+ return resolvedOffset;
+ }
+}
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionDimension.java b/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionDimension.java
new file mode 100644
index 0000000..df994ac
--- /dev/null
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionDimension.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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 android.support.constraint.solver.widgets;
+
+/**
+ * Resolution node for widget dimensions
+ */
+public class ResolutionDimension extends ResolutionNode {
+
+ float value = 0;
+
+ public void reset() {
+ super.reset();
+ value = 0;
+ }
+
+ public void resolve(int value) {
+ if (state == UNRESOLVED || this.value != value) {
+ this.value = value;
+ if (state == RESOLVED) {
+ invalidate();
+ }
+ didResolve();
+ }
+ }
+
+ public void remove() {
+ state = REMOVED;
+ }
+} \ No newline at end of file
diff --git a/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionNode.java b/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionNode.java
index d421f68..31fb7ec 100644
--- a/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionNode.java
+++ b/solver/src/main/java/android/support/constraint/solver/widgets/ResolutionNode.java
@@ -15,32 +15,13 @@
*/
package android.support.constraint.solver.widgets;
-import android.support.constraint.solver.LinearSystem;
-import android.support.constraint.solver.SolverVariable;
-
import java.util.HashSet;
/**
- * Implements a mechanism to resolve nodes via a dependency graph
+ * Root class for Resolution nodes used by the direct solver.
*/
public class ResolutionNode {
- ConstraintAnchor myAnchor;
- float computedValue;
- ResolutionNode target;
- float offset;
-
- HashSet<ResolutionNode> dependents = new HashSet<>(4);
- ResolutionNode resolvedTarget;
- float resolvedOffset;
-
- int type = UNCONNECTED;
-
- public static final int UNCONNECTED = 0;
- public static final int DIRECT_CONNECTION = 1;
- public static final int CENTER_CONNECTION = 2;
- public static final int MATCH_CONNECTION = 3;
- public static final int CHAIN_CONNECTION = 4;
- public static final int BARRIER_CONNECTION = 5;
+ HashSet<ResolutionNode> dependents = new HashSet<>(2);
/**
* A node has two possible states:
@@ -49,247 +30,57 @@ public class ResolutionNode {
*/
public static final int UNRESOLVED = 0;
public static final int RESOLVED = 1;
+ public static final int REMOVED = 2;
int state = UNRESOLVED;
- private ResolutionNode opposite;
- private float oppositeOffset;
-
- public ResolutionNode(ConstraintAnchor anchor) {
- myAnchor = anchor;
- }
-
- @Override
- public String toString() {
- if (state == RESOLVED) {
- if (resolvedTarget == this) {
- return "[" + myAnchor + ", RESOLVED: " + resolvedOffset + "]";
- }
- return "[" + myAnchor + ", RESOLVED: " + resolvedTarget + ":" + resolvedOffset + "]";
- }
- return "{ " + myAnchor + " UNRESOLVED}";
- }
-
- public void didResolve() {
- state = RESOLVED;
- if (ConstraintWidgetContainer.DEBUG_GRAPH) {
- System.out.println(" -> did resolve " + this + " type: " + sType(type));
- for (ResolutionNode node : dependents) {
- System.out.println(" * dependent: " + node);
- }
- }
-
- for (ResolutionNode node : dependents) {
- node.resolve();
- }
- }
-
- public void resolve(ResolutionNode target, float offset) {
- resolvedTarget = target;
- resolvedOffset = offset;
- didResolve();
- }
-
- String sType(int type) {
- if (type == 1) {
- return "DIRECT";
- } else if (type == 2) {
- return "CENTER";
- } else if (type == 3) {
- return "MATCH";
- } else if (type == 4) {
- return "CHAIN";
- } else if (type == 5) {
- return "BARRIER";
- }
- return "UNCONNECTED";
- }
-
- public void resolve() {
- if (ConstraintWidgetContainer.DEBUG_GRAPH) {
- System.out.println("resolve " + this + " type: " + sType(type)
- + " current target: " + target + " with offset " + offset);
- }
- if (state == RESOLVED) {
- return;
- }
- if (type == CHAIN_CONNECTION) {
- return;
- }
- if (type == DIRECT_CONNECTION
- && ((target == null) || (target.state == RESOLVED))) {
-
- // Let's solve direct connections...
-
- if (target == null) {
- resolvedTarget = this;
- resolvedOffset = offset;
- } else {
- resolvedTarget = target.resolvedTarget;
- resolvedOffset = target.resolvedOffset + offset;
- }
- didResolve();
- } else if (type == CENTER_CONNECTION
- && target != null
- && target.state == RESOLVED
- && opposite != null && opposite.target != null
- && opposite.target.state == RESOLVED) {
-
- // Let's solve center connections...
-
- if (LinearSystem.getMetrics() != null) {
- LinearSystem.getMetrics().centerConnectionResolved++;
- }
- resolvedTarget = target.resolvedTarget;
- opposite.resolvedTarget = opposite.target.resolvedTarget;
-
- float distance = 0;
- float percent = 0.5f;
-
- if (oppositeOffset > 0) {
- // we are right or bottom
- distance = target.resolvedOffset - opposite.target.resolvedOffset;
- } else {
- distance = opposite.target.resolvedOffset - target.resolvedOffset;
- }
-
- if (myAnchor.mType == ConstraintAnchor.Type.LEFT
- || myAnchor.mType == ConstraintAnchor.Type.RIGHT) {
- distance -= myAnchor.mOwner.getWidth();
- percent = myAnchor.mOwner.mHorizontalBiasPercent;
- } else {
- distance -= myAnchor.mOwner.getHeight();
- percent = myAnchor.mOwner.mVerticalBiasPercent;
- }
- int margin = myAnchor.getMargin();
- int oppositeMargin = opposite.myAnchor.getMargin();
- if (myAnchor.getTarget() == opposite.myAnchor.getTarget()) {
- percent = 0.5f;
- margin = 0;
- oppositeMargin = 0;
- }
-
- distance -= margin;
- distance -= oppositeMargin;
-
- if (oppositeOffset > 0) {
- // we are right or bottom
- opposite.resolvedOffset = opposite.target.resolvedOffset
- + oppositeMargin + distance * percent;
- resolvedOffset = target.resolvedOffset - margin - (distance * (1 - percent));
- } else {
- resolvedOffset = target.resolvedOffset + margin + distance * percent;
- opposite.resolvedOffset = opposite.target.resolvedOffset
- - oppositeMargin - (distance * (1 - percent));
- }
-
- didResolve();
- opposite.didResolve();
- } else if (type == MATCH_CONNECTION
- && target != null
- && target.state == RESOLVED
- && opposite != null && opposite.target != null
- && opposite.target.state == RESOLVED) {
-
- // Let's solve match connections...
-
- if (LinearSystem.getMetrics() != null) {
- LinearSystem.getMetrics().matchConnectionResolved++;
- }
- resolvedTarget = target.resolvedTarget;
- opposite.resolvedTarget = opposite.target.resolvedTarget;
-
- resolvedOffset = target.resolvedOffset + offset;
- opposite.resolvedOffset = opposite.target.resolvedOffset + opposite.offset;
-
- didResolve();
- opposite.didResolve();
- } else if (type == BARRIER_CONNECTION) {
- myAnchor.mOwner.resolve();
- }
- }
-
- // First pass we build a graph of ResolutionNode
-
- public void setType(int type) {
- this.type = type;
- }
public void addDependent(ResolutionNode node) {
dependents.add(node);
}
- public void resetResolution() {
+ public void reset() {
state = UNRESOLVED;
- resolvedTarget = null;
- resolvedOffset = 0;
+ dependents.clear();
}
- public void reset() {
- target = null;
- offset = 0;
- dependents.clear();
- resolvedTarget = null;
- resolvedOffset = 0;
- computedValue = 0;
- opposite = null;
- oppositeOffset = 0;
- type = UNCONNECTED;
+ public void invalidate() {
state = UNRESOLVED;
+ for (ResolutionNode node : dependents) {
+ node.invalidate();
+ }
}
- public void update() {
- ConstraintAnchor targetAnchor = myAnchor.getTarget();
- if (targetAnchor == null) {
- return;
+ public void invalidateAnchors() {
+ if (this instanceof ResolutionAnchor) {
+ state = UNRESOLVED;
}
- if (targetAnchor.getTarget() == myAnchor) {
- type = CHAIN_CONNECTION;
- targetAnchor.getResolutionNode().type = CHAIN_CONNECTION;
- }
- int margin = myAnchor.getMargin();
- if (myAnchor.mType == ConstraintAnchor.Type.RIGHT
- || myAnchor.mType == ConstraintAnchor.Type.BOTTOM) {
- margin = -margin;
+ for (ResolutionNode node : dependents) {
+ node.invalidateAnchors();
}
- dependsOn(targetAnchor.getResolutionNode(), margin);
}
- public void useAnchor(ConstraintAnchor anchor) {
- ResolutionNode node = anchor.getResolutionNode();
- }
-
- public void dependsOn(int type, ResolutionNode node, int offset) {
- this.type = type;
- target = node;
- this.offset = offset;
- target.addDependent(this);
+ public void didResolve() {
+ state = RESOLVED;
if (ConstraintWidgetContainer.DEBUG_GRAPH) {
- System.out.println("a- " + this + " DEPENDS [" + sType(type) + "] ON " + node + " WITH OFFSET " + offset);
+ System.out.println(" -> did resolve " + this);
+ for (ResolutionNode node : dependents) {
+ System.out.println(" * dependent: " + node);
+ }
}
- }
-
- public void dependsOn(ResolutionNode node, int offset) {
- target = node;
- this.offset = offset;
- target.addDependent(this);
- if (ConstraintWidgetContainer.DEBUG_GRAPH) {
- System.out.println("b- " + this + " DEPENDS [" + sType(type) + "] ON " + node + " WITH OFFSET " + offset);
+ for (ResolutionNode node : dependents) {
+ node.resolve();
}
}
- public void setOpposite(ResolutionNode opposite, float oppositeOffset) {
- this.opposite = opposite;
- this.oppositeOffset = oppositeOffset;
+ public boolean isResolved() {
+ return state == RESOLVED;
}
- void addResolvedValue(LinearSystem system) {
- SolverVariable sv = myAnchor.getSolverVariable();
+ public void resolve() {
+ // do nothing
+ }
- if (resolvedTarget == null) {
- system.addEquality(sv, (int) resolvedOffset);
- } else {
- SolverVariable v = system.createObjectVariable(resolvedTarget.myAnchor);
- system.addEquality(sv, v, (int) resolvedOffset, SolverVariable.STRENGTH_FIXED);
- }
+ public void remove(ResolutionDimension resolutionDimension) {
+ // do nothing
}
}