/* * Copyright (C) 2013 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 android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.annotation.TargetApi; import android.graphics.Interpolator; import android.os.Build; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.EditText; import android.widget.TextView; /** * This transition tracks changes to the visibility of target views in the * start and end scenes and translates views in or out when they become visible * or non-visible. Visibility is determined by both the * {@link View#setVisibility(int)} state of the view as well as whether it * is parented in the current view hierarchy. */ @TargetApi(Build.VERSION_CODES.KITKAT) public class ChangeTranslation extends Visibility { private static final String PROPNAME_TRANSLATE_X = "android:translationchange:translateX"; private static final String PROPNAME_TRANSLATE_Y = "android:translationchange:translateY"; private static final TimeInterpolator sAccelerator = new AccelerateInterpolator(); private static final TimeInterpolator sDecelerator = new DecelerateInterpolator(); private static final String[] sTransitionProperties = { PROPNAME_TRANSLATE_X, PROPNAME_TRANSLATE_Y }; /** * Translation mode used in {@link #ChangeTranslation(int)} to make the transition * operate on targets that are appearing. Maybe be combined with * {@link #OUT} to translate both in and out. */ public static final int IN = 0x1; /** * Translation mode used in {@link #ChangeTranslation(int)} to make the transition * operate on targets that are disappearing. Maybe be combined with * {@link #OUT} to translate both in and out. */ public static final int OUT = 0x2; /** * Translation direction used in {@link #ChangeTranslation(int, int)} */ public static final int X = 0x1; /** * Translation direction used in {@link #ChangeTranslation(int, int)} */ public static final int Y = 0x2; private int mTranslationMode = OUT; private int mTranslationDirection = X; public ChangeTranslation() { this(IN | OUT); } public ChangeTranslation(int translationMode) { this(translationMode, X); } public ChangeTranslation(int translationMode, int translationDirection) { mTranslationMode = translationMode; mTranslationDirection = translationDirection; } @Override public String[] getTransitionProperties() { return sTransitionProperties; } /** * Returns the type of changing animation that will be run. * @return either {@link #IN}, {@link #OUT}, or both. */ public int getTranslationMode() { return mTranslationMode; } /** * Returns the direction of changing animation that will be run. * @return either {@link #X}, {@link #Y}, or both. */ public int getTranslationDirection() { return mTranslationDirection; } private void captureValues(TransitionValues transitionValues) { transitionValues.values.put(PROPNAME_TRANSLATE_X, transitionValues.view.getTranslationX()); transitionValues.values.put(PROPNAME_TRANSLATE_Y, transitionValues.view.getTranslationX()); } @Override public void captureStartValues(TransitionValues transitionValues) { super.captureStartValues(transitionValues); captureValues(transitionValues); } @Override public void captureEndValues(TransitionValues transitionValues) { super.captureStartValues(transitionValues); captureValues(transitionValues); } private static boolean equals(float[] one, float[] two) { if (one == null) return (two == null); for (int i = 0; i < one.length; ++i) if (one[i] != two[i]) return false; return true; } /** * Utility method to handle creating and running the Animator. */ private Animator createAnimation(View view, float[] startTranslation, float[] endTranslation, Animator.AnimatorListener listener, TimeInterpolator interpolator) { if (startTranslation == null || endTranslation == null || startTranslation.length != endTranslation.length || equals(startTranslation, endTranslation)) { // run listener if we're noop'ing the animation, to get the end-state results now if (listener != null) { listener.onAnimationEnd(null); } return null; } final AnimatorSet anim = new AnimatorSet(); ObjectAnimator animX = null, animY = null; if ((mTranslationDirection & X) != 0) { animX = ObjectAnimator.ofFloat(view, View.X, startTranslation[0], endTranslation[0]); } if ((mTranslationDirection & Y) != 0) { animY = ObjectAnimator.ofFloat(view, View.Y, startTranslation[1], endTranslation[1]); } if (null != animX && null == animY) anim.play(animX); if (null == animX && null != animY) anim.play(animY); if (null != animX && null != animY) anim.playTogether(animX, animY); if (null == animX && null == animY) return null; if (listener != null) { anim.addListener(listener); } anim.setInterpolator(interpolator); return anim; } @Override public Animator onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, final TransitionValues endValues, int endVisibility) { if ((mTranslationMode & IN) != IN || endValues == null) { return null; } final View endView = endValues.view; final int startY = 2 * endView.getHeight(); final int startX = 2 * endView.getWidth(); final int endX = 0; final int endY = 0; return createAnimator(endView, new float[]{startX, startY}, new float[]{endX, endY}, sAccelerator); } @Override public Animator onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility) { if ((mTranslationMode & OUT) != OUT || endValues == null) { return null; } final View endView = endValues.view; final int endY = -2 * endView.getHeight(); final int endX = -2 * endView.getWidth(); final int startX = 0; final int startY = 0; return createAnimator(endView, new float[] { startX, startY }, new float[] { endX, endY }, sDecelerator); } protected Animator createAnimator(final View endView, final float[] startTranslation, final float[] endTranslation, TimeInterpolator interpolator) { TransitionListener transitionListener = new TransitionListener() { boolean mCanceled = false; float mPausedX, mPausedY; @Override public void onTransitionCancel(Transition transition) { jumpToEnd(); mCanceled = true; } @Override public void onTransitionStart(Transition transition) { } @Override public void onTransitionEnd(Transition transition) { if (!mCanceled) { jumpToEnd(); } } @Override public void onTransitionPause(Transition transition) { mPausedX = endView.getTranslationX(); mPausedY = endView.getTranslationY(); jumpToEnd(); } @Override public void onTransitionResume(Transition transition) { if ((mTranslationDirection & X) != 0) endView.setTranslationX(mPausedX); if ((mTranslationDirection & Y) != 0) endView.setTranslationY(mPausedY); } protected void jumpToEnd() { if ((mTranslationDirection & X) != 0) endView.setTranslationX(endTranslation[0]); if ((mTranslationDirection & Y) != 0) endView.setTranslationY(endTranslation[1]); } }; addListener(transitionListener); return createAnimation(endView, startTranslation, endTranslation, null, interpolator); } }