• 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.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