diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java index 55185e100..07539231d 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java @@ -36,6 +36,7 @@ import androidx.constraintlayout.core.state.helpers.ChainReference; import androidx.constraintlayout.core.state.helpers.FlowReference; import androidx.constraintlayout.core.state.helpers.GuidelineReference; +import androidx.constraintlayout.core.state.helpers.SplitReference; import androidx.constraintlayout.core.widgets.ConstraintWidget; import androidx.constraintlayout.core.widgets.Flow; @@ -538,6 +539,12 @@ public static void parseJSON(String content, State state, (CLObject) element ); break; + case "Split": + parseSplitType(state, + elementName, + layoutVariables, + (CLObject) element); + break; } } else { parseWidget(state, layoutVariables, @@ -854,6 +861,45 @@ private static void parseChainType(String orientation, } } + private static void parseSplitType(State state, + String name, + LayoutVariables layoutVariables, + CLObject element) throws CLParsingException { + + SplitReference split = state.getSplit(name); + + for (String param : element.names()) { + switch (param) { + case "contains": + CLArray list = element.getArrayOrNull(param); + if (list != null) { + for (int j = 0; j < list.size(); j++) { + + String elementNameReference = list.get(j).content(); + ConstraintReference elementReference = + state.constraints(elementNameReference); + if (PARSER_DEBUG) { + System.out.println( + "Add REFERENCE " + + "($elementNameReference = $elementReference) " + + "TO BARRIER " + ); + } + split.add(elementReference); + } + } + break; + case "orientation": + int orientation = element.get(param).getInt(); + split.setOrientation(orientation); + break; + default: + ConstraintReference reference = state.constraints(name); + applyAttribute(state, layoutVariables, reference, element, param); + } + } + } + /** * It's used to parse the Flow type of Helper with the following format: * flowID: { diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/State.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/State.java index 6ff97014f..d7f715cc9 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/State.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/State.java @@ -27,6 +27,7 @@ import androidx.constraintlayout.core.state.helpers.FlowReference; import androidx.constraintlayout.core.state.helpers.GuidelineReference; import androidx.constraintlayout.core.state.helpers.HorizontalChainReference; +import androidx.constraintlayout.core.state.helpers.SplitReference; import androidx.constraintlayout.core.state.helpers.VerticalChainReference; import androidx.constraintlayout.core.widgets.ConstraintWidget; import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer; @@ -93,6 +94,7 @@ public enum Helper { LAYER, HORIZONTAL_FLOW, VERTICAL_FLOW, + SPLIT, FLOW } @@ -324,6 +326,10 @@ public HelperReference helper(Object key, State.Helper type) { reference = new FlowReference(this, type); } break; + case SPLIT: { + reference = new SplitReference(this, type); + } + break; default: { reference = new HelperReference(this, type); } @@ -368,6 +374,15 @@ public BarrierReference barrier(Object key, Direction direction) { return (BarrierReference) reference.getFacade(); } + public SplitReference getSplit(Object key) { + ConstraintReference reference = constraints(key); + if (reference.getFacade() == null || !(reference.getFacade() instanceof SplitReference)) { + SplitReference flowReference = new SplitReference(this, Helper.SPLIT); + reference.setFacade(flowReference); + } + return (SplitReference) reference.getFacade(); + } + /** * Gets a reference to a Flow object. Creating it if needed. * @param key id of the reference diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/helpers/SplitReference.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/helpers/SplitReference.java new file mode 100644 index 000000000..7a7921d44 --- /dev/null +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/helpers/SplitReference.java @@ -0,0 +1,81 @@ +package androidx.constraintlayout.core.state.helpers; + +import androidx.constraintlayout.core.state.HelperReference; +import androidx.constraintlayout.core.state.State; +import androidx.constraintlayout.core.utils.Split; +import androidx.constraintlayout.core.widgets.ConstraintWidget; +import androidx.constraintlayout.core.widgets.Flow; +import androidx.constraintlayout.core.widgets.HelperWidget; + +public class SplitReference extends HelperReference { + + public SplitReference(State state, State.Helper type) { + super(state, type); + } + + protected Split mSplit; + + protected ConstraintWidget mLeft; + + protected ConstraintWidget mRight; + + protected int mOrientation; + + public ConstraintWidget getLeft() { + return mLeft; + } + + public void setLeft(ConstraintWidget mLeft) { + this.mLeft = mLeft; + } + + public ConstraintWidget getRight() { + return mRight; + } + + public void setRight(ConstraintWidget mRight) { + this.mRight = mRight; + } + + @Override + public HelperWidget getHelperWidget() { + if (mSplit == null) { + mSplit = new Split(); + } + return mSplit; + } + + @Override + public void setHelperWidget(HelperWidget widget) { + if (widget instanceof Flow) { + mSplit = (Split) widget; + } else { + mSplit = null; + } + } + + public int getOrientation() { + return mOrientation; + } + + public void setOrientation(int orientation) { + mOrientation = orientation; + + } + + @Override + public void apply() { + if (mLeft != null) { + mSplit.setFirst(mLeft); + } + + if (mRight != null) { + mSplit.setSecond(mRight); + } + + mSplit.setOrientation(mOrientation); + + // General attributes of a widget + applyBase(); + } +} diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/utils/Split.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/utils/Split.java new file mode 100644 index 000000000..d910e6f37 --- /dev/null +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/utils/Split.java @@ -0,0 +1,116 @@ +package androidx.constraintlayout.core.utils; + +import androidx.constraintlayout.core.LinearSystem; +import androidx.constraintlayout.core.widgets.ConstraintWidget; +import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer; +import androidx.constraintlayout.core.widgets.VirtualLayout; + +public class Split extends VirtualLayout { + + ConstraintWidget splitBar; + + ConstraintWidget first; + ConstraintWidget second; + ConstraintWidgetContainer mContainer; + int orientation; + + public Split() { + splitBar = new ConstraintWidget(); + first = new ConstraintWidget(); + second = new ConstraintWidget(); + } + + public void setFirst(ConstraintWidget a) { + first = a; + } + + public void setSecond(ConstraintWidget b) { + second = b; + } + + public void setSplitBar(ConstraintWidget splitBar) { + this.splitBar = splitBar; + } + + public ConstraintWidget getFirst() { + return first; + } + + public ConstraintWidget getSecond() { + return second; + } + + public ConstraintWidget getSplitBar() { + return splitBar; + } + + public ConstraintWidgetContainer getContainer() { + return mContainer; + } + + public void setContainer(ConstraintWidgetContainer container) { + mContainer = container; + } + + public int getOrientation() { + return orientation; + } + + public void setOrientation(int orientation) { + this.orientation = orientation; + } + + @Override + public void measure(int widthMode, int widthSize, int heightMode, int heightSize) { + super.measure(widthMode, widthSize, heightMode, heightSize); + for (int i = 0; i < mWidgetsCount; i++) { + if (i == 0) { + setFirst(mWidgets[i]); + } else if (i == 1) { + setSecond(mWidgets[i]); + } else if (i == 2) { + setSplitBar(mWidgets[i]); + } + } + splitBar.stringId = String.valueOf(splitBar.hashCode()); + first.stringId = String.valueOf(first.hashCode()); + second.stringId = String.valueOf(second.hashCode()); + mContainer = (ConstraintWidgetContainer) getParent(); + mContainer.add(splitBar); + } + + @Override + public void addToSolver(LinearSystem system, boolean optimize) { + this.mTop.connect(getParent().mTop, 0); + this.mLeft.connect(getParent().mLeft, 0); + this.mRight.connect(getParent().mRight, 0); + this.mBottom.connect(getParent().mBottom, 0); + + splitBar.mTop.connect(mTop, 0); + splitBar.mBottom.connect(mBottom, 0); + splitBar.mLeft.connect(mLeft, 0); + splitBar.mRight.connect(mRight, 0); + + if (orientation == 0) { + first.mLeft.connect(mLeft, 0); + first.mRight.connect(splitBar.mLeft, 0); + first.mTop.connect(mTop, 0); + first.mBottom.connect(mBottom, 0); + + second.mLeft.connect(splitBar.mRight, 0); + second.mRight.connect(mRight, 0); + second.mTop.connect(mTop, 0); + second.mBottom.connect(mBottom, 0); + } else { + first.mLeft.connect(mLeft, 0); + first.mRight.connect(mRight, 0); + first.mTop.connect(mTop, 0); + first.mBottom.connect(splitBar.mTop, 0); + + second.mLeft.connect(mLeft, 0); + second.mRight.connect(mRight, 0); + second.mTop.connect(splitBar.mBottom, 0); + second.mBottom.connect(mBottom, 0); + } + } +} diff --git a/constraintlayout/core/src/test/java/androidx/constraintlayout/core/FlowTest.java b/constraintlayout/core/src/test/java/androidx/constraintlayout/core/FlowTest.java index f56029fb2..14d9be700 100644 --- a/constraintlayout/core/src/test/java/androidx/constraintlayout/core/FlowTest.java +++ b/constraintlayout/core/src/test/java/androidx/constraintlayout/core/FlowTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; +import androidx.constraintlayout.core.utils.Split; import androidx.constraintlayout.core.widgets.ConstraintAnchor; import androidx.constraintlayout.core.widgets.ConstraintWidget; import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer; @@ -230,4 +231,40 @@ public void didMeasures() { System.out.println("A: " + a); System.out.println("B: " + b); } + @Test + public void testSplit() { + ConstraintWidgetContainer root = new ConstraintWidgetContainer(50, 50); + Split split = new Split(); + split.setDebugName("split"); + + split.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.MATCH_PARENT); + split.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.MATCH_PARENT); + split.connect(ConstraintAnchor.Type.LEFT, root, ConstraintAnchor.Type.LEFT); + split.connect(ConstraintAnchor.Type.RIGHT, root, ConstraintAnchor.Type.RIGHT); + split.connect(ConstraintAnchor.Type.TOP, root, ConstraintAnchor.Type.TOP); + split.connect(ConstraintAnchor.Type.BOTTOM, root, ConstraintAnchor.Type.BOTTOM); + split.setOrientation(1); + root.add(split); + split.setContainer(root); + root.setMeasurer(new BasicMeasure.Measurer() { + @Override + public void measure(ConstraintWidget widget, BasicMeasure.Measure measure) { + measure.measuredWidth = widget.getWidth(); + measure.measuredHeight = widget.getHeight(); + } + + @Override + public void didMeasures() { + + } + }); + root.setMeasurer(sMeasurer); + root.measure(Optimizer.OPTIMIZATION_NONE, + 0, 0, 0, 0, 0, 0, 0, 0); + //root.layout(); + System.out.println("root: " + root); + System.out.println("flow: " + split); + System.out.println("A: " + split.getFirst()); + System.out.println("B: " + split.getSecond()); + } } diff --git a/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/FlowDemo.kt b/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/FlowDemo.kt index 174ba2f93..548257dcb 100644 --- a/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/FlowDemo.kt +++ b/projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/FlowDemo.kt @@ -209,3 +209,36 @@ fun FlowBasicDemo6() { } } +// test adding constraints to parent +@Preview(group = "split") +@Composable +fun SplitDemo1() { + ConstraintLayout( + ConstraintSet(""" + { + split: { + height: 'parent', + width: 'parent', + type: 'Split', + orientation: 0, + contains: ['btn1', 'btn2'], + }, + btn1: { + height: 'spread', + width: 'spread', + } + } + """.trimIndent()), + modifier = Modifier.fillMaxSize()) { + val numArray = arrayOf("btn1", "btn2") + for (num in numArray) { + Button( + modifier = Modifier.layoutId(num), + onClick = {}, + ) { + Text(text = num) + } + } + } +} +