OSDN Git Service

Handle interruptions in Explode and Slide transitions.
authorGeorge Mount <mount@google.com>
Tue, 17 Jun 2014 17:10:13 +0000 (10:10 -0700)
committerGeorge Mount <mount@google.com>
Wed, 18 Jun 2014 23:48:45 +0000 (16:48 -0700)
Bug 15618842

Change-Id: I22431b59c6516ee330d897791b79fa9ed17ba727

core/java/android/transition/Explode.java
core/java/android/transition/Slide.java
core/java/android/transition/TranslationAnimationCreator.java [new file with mode: 0644]
core/res/res/values/ids.xml
core/res/res/values/symbols.xml

index fae527c..feb8efd 100644 (file)
  */
 package android.transition;
 
+import com.android.internal.R;
+
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.graphics.Path;
 import android.graphics.Rect;
 import android.util.FloatMath;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
-
 /**
  * This transition tracks changes to the visibility of target views in the
  * start and end scenes and moves views in or out from the edges of the
@@ -44,8 +40,7 @@ public class Explode extends Visibility {
     private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
     private static final String TAG = "Explode";
-
-    private static final String PROPNAME_SCREEN_BOUNDS = "android:out:screenBounds";
+    private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds";
 
     private int[] mTempLoc = new int[2];
 
@@ -56,8 +51,8 @@ public class Explode extends Visibility {
     private void captureValues(TransitionValues transitionValues) {
         View view = transitionValues.view;
         view.getLocationOnScreen(mTempLoc);
-        int left = mTempLoc[0] + Math.round(view.getTranslationX());
-        int top = mTempLoc[1] + Math.round(view.getTranslationY());
+        int left = mTempLoc[0];
+        int top = mTempLoc[1];
         int right = left + view.getWidth();
         int bottom = top + view.getHeight();
         transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
@@ -75,27 +70,6 @@ public class Explode extends Visibility {
         captureValues(transitionValues);
     }
 
-    private Animator createAnimation(final View view, float startX, float startY, float endX,
-            float endY, float terminalX, float terminalY, TimeInterpolator interpolator) {
-        view.setTranslationX(startX);
-        view.setTranslationY(startY);
-        if (startY == endY && startX == endX) {
-            return null;
-        }
-        Path path = new Path();
-        path.moveTo(startX, startY);
-        path.lineTo(endX, endY);
-        ObjectAnimator pathAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
-                View.TRANSLATION_Y, path);
-        pathAnimator.setInterpolator(interpolator);
-        OutAnimatorListener listener = new OutAnimatorListener(view, terminalX, terminalY,
-                endX, endY);
-        pathAnimator.addListener(listener);
-        pathAnimator.addPauseListener(listener);
-
-        return pathAnimator;
-    }
-
     @Override
     public Animator onAppear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
@@ -103,29 +77,43 @@ public class Explode extends Visibility {
             return null;
         }
         Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
+        float endX = view.getTranslationX();
+        float endY = view.getTranslationY();
         calculateOut(sceneRoot, bounds, mTempLoc);
+        float startX = endX + mTempLoc[0];
+        float startY = endY + mTempLoc[1];
 
-        final float endX = view.getTranslationX();
-        final float startX = endX + mTempLoc[0];
-        final float endY = view.getTranslationY();
-        final float startY = endY + mTempLoc[1];
-
-        return createAnimation(view, startX, startY, endX, endY, endX, endY, sDecelerate);
+        return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
+                startX, startY, endX, endY, sDecelerate);
     }
 
     @Override
     public Animator onDisappear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null) {
+            return null;
+        }
         Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
+        int viewPosX = bounds.left;
+        int viewPosY = bounds.top;
+        float startX = view.getTranslationX();
+        float startY = view.getTranslationY();
+        float endX = startX;
+        float endY = startY;
+        int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transitionPosition);
+        if (interruptedPosition != null) {
+            // We want to have the end position relative to the interrupted position, not
+            // the position it was supposed to start at.
+            endX += interruptedPosition[0] - bounds.left;
+            endY += interruptedPosition[1] - bounds.top;
+            bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]);
+        }
         calculateOut(sceneRoot, bounds, mTempLoc);
+        endX += mTempLoc[0];
+        endY += mTempLoc[1];
 
-        final float startX = view.getTranslationX();
-        final float endX = startX + mTempLoc[0];
-        final float startY = view.getTranslationY();
-        final float endY = startY + mTempLoc[1];
-
-        return createAnimation(view, startX, startY, endX, endY, startX, startY,
-                sAccelerate);
+        return TranslationAnimationCreator.createAnimation(view, startValues,
+                viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate);
     }
 
     private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
@@ -153,8 +141,8 @@ public class Explode extends Visibility {
 
         if (xVector == 0 && yVector == 0) {
             // Random direction when View is centered on focal View.
-            xVector = (float)(Math.random() * 2) - 1;
-            yVector = (float)(Math.random() * 2) - 1;
+            xVector = (float) (Math.random() * 2) - 1;
+            yVector = (float) (Math.random() * 2) - 1;
         }
         float vectorSize = calculateDistance(xVector, yVector);
         xVector /= vectorSize;
@@ -176,53 +164,4 @@ public class Explode extends Visibility {
     private static float calculateDistance(float x, float y) {
         return FloatMath.sqrt((x * x) + (y * y));
     }
-
-    private static class OutAnimatorListener extends AnimatorListenerAdapter {
-        private final View mView;
-        private boolean mCanceled = false;
-        private float mPausedX;
-        private float mPausedY;
-        private final float mTerminalX;
-        private final float mTerminalY;
-        private final float mEndX;
-        private final float mEndY;
-
-        public OutAnimatorListener(View view, float terminalX, float terminalY,
-                float endX, float endY) {
-            mView = view;
-            mTerminalX = terminalX;
-            mTerminalY = terminalY;
-            mEndX = endX;
-            mEndY = endY;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animator) {
-            mView.setTranslationX(mTerminalX);
-            mView.setTranslationY(mTerminalY);
-            mCanceled = true;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animator) {
-            if (!mCanceled) {
-                mView.setTranslationX(mTerminalX);
-                mView.setTranslationY(mTerminalY);
-            }
-        }
-
-        @Override
-        public void onAnimationPause(Animator animator) {
-            mPausedX = mView.getTranslationX();
-            mPausedY = mView.getTranslationY();
-            mView.setTranslationY(mEndX);
-            mView.setTranslationY(mEndY);
-        }
-
-        @Override
-        public void onAnimationResume(Animator animator) {
-            mView.setTranslationX(mPausedX);
-            mView.setTranslationY(mPausedY);
-        }
-    }
 }
index 8269258..0d2e487 100644 (file)
 package android.transition;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
-import android.util.Log;
-import android.util.Property;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,71 +34,60 @@ import android.view.animation.DecelerateInterpolator;
  */
 public class Slide extends Visibility {
     private static final String TAG = "Slide";
-
     private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
-
+    private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
     private CalculateSlide mSlideCalculator = sCalculateBottom;
 
     private interface CalculateSlide {
-        /** Returns the translation value for view when it out of the scene */
-        float getGone(ViewGroup sceneRoot, View view);
 
-        /** Returns the translation value for view when it is in the scene */
-        float getHere(View view);
+        /** Returns the translation value for view when it goes out of the scene */
+        float getGoneX(ViewGroup sceneRoot, View view);
 
-        /** Returns the property to animate translation */
-        Property<View, Float> getProperty();
+        /** Returns the translation value for view when it goes out of the scene */
+        float getGoneY(ViewGroup sceneRoot, View view);
     }
 
     private static abstract class CalculateSlideHorizontal implements CalculateSlide {
-        @Override
-        public float getHere(View view) {
-            return view.getTranslationX();
-        }
 
         @Override
-        public Property<View, Float> getProperty() {
-            return View.TRANSLATION_X;
+        public float getGoneY(ViewGroup sceneRoot, View view) {
+            return view.getTranslationY();
         }
     }
 
     private static abstract class CalculateSlideVertical implements CalculateSlide {
-        @Override
-        public float getHere(View view) {
-            return view.getTranslationY();
-        }
 
         @Override
-        public Property<View, Float> getProperty() {
-            return View.TRANSLATION_Y;
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            return view.getTranslationX();
         }
     }
 
     private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view) {
             return view.getTranslationX() - sceneRoot.getWidth();
         }
     };
 
     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneY(ViewGroup sceneRoot, View view) {
             return view.getTranslationY() - sceneRoot.getHeight();
         }
     };
 
     private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view) {
             return view.getTranslationX() + sceneRoot.getWidth();
         }
     };
 
     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneY(ViewGroup sceneRoot, View view) {
             return view.getTranslationY() + sceneRoot.getHeight();
         }
     };
