SlideKitkat.java revision 33f66eb67b6457ea75434dfd9f79703ad9e03560
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.support.v17.leanback.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.View; 28import android.view.ViewGroup; 29import android.view.animation.AccelerateInterpolator; 30import android.view.animation.DecelerateInterpolator; 31import android.transition.Visibility; 32import android.transition.Transition; 33import android.transition.TransitionValues; 34import android.support.v17.leanback.R; 35 36/** 37 * Slide distance toward/from a edge. The direction and distance are determined by 38 * {@link SlideCallback}. 39 */ 40class Slide extends Visibility { 41 private static final String TAG = "Slide"; 42 43 /** 44 * Move Views in or out of the left edge of the scene. 45 * @see #setSlideEdge(int) 46 */ 47 public static final int LEFT = 0; 48 49 /** 50 * Move Views in or out of the top edge of the scene. 51 * @see #setSlideEdge(int) 52 */ 53 public static final int TOP = 1; 54 55 /** 56 * Move Views in or out of the right edge of the scene. 57 * @see #setSlideEdge(int) 58 */ 59 public static final int RIGHT = 2; 60 61 /** 62 * Move Views in or out of the bottom edge of the scene. This is the 63 * default slide direction. 64 * @see #setSlideEdge(int) 65 */ 66 public static final int BOTTOM = 3; 67 68 private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); 69 private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); 70 71 private int[] mTempLoc = new int[2]; 72 SlideCallback mCallback; 73 private int[] mTempEdge = new int[1]; 74 private float[] mTempDistance = new float[1]; 75 76 private interface CalculateSlide { 77 /** Returns the translation value for view when it out of the scene */ 78 float getGone(float slide, View view); 79 80 /** Returns the translation value for view when it is in the scene */ 81 float getHere(View view); 82 83 /** Returns the property to animate translation */ 84 Property<View, Float> getProperty(); 85 } 86 87 private static abstract class CalculateSlideHorizontal implements CalculateSlide { 88 @Override 89 public float getHere(View view) { 90 return view.getTranslationX(); 91 } 92 93 @Override 94 public Property<View, Float> getProperty() { 95 return View.TRANSLATION_X; 96 } 97 } 98 99 private static abstract class CalculateSlideVertical implements CalculateSlide { 100 @Override 101 public float getHere(View view) { 102 return view.getTranslationY(); 103 } 104 105 @Override 106 public Property<View, Float> getProperty() { 107 return View.TRANSLATION_Y; 108 } 109 } 110 111 private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { 112 @Override 113 public float getGone(float distance, View view) { 114 return view.getTranslationX() - distance; 115 } 116 }; 117 118 private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { 119 @Override 120 public float getGone(float distance, View view) { 121 return view.getTranslationY() - distance; 122 } 123 }; 124 125 private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { 126 @Override 127 public float getGone(float distance, View view) { 128 return view.getTranslationX() + distance; 129 } 130 }; 131 132 private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { 133 @Override 134 public float getGone(float distance, View view) { 135 return view.getTranslationY() + distance; 136 } 137 }; 138 139 public Slide() { 140 } 141 142 public void setCallback(SlideCallback callback) { 143 mCallback = callback; 144 } 145 146 private CalculateSlide getSlideEdge(int slideEdge) { 147 switch (slideEdge) { 148 case LEFT: 149 return sCalculateLeft; 150 case TOP: 151 return sCalculateTop; 152 case RIGHT: 153 return sCalculateRight; 154 case BOTTOM: 155 return sCalculateBottom; 156 default: 157 throw new IllegalArgumentException("Invalid slide direction"); 158 } 159 } 160 161 private Animator createAnimation(final View view, Property<View, Float> property, 162 float start, float end, float terminalValue, TimeInterpolator interpolator, 163 int finalVisibility) { 164 float[] startPosition = (float[]) view.getTag(R.id.lb_slide_transition_value); 165 if (startPosition != null) { 166 start = View.TRANSLATION_Y == property ? startPosition[1] : startPosition[0]; 167 view.setTag(R.id.lb_slide_transition_value, null); 168 } 169 final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end); 170 171 SlideAnimatorListener listener = new SlideAnimatorListener(view, property, terminalValue, end, 172 finalVisibility); 173 anim.addListener(listener); 174 anim.addPauseListener(listener); 175 anim.setInterpolator(interpolator); 176 return anim; 177 } 178 179 @Override 180 public Animator onAppear(ViewGroup sceneRoot, 181 TransitionValues startValues, int startVisibility, 182 TransitionValues endValues, int endVisibility) { 183 View view = (endValues != null) ? endValues.view : null; 184 if (view == null) { 185 return null; 186 } 187 if (mCallback == null || !mCallback.getSlide(view, true, mTempEdge, mTempDistance)) { 188 return null; 189 } 190 final CalculateSlide slideCalculator = getSlideEdge(mTempEdge[0]); 191 float end = slideCalculator.getHere(view); 192 float start = slideCalculator.getGone(mTempDistance[0], view); 193 return createAnimation(view, slideCalculator.getProperty(), start, end, end, sDecelerate, 194 View.VISIBLE); 195 } 196 197 @Override 198 public Animator onDisappear(ViewGroup sceneRoot, 199 TransitionValues startValues, int startVisibility, 200 TransitionValues endValues, int endVisibility) { 201 View view = (startValues != null) ? startValues.view : null; 202 if (view == null) { 203 return null; 204 } 205 if (mCallback == null || !mCallback.getSlide(view, false, mTempEdge, mTempDistance)) { 206 return null; 207 } 208 final CalculateSlide slideCalculator = getSlideEdge(mTempEdge[0]); 209 float start = slideCalculator.getHere(view); 210 float end = slideCalculator.getGone(mTempDistance[0], view); 211 212 return createAnimation(view, slideCalculator.getProperty(), start, end, start, 213 sAccelerate, View.INVISIBLE); 214 } 215 216 private static class SlideAnimatorListener extends AnimatorListenerAdapter { 217 private boolean mCanceled = false; 218 private float mPausedValue; 219 private final View mView; 220 private final float mEndValue; 221 private final float mTerminalValue; 222 private final int mFinalVisibility; 223 private final Property<View, Float> mProp; 224 225 public SlideAnimatorListener(View view, Property<View, Float> prop, 226 float terminalValue, float endValue, int finalVisibility) { 227 mProp = prop; 228 mView = view; 229 mTerminalValue = terminalValue; 230 mEndValue = endValue; 231 mFinalVisibility = finalVisibility; 232 view.setVisibility(View.VISIBLE); 233 } 234 235 @Override 236 public void onAnimationCancel(Animator animator) { 237 float[] transitionPosition = new float[2]; 238 transitionPosition[0] = mView.getTranslationX(); 239 transitionPosition[1] = mView.getTranslationY(); 240 mView.setTag(R.id.lb_slide_transition_value, transitionPosition); 241 mProp.set(mView, mTerminalValue); 242 mCanceled = true; 243 } 244 245 @Override 246 public void onAnimationEnd(Animator animator) { 247 if (!mCanceled) { 248 mProp.set(mView, mTerminalValue); 249 } 250 mView.setVisibility(mFinalVisibility); 251 } 252 253 @Override 254 public void onAnimationPause(Animator animator) { 255 mPausedValue = mProp.get(mView); 256 mProp.set(mView, mEndValue); 257 mView.setVisibility(mFinalVisibility); 258 } 259 260 @Override 261 public void onAnimationResume(Animator animator) { 262 mProp.set(mView, mPausedValue); 263 mView.setVisibility(View.VISIBLE); 264 } 265 } 266}