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 */ 50 float getGoneX(ViewGroup sceneRoot, View view); 51 52 /** Returns the translation value for view when it goes out of the scene */ 53 float getGoneY(ViewGroup sceneRoot, View view); 54 } 55 56 private static abstract class CalculateSlideHorizontal implements CalculateSlide { 57 58 @Override 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 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 sCalculateTop = new CalculateSlideVertical() { 80 @Override 81 public float getGoneY(ViewGroup sceneRoot, View view) { 82 return view.getTranslationY() - sceneRoot.getHeight(); 83 } 84 }; 85 86 private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { 87 @Override 88 public float getGoneX(ViewGroup sceneRoot, View view) { 89 return view.getTranslationX() + sceneRoot.getWidth(); 90 } 91 }; 92 93 private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { 94 @Override 95 public float getGoneY(ViewGroup sceneRoot, View view) { 96 return view.getTranslationY() + sceneRoot.getHeight(); 97 } 98 }; 99 100 /** 101 * Constructor using the default {@link Gravity#BOTTOM} 102 * slide edge direction. 103 */ 104 public Slide() { 105 setSlideEdge(Gravity.BOTTOM); 106 } 107 108 /** 109 * Constructor using the provided slide edge direction. 110 */ 111 public Slide(int slideEdge) { 112 setSlideEdge(slideEdge); 113 } 114 115 public Slide(Context context, AttributeSet attrs) { 116 super(context, attrs); 117 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slide); 118 int edge = a.getInt(R.styleable.Slide_slideEdge, Gravity.BOTTOM); 119 a.recycle(); 120 setSlideEdge(edge); 121 } 122 123 private void captureValues(TransitionValues transitionValues) { 124 View view = transitionValues.view; 125 int[] position = new int[2]; 126 view.getLocationOnScreen(position); 127 transitionValues.values.put(PROPNAME_SCREEN_POSITION, position); 128 } 129 130 @Override 131 public void captureStartValues(TransitionValues transitionValues) { 132 super.captureStartValues(transitionValues); 133 captureValues(transitionValues); 134 } 135 136 @Override 137 public void captureEndValues(TransitionValues transitionValues) { 138 super.captureEndValues(transitionValues); 139 captureValues(transitionValues); 140 } 141 142 /** 143 * Change the edge that Views appear and disappear from. 144 * 145 * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of 146 * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP}, 147 * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}. 148 * @attr ref android.R.styleable#Slide_slideEdge 149 */ 150 public void setSlideEdge(int slideEdge) { 151 switch (slideEdge) { 152 case Gravity.LEFT: 153 mSlideCalculator = sCalculateLeft; 154 break; 155 case Gravity.TOP: 156 mSlideCalculator = sCalculateTop; 157 break; 158 case Gravity.RIGHT: 159 mSlideCalculator = sCalculateRight; 160 break; 161 case Gravity.BOTTOM: 162 mSlideCalculator = sCalculateBottom; 163 break; 164 default: 165 throw new IllegalArgumentException("Invalid slide direction"); 166 } 167 mSlideEdge = slideEdge; 168 SidePropagation propagation = new SidePropagation(); 169 propagation.setSide(slideEdge); 170 setPropagation(propagation); 171 } 172 173 /** 174 * Returns the edge that Views appear and disappear from. 175 * 176 * @return the edge of the scene to use for Views appearing and disappearing. One of 177 * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP}, 178 * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}. 179 * @attr ref android.R.styleable#Slide_slideEdge 180 */ 181 public int getSlideEdge() { 182 return mSlideEdge; 183 } 184 185 @Override 186 public Animator onAppear(ViewGroup sceneRoot, View view, 187 TransitionValues startValues, TransitionValues endValues) { 188 if (endValues == null) { 189 return null; 190 } 191 int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); 192 float endX = view.getTranslationX(); 193 float endY = view.getTranslationY(); 194 float startX = mSlideCalculator.getGoneX(sceneRoot, view); 195 float startY = mSlideCalculator.getGoneY(sceneRoot, view); 196 return TranslationAnimationCreator 197 .createAnimation(view, endValues, position[0], position[1], 198 startX, startY, endX, endY, sDecelerate); 199 } 200 201 @Override 202 public Animator onDisappear(ViewGroup sceneRoot, View view, 203 TransitionValues startValues, TransitionValues endValues) { 204 if (startValues == null) { 205 return null; 206 } 207 int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); 208 float startX = view.getTranslationX(); 209 float startY = view.getTranslationY(); 210 float endX = mSlideCalculator.getGoneX(sceneRoot, view); 211 float endY = mSlideCalculator.getGoneY(sceneRoot, view); 212 return TranslationAnimationCreator 213 .createAnimation(view, startValues, position[0], position[1], 214 startX, startY, endX, endY, sAccelerate); 215 } 216 } 217