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