| /* |
| * 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 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.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 one of the edges of the |
| * scene. 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. Disappearing Views are |
| * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup, |
| * TransitionValues, int, TransitionValues, int)}. |
| */ |
| public class Slide extends Visibility { |
| private static final String TAG = "Slide"; |
| |
| /** |
| * Move Views in or out of the left edge of the scene. |
| * @see #setSlideEdge(int) |
| */ |
| public static final int LEFT = 0; |
| |
| /** |
| * Move Views in or out of the top edge of the scene. |
| * @see #setSlideEdge(int) |
| */ |
| public static final int TOP = 1; |
| |
| /** |
| * Move Views in or out of the right edge of the scene. |
| * @see #setSlideEdge(int) |
| */ |
| public static final int RIGHT = 2; |
| |
| /** |
| * Move Views in or out of the bottom edge of the scene. This is the |
| * default slide direction. |
| * @see #setSlideEdge(int) |
| */ |
| public static final int BOTTOM = 3; |
| |
| private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); |
| private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); |
| |
| private int[] mTempLoc = new int[2]; |
| 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 property to animate translation */ |
| Property<View, Float> getProperty(); |
| } |
| |
| 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; |
| } |
| } |
| |
| 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; |
| } |
| } |
| |
| private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { |
| @Override |
| public float getGone(ViewGroup sceneRoot, View view) { |
| return view.getTranslationX() - sceneRoot.getWidth(); |
| } |
| }; |
| |
| private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { |
| @Override |
| public float getGone(ViewGroup sceneRoot, View view) { |
| return view.getTranslationY() - sceneRoot.getHeight(); |
| } |
| }; |
| |
| private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { |
| @Override |
| public float getGone(ViewGroup sceneRoot, View view) { |
| return view.getTranslationX() + sceneRoot.getWidth(); |
| } |
| }; |
| |
| private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { |
| @Override |
| public float getGone(ViewGroup sceneRoot, View view) { |
| return view.getTranslationY() + sceneRoot.getHeight(); |
| } |
| }; |
| |
| /** |
| * Constructor using the default {@link android.transition.Slide#BOTTOM} |
| * slide edge direction. |
| */ |
| public Slide() { |
| setSlideEdge(BOTTOM); |
| } |
| |
| /** |
| * Constructor using the provided slide edge direction. |
| */ |
| public Slide(int slideEdge) { |
| setSlideEdge(slideEdge); |
| } |
| |
| /** |
| * Change the edge that Views appear and disappear from. |
| * @param slideEdge The edge of the scene to use for Views appearing and disappearing. |
| */ |
| public void setSlideEdge(int slideEdge) { |
| switch (slideEdge) { |
| case LEFT: |
| mSlideCalculator = sCalculateLeft; |
| break; |
| case TOP: |
| mSlideCalculator = sCalculateTop; |
| break; |
| case RIGHT: |
| mSlideCalculator = sCalculateRight; |
| break; |
| case BOTTOM: |
| mSlideCalculator = sCalculateBottom; |
| break; |
| default: |
| throw new IllegalArgumentException("Invalid slide direction"); |
| } |
| SidePropagation propagation = new SidePropagation(); |
| propagation.setSide(slideEdge); |
| 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); |
| } |
| |
| @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); |
| } |
| } |
| } |