Slide.java revision dc21d3b2804c24fe29ec860796d11185901364c4
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package android.transition; 17 18import android.animation.Animator; 19import android.animation.AnimatorListenerAdapter; 20import android.animation.AnimatorSet; 21import android.animation.ObjectAnimator; 22import android.animation.TimeInterpolator; 23import android.animation.ValueAnimator; 24import android.graphics.Rect; 25import android.util.Log; 26import android.util.Property; 27import android.view.Gravity; 28import android.view.View; 29import android.view.ViewGroup; 30import android.view.animation.AccelerateInterpolator; 31import android.view.animation.DecelerateInterpolator; 32 33/** 34 * This transition tracks changes to the visibility of target views in the 35 * start and end scenes and moves views in or out from one of the edges of the 36 * scene. Visibility is determined by both the 37 * {@link View#setVisibility(int)} state of the view as well as whether it 38 * is parented in the current view hierarchy. Disappearing Views are 39 * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup, 40 * TransitionValues, int, TransitionValues, int)}. 41 */ 42public class Slide extends Visibility { 43 private static final String TAG = "Slide"; 44 45 private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); 46 private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); 47 48 private CalculateSlide mSlideCalculator = sCalculateBottom; 49 50 private interface CalculateSlide { 51 /** Returns the translation value for view when it out of the scene */ 52 float getGone(ViewGroup sceneRoot, View view); 53 54 /** Returns the translation value for view when it is in the scene */ 55 float getHere(View view); 56 57 /** Returns the property to animate translation */ 58 Property<View, Float> getProperty(); 59 } 60 61 private static abstract class CalculateSlideHorizontal implements CalculateSlide { 62 @Override 63 public float getHere(View view) { 64 return view.getTranslationX(); 65 } 66 67 @Override 68 public Property<View, Float> getProperty() { 69 return View.TRANSLATION_X; 70 } 71 } 72 73 private static abstract class CalculateSlideVertical implements CalculateSlide { 74 @Override 75 public float getHere(View view) { 76 return view.getTranslationY(); 77 } 78 79 @Override 80 public Property<View, Float> getProperty() { 81 return View.TRANSLATION_Y; 82 } 83 } 84 85 private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { 86 @Override 87 public float getGone(ViewGroup sceneRoot, View view) { 88 return view.getTranslationX() - sceneRoot.getWidth(); 89 } 90 }; 91 92 private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { 93 @Override 94 public float getGone(ViewGroup sceneRoot, View view) { 95 return view.getTranslationY() - sceneRoot.getHeight(); 96 } 97 }; 98 99 private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { 100 @Override 101 public float getGone(ViewGroup sceneRoot, View view) { 102 return view.getTranslationX() + sceneRoot.getWidth(); 103 } 104 }; 105 106 private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { 107 @Override 108 public float getGone(ViewGroup sceneRoot, View view) { 109 return view.getTranslationY() + sceneRoot.getHeight(); 110 } 111 }; 112 113 /** 114 * Constructor using the default {@link Gravity#BOTTOM} 115 * slide edge direction. 116 */ 117 public Slide() { 118 setSlideEdge(Gravity.BOTTOM); 119 } 120 121 /** 122 * Constructor using the provided slide edge direction. 123 */ 124 public Slide(int slideEdge) { 125 setSlideEdge(slideEdge); 126 } 127 128 /** 129 * Change the edge that Views appear and disappear from. 130 * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of 131 * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP}, 132 * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}. 133 */ 134 public void setSlideEdge(int slideEdge) { 135 switch (slideEdge) { 136 case Gravity.LEFT: 137 mSlideCalculator = sCalculateLeft; 138 break; 139 case Gravity.TOP: 140 mSlideCalculator = sCalculateTop; 141 break; 142 case Gravity.RIGHT: 143 mSlideCalculator = sCalculateRight; 144 break; 145 case Gravity.BOTTOM: 146 mSlideCalculator = sCalculateBottom; 147 break; 148 default: 149 throw new IllegalArgumentException("Invalid slide direction"); 150 } 151 SidePropagation propagation = new SidePropagation(); 152 propagation.setSide(slideEdge); 153 setPropagation(propagation); 154 } 155 156 private Animator createAnimation(final View view, Property<View, Float> property, 157 float start, float end, float terminalValue, TimeInterpolator interpolator) { 158 view.setTranslationY(start); 159 if (start == end) { 160 return null; 161 } 162 final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end); 163 164 SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end); 165 anim.addListener(listener); 166 anim.addPauseListener(listener); 167 anim.setInterpolator(interpolator); 168 return anim; 169 } 170 171 @Override 172 public Animator onAppear(ViewGroup sceneRoot, View view, 173 TransitionValues startValues, TransitionValues endValues) { 174 if (endValues == null) { 175 return null; 176 } 177 float end = mSlideCalculator.getHere(view); 178 float start = mSlideCalculator.getGone(sceneRoot, view); 179 return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate); 180 } 181 182 @Override 183 public Animator onDisappear(ViewGroup sceneRoot, View view, 184 TransitionValues startValues, TransitionValues endValues) { 185 float start = mSlideCalculator.getHere(view); 186 float end = mSlideCalculator.getGone(sceneRoot, view); 187 188 return createAnimation(view, mSlideCalculator.getProperty(), start, end, start, 189 sAccelerate); 190 } 191 192 private static class SlideAnimatorListener extends AnimatorListenerAdapter { 193 private boolean mCanceled = false; 194 private float mPausedY; 195 private final View mView; 196 private final float mEndY; 197 private final float mTerminalY; 198 199 public SlideAnimatorListener(View view, float terminalY, float endY) { 200 mView = view; 201 mTerminalY = terminalY; 202 mEndY = endY; 203 } 204 205 @Override 206 public void onAnimationCancel(Animator animator) { 207 mView.setTranslationY(mTerminalY); 208 mCanceled = true; 209 } 210 211 @Override 212 public void onAnimationEnd(Animator animator) { 213 if (!mCanceled) { 214 mView.setTranslationY(mTerminalY); 215 } 216 } 217 218 @Override 219 public void onAnimationPause(Animator animator) { 220 mPausedY = mView.getTranslationY(); 221 mView.setTranslationY(mEndY); 222 } 223 224 @Override 225 public void onAnimationResume(Animator animator) { 226 mView.setTranslationY(mPausedY); 227 } 228 } 229} 230