Skip to content
This repository was archived by the owner on Dec 27, 2024. It is now read-only.

Commit d9b602a

Browse files
authored
Add support for circular constraints in compose (#243)
+ json support + added a couple of examples
1 parent bef73b8 commit d9b602a

File tree

4 files changed

+201
-8
lines changed

4 files changed

+201
-8
lines changed

constraintlayout/compose/src/main/java/androidx/constraintlayout/compose/ConstraintSetParser.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,15 @@ private fun parseConstraint(
333333
state.constraints(target)
334334
}
335335
when (constraintName) {
336-
"centerHorizontally" -> {
337-
336+
"circular" -> {
337+
var angle = 0f
338+
if (constraint[1] is Float) {
339+
angle = constraint[1] as Float
340+
}
341+
if (constraint[1] is Int) {
342+
angle = (constraint[1] as Int).toFloat()
343+
}
344+
reference.circularConstraint(targetReference, angle, 0f)
338345
}
339346
"start" -> {
340347
when (anchor) {

constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/ConstraintReference.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ public interface ConstraintReferenceFactory {
9191
protected Object mBottomToTop = null;
9292
protected Object mBottomToBottom = null;
9393
Object mBaselineToBaseline = null;
94+
Object mCircularConstraint = null;
95+
private float mCircularAngle;
96+
private float mCircularDistance;
9497

9598
State.Constraint mLast = null;
9699

@@ -454,6 +457,15 @@ public ConstraintReference centerVertically(Object reference) {
454457
return this;
455458
}
456459

460+
public ConstraintReference circularConstraint(Object reference, float angle, float distance) {
461+
Object ref = get(reference);
462+
mCircularConstraint = ref;
463+
mCircularAngle = angle;
464+
mCircularDistance = distance;
465+
mLast = State.Constraint.CIRCULAR_CONSTRAINT;
466+
return this;
467+
}
468+
457469
public ConstraintReference width(Dimension dimension) {
458470
return setWidth(dimension);
459471
}
@@ -509,6 +521,9 @@ public ConstraintReference margin(int value) {
509521
case BASELINE_TO_BASELINE: {
510522
// nothing
511523
} break;
524+
case CIRCULAR_CONSTRAINT: {
525+
mCircularDistance = value;
526+
}
512527
}
513528
} else {
514529
mMarginLeft = value;
@@ -655,6 +670,9 @@ public ConstraintReference clear() {
655670
mBaselineToBaseline = null;
656671
}
657672
break;
673+
case CIRCULAR_CONSTRAINT: {
674+
mCircularConstraint = null;
675+
}
658676
}
659677
} else {
660678
mLeftToLeft = null;
@@ -676,6 +694,7 @@ public ConstraintReference clear() {
676694
mBottomToBottom = null;
677695
mMarginBottom = 0;
678696
mBaselineToBaseline = null;
697+
mCircularConstraint = null;
679698
mHorizontalBias = 0.5f;
680699
mVerticalBias = 0.5f;
681700
mMarginLeftGone = 0;
@@ -756,6 +775,9 @@ private void applyConnection(ConstraintWidget widget, Object opaqueTarget, State
756775
case BASELINE_TO_BASELINE: {
757776
widget.immediateConnect(ConstraintAnchor.Type.BASELINE, target, ConstraintAnchor.Type.BASELINE, 0, 0);
758777
} break;
778+
case CIRCULAR_CONSTRAINT: {
779+
widget.connectCircularConstraint(target, mCircularAngle, (int) mCircularDistance);
780+
} break;
759781
}
760782
}
761783

@@ -783,6 +805,7 @@ public void apply() {
783805
applyConnection(mConstraintWidget, mBottomToTop, State.Constraint.BOTTOM_TO_TOP);
784806
applyConnection(mConstraintWidget, mBottomToBottom, State.Constraint.BOTTOM_TO_BOTTOM);
785807
applyConnection(mConstraintWidget, mBaselineToBaseline, State.Constraint.BASELINE_TO_BASELINE);
808+
applyConnection(mConstraintWidget, mCircularConstraint, State.Constraint.CIRCULAR_CONSTRAINT);
786809

787810
if (mHorizontalChainStyle != ConstraintWidget.CHAIN_SPREAD) {
788811
mConstraintWidget.setHorizontalChainStyle(mHorizontalChainStyle);

constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/State.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public enum Constraint {
6060
BOTTOM_TO_BOTTOM,
6161
BASELINE_TO_BASELINE,
6262
CENTER_HORIZONTALLY,
63-
CENTER_VERTICALLY
63+
CENTER_VERTICALLY,
64+
CIRCULAR_CONSTRAINT
6465
}
6566

6667
public enum Direction {

projects/ComposeConstraintLayout/app/src/main/java/com/example/constraintlayout/test.kt

Lines changed: 167 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package androidx.constraintlayout.compose
22

33
import androidx.compose.animation.core.*
44
import androidx.compose.foundation.Image
5-
import androidx.compose.foundation.layout.Column
6-
import androidx.compose.foundation.layout.fillMaxSize
7-
import androidx.compose.foundation.layout.fillMaxWidth
8-
import androidx.compose.foundation.layout.height
5+
import androidx.compose.foundation.background
6+
import androidx.compose.foundation.layout.*
97
import androidx.compose.material.Button
108
import androidx.compose.material.MaterialTheme
119
import androidx.compose.ui.Modifier
@@ -15,6 +13,7 @@ import androidx.compose.ui.unit.dp
1513
import androidx.compose.material.Text
1614
import androidx.compose.runtime.*
1715
import androidx.compose.ui.geometry.Offset
16+
import androidx.compose.ui.graphics.Color
1817
import androidx.compose.ui.layout.layoutId
1918
import androidx.compose.ui.tooling.preview.Preview
2019
import androidx.constraintlayout.core.widgets.Optimizer
@@ -788,4 +787,167 @@ public fun ScreenExample8() {
788787
}
789788
}
790789
}
791-
}
790+
}
791+
792+
@Preview(group = "new5")
793+
@Composable
794+
public fun ScreenExample9() {
795+
ConstraintLayout(
796+
ConstraintSet("""
797+
{
798+
center: {
799+
center: 'parent'
800+
},
801+
h1: { circular: ['center', 30, 100] },
802+
h2: { circular: ['center', 60, 100] },
803+
h3: { circular: ['center', 90, 100] },
804+
h4: { circular: ['center', 120, 100] },
805+
h5: { circular: ['center', 150, 100] },
806+
h6: { circular: ['center', 180, 100] },
807+
h7: { circular: ['center', 210, 100] },
808+
h8: { circular: ['center', 240, 100] },
809+
h9: { circular: ['center', 270, 100] },
810+
h10: { circular: ['center', 300, 100] },
811+
h11: { circular: ['center', 330, 100] },
812+
h12: { circular: ['center', 0, 100] }
813+
}
814+
"""),
815+
modifier = Modifier
816+
.fillMaxSize()
817+
) {
818+
Text(modifier = Modifier.layoutId("center"), text = "C")
819+
Text(modifier = Modifier.layoutId("h1"), text = "1")
820+
Text(modifier = Modifier.layoutId("h2"), text = "2")
821+
Text(modifier = Modifier.layoutId("h3"), text = "3")
822+
Text(modifier = Modifier.layoutId("h4"), text = "4")
823+
Text(modifier = Modifier.layoutId("h5"), text = "5")
824+
Text(modifier = Modifier.layoutId("h6"), text = "6")
825+
Text(modifier = Modifier.layoutId("h7"), text = "7")
826+
Text(modifier = Modifier.layoutId("h8"), text = "8")
827+
Text(modifier = Modifier.layoutId("h9"), text = "9")
828+
Text(modifier = Modifier.layoutId("h10"), text = "10")
829+
Text(modifier = Modifier.layoutId("h11"), text = "11")
830+
Text(modifier = Modifier.layoutId("h12"), text = "12")
831+
}
832+
}
833+
834+
@Preview(group = "new6")
835+
@Composable
836+
public fun ScreenExample10() {
837+
ConstraintLayout(
838+
ConstraintSet("""
839+
{
840+
h1: { circular: ['parent', 0, 100] },
841+
h2: { circular: ['parent', 40, 100], rotationZ: 40 },
842+
h3: { circular: ['parent', 80, 100], rotationZ: 80 },
843+
h4: { circular: ['parent', 120, 100], rotationZ: 120 },
844+
h5: { circular: ['parent', 160, 100], rotationZ: 160 },
845+
h6: { circular: ['parent', 200, 100], rotationZ: 200 },
846+
h7: { circular: ['parent', 240, 100], rotationZ: 240 },
847+
h8: { circular: ['parent', 280, 100], rotationZ: 280 },
848+
h9: { circular: ['parent', 320, 100], rotationZ: 320 }
849+
}
850+
"""),
851+
modifier = Modifier
852+
.fillMaxSize()
853+
) {
854+
Box(modifier = Modifier.layoutId("h1").width(100.dp).height(60.dp).background(Color.Red))
855+
Box(modifier = Modifier.layoutId("h2").width(100.dp).height(60.dp).background(Color.Green))
856+
Box(modifier = Modifier.layoutId("h3").width(100.dp).height(60.dp).background(Color.Blue))
857+
Box(modifier = Modifier.layoutId("h4").width(100.dp).height(60.dp).background(Color.Gray))
858+
Box(modifier = Modifier.layoutId("h5").width(100.dp).height(60.dp).background(Color.Yellow))
859+
Box(modifier = Modifier.layoutId("h6").width(100.dp).height(60.dp).background(Color.Cyan))
860+
Box(modifier = Modifier.layoutId("h7").width(100.dp).height(60.dp).background(Color.Magenta))
861+
Box(modifier = Modifier.layoutId("h8").width(100.dp).height(60.dp).background(Color.Red))
862+
Box(modifier = Modifier.layoutId("h9").width(100.dp).height(60.dp).background(Color.DarkGray))
863+
}
864+
}
865+
866+
@Preview(group = "motion3")
867+
@Composable
868+
public fun ScreenExample11() {
869+
var animateToEnd by remember { mutableStateOf(false) }
870+
val progress by animateFloatAsState(
871+
targetValue = if (animateToEnd) 1f else 0f,
872+
animationSpec = tween(4000)
873+
)
874+
Column {
875+
Button(onClick = { animateToEnd = !animateToEnd }) {
876+
Text(text = "Run")
877+
}
878+
MotionLayout(
879+
ConstraintSet("""
880+
{
881+
h1: { circular: ['parent', 0, 100] },
882+
h2: { circular: ['parent', 40, 100], rotationZ: 40 },
883+
h3: { circular: ['parent', 80, 100], rotationZ: 80 },
884+
h4: { circular: ['parent', 120, 100], rotationZ: 120 },
885+
h5: { circular: ['parent', 160, 100], rotationZ: 160 },
886+
h6: { circular: ['parent', 200, 100], rotationZ: 200 },
887+
h7: { circular: ['parent', 240, 100], rotationZ: 240 },
888+
h8: { circular: ['parent', 280, 100], rotationZ: 280 },
889+
h9: { circular: ['parent', 320, 100], rotationZ: 320 }
890+
}
891+
"""),
892+
ConstraintSet("""
893+
{
894+
h1: { circular: ['parent', 0, 100], rotationZ: 360 },
895+
h2: { circular: ['parent', 40, 100], rotationZ: 400 },
896+
h3: { circular: ['parent', 80, 100], rotationZ: 440 },
897+
h4: { circular: ['parent', 120, 100], rotationZ: 480 },
898+
h5: { circular: ['parent', 160, 100], rotationZ: 520 },
899+
h6: { circular: ['parent', 200, 100], rotationZ: 560 },
900+
h7: { circular: ['parent', 240, 100], rotationZ: 600 },
901+
h8: { circular: ['parent', 280, 100], rotationZ: 640 },
902+
h9: { circular: ['parent', 320, 100], rotationZ: 680 }
903+
}
904+
"""),
905+
progress = progress,
906+
modifier = Modifier
907+
.fillMaxSize().background(Color.White)
908+
) {
909+
Box(modifier = Modifier.layoutId("h1").width(100.dp).height(60.dp).background(Color.Red))
910+
Box(modifier = Modifier.layoutId("h2").width(100.dp).height(60.dp).background(Color.Green))
911+
Box(modifier = Modifier.layoutId("h3").width(100.dp).height(60.dp).background(Color.Blue))
912+
Box(modifier = Modifier.layoutId("h4").width(100.dp).height(60.dp).background(Color.Gray))
913+
Box(modifier = Modifier.layoutId("h5").width(100.dp).height(60.dp).background(Color.Yellow))
914+
Box(modifier = Modifier.layoutId("h6").width(100.dp).height(60.dp).background(Color.Cyan))
915+
Box(modifier = Modifier.layoutId("h7").width(100.dp).height(60.dp).background(Color.Magenta))
916+
Box(modifier = Modifier.layoutId("h8").width(100.dp).height(60.dp).background(Color.Red))
917+
Box(modifier = Modifier.layoutId("h9").width(100.dp).height(60.dp).background(Color.DarkGray))
918+
}
919+
}
920+
}
921+
922+
923+
924+
925+
926+
927+
928+
929+
930+
931+
932+
933+
934+
935+
936+
937+
938+
939+
940+
941+
942+
943+
944+
945+
946+
947+
948+
949+
950+
951+
952+
953+

0 commit comments

Comments
 (0)