diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/Motion.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/Motion.java index 43e6056f7..0d5780ace 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/Motion.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/Motion.java @@ -78,9 +78,9 @@ public class Motion implements TypedValues { private static final boolean DEBUG = false; private static final boolean FAVOR_FIXED_SIZE_VIEWS = false; MotionWidget mView; - int mId; + public String mId; String mConstraintTag; - private int mCurveFitType = UNSET; + private int mCurveFitType = CurveFit.SPLINE; private MotionPaths mStartMotionPath = new MotionPaths(); private MotionPaths mEndMotionPath = new MotionPaths(); @@ -125,7 +125,7 @@ public class Motion implements TypedValues { private float mQuantizeMotionPhase = Float.NaN; private DifferentialInterpolator mQuantizeMotionInterpolator = null; private boolean mNoMovement = false; - + Motion mRelativeMotion; /** * Get the view to pivot around * @@ -237,16 +237,23 @@ public float getFinalHeight() { * * @return the view id of the view this is in polar mode to or -1 if not in polar */ - public int getAnimateRelativeTo() { + public String getAnimateRelativeTo() { return mStartMotionPath.mAnimateRelativeTo; } /** - * @TODO: add description + * set up the motion to be relative to this other motionController */ public void setupRelative(Motion motionController) { - mStartMotionPath.setupRelative(motionController, motionController.mStartMotionPath); - mEndMotionPath.setupRelative(motionController, motionController.mEndMotionPath); + mRelativeMotion = motionController; + } + + private void setupRelative() { + if (mRelativeMotion == null) { + return; + } + mStartMotionPath.setupRelative(mRelativeMotion, mRelativeMotion.mStartMotionPath); + mEndMotionPath.setupRelative(mRelativeMotion, mRelativeMotion.mEndMotionPath); } public float getCenterX() { @@ -673,6 +680,8 @@ public void setup(int parentWidth, HashSet cycleAttributes = new HashSet<>(); // attributes we need to oscillate HashMap interpolation = new HashMap<>(); ArrayList triggerList = null; + + setupRelative(); if (DEBUG) { if (mKeyList == null) { Utils.log(TAG, ">>>>>>>>>>>>>>> mKeyList==null"); @@ -934,6 +943,7 @@ public void setup(int parentWidth, mSpline[i + 1] = CurveFit.get(mCurveFitType, timePoints, splinePoints); } + // Spline for positions mSpline[0] = CurveFit.get(mCurveFitType, timePoint, splineData); // --------------------------- SUPPORT ARC MODE -------------- if (points[0].mPathMotionArc != UNSET) { @@ -1722,7 +1732,10 @@ public boolean setValue(int id, String value) { mQuantizeMotionInterpolator = getInterpolator(SPLINE_STRING, value, 0); return true; } - + if (MotionType.TYPE_ANIMATE_RELATIVE_TO == id) { + mStartMotionPath.mAnimateRelativeTo = value; + return true; + } return false; } @@ -1763,4 +1776,9 @@ public void setStaggerOffset(float staggerOffset) { public float getMotionStagger() { return mMotionStagger; } + + public void setIdString(String stringId) { + mId = stringId; + mStartMotionPath.mId = mId; + } } diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionPaths.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionPaths.java index c4b370539..beade20ac 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionPaths.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionPaths.java @@ -47,6 +47,7 @@ public class MotionPaths implements Comparable { public static final int CARTESIAN = MotionKeyPosition.TYPE_CARTESIAN; public static final int SCREEN = MotionKeyPosition.TYPE_SCREEN; static String[] sNames = {"position", "x", "y", "width", "height", "pathRotate"}; + public String mId; Easing mKeyFrameEasing; int mDrawPath = 0; float mTime; @@ -58,7 +59,7 @@ public class MotionPaths implements Comparable { float mPathRotate = Float.NaN; float mProgress = Float.NaN; int mPathMotionArc = UNSET; - int mAnimateRelativeTo = UNSET; + String mAnimateRelativeTo = null; float mRelativeAngle = Float.NaN; Motion mRelativeToController = null; @@ -121,7 +122,7 @@ public MotionPaths(int parentWidth, MotionKeyPosition c, MotionPaths startTimePoint, MotionPaths endTimePoint) { - if (startTimePoint.mAnimateRelativeTo != UNSET) { + if (startTimePoint.mAnimateRelativeTo != null) { initPolar(parentWidth, parentHeight, c, startTimePoint, endTimePoint); return; } @@ -157,6 +158,7 @@ void initPolar(int parentWidth, mHeight = (int) (s.mHeight + scaleY * scaleHeight); float startfactor = 1 - position; float endfactor = position; + switch (c.mPositionType) { case MotionKeyPosition.TYPE_SCREEN: this.mX = Float.isNaN(c.mPercentX) ? (position * (e.mX - s.mX) + s.mX) @@ -198,7 +200,6 @@ public void setupRelative(Motion mc, MotionPaths relative) { mY = (float) (Math.atan2(dy, dx) + Math.PI / 2); } else { mY = (float) Math.toRadians(mRelativeAngle); - } } @@ -926,7 +927,10 @@ public void applyParameters(MotionWidget c) { point.mDrawPath = c.mMotion.mDrawPath; point.mAnimateCircleAngleTo = c.mMotion.mAnimateCircleAngleTo; point.mProgress = c.mPropertySet.mProgress; - point.mRelativeAngle = 0; // c.layout.circleAngle; + if (c.mWidgetFrame != null && c.mWidgetFrame.widget != null) { + point.mRelativeAngle = c.mWidgetFrame.widget.mCircleConstraintAngle; + } + Set at = c.getCustomAttributeNames(); for (String s : at) { CustomVariable attr = c.getCustomAttribute(s); diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionWidget.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionWidget.java index a09ff4180..269a85331 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionWidget.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/motion/MotionWidget.java @@ -57,7 +57,7 @@ public class MotionWidget implements TypedValues { * @DoNotShow */ public static class Motion { - public int mAnimateRelativeTo = UNSET; + public String mAnimateRelativeTo = null; public int mAnimateCircleAngleTo = 0; public String mTransitionEasing = null; public int mPathMotionArc = UNSET; @@ -166,6 +166,10 @@ public boolean setValue(int id, float value) { @Override public boolean setValue(int id, String value) { + if (id == MotionType.TYPE_ANIMATE_RELATIVE_TO) { + mMotion.mAnimateRelativeTo = value; + return true; + } return setValueMotion(id, value); } @@ -179,9 +183,6 @@ public boolean setValue(int id, boolean value) { */ public boolean setValueMotion(int id, int value) { switch (id) { - case MotionType.TYPE_ANIMATE_RELATIVE_TO: - mMotion.mAnimateRelativeTo = value; - break; case MotionType.TYPE_ANIMATE_CIRCLEANGLE_TO: mMotion.mAnimateCircleAngleTo = value; break; diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java index c66bc1f3c..42abed015 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/Transition.java @@ -705,16 +705,26 @@ public void addCustomColor(int state, String widgetId, String property, int colo } /** - * @TODO: add description + * Update container of parameters for the state + * @param container contains all the widget parameters + * @param state starting or ending */ public void updateFrom(ConstraintWidgetContainer container, int state) { final ArrayList children = container.getChildren(); final int count = children.size(); + WidgetState[] states = new WidgetState[count]; + for (int i = 0; i < count; i++) { ConstraintWidget child = children.get(i); WidgetState widgetState = getWidgetState(child.stringId, null, state); + states[i] = widgetState; widgetState.update(child, state); + String id = widgetState.getPathRelativeId(); + if (id != null) { + widgetState.setPathRelative(getWidgetState(id, null, state)); + } } + calcStagger(); } @@ -844,6 +854,7 @@ static class WidgetState { WidgetFrame mEnd; WidgetFrame mInterpolated; Motion mMotionControl; + boolean mNeedSetup = true; MotionWidget mMotionWidgetStart; MotionWidget mMotionWidgetEnd; MotionWidget mMotionWidgetInterpolated; @@ -886,13 +897,23 @@ public void update(ConstraintWidget child, int state) { mStart.update(child); mMotionWidgetStart.updateMotion(mMotionWidgetStart); mMotionControl.setStart(mMotionWidgetStart); + mNeedSetup = true; } else if (state == END) { mEnd.update(child); mMotionControl.setEnd(mMotionWidgetEnd); + mNeedSetup = true; } mParentWidth = -1; } + /** + * Return the id of the widget to animate relative to + * @return id of widget or null + */ + String getPathRelativeId() { + return mMotionControl.getAnimateRelativeTo(); + } + public WidgetFrame getFrame(int type) { if (type == START) { return mStart; @@ -909,14 +930,20 @@ public void interpolate(int parentWidth, // TODO only update if parentHeight != mParentHeight || parentWidth != mParentWidth) { mParentHeight = parentHeight; mParentWidth = parentWidth; - mMotionControl.setup(parentWidth, parentHeight, 1, System.nanoTime()); - + if (mNeedSetup) { + mMotionControl.setup(parentWidth, parentHeight, 1, System.nanoTime()); + mNeedSetup = false; + } WidgetFrame.interpolate(parentWidth, parentHeight, mInterpolated, mStart, mEnd, transition, progress); mInterpolated.interpolatedPos = progress; mMotionControl.interpolate(mMotionWidgetInterpolated, progress, System.nanoTime(), mKeyCache); } + + public void setPathRelative(WidgetState widgetState) { + mMotionControl.setupRelative( widgetState.mMotionControl); + } } static class KeyPosition { diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java index 6fc562c97..6c960889c 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java @@ -18,6 +18,7 @@ import androidx.constraintlayout.core.motion.utils.TypedBundle; import androidx.constraintlayout.core.motion.utils.TypedValues; +import androidx.constraintlayout.core.motion.utils.Utils; import androidx.constraintlayout.core.parser.CLArray; import androidx.constraintlayout.core.parser.CLContainer; import androidx.constraintlayout.core.parser.CLElement; @@ -133,7 +134,7 @@ private static int map(String val, String... types) { private static void map(TypedBundle bundle, int type, String val, String... types) { for (int i = 0; i < types.length; i++) { if (types[i].equals(val)) { - bundle.add(TypedValues.PositionType.TYPE_PATH_MOTION_ARC, i); + bundle.add(type, i); } } } diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/widgets/ConstraintWidget.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/widgets/ConstraintWidget.java index 52fb00484..a74ac0122 100644 --- a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/widgets/ConstraintWidget.java +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/widgets/ConstraintWidget.java @@ -23,6 +23,7 @@ import androidx.constraintlayout.core.Cache; import androidx.constraintlayout.core.LinearSystem; import androidx.constraintlayout.core.SolverVariable; +import androidx.constraintlayout.core.motion.utils.Utils; import androidx.constraintlayout.core.state.WidgetFrame; import androidx.constraintlayout.core.widgets.analyzer.ChainRun; import androidx.constraintlayout.core.widgets.analyzer.HorizontalWidgetRun; @@ -361,7 +362,7 @@ public boolean hasResolvedTargets(int orientation, int size) { float mResolvedDimensionRatio = 1.0f; private int[] mMaxDimension = {Integer.MAX_VALUE, Integer.MAX_VALUE}; - private float mCircleConstraintAngle = 0; + public float mCircleConstraintAngle = Float.NaN; private boolean mHasBaseline = false; private boolean mInPlaceholder; @@ -602,7 +603,7 @@ public void reset() { mCenterY.reset(); mCenter.reset(); mParent = null; - mCircleConstraintAngle = 0; + mCircleConstraintAngle = Float.NaN; mWidth = 0; mHeight = 0; mDimensionRatio = 0; @@ -676,7 +677,7 @@ private void serializeAnchor(StringBuilder ret, String side, ConstraintAnchor a) } private void serializeCircle(StringBuilder ret, ConstraintAnchor a, float angle) { - if (a.mTarget == null) { + if (a.mTarget == null || Float.isNaN(angle)) { return; } @@ -3583,6 +3584,8 @@ public void copy(ConstraintWidget src, HashMap = ArrayList() var map = HashMap(); val linkServer = LinkServer() @@ -162,6 +162,8 @@ class MainActivity : AppCompatActivity() { demos.add(object : CompFunc { @Composable override fun Run() { MotionQuantize2() } }) demos.add(object : CompFunc { @Composable override fun Run() { MotionStagger1() } }) demos.add(object : CompFunc { @Composable override fun Run() { MotionStagger2() } }) + demos.add(object : CompFunc { @Composable override fun Run() { MotionOrbit1() } }) + demos.add(object : CompFunc { @Composable override fun Run() { MotionOrbit2() } }) demos.add(object : CompFunc { @Composable override fun Run() { Example () } }) demos.add(object : CompFunc { @Composable override fun Run() { RowColExample () } })