@@ -125,8 +107,28 @@ public class Slide extends Visibility {
         setSlideEdge(slideEdge);
     }
 
+    private void captureValues(TransitionValues transitionValues) {
+        View view = transitionValues.view;
+        int[] position = new int[2];
+        view.getLocationOnScreen(position);
+        transitionValues.values.put(PROPNAME_SCREEN_POSITION, position);
+    }
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        super.captureStartValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        super.captureEndValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
     /**
      * Change the edge that Views appear and disappear from.
+     *
      * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
      *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
      *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
@@ -153,77 +155,35 @@ public class Slide extends Visibility {
         setPropagation(propagation);
     }
 
-    private Animator createAnimation(final View view, Property<View, Float> property,
-            float start, float end, float terminalValue, TimeInterpolator interpolator) {
-        view.setTranslationY(start);
-        if (start == end) {
-            return null;
-        }
-        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end);
-
-        SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end);
-        anim.addListener(listener);
-        anim.addPauseListener(listener);
-        anim.setInterpolator(interpolator);
-        return anim;
-    }
-
     @Override
     public Animator onAppear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
         if (endValues == null) {
             return null;
         }
-        float end = mSlideCalculator.getHere(view);
-        float start = mSlideCalculator.getGone(sceneRoot, view);
-        return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate);
+        int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
+        float endX = view.getTranslationX();
+        float endY = view.getTranslationY();
+        float startX = mSlideCalculator.getGoneX(sceneRoot, view);
+        float startY = mSlideCalculator.getGoneY(sceneRoot, view);
+        return TranslationAnimationCreator
+                .createAnimation(view, endValues, position[0], position[1],
+                        startX, startY, endX, endY, sDecelerate);
     }
 
     @Override
     public Animator onDisappear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
-        float start = mSlideCalculator.getHere(view);
-        float end = mSlideCalculator.getGone(sceneRoot, view);
-
-        return createAnimation(view, mSlideCalculator.getProperty(), start, end, start,
-                sAccelerate);
-    }
-
-    private static class SlideAnimatorListener extends AnimatorListenerAdapter {
-        private boolean mCanceled = false;
-        private float mPausedY;
-        private final View mView;
-        private final float mEndY;
-        private final float mTerminalY;
-
-        public SlideAnimatorListener(View view, float terminalY, float endY) {
-            mView = view;
-            mTerminalY = terminalY;
-            mEndY = endY;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animator) {
-            mView.setTranslationY(mTerminalY);
-            mCanceled = true;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animator) {
-            if (!mCanceled) {
-                mView.setTranslationY(mTerminalY);
-            }
-        }
-
-        @Override
-        public void onAnimationPause(Animator animator) {
-            mPausedY = mView.getTranslationY();
-            mView.setTranslationY(mEndY);
-        }
-
-        @Override
-        public void onAnimationResume(Animator animator) {
-            mView.setTranslationY(mPausedY);
+        if (startValues == null) {
+            return null;
         }
+        int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
+        float startX = view.getTranslationX();
+        float startY = view.getTranslationY();
+        float endX = mSlideCalculator.getGoneX(sceneRoot, view);
+        float endY = mSlideCalculator.getGoneY(sceneRoot, view);
+        return TranslationAnimationCreator
+                .createAnimation(view, startValues, position[0], position[1],
+                        startX, startY, endX, endY, sAccelerate);
     }
 }
diff --git a/core/java/android/transition/TranslationAnimationCreator.java b/core/java/android/transition/TranslationAnimationCreator.java
new file mode 100644 (file)
index 0000000..de71fd7
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 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.transition;
+
+import com.android.internal.R;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.graphics.Path;
+import android.view.View;
+
+/**
+ * This class is used by Slide and Explode to create an animator that goes from the start
+ * position to the end position. It takes into account the canceled position so that it
+ * will not blink out or shift suddenly when the transition is interrupted.
+ */
+class TranslationAnimationCreator {
+
+    /**
+     * Creates an animator that can be used for x and/or y translations. When interrupted,
+     * it sets a tag to keep track of the position so that it may be continued from position.
+     *
+     * @param view The view being moved. This may be in the overlay for onDisappear.
+     * @param values The values containing the view in the view hierarchy.
+     * @param viewPosX The x screen coordinate of view
+     * @param viewPosY The y screen coordinate of view
+     * @param startX The start translation x of view
+     * @param startY The start translation y of view
+     * @param endX The end translation x of view
+     * @param endY The end translation y of view
+     * @param interpolator The interpolator to use with this animator.
+     * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
+     * a previous interruption, in which case it moves from the current position to (endX, endY).
+     */
+    static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
+            float startX, float startY, float endX, float endY, TimeInterpolator interpolator) {
+        float terminalX = view.getTranslationX();
+        float terminalY = view.getTranslationY();
+        int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition);
+        if (startPosition != null) {
+            startX = startPosition[0] - viewPosX + terminalX;
+            startY = startPosition[1] - viewPosY + terminalY;
+        }
+        // Initial position is at translation startX, startY, so position is offset by that amount
+        int startPosX = viewPosX + Math.round(startX - terminalX);
+        int startPosY = viewPosY + Math.round(startY - terminalY);
+
+        view.setTranslationX(startX);
+        view.setTranslationY(startY);
+        if (startX == endX && startY == endY) {
+            return null;
+        }
+        Path path = new Path();
+        path.moveTo(startX, startY);
+        path.lineTo(endX, endY);
+        ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y,
+                path);
+
+        TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
+                startPosX, startPosY, terminalX, terminalY);
+        anim.addListener(listener);
+        anim.addPauseListener(listener);
+        anim.setInterpolator(interpolator);
+        return anim;
+    }
+
+    private static class TransitionPositionListener extends AnimatorListenerAdapter {
+
+        private final View mViewInHierarchy;
+        private final View mMovingView;
+        private final int mStartX;
+        private final int mStartY;
+        private int[] mTransitionPosition;
+        private float mPausedX;
+        private float mPausedY;
+        private final float mTerminalX;
+        private final float mTerminalY;
+
+        private TransitionPositionListener(View movingView, View viewInHierarchy,
+                int startX, int startY, float terminalX, float terminalY) {
+            mMovingView = movingView;
+            mViewInHierarchy = viewInHierarchy;
+            mStartX = startX - Math.round(mMovingView.getTranslationX());
+            mStartY = startY - Math.round(mMovingView.getTranslationY());
+            mTerminalX = terminalX;
+            mTerminalY = terminalY;
+            mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition);
+            if (mTransitionPosition != null) {
+                mViewInHierarchy.setTagInternal(R.id.transitionPosition, null);
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (mTransitionPosition == null) {
+                mTransitionPosition = new int[2];
+            }
+            mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
+            mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
+            mViewInHierarchy.setTagInternal(R.id.transitionPosition, mTransitionPosition);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animator) {
+            mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
+        }
+
+        @Override
+        public void onAnimationPause(Animator animator) {
+            mPausedX = mMovingView.getTranslationX();
+            mPausedY = mMovingView.getTranslationY();
+            mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
+        }
+
+        @Override
+        public void onAnimationResume(Animator animator) {
+            mMovingView.setTranslationX(mPausedX);
+            mMovingView.setTranslationY(mPausedY);
+        }
+    }
+
+}
index 639091e..c64e910 100644 (file)
@@ -84,4 +84,5 @@
   <item type="id" name="current_scene" />
   <item type="id" name="scene_layoutid_cache" />
   <item type="id" name="mask" />
+  <item type="id" name="transitionPosition" />
 </resources>
index 5f4553b..6a6a045 100644 (file)
   <java-symbol type="id" name="pin_error_message" />
   <java-symbol type="id" name="timePickerLayout" />
   <java-symbol type="id" name="profile_icon" />
+  <java-symbol type="id" name="transitionPosition" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
   <java-symbol type="attr" name="alertDialogCenterButtons" />