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 */ 16 package android.transition; 17 18 import android.animation.Animator; 19 import android.animation.TimeInterpolator; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.util.AttributeSet; 23 import android.view.Gravity; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.view.animation.AccelerateInterpolator; 27 import android.view.animation.DecelerateInterpolator; 28 import com.android.internal.R; 29 30 /** 31 * This transition tracks changes to the visibility of target views in the 32 * start and end scenes and moves views in or out from one of the edges of the 33 * scene. Visibility is determined by both the 34 * {@link View#setVisibility(int)} state of the view as well as whether it 35 * is parented in the current view hierarchy. Disappearing Views are 36 * limited as described in {@link Visibility#onDisappear(android.view.ViewGroup, 37 * TransitionValues, int, TransitionValues, int)}. 38 */ 39 public class Slide extends Visibility { 40 private static final String TAG = "Slide"; 41 private static final TimeInterpolator sDecelerate = new DecelerateInterpolator(); 42 private static final TimeInterpolator sAccelerate = new AccelerateInterpolator(); 43 private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition"; 44 private CalculateSlide mSlideCalculator = sCalculateBottom; 45 private int mSlideEdge = Gravity.BOTTOM; 46 47 private interface CalculateSlide { 48 49 /** Returns the translation value for view when it goes out of the scene */ getGoneX(ViewGroup sceneRoot, View view)50 float getGoneX(ViewGroup sceneRoot, View view); 51 52 /** Returns the translation value for view when it goes out of the scene */ getGoneY(ViewGroup sceneRoot, View view)53 float getGoneY(ViewGroup sceneRoot, View view); 54 } 55 56 private static abstract class CalculateSlideHorizontal implements CalculateSlide { 57 58 @Override getGoneY(ViewGroup sceneRoot, View view)59 public float getGoneY(ViewGroup sceneRoot, View view) { 60 return view.getTranslationY(); 61 } 62 } 63 64 private static abstract class CalculateSlideVertical implements CalculateSlide { 65 66 @Override getGoneX(ViewGroup sceneRoot, View view)67 public float getGoneX(ViewGroup sceneRoot, View view) { 68 return view.getTranslationX(); 69 } 70 } 71 72 private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { 73 @Override 74 public float getGoneX(ViewGroup sceneRoot, View view) { 75 return view.getTranslationX() - sceneRoot.getWidth(); 76 } 77 }; 78 79 private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() { 80 @Override 81 public float getGoneX(ViewGroup sceneRoot, View view) { 82 final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 83 final float x; 84 if (isRtl) { 85 x = view.getTranslationX() + sceneRoot.getWidth(); 86 } else { 87 x = view.getTranslationX() - sceneRoot.getWidth(); 88 } 89 return x; 90 } 91 }; 92 93 private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { 94 @Override 95 public float getGoneY(ViewGroup sceneRoot, View view) { 96 return view.getTranslationY() - sceneRoot.getHeight(); 97 } 98 }; 99 100 private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { 101 @Override 102 public float getGoneX(ViewGroup sceneRoot, View view) { 103 return view.getTranslationX() + sceneRoot.getWidth(); 104 } 105 }; 106 107 private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() { 108 @Override 109 public float getGoneX(ViewGroup sceneRoot, View view) { 110 final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; 111 final float x; 112 if (isRtl) { 113 x = view.getTranslationX() - sceneRoot.getWidth(); 114 } else { 115 x = view.getTranslationX() + sceneRoot.getWidth(); 116 } 117 return x; 118 } 119 }; 120 121 private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { 122 @Override 123 public float getGoneY(ViewGroup sceneRoot, View view) { 124 return view.getTranslationY() + sceneRoot.getHeight(); 125 } 126 }; 127 128 /** 129 * Constructor using the default {@link Gravity#BOTTOM} 130 * slide edge direction. 131 */ Slide()132 public Slide() { 133 setSlideEdge(Gravity.BOTTOM); 134 } 135 136 /** 137 * Constructor using the provided slide edge direction. 138 */ Slide(int slideEdge)139 public Slide(int slideEdge) { 140 setSlideEdge(slideEdge); 141 } 142 Slide(Context context, AttributeSet attrs)143 public Slide(Context context, AttributeSet attrs) { 144 super(context, attrs); 145 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slide); 146 int edge = a.getInt(R.styleable.Slide_slideEdge, Gravity.BOTTOM); 147 a.recycle(); 148 setSlideEdge(edge); 149 } 150 captureValues(TransitionValues transitionValues)151 private void captureValues(TransitionValues transitionValues) { 152 View view = transitionValues.view; 153 int[] position = new int[2]; 154 view.getLocationOnScreen(position); 155 transitionValues.values.put(PROPNAME_SCREEN_POSITION, position); 156 } 157 158 @Override captureStartValues(TransitionValues transitionValues)159 public void captureStartValues(TransitionValues transitionValues) { 160 super.captureStartValues(transitionValues); 161 captureValues(transitionValues); 162 } 163 164 @Override captureEndValues(TransitionValues transitionValues)165 public void captureEndValues(TransitionValues transitionValues) { 166 super.captureEndValues(transitionValues); 167 captureValues(transitionValues); 168 } 169 170 /** 171 * Change the edge that Views appear and disappear from. 172 * 173 * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of 174 * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP}, 175 * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}, 176 * {@link android.view.Gravity#START}, {@link android.view.Gravity#END}. 177 * @attr ref android.R.styleable#Slide_slideEdge 178 */ setSlideEdge(int slideEdge)179 public void setSlideEdge(int slideEdge) { 180 switch (slideEdge) { 181 case Gravity.LEFT: 182 mSlideCalculator = sCalculateLeft; 183 break; 184 case Gravity.TOP: 185 mSlideCalculator = sCalculateTop; 186 break; 187 case Gravity.RIGHT: 188 mSlideCalculator = sCalculateRight; 189 break; 190 case Gravity.BOTTOM: 191 mSlideCalculator = sCalculateBottom; 192 break; 193 case Gravity.START: 194 mSlideCalculator = sCalculateStart; 195 break; 196 case Gravity.END: 197 mSlideCalculator = sCalculateEnd; 198 break; 199 default: 200 throw new IllegalArgumentException("Invalid slide direction"); 201 } 202 mSlideEdge = slideEdge; 203 SidePropagation propagation = new SidePropagation(); 204 propagation.setSide(slideEdge); 205 setPropagation(propagation); 206 } 207 208 /** 209 * Returns the edge that Views appear and disappear from. 210 * 211 * @return the edge of the scene to use for Views appearing and disappearing. One of 212 * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP}, 213 * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}, 214 * {@link android.view.Gravity#START}, {@link android.view.Gravity#END}. 215 * @attr ref android.R.styleable#Slide_slideEdge 216 */ getSlideEdge()217 public int getSlideEdge() { 218 return mSlideEdge; 219 } 220 221 @Override onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)222 public Animator onAppear(ViewGroup sceneRoot, View view, 223 TransitionValues startValues, TransitionValues endValues) { 224 if (endValues == null) { 225 return null; 226 } 227 int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); 228 float endX = view.getTranslationX(); 229 float endY = view.getTranslationY(); 230 float startX = mSlideCalculator.getGoneX(sceneRoot, view); 231 float startY = mSlideCalculator.getGoneY(sceneRoot, view); 232 return TranslationAnimationCreator 233 .createAnimation(view, endValues, position[0], position[1], 234 startX, startY, endX, endY, sDecelerate); 235 } 236 237 @Override onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)238 public Animator onDisappear(ViewGroup sceneRoot, View view, 239 TransitionValues startValues, TransitionValues endValues) { 240 if (startValues == null) { 241 return null; 242 } 243 int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); 244 float startX = view.getTranslationX(); 245 float startY = view.getTranslationY(); 246 float endX = mSlideCalculator.getGoneX(sceneRoot, view); 247 float endY = mSlideCalculator.getGoneY(sceneRoot, view); 248 return TranslationAnimationCreator 249 .createAnimation(view, startValues, position[0], position[1], 250 startX, startY, endX, endY, sAccelerate); 251 } 252 } 253