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