From ae8cbbdfa3c7b7adbd634a88165a112919350f54 Mon Sep 17 00:00:00 2001 From: John Hoford Date: Mon, 22 Mar 2021 18:05:16 -0700 Subject: [PATCH] Support downUp ViewTranstions (#190) * Support downUp ViewTranstions * format new Animation --- constraintlayout/.idea/codeStyles/Project.xml | 116 ----------------- .../motion/widget/ViewTransition.java | 123 ++++++++++++++++-- .../widget/ViewTransitionController.java | 5 + .../src/main/res/values/attrs.xml | 6 +- .../app/src/main/AndroidManifest.xml | 1 + .../constraint/app/CheckSetStates.java | 72 ++++++++++ .../constraint/app/VerificationActivity.java | 20 ++- .../src/main/res/layout/verification_057.xml | 97 ++++++++++++++ .../app/src/main/res/values/themes.xml | 5 +- .../main/res/xml/verification_scene_057.xml | 69 ++++++++++ .../main/res/xml/verification_scene_450.xml | 30 ++--- 11 files changed, 377 insertions(+), 167 deletions(-) delete mode 100644 constraintlayout/.idea/codeStyles/Project.xml create mode 100644 projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java create mode 100644 projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml create mode 100644 projects/MotionLayoutVerification/app/src/main/res/xml/verification_scene_057.xml diff --git a/constraintlayout/.idea/codeStyles/Project.xml b/constraintlayout/.idea/codeStyles/Project.xml deleted file mode 100644 index 681f41ae2..000000000 --- a/constraintlayout/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - -
- - - - xmlns:android - - ^$ - - - -
-
- - - - xmlns:.* - - ^$ - - - BY_NAME - -
-
- - - - .*:id - - http://schemas.android.com/apk/res/android - - - -
-
- - - - .*:name - - http://schemas.android.com/apk/res/android - - - -
-
- - - - name - - ^$ - - - -
-
- - - - style - - ^$ - - - -
-
- - - - .* - - ^$ - - - BY_NAME - -
-
- - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - -
-
- - - - .* - - .* - - - BY_NAME - -
-
-
-
-
-
\ No newline at end of file diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransition.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransition.java index 72abead3d..6e0ec589e 100644 --- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransition.java +++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransition.java @@ -19,6 +19,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; @@ -69,8 +70,9 @@ public class ViewTransition { // Transition can be up or down of manually fired public static final int ONSTATE_ACTION_DOWN = 1; public static final int ONSTATE_ACTION_UP = 2; - public static final int ONSTATE_SHARED_VALUE_SET = 3; - public static final int ONSTATE_SHARED_VALUE_UNSET = 4; + public static final int ONSTATE_ACTION_DOWN_UP = 3; + public static final int ONSTATE_SHARED_VALUE_SET = 4; + public static final int ONSTATE_SHARED_VALUE_UNSET = 5; private int mOnStateTransition = UNSET; private boolean mDisabled = false; @@ -82,6 +84,8 @@ public class ViewTransition { KeyFrames mKeyFrames; ConstraintSet.Constraint mConstraintDelta; private int mDuration = UNSET; + private int mUpDuration = UNSET; + private int mTargetId; private String mTargetString; @@ -140,6 +144,7 @@ public void setStateTransition(int stateTransition) { /** * Gets the SharedValue it will be listening for. + * * @return */ public int getSharedValue() { @@ -155,6 +160,7 @@ public void setSharedValue(int sharedValue) { /** * Gets the ID of the SharedValue it will be listening for. + * * @return the id of the shared value */ public int getSharedValueID() { @@ -281,6 +287,8 @@ private void parseViewTransitionTags(Context context, XmlPullParser parser) { mPathMotionArc = a.getInt(attr, mPathMotionArc); } else if (attr == R.styleable.ViewTransition_duration) { mDuration = a.getInt(attr, mDuration); + } else if (attr == R.styleable.ViewTransition_upDuration) { + mUpDuration = a.getInt(attr, mUpDuration); } else if (attr == R.styleable.ViewTransition_viewTransitionMode) { mViewTransitionMode = a.getInt(attr, mViewTransitionMode); } else if (attr == R.styleable.ViewTransition_motionInterpolator) { @@ -309,21 +317,23 @@ private void parseViewTransitionTags(Context context, XmlPullParser parser) { mIfTagSet = a.getResourceId(attr, mIfTagSet); } else if (attr == R.styleable.ViewTransition_ifTagNotSet) { mIfTagNotSet = a.getResourceId(attr, mIfTagNotSet); - }else if (attr == R.styleable.ViewTransition_SharedValueId) { + } else if (attr == R.styleable.ViewTransition_SharedValueId) { mSharedValueID = a.getResourceId(attr, mSharedValueID); - }else if (attr == R.styleable.ViewTransition_SharedValue) { + } else if (attr == R.styleable.ViewTransition_SharedValue) { mSharedValueTarget = a.getInteger(attr, mSharedValueTarget); } } a.recycle(); } - void applyIndependentTransition(ViewTransitionController controller, MotionLayout motionLayout,View view) { + void applyIndependentTransition(ViewTransitionController controller, MotionLayout motionLayout, View view) { MotionController motionController = new MotionController(view); motionController.setBothStates(view); mKeyFrames.addAllFrames(motionController); motionController.setup(motionLayout.getWidth(), motionLayout.getHeight(), mDuration, System.nanoTime()); - new Animate(controller, motionController, mDuration, getInterpolator(motionLayout.getContext()), mSetsTag, mClearsTag); + new Animate(controller, motionController, + mDuration, mUpDuration, mOnStateTransition, + getInterpolator(motionLayout.getContext()), mSetsTag, mClearsTag); } static class Animate { @@ -332,35 +342,69 @@ static class Animate { long mStart; MotionController mMC; int mDuration; + int mUpDuration; KeyCache mCache = new KeyCache(); ViewTransitionController mVtController; Interpolator mInterpolator; + boolean reverse = false; + float mPosition; + float mDpositionDt; + long mLastRender; + Rect mTempRec = new Rect(); + boolean hold_at_100 = false; Animate(ViewTransitionController controller, MotionController motionController, - int duration, + int duration, int upDuration, int mode, Interpolator interpolator, int setTag, int clearTag) { mVtController = controller; mMC = motionController; mDuration = duration; + mUpDuration = upDuration; mStart = System.nanoTime(); + mLastRender = mStart; mVtController.addAnimation(this); mInterpolator = interpolator; mSetsTag = setTag; mClearsTag = clearTag; + if (mode == ONSTATE_ACTION_DOWN_UP) { + hold_at_100 = true; + } + mDpositionDt = (duration == 0) ? Float.MAX_VALUE : 1f / duration; mutate(); } + void reverse(boolean dir) { + reverse = dir; + if (reverse && mUpDuration != UNSET) { + mDpositionDt = (mUpDuration == 0) ? Float.MAX_VALUE : 1f / mUpDuration; + } + mVtController.invalidate(); + mLastRender = System.nanoTime(); + } + void mutate() { + if (reverse) { + mutateReverse(); + } else { + mutateForward(); + } + } + + void mutateReverse() { long current = System.nanoTime(); - long elapse = current - mStart; - float position = ((float) (elapse * 1E-6)) / mDuration; - if (position >= 1.0f) { - position = 1.0f; + long elapse = current - mLastRender; + mLastRender = current; + + mPosition -= ((float) (elapse * 1E-6)) * mDpositionDt; + if (mPosition < 0.0f) { + mPosition = 0.0f; } - float ipos = (mInterpolator == null) ? position : mInterpolator.getInterpolation(position); + + float ipos = (mInterpolator == null) ? mPosition : mInterpolator.getInterpolation(mPosition); boolean repaint = mMC.interpolate(mMC.mView, ipos, current, mCache); - if (position >= 1) { + + if (mPosition <= 0) { if (mSetsTag != UNSET) { mMC.getView().setTag(mSetsTag, System.nanoTime()); } @@ -369,10 +413,58 @@ void mutate() { } mVtController.removeAnimation(this); } - if (position < 1f || repaint) { + if (mPosition > 0f || repaint) { + mVtController.invalidate(); + } + } + + void mutateForward() { + + long current = System.nanoTime(); + long elapse = current - mLastRender; + mLastRender = current; + + mPosition += ((float) (elapse * 1E-6)) * mDpositionDt; + if (mPosition >= 1.0f) { + mPosition = 1.0f; + } + + float ipos = (mInterpolator == null) ? mPosition : mInterpolator.getInterpolation(mPosition); + boolean repaint = mMC.interpolate(mMC.mView, ipos, current, mCache); + + + if (mPosition >= 1) { + if (mSetsTag != UNSET) { + mMC.getView().setTag(mSetsTag, System.nanoTime()); + } + if (mClearsTag != UNSET) { + mMC.getView().setTag(mClearsTag, null); + } + if (!hold_at_100) { + mVtController.removeAnimation(this); + } + } + if (mPosition < 1f || repaint) { mVtController.invalidate(); } } + + public void reactTo(int action, float x, float y) { + switch (action) { + case MotionEvent.ACTION_UP: + if (!reverse) { + reverse(true); + } + return; + case MotionEvent.ACTION_MOVE: + View view = mMC.getView(); + view.getHitRect(mTempRec); + if (!mTempRec.contains((int) x, (int) y)) { + if (!reverse) + reverse(true); + } + } + } } void applyTransition(ViewTransitionController controller, @@ -496,6 +588,9 @@ boolean supports(int action) { if (mOnStateTransition == ONSTATE_ACTION_UP) { return action == MotionEvent.ACTION_UP; } + if (mOnStateTransition == ONSTATE_ACTION_DOWN_UP) { + return action == MotionEvent.ACTION_DOWN; + } return false; } diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransitionController.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransitionController.java index 3adebacb0..bdc5c9b25 100644 --- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransitionController.java +++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/ViewTransitionController.java @@ -160,6 +160,11 @@ void touchEvent(MotionEvent event) { float y = event.getY(); Rect rec = new Rect(); int action = event.getAction(); + if (animations != null && !animations.isEmpty()) { + for (ViewTransition.Animate animation : animations) { + animation.reactTo(action,x,y); + } + } switch (action) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_DOWN: diff --git a/constraintlayout/constraintlayout/src/main/res/values/attrs.xml b/constraintlayout/constraintlayout/src/main/res/values/attrs.xml index 002e994dd..9bc2e1e1d 100644 --- a/constraintlayout/constraintlayout/src/main/res/values/attrs.xml +++ b/constraintlayout/constraintlayout/src/main/res/values/attrs.xml @@ -1730,8 +1730,9 @@ - - + + + @@ -1747,6 +1748,7 @@ + diff --git a/projects/MotionLayoutVerification/app/src/main/AndroidManifest.xml b/projects/MotionLayoutVerification/app/src/main/AndroidManifest.xml index 366d1e1d8..5d8633762 100644 --- a/projects/MotionLayoutVerification/app/src/main/AndroidManifest.xml +++ b/projects/MotionLayoutVerification/app/src/main/AndroidManifest.xml @@ -35,6 +35,7 @@ android:label="Test ConstraintSet on ConstraintLayout" /> + diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java new file mode 100644 index 000000000..6de2f959f --- /dev/null +++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/CheckSetStates.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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.app; + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.motion.widget.Debug; +import androidx.constraintlayout.motion.widget.MotionLayout; + +// used with verification_057.xml +public class CheckSetStates extends AppCompatActivity { + private static final String TAG = "CustomSwipeClick"; + String layout_name; + MotionLayout mMotionLayout; + int []states = {R.id.s1,R.id.s2,R.id.s3,R.id.s4,R.id.s5,R.id.s6,R.id.s7,R.id.s8,R.id.s9}; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle extra = getIntent().getExtras(); + String prelayout = extra.getString(Utils.KEY); + setTitle(layout_name = prelayout); + Context ctx = getApplicationContext(); + int id = ctx.getResources().getIdentifier(prelayout, "layout", ctx.getPackageName()); + setContentView(id); + mMotionLayout = Utils.findMotionLayout(this); + + TextView text = findViewById(R.id.text); + } + + + public void jump1(View view) { + for (int i = 0; i < states.length; i++) { + if (mMotionLayout.getCurrentState() == states[i]) { +// mMotionLayout.setState(states[(i+1)%states.length],-1,-1); + mMotionLayout.transitionToState(states[(i+1)%states.length]); + Log.v(TAG, Debug.getLoc()+" "+i ); + return; + } + } + } + public void jump2(View view) { + for (int i = 0; i < states.length; i++) { + if (mMotionLayout.getCurrentState() == states[i]) { + mMotionLayout.setState(states[(i+1)%states.length],-1,-1); + mMotionLayout.setProgress(0); + Log.v(TAG, Debug.getLoc()+" "+i ); + return; + } + } + } +} diff --git a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java index 3534cd0ec..d1b194baa 100644 --- a/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java +++ b/projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java @@ -21,7 +21,6 @@ import android.content.Intent; import android.content.res.Configuration; import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.media.AudioManager; import android.media.ToneGenerator; import android.os.Build; @@ -30,7 +29,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; -import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; @@ -76,7 +74,7 @@ public class VerificationActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "Verification00"; private String KEY = "layout"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; String layout_name; HashMap activity_map = new HashMap<>(); @@ -89,6 +87,8 @@ public class VerificationActivity extends AppCompatActivity implements View.OnCl activity_map.put("verification_350", CustomSwipeClick.class); activity_map.put("constraint_set_01", ConstraintSetVerify.class); activity_map.put("verification_042", CheckCallbackActivity.class); + activity_map.put("verification_057", CheckSetStates.class); + // activity_map.put("verification_037", RotationToolbar.class); // activity_map.put("verification_038", RotationRotateToToolbar.class); } @@ -97,8 +97,8 @@ public class VerificationActivity extends AppCompatActivity implements View.OnCl private static boolean REVERSE = false; - private final String RUN_FIRST = "verification_042"; - private final String LAYOUTS_MATCHES = ".*_\\d+"; + private final String RUN_FIRST = "verification_450"; + private final String LAYOUTS_MATCHES = "verification_\\d+"; private static String SHOW_FIRST = ""; MotionLayout mMotionLayout; @@ -135,15 +135,13 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { layout_name = prelayout; Context ctx = getApplicationContext(); int id = ctx.getResources().getIdentifier(prelayout, "layout", ctx.getPackageName()); - this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - ActionBar bar = getSupportActionBar(); - if (bar != null) { - bar.hide(); - } + // this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + + setTitle("layout:"+layout_name); + setContentView(id); focusJump(); ActionBar actionBar = getSupportActionBar(); - actionBar.setBackgroundDrawable(new ColorDrawable(0xFFfd401d)); RecyclerView rv = findView(RecyclerView.class); populateRecyclerView(rv); diff --git a/projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml new file mode 100644 index 000000000..37fb6286b --- /dev/null +++ b/projects/MotionLayoutVerification/app/src/main/res/layout/verification_057.xml @@ -0,0 +1,97 @@ + + + +