*/
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
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];
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));
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) {
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) {
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;
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);
- }
- }
}
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;
*/
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();
}
};
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}.
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);
}
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
<item type="id" name="current_scene" />
<item type="id" name="scene_layoutid_cache" />
<item type="id" name="mask" />
+ <item type="id" name="transitionPosition" />
</resources>
<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" />