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